commit a6bd00925cdaa4238a6cfa09f43c542d72ef63ed Author: npmrun <1549469775@qq.com> Date: Wed Sep 15 22:30:25 2021 +0800 init diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b82fe55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +unpackage \ No newline at end of file diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json new file mode 100644 index 0000000..07c1d5f --- /dev/null +++ b/.hbuilderx/launch.json @@ -0,0 +1,16 @@ +{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ + // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 + "version": "0.0", + "configurations": [{ + "default" : + { + "launchtype" : "local" + }, + "h5" : + { + "launchtype" : "local" + }, + "type" : "uniCloud" + } + ] +} diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..8c2b732 --- /dev/null +++ b/App.vue @@ -0,0 +1,17 @@ + + + diff --git a/components/HQChartControl/HQChartControl.vue b/components/HQChartControl/HQChartControl.vue new file mode 100644 index 0000000..a5ed1cc --- /dev/null +++ b/components/HQChartControl/HQChartControl.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..b61f63e --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + +
+ + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..ffd2f81 --- /dev/null +++ b/main.js @@ -0,0 +1,10 @@ +import App from './App' + + +import Vue from 'vue' +Vue.config.productionTip = false +App.mpType = 'app' +const app = new Vue({ + ...App +}) +app.$mount() diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..74ebdee --- /dev/null +++ b/manifest.json @@ -0,0 +1,72 @@ +{ + "name" : "k_graph", + "appid" : "", + "description" : "", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + /* ios打包配置 */ + "ios" : {}, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "", + "setting" : { + "urlCheck" : false + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "2" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cc01a18 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3286 @@ +{ + "name": "k_graph", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.nlark.com/acorn/download/acorn-5.7.4.tgz", + "integrity": "sha1-Po2KmUfQWZoXltECJddDL0pKz14=" + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.nlark.com/acorn-dynamic-import/download/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "requires": { + "acorn": "^4.0.3" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.nlark.com/acorn/download/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.nlark.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1631470912358&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz", + "integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz?cache=0&sync_timestamp=1616882441894&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz", + "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.nlark.com/ansi-regex/download/ansi-regex-2.1.1.tgz?cache=0&sync_timestamp=1631634988487&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fansi-regex%2Fdownload%2Fansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.2.tgz", + "integrity": "sha1-wFV8CWrzLxBhmPT04qODU343hxY=", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.nlark.com/arr-diff/download/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "optional": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/arr-flatten/download/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "optional": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/arr-union/download/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "optional": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "optional": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npm.taobao.org/asn1.js/download/asn1.js-5.4.1.tgz", + "integrity": "sha1-EamAuE67kXgc41sP3C7ilON4Pwc=", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.nlark.com/assert/download/assert-1.5.0.tgz?cache=0&sync_timestamp=1618847153747&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fassert%2Fdownload%2Fassert-1.5.0.tgz", + "integrity": "sha1-VcEJqvbgrv2z3EtxJAxwv1dLGOs=", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.nlark.com/util/download/util-0.10.3.tgz?cache=0&sync_timestamp=1622212984161&other_urls=https%3A%2F%2Fregistry.nlark.com%2Futil%2Fdownload%2Futil-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/assign-symbols/download/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "optional": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.nlark.com/async/download/async-2.6.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync%2Fdownload%2Fasync-2.6.3.tgz", + "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/async-each/download/async-each-1.0.3.tgz", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "optional": true + }, + "async-validator": { + "version": "1.8.5", + "resolved": "https://registry.nlark.com/async-validator/download/async-validator-1.8.5.tgz?cache=0&sync_timestamp=1630393256517&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync-validator%2Fdownload%2Fasync-validator-1.8.5.tgz", + "integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=", + "requires": { + "babel-runtime": "6.x" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "optional": true + }, + "axios": { + "version": "0.16.2", + "resolved": "https://registry.nlark.com/axios/download/axios-0.16.2.tgz?cache=0&sync_timestamp=1630942582656&other_urls=https%3A%2F%2Fregistry.nlark.com%2Faxios%2Fdownload%2Faxios-0.16.2.tgz", + "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=", + "requires": { + "follow-redirects": "^1.2.3", + "is-buffer": "^1.1.5" + } + }, + "babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY=" + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-runtime%2Fdownload%2Fbabel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npm.taobao.org/base/download/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "optional": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npm.taobao.org/base64-js/download/base64-js-1.5.1.tgz?cache=0&sync_timestamp=1605123440207&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbase64-js%2Fdownload%2Fbase64-js-1.5.1.tgz", + "integrity": "sha1-GxtEAWClv3rUC2UPCVljSBkDkwo=" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.nlark.com/big.js/download/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.2.0.tgz?cache=0&sync_timestamp=1610299308660&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.2.0.tgz", + "integrity": "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=", + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.nlark.com/bindings/download/bindings-1.5.0.tgz", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-5.2.0.tgz", + "integrity": "sha1-NYhgZ0OWxpl3canQUfzBtX1K4AI=" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/brorand/download/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/browserify-aes/download/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/browserify-cipher/download/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/browserify-des/download/browserify-des-1.0.2.tgz", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/browserify-rsa/download/browserify-rsa-4.1.0.tgz", + "integrity": "sha1-sv0Gtbda4pf3zi3GUfkY9b4VjI0=", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.nlark.com/browserify-sign/download/browserify-sign-4.2.1.tgz", + "integrity": "sha1-6vSt1G3VS+O7OzbAzxWrvrp5VsM=", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=" + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/browserify-zlib/download/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.2.tgz?cache=0&sync_timestamp=1606098159535&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-4.9.2.tgz", + "integrity": "sha1-Iw6tNEACmIZEhBqwJEr4xEu+Pvg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/buffer-xor/download/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/cache-base/download/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "optional": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/call-bind/download/call-bind-1.0.2.tgz", + "integrity": "sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz?cache=0&sync_timestamp=1603923709404&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.nlark.com/center-align/download/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.nlark.com/chokidar/download/chokidar-3.5.2.tgz", + "integrity": "sha1-26OXb8rbAW9m/TZQIdkWANAcHnU=", + "optional": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/cipher-base/download/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.nlark.com/class-utils/download/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/cliui/download/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "optional": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.nlark.com/component-emitter/download/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "optional": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/console-browserify/download/console-browserify-1.2.0.tgz", + "integrity": "sha1-ZwY871fOts9Jk6KrOlWECujEkzY=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/constants-browserify/download/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/copy-descriptor/download/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "optional": true + }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.nlark.com/core-js/download/core-js-2.6.12.tgz", + "integrity": "sha1-2TM9+nsGXjR8xWgiGdb2kIWcwuw=" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.nlark.com/core-util-is/download/core-util-is-1.0.3.tgz", + "integrity": "sha1-pgQtNjTCsn6TKPg3uWX6yDgI24U=" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.nlark.com/create-ecdh/download/create-ecdh-4.0.4.tgz", + "integrity": "sha1-1uf0v/pmc2CFoHYv06YyaE2rzE4=", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/create-hash/download/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npm.taobao.org/create-hmac/download/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.nlark.com/cross-spawn/download/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npm.taobao.org/crypto-browserify/download/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/d/download/d-1.0.1.tgz", + "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.nlark.com/debug/download/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.nlark.com/decode-uri-component/download/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "optional": true + }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz", + "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M=" + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + } + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/des.js/download/des.js-1.0.1.tgz", + "integrity": "sha1-U4IULhvcU/hdhtU+X0qn3rkeCEM=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npm.taobao.org/diffie-hellman/download/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.nlark.com/domain-browser/download/domain-browser-1.2.0.tgz?cache=0&sync_timestamp=1627591557212&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fdomain-browser%2Fdownload%2Fdomain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=" + }, + "echarts": { + "version": "4.9.0", + "resolved": "https://registry.nlark.com/echarts/download/echarts-4.9.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fecharts%2Fdownload%2Fecharts-4.9.0.tgz", + "integrity": "sha1-qbm6oD8Doqcx5jQMVb77V6nhNH0=", + "requires": { + "zrender": "4.3.2" + } + }, + "element-ui": { + "version": "1.4.13", + "resolved": "https://registry.nlark.com/element-ui/download/element-ui-1.4.13.tgz", + "integrity": "sha1-0LBVmHrwy5N6ETW6v0NgroDUoSE=", + "requires": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "throttle-debounce": "^1.0.1" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.4.tgz", + "integrity": "sha1-2jfOvTHnmhNn6UG1ku0fvr1Yq7s=", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=" + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.nlark.com/enhanced-resolve/download/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npm.taobao.org/errno/download/errno-0.1.8.tgz", + "integrity": "sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8=", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/error-ex/download/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.nlark.com/es5-ext/download/es5-ext-0.10.53.tgz", + "integrity": "sha1-k8WjrP2+8nUiCtcmRK0C7hg2jeE=", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/es6-iterator/download/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/es6-map/download/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/es6-set/download/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/es6-symbol/download/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/es6-symbol/download/es6-symbol-3.1.3.tgz", + "integrity": "sha1-utXTwbzawoJp9MszHkMceKxwXRg=", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.nlark.com/es6-weak-map/download/es6-weak-map-2.0.3.tgz", + "integrity": "sha1-ttofFswswNm+Q+a9v8Xn383zHVM=", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/escope/download/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.nlark.com/esrecurse/download/esrecurse-4.3.0.tgz", + "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.nlark.com/estraverse/download/estraverse-5.2.0.tgz?cache=0&sync_timestamp=1622604531280&other_urls=https%3A%2F%2Fregistry.nlark.com%2Festraverse%2Fdownload%2Festraverse-5.2.0.tgz", + "integrity": "sha1-MH30JUfmzHMk088DwVXVzbjFOIA=" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.nlark.com/estraverse/download/estraverse-4.3.0.tgz?cache=0&sync_timestamp=1622604531280&other_urls=https%3A%2F%2Fregistry.nlark.com%2Festraverse%2Fdownload%2Festraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npm.taobao.org/event-emitter/download/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npm.taobao.org/events/download/events-3.3.0.tgz", + "integrity": "sha1-Mala0Kkk4tLEGagTrrLE6HjqdAA=" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.nlark.com/evp_bytestokey/download/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.nlark.com/execa/download/execa-0.7.0.tgz?cache=0&sync_timestamp=1622825396605&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fexeca%2Fdownload%2Fexeca-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npm.taobao.org/expand-brackets/download/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "ext": { + "version": "1.5.0", + "resolved": "https://registry.nlark.com/ext/download/ext-1.5.0.tgz", + "integrity": "sha1-6TuXrgyyP4NwOA9hB9LSt4h2h60=", + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npm.taobao.org/type/download/type-2.5.0.tgz", + "integrity": "sha1-Ci54wud5B7JSq+XymMGwHGPw2z0=" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "optional": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/extglob/download/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz?cache=0&sync_timestamp=1624543041215&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ffast-json-stable-stringify%2Fdownload%2Ffast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", + "optional": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.nlark.com/fill-range/download/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/find-up/download/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "follow-redirects": { + "version": "1.14.4", + "resolved": "https://registry.nlark.com/follow-redirects/download/follow-redirects-1.14.4.tgz", + "integrity": "sha1-g4/fSKi73XnlLuUfsclOPtmLk3k=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "optional": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.nlark.com/fragment-cache/download/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "optional": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536512306&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz", + "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/get-caller-file/download/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/get-intrinsic/download/get-intrinsic-1.1.1.tgz", + "integrity": "sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/get-stream/download/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/get-value/download/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.nlark.com/glob-parent/download/glob-parent-5.1.2.tgz?cache=0&sync_timestamp=1626760165717&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-5.1.2.tgz", + "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.nlark.com/graceful-fs/download/graceful-fs-4.2.8.tgz?cache=0&sync_timestamp=1628194078324&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.8.tgz", + "integrity": "sha1-5BK40z9eAGWTy9PO5t+fLOu+gCo=" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.nlark.com/has/download/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/has-flag/download/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.2.tgz", + "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/has-value/download/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "optional": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/has-values/download/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/hash-base/download/hash-base-3.1.0.tgz", + "integrity": "sha1-VcOB2eBuHSmXqIO0o/3f5/DTrzM=", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=" + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npm.taobao.org/hash.js/download/hash.js-1.1.7.tgz", + "integrity": "sha1-C6vKU46NTuSg+JiNaIZlN6ADz0I=", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/hmac-drbg/download/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.9.tgz?cache=0&sync_timestamp=1617826545071&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhosted-git-info%2Fdownload%2Fhosted-git-info-2.8.9.tgz", + "integrity": "sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k=" + }, + "hqchart": { + "version": "1.1.10351", + "resolved": "https://registry.nlark.com/hqchart/download/hqchart-1.1.10351.tgz", + "integrity": "sha1-L6n7krWJYK3q7yqD8wP9dNld+5g=", + "requires": { + "axios": "^0.16.2", + "echarts": "^4.8.0", + "element-ui": "^1.4.13", + "promise-polyfill": "^6.0.2", + "qs": "^6.5.0", + "vue": "^2.6.9", + "vue-plugin-timers": "^0.1.6", + "vue-router": "^2.8.1", + "webpack": "^3.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/https-browserify/download/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/ieee754/download/ieee754-1.2.1.tgz?cache=0&sync_timestamp=1603838208740&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fieee754%2Fdownload%2Fieee754-1.2.1.tgz", + "integrity": "sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/interpret/download/interpret-1.4.0.tgz", + "integrity": "sha1-Zlq4vE2iendKQFhOgS4+D6RbGh4=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/invert-kv/download/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/is-binary-path/download/is-binary-path-2.1.0.tgz", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.nlark.com/is-buffer/download/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=" + }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.nlark.com/is-core-module/download/is-core-module-2.6.0.tgz?cache=0&sync_timestamp=1629224656971&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fis-core-module%2Fdownload%2Fis-core-module-2.6.0.tgz", + "integrity": "sha1-11U7JSb+Wbkro+QMjfdX7Ipwnhk=", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "optional": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "optional": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz", + "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.nlark.com/is-number/download/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "optional": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/is-plain-object/download/is-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.nlark.com/is-stream/download/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.nlark.com/is-windows/download/is-windows-1.0.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fis-windows%2Fdownload%2Fis-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/isexe/download/isexe-2.0.0.tgz?cache=0&sync_timestamp=1618847054312&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fisexe%2Fdownload%2Fisexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "optional": true + }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/jquery/download/jquery-3.6.0.tgz", + "integrity": "sha1-xyoJ8Vwb3OFC9J2/EXC9+K2sJHA=" + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/json-loader/download/json-loader-0.5.7.tgz", + "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1607999852153&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/load-json-file/download/load-json-file-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fload-json-file%2Fdownload%2Fload-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/loader-runner/download/loader-runner-2.4.0.tgz?cache=0&sync_timestamp=1610027918622&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-runner%2Fdownload%2Floader-runner-2.4.0.tgz", + "integrity": "sha1-7UcGa/5TTX6ExMe5mYwqdWB9k1c=" + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.nlark.com/loader-utils/download/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-1.0.1.tgz", + "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/locate-path/download/locate-path-2.0.0.tgz?cache=0&sync_timestamp=1629895618224&other_urls=https%3A%2F%2Fregistry.nlark.com%2Flocate-path%2Fdownload%2Flocate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.nlark.com/lodash/download/lodash-4.17.21.tgz", + "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.nlark.com/lru-cache/download/lru-cache-4.1.5.tgz", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/map-cache/download/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "optional": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/map-visit/download/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "optional": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.nlark.com/mem/download/mem-1.1.0.tgz?cache=0&sync_timestamp=1626534352883&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fmem%2Fdownload%2Fmem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.4.1.tgz?cache=0&sync_timestamp=1599056143395&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmemory-fs%2Fdownload%2Fmemory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.nlark.com/micromatch/download/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.nlark.com/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/miller-rabin/download/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/mimic-fn/download/mimic-fn-1.2.0.tgz", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/minimalistic-assert/download/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/minimalistic-crypto-utils/download/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.nlark.com/minimist/download/minimist-1.2.5.tgz?cache=0&sync_timestamp=1618846813226&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz", + "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=" + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "optional": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.nlark.com/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1624625160603&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1629910562433&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "optional": true + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.nlark.com/nan/download/nan-2.15.0.tgz?cache=0&sync_timestamp=1628093656744&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fnan%2Fdownload%2Fnan-2.15.0.tgz", + "integrity": "sha1-PzSkc/8Y4VwbVia2KQO1rW5mX+4=", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/nanomatch/download/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + } + } + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.nlark.com/neo-async/download/neo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/next-tick/download/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/node-libs-browser/download/node-libs-browser-2.2.1.tgz", + "integrity": "sha1-tk9RPRgzhiX5A0bSew0jXmMfZCU=", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.nlark.com/normalize-package-data/download/normalize-package-data-2.5.0.tgz?cache=0&sync_timestamp=1629301911873&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fnormalize-package-data%2Fdownload%2Fnormalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "optional": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz?cache=0&sync_timestamp=1617776101309&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnumber-is-nan%2Fdownload%2Fnumber-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "optional": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.nlark.com/object-inspect/download/object-inspect-1.11.0.tgz?cache=0&sync_timestamp=1626120241132&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fobject-inspect%2Fdownload%2Fobject-inspect-1.11.0.tgz", + "integrity": "sha1-nc6xRs7dQUig2eUauI00z1CZIrE=" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/object-visit/download/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "optional": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/object.pick/download/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/os-browserify/download/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/os-locale/download/os-locale-2.1.0.tgz?cache=0&sync_timestamp=1627564416279&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fos-locale%2Fdownload%2Fos-locale-2.1.0.tgz", + "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-finally%2Fdownload%2Fp-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.nlark.com/p-limit/download/p-limit-1.3.0.tgz", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/p-locate/download/p-locate-2.0.0.tgz?cache=0&sync_timestamp=1629892721671&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fp-locate%2Fdownload%2Fp-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/p-try/download/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.nlark.com/pako/download/pako-1.0.11.tgz?cache=0&sync_timestamp=1627560125189&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpako%2Fdownload%2Fpako-1.0.11.tgz", + "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=" + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npm.taobao.org/parse-asn1/download/parse-asn1-5.1.6.tgz", + "integrity": "sha1-OFCAo+wTy2KmLTlAnLPoiETNrtQ=", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1610966709037&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/pascalcase/download/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "optional": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npm.taobao.org/path-browserify/download/path-browserify-0.0.1.tgz", + "integrity": "sha1-5sTd1+06onxoogzE5Q4aTug7vEo=" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/path-dirname/download/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/path-exists/download/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-2.0.1.tgz?cache=0&sync_timestamp=1617971695678&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-key%2Fdownload%2Fpath-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.nlark.com/path-parse/download/path-parse-1.0.7.tgz", + "integrity": "sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-2.0.0.tgz?cache=0&sync_timestamp=1611752058913&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-type%2Fdownload%2Fpath-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npm.taobao.org/pbkdf2/download/pbkdf2-3.1.2.tgz?cache=0&sync_timestamp=1617975984684&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpbkdf2%2Fdownload%2Fpbkdf2-3.1.2.tgz", + "integrity": "sha1-3YIqoIh1gOUvGgOdw+2hCO+uMHU=", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz?cache=0&sync_timestamp=1621648246651&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpicomatch%2Fdownload%2Fpicomatch-2.3.0.tgz", + "integrity": "sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=", + "optional": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/posix-character-classes/download/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "optional": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npm.taobao.org/process/download/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" + }, + "promise-polyfill": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/promise-polyfill/download/promise-polyfill-6.1.0.tgz", + "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.nlark.com/pseudomap/download/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/public-encrypt/download/public-encrypt-4.0.3.tgz", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.12.0.tgz", + "integrity": "sha1-d1s/J477uXGO7HNh9IP7Nvu/6og=" + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.nlark.com/qs/download/qs-6.10.1.tgz", + "integrity": "sha1-STFIL6jWR6Wqt5nFJx0hM7mB+2o=", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.nlark.com/querystring/download/querystring-0.2.0.tgz?cache=0&sync_timestamp=1626179435543&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fquerystring%2Fdownload%2Fquerystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/querystring-es3/download/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/randomfill/download/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/read-pkg/download/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.6.0.tgz?cache=0&sync_timestamp=1615717369278&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-3.6.0.tgz", + "integrity": "sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.nlark.com/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz?cache=0&sync_timestamp=1626993001371&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.11.1.tgz", + "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/regex-not/download/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "optional": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "optional": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.nlark.com/repeat-element/download/repeat-element-1.1.4.tgz", + "integrity": "sha1-vmgVIIR6tYx1aKx1+/rSjtQtOek=", + "optional": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.nlark.com/require-directory/download/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.nlark.com/require-main-filename/download/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.nlark.com/resolve/download/resolve-1.20.0.tgz", + "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "optional": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npm.taobao.org/ret/download/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "optional": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.nlark.com/right-align/download/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/ripemd160/download/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/safe-regex/download/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "optional": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.nlark.com/set-value/download/set-value-2.0.1.tgz", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.nlark.com/setimmediate/download/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/shebang-regex/download/shebang-regex-1.0.0.tgz?cache=0&sync_timestamp=1628896299850&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fshebang-regex%2Fdownload%2Fshebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.nlark.com/side-channel/download/side-channel-1.0.4.tgz", + "integrity": "sha1-785cj9wQTudRslxY1CkAEfpeos8=", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.nlark.com/signal-exit/download/signal-exit-3.0.3.tgz", + "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npm.taobao.org/snapdragon/download/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "optional": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/snapdragon-node/download/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "optional": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "optional": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/snapdragon-util/download/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "optional": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/source-list-map/download/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "optional": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.1.tgz", + "integrity": "sha1-CvZmBadFpaL5HPG7+KevvCg97FY=", + "optional": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/spdx-correct/download/spdx-correct-3.1.1.tgz", + "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/spdx-exceptions/download/spdx-exceptions-2.3.0.tgz", + "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.nlark.com/spdx-expression-parse/download/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.nlark.com/spdx-license-ids/download/spdx-license-ids-3.0.10.tgz", + "integrity": "sha1-DZvszN5wA9bGWNSH3UijLwvzAUs=" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/split-string/download/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "optional": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/static-extend/download/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "optional": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/stream-browserify/download/stream-browserify-2.0.2.tgz", + "integrity": "sha1-h1IdOKRKp+6RzhzSpH3wy0ndZgs=", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npm.taobao.org/stream-http/download/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.nlark.com/string-width/download/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/ansi-regex/download/ansi-regex-3.0.0.tgz?cache=0&sync_timestamp=1631634988487&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fansi-regex%2Fdownload%2Fansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.nlark.com/strip-ansi/download/strip-ansi-4.0.0.tgz?cache=0&sync_timestamp=1631350330859&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.nlark.com/string_decoder/download/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.nlark.com/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1631350330859&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/strip-bom/download/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/strip-eof/download/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.nlark.com/supports-color/download/supports-color-4.5.0.tgz?cache=0&sync_timestamp=1626703414084&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsupports-color%2Fdownload%2Fsupports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "^2.0.0" + } + }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.nlark.com/tapable/download/tapable-0.2.9.tgz?cache=0&sync_timestamp=1631526982870&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftapable%2Fdownload%2Ftapable-0.2.9.tgz", + "integrity": "sha1-ry2LvJsE907hevK02QSPgHrNGKg=" + }, + "throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.nlark.com/throttle-debounce/download/throttle-debounce-1.1.0.tgz", + "integrity": "sha1-UYU9o3vmihVctugns1FKPEIuic0=" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npm.taobao.org/timers-browserify/download/timers-browserify-2.0.12.tgz?cache=0&sync_timestamp=1603793741116&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftimers-browserify%2Fdownload%2Ftimers-browserify-2.0.12.tgz", + "integrity": "sha1-RKRcEfv0B/NPl7zNFXfGUjYbAO4=", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/to-object-path/download/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.nlark.com/to-regex/download/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "optional": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npm.taobao.org/tty-browserify/download/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/type/download/type-1.2.0.tgz", + "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.nlark.com/uglify-js/download/uglify-js-2.8.29.tgz?cache=0&sync_timestamp=1631026519465&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fuglify-js%2Fdownload%2Fuglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.nlark.com/yargs/download/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.nlark.com/uglifyjs-webpack-plugin/download/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/union-value/download/union-value-1.0.1.tgz", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.nlark.com/unset-value/download/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "optional": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.nlark.com/has-value/download/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "optional": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "optional": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.nlark.com/has-values/download/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "optional": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/upath/download/upath-1.2.0.tgz", + "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", + "optional": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.nlark.com/uri-js/download/uri-js-4.4.1.tgz", + "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "optional": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.nlark.com/url/download/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/use/download/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "optional": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.nlark.com/util/download/util-0.11.1.tgz?cache=0&sync_timestamp=1622212984161&other_urls=https%3A%2F%2Fregistry.nlark.com%2Futil%2Fdownload%2Futil-0.11.1.tgz", + "integrity": "sha1-MjZzNyDsZLsn9uJvQhqqLhtYjWE=", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.nlark.com/vm-browserify/download/vm-browserify-1.1.2.tgz?cache=0&sync_timestamp=1624521940875&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvm-browserify%2Fdownload%2Fvm-browserify-1.1.2.tgz", + "integrity": "sha1-eGQcSIuObKkadfUR56OzKobl3aA=" + }, + "vue": { + "version": "2.6.14", + "resolved": "https://registry.nlark.com/vue/download/vue-2.6.14.tgz?cache=0&sync_timestamp=1631141991995&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue%2Fdownload%2Fvue-2.6.14.tgz", + "integrity": "sha1-5RqlJQJQ1Wmj+606ilpofWA24jU=" + }, + "vue-plugin-timers": { + "version": "0.1.6", + "resolved": "https://registry.nlark.com/vue-plugin-timers/download/vue-plugin-timers-0.1.6.tgz", + "integrity": "sha1-6INTdzOxzUmgsadXoLJXtM2H51s=" + }, + "vue-router": { + "version": "2.8.1", + "resolved": "https://registry.nlark.com/vue-router/download/vue-router-2.8.1.tgz?cache=0&sync_timestamp=1628495505697&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue-router%2Fdownload%2Fvue-router-2.8.1.tgz", + "integrity": "sha1-mDPJ7lesg76wJpBW/v7nFxPyBpU=" + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.nlark.com/watchpack/download/watchpack-1.7.5.tgz?cache=0&sync_timestamp=1621437868630&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fwatchpack%2Fdownload%2Fwatchpack-1.7.5.tgz", + "integrity": "sha1-EmfmxV4Lm1vkTCAjrtVDeiwmxFM=", + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/watchpack-chokidar2/download/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha1-OFAAcu5uzmbzdpk2lQ6hdxvhyVc=", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.13.1.tgz?cache=0&sync_timestamp=1610299308660&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.nlark.com/chokidar/download/chokidar-2.1.8.tgz", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.nlark.com/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-1.2.13.tgz?cache=0&sync_timestamp=1612536512306&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-1.2.13.tgz", + "integrity": "sha1-8yXLBFVZJCi88Rs4M3DvcOO/zDg=", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.nlark.com/glob-parent/download/glob-parent-3.1.0.tgz?cache=0&sync_timestamp=1626760165717&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.nlark.com/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.nlark.com/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-2.2.1.tgz?cache=0&sync_timestamp=1615717369278&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webpack": { + "version": "3.12.0", + "resolved": "https://registry.nlark.com/webpack/download/webpack-3.12.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fwebpack%2Fdownload%2Fwebpack-3.12.0.tgz", + "integrity": "sha1-P540NgNwYC/PY56Xk520hvTsDXQ=", + "requires": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.nlark.com/webpack-sources/download/webpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/which/download/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.nlark.com/which-module/download/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.nlark.com/wordwrap/download/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/wrap-ansi/download/wrap-ansi-2.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fwrap-ansi%2Fdownload%2Fwrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.nlark.com/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=" + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.nlark.com/y18n/download/y18n-3.2.2.tgz", + "integrity": "sha1-hckBvWRwznH8S7cjrSCbcPfyhpY=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.nlark.com/yargs/download/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz?cache=0&sync_timestamp=1603923709404&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.nlark.com/cliui/download/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.nlark.com/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + } + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.nlark.com/yargs-parser/download/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz?cache=0&sync_timestamp=1603923709404&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + } + } + }, + "zrender": { + "version": "4.3.2", + "resolved": "https://registry.nlark.com/zrender/download/zrender-4.3.2.tgz?cache=0&sync_timestamp=1630140908990&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fzrender%2Fdownload%2Fzrender-4.3.2.tgz", + "integrity": "sha1-7HQy+UFcgsc1hLa3uMR+GwFiCcY=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9ec433f --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "k_graph", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "hqchart": "^1.1.10351", + "jquery": "^3.6.0" + } +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..1aea851 --- /dev/null +++ b/pages.json @@ -0,0 +1,16 @@ +{ + "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "uni-app" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "uni-app", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + } +} diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..20d308d --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/plugins/graph.js b/plugins/graph.js new file mode 100644 index 0000000..fbf1c8b --- /dev/null +++ b/plugins/graph.js @@ -0,0 +1,15 @@ +// #ifdef H5 +import HQChart from '@/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.h5.js' +//HQChart.JSChart.SetDomain("xxxxx.com"); +//HQChart.JSComplier.SetDomain("xxxx.com"); +// #endif + +// #ifndef H5 +import {JSCommon} from '@/uni_modules/jones-hqchart2/js_sdk/umychart.wechat.3.0.js' +import {JSCommonHQStyle} from '@/uni_modules/jones-hqchart2/js_sdk/umychart.style.wechat.js' +import {JSCommonComplier} from "@/uni_modules/jones-hqchart2/js_sdk/umychart.complier.wechat.js" + +//禁用日志 +JSConsole.Complier.Log=()=>{ }; +JSConsole.Chart.Log=()=>{ }; +// #endif \ No newline at end of file diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000..b5771e2 Binary files /dev/null and b/static/logo.png differ diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..a05adb4 --- /dev/null +++ b/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; diff --git a/uni_modules/jones-hqchart2/changelog.md b/uni_modules/jones-hqchart2/changelog.md new file mode 100644 index 0000000..1cae04d --- /dev/null +++ b/uni_modules/jones-hqchart2/changelog.md @@ -0,0 +1,274 @@ +## 1.1.10333(2021-09-13) +10332 叠加指标支持VERTLINE,HORLINE
+10330 增加叠加指标移动到新窗口,MoveOverlayIndexToNewWindow
+10328 K线图支持 IsFixXLastTime
+10326 APIScriptIndex派生CopyTo虚函数
+10325 增加叠加指标移动
+10324 小程序MULTI_LINE 支持设置线段宽度
+10322 ChartMultiLine 支持设置线段宽度
+## 1.1.10321(2021-09-09) +10320 AddIndexWindow()修正OverlayIndexType参数位置写错了
+10318 单行显示指标标题支持横屏
+10316 1. 修正IChartFramePainting::ClearCoordinateText()没有判断Message字段是否有效
+ 2. 修正HQTradeFrame::Draw()在动画场景时不自动调整左右边框间距
+10315 叠加指标标题支持单行显示
+## 1.1.10309(2021-09-01) +10309 小程序修正分时图ChartStickLine柱子太粗了。 +## 1.1.10306(2021-08-31) +10305 修正uniapp分时图指标工具栏$报错
+10302 修正OnTouchDBClick()手势坐标没有转换到K线图相对坐标
+ +## 1.1.10301 (2021-08-27) +增加用户协议
+10285 修正DrawInsideHorizontal,DrawCustomHorizontal没有处理最小化窗口指标
+10284 小程序增加双击副图缩放指标窗口
+ +## 1.1.10283(2021-08-22) +10282 小程序JSChartContainer::FullDraw()数据加载去掉坐标文字
+10281 显示数据加载时, 不显示刻度文字
+10276 增加手势双击缩放附图指标窗口
+10274 画图工具支持附图窗口最小化
+10273 部分图形画法支持指标窗口隐藏模式
+10271 修正右边自动调整叠加指标宽度是没有动态计算叠加刻度
+10270 增加是否启动双击缩放附图窗口配置
+10269 增加子窗口双击缩放
+ +## 1.1.10265(2021-08-17) +10264 修正KLineChartContainer::Update()没有更新Y轴坐标分割线数据
+10263 DrawCustomItem() 支持多行自定义刻度文字
+## 1.1.10251(2021-08-12) +10250 小程序指标翻译器函数翻译代码整理
+10249 指标解释器函数解释代码整理
+10247 小程序指标翻译器增加部分字符串函数翻译
+10245 指标翻译器增加部分字符串函数翻译
+10241 ON_TITLE_DRAW事件增加叠加股票信息
+## 1.1.10238(2021-08-09) +10237 h5修正左右边框间距自动调整没有考虑筹码图宽度
+10235 h5K线图边框间距自适应支持横屏
+10234 小程序自动调整左右边框距离支持横屏
+10233 小程序K线图支持左右边框间距根据刻度文字自动调整
+10230 修正拖拽指标边框触发区间选择
+10227 h5K线图增加左右两侧边框间距根据刻度自动调整
+## 1.1.110226(2021-08-09) +10225 小程序修正指标翻译是LLVBARS,HHVBARS报错
+10223 K线增加复权因子读取.
+10222 指标翻译器修正HHVBARS, LLVBARS报错
+10221 增加复权使用复权因子算法
+## 1.1.110220(2021-08-05) +10219 修正 ChartMinutePriceLine::GetTooltipData()报错
+10217 h5信息地雷数据支持修改域名
+## 1.1.110216(2021-08-04) +10216 小程序修正百分比坐标Y轴超出刻度范围
+10214 修正K线百分比坐标超出当前Y轴范围
+10212 修正分笔图十字光标区域背景最右边溢出
+10210 分笔图增加区间背景
+10209 JsonDataToTickData() 每个分笔数据的前收盘使用当日的前收盘
+10208 分笔图支持tooltip提示信息
+## 1.1.110189(2021-07-27) +10189 修正小程序MinuteFrame里的成员变量DataWidth, DistanceWidth 没有设置为1
+10188 OnDoubleClick() 代码整理
+10187 小程序CallFunctionExplain() 增加函数不存在报错
+10185 CallFunctionExplain()增加函数不存在报错
+10183 十字光标竖线支持连续
+10182 K线tooltip和K线信息地雷禁止选中
+10181 判断tooltip代码整理, 独立成2个函数 PtInChartPaintTooltip,PtInOverlayChartPaintTooltip
+10179 ChartOverlayMinutePriceLine, ChartMinutePriceLine 增加函数GetTooltipData()
+10178 OnMinuteSelectRectMouseUp()增加CLICK事件通知
+## 1.1.110177(2021-07-23) +10174 K线左右拖拽数据增加自动模式 (StepPixel=0) +## 1.1.110173(2021-07-22) +10172 自定义K线支持横屏
+10171 小程序K线图最大最小显示配置支持横屏
+10170 K线最大最小值显示设置支持横屏
+10169 K线图支持自定义K线颜色
+10167 ChartKLine::DrawKBar()代码整理
+## 1.1.110166(2021-07-19) +10165 GetVariantData()增加错误提示
+10161 修正GetVariantData()外部数据对接格式错误
+10159 小程序增加INBLOCK
+10157 增加INBLOCK
+10156 小程序支持板块函数
+10154 增加板块函数
+ +## 1.1.110127(2021-07-14) +10125 K线图增加叠加指标参数修改更新接口
+10124 去掉scss相关内容和webpack配置
+10122 修正分时图区间选择最后的结束时间点超出当前交易时间报错
+ + +## 1.1.110119 (2021-07-12) +10117 JSExplainer语法检测器迁移到小程序
+10115 小程序AddIndexWindow()增加SplitCount,IsShowLeftText,IsShowRightText属性设置
+10114 AddIndexWindow() 增加SplitCount属性
+10110 增加区间统计框关闭以后, 操作选中区域完毕以后,弹出对应的菜单或框
+## 1.1.10095(2021-07-07) +10094 ChangeIndexWindowCount() 增加IsShowLeftText, IsShowRightText设置
+10092 AddIndexWindow() 增加IsShowLeftText,IsShowRightText设置
+10090 修正多日分时图切换最大最小值没有清空
+ +## 1.1.10087(2021-07-01) +10087 小程序修正分时图RecvMinuteData()报错 +## 1.1.10086(2021-07-01) +10086 小程序国内期货增加新品种
+10085 小程序DynamicTitleData增加是否显示标识
+10084 小程序修正IFrameSplitOperator.IsString()空字符串判断错误
+10083 IFrameSplitOperator.IsString()修正""空字符串判断错误
+10081 MULTI_BAR叠加指标增加往标题实例中设置数据
+10080 自定义多柱子增加数据设置到标题
+## 1.1.10076(2021-06-29) +10074 小程序分时图Y轴增加涨跌停坐标轴
+10073 小程序 分时图Y周支持根据最高最低加分割
+10072 1. 小程序 IChangeStringFormat 迁移到umychart.framesplit.wechart.js中
+ 2. 小程序 增加IChangeStringFormat工厂类
+10068 K线tooltip和标题信息成交量A股统一改成手单位
+## 1.1.10065(2021-06-28) +10064 修正分时图没有判断是否支持区间选择导致报错 +## 1.1.10063(2021-06-28) +10061 小程序十字光标X轴日期文字支持3种格式 0=YYYY-MM-DD 1=YYYY/MM/DD 2=YYYY/MM/DD/W 3=DD/MM/YYYY
+10060 十字光标X轴日期显示 支持YY/MM/YYYY
+10057 修正百分比坐标价格和百分比两个颜色配置颠倒了.
+10055 修正分笔K线区间统计框起始时间格式显示不对.
+## 1.1.10040(2021-06-25) +10039 K线图支持上下拖拽
+10037 分笔K线增加支持叠加指标
+10035 分笔K线数据更新增加2.0格式
+10034 1. 分笔K线全量数据增加新的数据格式
+ 2. 分笔标题信息和tooltip显示样式修改
+ 3. 分笔增加百分比坐标
+ +## 1.1.9984(2021-06-17) +9983 修正SplitLogarithmicXYCoordinate()预留高度为负数是,显示错误
+9978 修正KLineChartContainer::ChangeIndexWindowCount() 显示错位
+ 增加AddIndexWindow() 添加指标窗口
+9977 K线图增加事件ON_SPLIT_XCOORDINATE
+9958 KLineChartContainer.JsonDataToHistoryData(), KLineChartContainer.JsonDataToMinuteRealtimeData(), KLineChartContainer.JsonDataToMinuteHistoryData() 去掉价格<0的无效价格判断.
+9954 ChartVericaltLine, ChartHorizontalLine 支持横屏
+9953 绘图函数HORLINE()支持单数值条件
+9951 增加绘图函数HORLINE
+9950 增加绘图函数VERTLINE
+9949 部分替换 for(var i in array ) 改成 for(var i=0;i +## 1.1.9948(2021-06-12) +9948 小程序分时图和K线图十字光标移动优化
+9947 小程序增加分时图十字光标移动延迟绘图间隔设置
+## 1.1.9946(2021-06-12) +9946 小程序优化分时图十字光标移动效率
+9945 小程序修正ChangePeriod()未完成数据下载切换周期报错
+9942 小程序ChartMinutePriceLine迁移到umychart.chartpaint.wechart.js中
+## 1.1.9938(2021-06-10) +9937 小程序K线训练增加是否显示最高最低价格显示控制
+9936, 9935 把部分数组循环遍历 (var i in array) 改成=> (var i=0;i +## 1.1.9933(2021-06-09) +9931 小程序更新定时器增加判断是否图形已销毁
+9925 期货增加 郑州商品交易所-红枣(CJ)
+9921 JsonDataToMinuteDataArray 修正跨天数据 日期时间显示错误
+9919 修正分时图标题高度没有设置为0
+9917 大盘指数(INDEXA, INDEXC....)支持对3放数据对接
+## 1.1.9907(2021-06-06) +9907 修正IFrameSplitOperator::IntegerCoordinateSplit() 负数最小值计算错误
+9902 增加指标标题绘制回调函数 ON_INDEXTITLE_DRAW
+## 1.1.9901(2021-06-06) +9900 小程序修正IFrameSplitOperator::IntegerCoordinateSplit()最低价计算错误
+9899 IFrameSplitOperator::IntegerCoordinateSplit() 修正最低价计算错误
+## 1.1.9893(2021-06-04) +9892 小程序IFrameSplitOperator::IntegerCoordinateSplit()算法优化
+9890 IFrameSplitOperator::IntegerCoordinateSplit() 修正计算错误
+9886 增加多日集合竞价最新数据更新
+9885 修正FrameSplitY::GetCallAcutionSplitY() 多日分时Y轴多算了一个刻度
+ 多日分时图集合竞价Y轴统一最大,最小值
+## 1.1.9864(2021-05-30) +9863 分时图集合竞价十字光标显示最近的有效数据
+9862 多日集合竞价十字光标支持Y轴数据显示
+9861 多日分时图Y轴显示集合竞价刻度
+## 1.1.9792(2021-05-21) +9792 小程序
+1. 修正DRAWRECTREL()位置正确性没有判断
+2. Y轴内部刻度增加属性YTextBaseline,支持上下位置可以配置
+9790 FrameSplitMinutePriceY::GetMaxMin() 修正计算错误. Y轴内部刻度文字支持上下位子配置
+9788 导出 CoordinateInfo
+ +## 1.1.9759(2021-05-19) +9758 DynamicMinuteTitlePainting调整集合竞价1.0数据格式显示样式
+9756 小程序 ChartVolStick柱子高度<1,统一显示为1
+9754 ChartVolStick() 高度小于1, 统一调整为1
+9748 ShowSelectRect() 分时图显示位置调整
+9747 分时图增加区间选择接口
+9746 RequestDragMinuteData, RequestDragDayData 数据回调增加周期和复权参数
+9743 集合竞价支持绘制点
+ + +## 1.1.9725(2021-05-15) +9724 修正ChartMultiHtmlDom() 在页面放大倍数以后, DOM坐标计算错误
+9719 小程序 JSComplier.Execute() 去掉ios回报错的日志
+## 1.1.9694(2021-05-10) +9694 小程序 1. JSCHART_EVENT_ID定义迁移到umychart.data.wechart.js里面
+ 2. 支持 JSCHART_EVENT_ID.ON_SPLIT_YCOORDINATE 事件
+9687 小程序ChartBorder增加 GetBorder(),GetHScreenBorder()
+## 1.1.9683(2021-05-09) +修改readme +## 1.1.9682(2021-05-09) +修改readme +## 1.1.9681(2021-05-09) +9671 增加分时图集合竞价关闭按钮
+9670 分时图集合竞价支持横屏
+9668 增加收盘集合竞价
+## 1.1.9654(2021-05-04) +9653 1.分时图双击事件
+ 2.集合竞价宽度设置
+9651 修正ChartMinutePriceLine::DrawBeforeOpen()报错
+9649 MULTI_LINE多线段支持"C","H"等特殊标识价格
+9648 1.修正分时图叠加指标共享Y轴横屏没有读取主图Y轴信息
+ 2.ChartMultiSVGIcon 使用GetBorder获取坐标信息
+9647 分时图集合竞价框架重新调整
+## 1.1.9617(2021-04-28) +9616 修正IsSHSZStockA() 没有包含300创业板
+9610 UpdateFrameMaxMin() 主图计算Y轴最大最小值增加叠加指标数据
+9604 分时图和K线主图支持ON_SPLIT_YCOORDINATE事件
+9599 Y轴分割函数,增加是否支持Y轴缩放查询接口
+9597 增加 Y轴分割刻度回调事件
+ + +## 1.1.9536(2021-04-23) +增加插件教程说明 +## 1.1.9535(2021-04-22) +增加HQChart模板样例 +## 1.1.9534(2021-04-22) +9532 公告信息地雷支持去重更新
+9529 信息地理拖拽下载数据以后,支持更新调用
+9520 增加AB波浪尺
+## 1.1.9518(2021-04-21) +9517 修正多日分时图更新当天数据时,叠加指标没有重新计算
+9516 小程序增加ON_PHONE_TOUCH事件回调
+9509 修正OverlayScriptIndex::CreateTextLine()画法保存错误
+9501 1. 信息地雷顶部画不下了, 移动下面画
+ 2. 修正单击K线和双击K线同时触发回调的问题
+ +## 1.1.9500(2021-04-19) +9481 增加画图工具图标
+9479 小程序修正BARSSINCEN算法错误
+9478 修正BARSSINCEN算法
+9477 修正 JSCHART_OPERATOR_ID.OP_LEFT_ZOOM_OUT 数据索引计算错误
+9497 走势图支持后台指标API数据
+9496 修正"BindOverlayPositionData", "ClearBindOverlayPositionData" 指标变量写错了.
+9495 分时图支持叠加指标
+9499 h5增加手势事件回调ON_PHONE_TOUCH.
+ +## 1.1.9473(2021-04-12) +9472 修正K线数据更新以后叠加指标没有执行更新
+9471 小程序"CIRCLEDOT","POINTDOT"点半径支持设置
+9469 CIRCLEDOT, POINTDOT支持半径大小设置
+9466 小程序ChartOperator右移K线支持下载功能
+9465 小程序COLORSTICK支持标题字体颜色设置
+9469 COLORSTICK 支持后面增加颜色控制标题的字体颜色
+9462 增加点击回调事件
+9458 ChartOperator() 数据右移增加,历史数据下载功能
+## 1.1.9444(2021-04-08) +9443 增加K线叠加指标是否显示指标名字配置 +9441 ChangeIndexTemplate() 支持指标标题字体设置 +9338 ChartSingleText 获取X轴坐标分时图和K线图分开 + +## 1.1.9417(2021-04-01) +9416 增加水平线段 +## 1.1.9416(2021-03-31) +增加测试用例项目 +## 1.1.9415(2021-03-31) +把原来hqchart改成uniapp插件模式. 版本号 1.9415 diff --git a/uni_modules/jones-hqchart2/js_sdk/HQChartControl.vue b/uni_modules/jones-hqchart2/js_sdk/HQChartControl.vue new file mode 100644 index 0000000..df0b016 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/HQChartControl.vue @@ -0,0 +1,511 @@ + + + + + diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.chartpaint.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.chartpaint.wechat.js new file mode 100644 index 0000000..08d3733 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.chartpaint.wechat.js @@ -0,0 +1,6134 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 小程序图形库 +*/ + +//行情数据结构体 及涉及到的行情算法(复权,周期等) +import +{ + JSCommon_ChartData as ChartData, JSCommon_HistoryData as HistoryData, + JSCommon_SingleData as SingleData, JSCommon_MinuteData as MinuteData, + JSCommon_Rect as Rect, +} from "./umychart.data.wechat.js"; + +import +{ + JSCommonResource_Global_JSChartResource as g_JSChartResource, +} from './umychart.resource.wechat.js' + +import +{ + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, +} from './umychart.framesplit.wechat.js' + +import +{ + JSCommonCoordinateData as JSCommonCoordinateData, + JSCommonCoordinateData_MARKET_SUFFIX_NAME as MARKET_SUFFIX_NAME +} from "./umychart.coordinatedata.wechat.js"; + +//配色 +function JSChartPaintResource() +{ + //指标不支持信息 + this.Index= + { + NotSupport : { Font: "14px 微软雅黑", TextColor: "rgb(52,52,52)" } + } +} +var g_JSChartPaintResource = new JSChartPaintResource(); + + +//图新画法接口类 +function IChartPainting() +{ + this.Canvas; //画布 + this.ChartBorder; //边框信息 + this.ChartFrame; //框架画法 + this.Name; //名称 + this.ClassName = 'IChartPainting'; //类名 + this.Data = new ChartData(); //数据区 + + this.NotSupportMessage = null; + this.MessageFont = g_JSChartPaintResource.Index.NotSupport.Font; + this.MessageColor = g_JSChartPaintResource.Index.NotSupport.TextColor; + + this.IsDrawFirst = false; //是否比K线先画 + this.IsShow = true; //是否显示 + + this.Draw = function () { } + + this.IsMinuteFrame=function() + { + var isMinute=(this.ChartFrame.ClassName=="MinuteFrame" || this.ChartFrame.ClassName=="MinuteHScreenFrame"); + return isMinute + } + + this.DrawNotSupportmessage = function () + { + this.Canvas.font = this.MessageFont; + this.Canvas.fillStyle = this.MessageColor; + + var left = this.ChartBorder.GetLeft(); + var width = this.ChartBorder.GetWidth(); + var top = this.ChartBorder.GetTopEx(); + var height = this.ChartBorder.GetHeightEx(); + + var x = left + width / 2; + var y = top + height / 2; + + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillText(this.NotSupportMessage, x, y); + } + + this.GetTooltipData = function (x, y, tooltip) + { + return false; + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null || isNaN(value)) continue; + + if (range.Max == null) range.Max = value; + if (range.Min == null) range.Min = value; + + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + + return range; + } + + this.GetDynamicFont = function (dataWidth) //根据宽度自动获取对应字体 + { + var font; + if (dataWidth < 5) font = '4px Arial'; //字体根据数据宽度动态调整 + else if (dataWidth < 7) font = '6px Arial'; + else if (dataWidth < 9) font = '8px Arial'; + else if (dataWidth < 11) font = '10px Arial'; + else if (dataWidth < 13) font = '12px Arial'; + else if (dataWidth < 15) font = '14px Arial'; + else font = '16px Arial'; + + return font; + } + + this.GetDynamicFontEx=function(dataWidth, distanceWidth, maxSize, minSize, zoom, fontname) //根据宽度自动获取对应字体 + { + if (maxSize==minSize) + { + var font=`${maxSize.toFixed(0)}px ${fontname}` ; + return font; + } + + var fontSize=(dataWidth+distanceWidth); + if (zoom) + { + if (zoom.Type==0) + { + if (zoom.Value>0) fontSize=(dataWidth*zoom.Value); + } + else if (zoom.Type==1) + { + if (zoom.Value>0) fontSize=(dataWidth+distanceWidth)*zoom.Value; + } + else if (zoom.Type==2) + { + if (IFrameSplitOperator.IsNumber(zoom.Value)) + fontSize=(dataWidth+distanceWidth) + (2*zoom.Value); + } + } + + if (fontSizemaxSize) fontSize=maxSize; + + var font=`${fontSize.toFixed(0)}px ${fontname}` ; + return font; + } + + this.SetFillStyle = function (color, x0, y0, x1, y1) + { + if (Array.isArray(color)) + { + let gradient = this.Canvas.createLinearGradient(x0, y0, x1, y1); + var offset = 1 / (color.length); + for (var i in color) + { + gradient.addColorStop(i * offset, color[i]); + } + this.Canvas.fillStyle = gradient; + } + else + { + this.Canvas.fillStyle = color; + } + } +} + +//K线画法 +function ChartKLine() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'ChartKLine'; + this.Symbol; //股票代码 + this.DrawType = 0; // 0=K线 1=收盘价线 2=美国线 3=空心K线柱子 4=收盘价面积 + this.CloseLineColor = g_JSChartResource.CloseLineColor; + this.CloseLineAreaColor = g_JSChartResource.CloseLineAreaColor; + this.UpColor = g_JSChartResource.UpBarColor; + this.DownColor = g_JSChartResource.DownBarColor; + this.UnchagneColor = g_JSChartResource.UnchagneBarColor; //平盘 + this.ColorData; //五彩K线颜色 >0 UpColor 其他 DownColor + this.TradeData; //交易系统 包含买卖数据{Buy:, Sell:} + + this.IsShowMaxMinPrice = true; //是否显示最大最小值 + this.TextFont = g_JSChartResource.KLine.MaxMin.Font; + this.TextColor = g_JSChartResource.KLine.MaxMin.Color; + this.InfoPointColor = g_JSChartResource.KLine.Info.Color; + this.InfoPointColor2 = g_JSChartResource.KLine.Info.Color2; + this.InfoDrawType = 0; //0=在底部画远点 1=在最低价画三角 + + this.PtMax; //最大值的位置 + this.PtMin; //最小值的位置 + + this.MinBarWidth=g_JSChartResource.MinKLineBarWidth; //最小的柱子宽度 + + this.DrawAKLine = function () //美国线 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var ptMax = { X: null, Y: null, Value: null, Align: 'left' }; + var ptMin = { X: null, Y: null, Value: null, Align: 'left' }; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yLow = this.ChartFrame.GetYFromData(data.Low); + var yHigh = this.ChartFrame.GetYFromData(data.High); + var yOpen = this.ChartFrame.GetYFromData(data.Open); + var yClose = this.ChartFrame.GetYFromData(data.Close); + + if (ptMax.Value == null || ptMax.Value < data.High) //求最大值 + { + ptMax.X = x; + ptMax.Y = yHigh; + ptMax.Value = data.High; + ptMax.Align = j < xPointCount / 2 ? 'left' : 'right'; + } + + if (ptMin.Value == null || ptMin.Value > data.Low) //求最小值 + { + ptMin.X = x; + ptMin.Y = yLow; + ptMin.Value = data.Low; + ptMin.Align = j < xPointCount / 2 ? 'left' : 'right'; + } + + if (data.Open < data.Close) this.Canvas.strokeStyle = this.UpColor; //阳线 + else if (data.Open > data.Close) this.Canvas.strokeStyle = this.DownColor; //阳线 + else this.Canvas.strokeStyle = this.UnchagneColor; //平线 + + this.Canvas.beginPath(); //最高-最低 + if (isHScreen) { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + + this.Canvas.stroke(); + + if (dataWidth >= this.MinBarWidth) { + this.Canvas.beginPath(); //开盘 + if (isHScreen) { + this.Canvas.moveTo(ToFixedPoint(yOpen), left); + this.Canvas.lineTo(ToFixedPoint(yOpen), x); + } + else { + this.Canvas.moveTo(left, ToFixedPoint(yOpen)); + this.Canvas.lineTo(x, ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + + this.Canvas.beginPath(); //收盘 + if (isHScreen) { + this.Canvas.moveTo(ToFixedPoint(yClose), right); + this.Canvas.lineTo(ToFixedPoint(yClose), x); + } + else { + this.Canvas.moveTo(right, ToFixedPoint(yClose)); + this.Canvas.lineTo(x, ToFixedPoint(yClose)); + } + this.Canvas.stroke(); + } + + if (this.Data.DataType == 0 && ChartData.IsDayPeriod(this.Data.Period,true)) //信息地雷 + { + var infoItem = { X: x, Xleft: left, XRight: right, YMax: yHigh, YMin: yLow, DayData: data, Index: j }; + this.DrawInfoDiv(infoItem); + } + } + + this.PtMax = ptMax; + this.PtMin = ptMin; + } + + this.DrawCloseLine = function () //收盘价线 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var bFirstPoint = true; + this.Canvas.beginPath(); + this.Canvas.strokeStyle = this.CloseLineColor; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yClose = this.ChartFrame.GetYFromData(data.Close); + + if (bFirstPoint) { + if (isHScreen) this.Canvas.moveTo(yClose, x); + else this.Canvas.moveTo(x, yClose); + bFirstPoint = false; + } + else { + if (isHScreen) this.Canvas.lineTo(yClose, x); + else this.Canvas.lineTo(x, yClose); + } + } + + if (bFirstPoint == false) this.Canvas.stroke(); + } + + this.DrawCloseArea = function () //收盘价面积 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var bFirstPoint = true; + var firstPoint = null; + this.Canvas.beginPath(); + this.Canvas.strokeStyle = this.CloseLineColor; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yClose = this.ChartFrame.GetYFromData(data.Close); + + if (bFirstPoint) { + if (isHScreen) { + this.Canvas.moveTo(yClose, x); + firstPoint = { X: yClose, Y: x }; + } + else { + this.Canvas.moveTo(x, yClose); + firstPoint = { X: x, Y: yClose }; + } + bFirstPoint = false; + } + else { + if (isHScreen) this.Canvas.lineTo(yClose, x); + else this.Canvas.lineTo(x, yClose); + } + } + + if (bFirstPoint) return; + + this.Canvas.stroke(); + //画面积 + if (isHScreen) { + this.Canvas.lineTo(this.ChartBorder.GetLeft(), x); + this.Canvas.lineTo(this.ChartBorder.GetLeft(), firstPoint.Y); + } + else { + this.Canvas.lineTo(x, this.ChartBorder.GetBottom()); + this.Canvas.lineTo(firstPoint.X, this.ChartBorder.GetBottom()); + } + this.Canvas.closePath(); + + if (Array.isArray(this.CloseLineAreaColor)) { + if (isHScreen) { + let gradient = this.Canvas.createLinearGradient(this.ChartBorder.GetRightEx(), this.ChartBorder.GetTop(), this.ChartBorder.GetLeft(), this.ChartBorder.GetTop()); + gradient.addColorStop(0, this.CloseLineAreaColor[0]); + gradient.addColorStop(1, this.CloseLineAreaColor[1]); + this.Canvas.fillStyle = gradient; + } + else { + let gradient = this.Canvas.createLinearGradient(firstPoint.X, this.ChartBorder.GetTopEx(), firstPoint.X, this.ChartBorder.GetBottom()); + gradient.addColorStop(0, this.CloseLineAreaColor[0]); + gradient.addColorStop(1, this.CloseLineAreaColor[1]); + this.Canvas.fillStyle = gradient; + } + } + else { + this.Canvas.fillStyle = this.CloseLineAreaColor; + } + this.Canvas.fill(); + } + + this.DrawKBar = function () + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + + if (isHScreen) { + xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + chartright = this.ChartBorder.GetBottom(); + } + + var ptMax = { X: null, Y: null, Value: null, Align: 'left' }; + var ptMin = { X: null, Y: null, Value: null, Align: 'left' }; + + var upColor = this.UpColor; + var downColor = this.DownColor; + var unchagneColor = this.UnchagneColor; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) + break; + + var x = left + (right - left) / 2; + var yLow = this.ChartFrame.GetYFromData(data.Low); + var yHigh = this.ChartFrame.GetYFromData(data.High); + var yOpen = this.ChartFrame.GetYFromData(data.Open); + var yClose = this.ChartFrame.GetYFromData(data.Close); + var y = yHigh; + + if (ptMax.Value == null || ptMax.Value < data.High) //求最大值 + { + ptMax.X = x; + ptMax.Y = yHigh; + ptMax.Value = data.High; + ptMax.Align = j < xPointCount / 2 ? 'left' : 'right'; + } + + if (ptMin.Value == null || ptMin.Value > data.Low) //求最小值 + { + ptMin.X = x; + ptMin.Y = yLow; + ptMin.Value = data.Low; + ptMin.Align = j < xPointCount / 2 ? 'left' : 'right'; + } + + if (this.ColorData) ///五彩K线颜色设置 + { + if (i < this.ColorData.length) + upColor = downColor = unchagneColor = (this.ColorData[i] > 0 ? this.UpColor : this.DownColor); + else + upColor = downColor = unchagneColor = this.DownColor; + } + + if (data.Open < data.Close) //阳线 + { + if (dataWidth >= this.MinBarWidth) + { + this.Canvas.strokeStyle = upColor; + if (data.High > data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(this.DrawType == 3 ? Math.max(yClose, yOpen) : yClose), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(this.DrawType == 3 ? Math.min(yClose, yOpen) : yClose)); + } + this.Canvas.stroke(); + y = yClose; + } + else + { + y = yClose; + } + + this.Canvas.fillStyle = upColor; + if (isHScreen) + { + if (Math.abs(yOpen - y) < 1) + { + this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), 1, ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType == 3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(y), ToFixedPoint(left), ToFixedRect(yOpen - y), ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + //宽度是负数竟然不会画, h5就可以 + //this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), ToFixedRect(yOpen - y), ToFixedRect(dataWidth)); + this.Canvas.fillRect(ToFixedRect(Math.min(yOpen, y)), ToFixedRect(left), ToFixedRect(Math.abs(yOpen - y)), ToFixedRect(dataWidth)); + } + } + } + else + { + if (Math.abs(yOpen - y) < 1) + { + this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), 1); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType == 3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(y), ToFixedRect(dataWidth), ToFixedRect(yOpen - y)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), ToFixedRect(yOpen - y)); + } + } + } + + if (data.Open > data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(this.DrawType == 3 ? Math.min(yClose, yOpen) : y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(this.DrawType == 3 ? Math.max(yClose, yOpen) : y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + this.Canvas.strokeStyle = upColor; + this.Canvas.stroke(); + } + } + else if (data.Open > data.Close) //阴线 + { + if (dataWidth >= this.MinBarWidth) + { + this.Canvas.strokeStyle = downColor; + if (data.High > data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yOpen), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + y = yOpen; + } + else + { + y = yOpen + } + + this.Canvas.fillStyle = downColor; + if (isHScreen) + { + if (Math.abs(yClose - y) < 1) + { + this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), 1, ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + //宽度是负数竟然不会画, h5就可以 + this.Canvas.fillRect(ToFixedRect(Math.min(yClose, y)), ToFixedRect(left), ToFixedRect(Math.abs(yClose - y)), ToFixedRect(dataWidth)); + } + } + else + { + if (Math.abs(yClose - y) < 1) this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), 1); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), ToFixedRect(yClose - y)); + } + + if (data.Open > data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + this.Canvas.strokeStyle = downColor; + this.Canvas.stroke(); + } + } + else // 平线 + { + if (dataWidth >= this.MinBarWidth) + { + this.Canvas.strokeStyle = unchagneColor; + this.Canvas.beginPath(); + if (data.High > data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(yOpen, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yOpen); + } + y = yOpen; + } + else + { + y = yOpen; + } + + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(left)); + this.Canvas.lineTo(ToFixedPoint(y), ToFixedPoint(right)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(left), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(y)); + } + + if (data.Open > data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + } + + this.Canvas.stroke(); + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + this.Canvas.strokeStyle = unchagneColor; + this.Canvas.stroke(); + } + } + + if (this.Data.DataType == 0 && ChartData.IsDayPeriod(this.Data.Period,true)) //信息地雷 + { + var infoItem = { X: x, Xleft: left, XRight: right, YMax: yHigh, YMin: yLow, DayData: data, Index: j }; + this.DrawInfoDiv(infoItem); + } + } + + this.PtMax = ptMax; + this.PtMin = ptMin; + } + + this.DrawTrade = function () //交易系统 + { + if (!this.TradeData) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + + if (isHScreen) { + xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + chartright = this.ChartBorder.GetBottom(); + } + + var sellData = this.TradeData.Sell; + var buyData = this.TradeData.Buy; + var arrowWidth = dataWidth; + if (arrowWidth > 10) arrowWidth = 10; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var buy = false, sell = false; + if (sellData && i < sellData.length) sell = sellData[i] > 0; + if (buyData && i < buyData.length) buy = buyData[i] > 0; + if (!sell && !buy) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yLow = this.ChartFrame.GetYFromData(data.Low); + var yHigh = this.ChartFrame.GetYFromData(data.High); + var yOpen = this.ChartFrame.GetYFromData(data.Open); + var yClose = this.ChartFrame.GetYFromData(data.Close); + var y = yHigh; + + if (buy) { + this.Canvas.fillStyle = this.UpColor; + this.Canvas.strokeStyle = this.UnchagneColor; + this.Canvas.beginPath(); + if (isHScreen) { + this.Canvas.moveTo(yLow - 1, x); + this.Canvas.lineTo(yLow - arrowWidth - 1, x - arrowWidth / 2); + this.Canvas.lineTo(yLow - arrowWidth - 1, x + arrowWidth / 2); + } + else { + this.Canvas.moveTo(x, yLow + 1); + this.Canvas.lineTo(x - arrowWidth / 2, yLow + arrowWidth + 1); + this.Canvas.lineTo(x + arrowWidth / 2, yLow + arrowWidth + 1); + } + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + + if (sell) { + this.Canvas.fillStyle = this.DownColor; + this.Canvas.strokeStyle = this.UnchagneColor; + this.Canvas.beginPath(); + if (isHScreen) { + this.Canvas.moveTo(yHigh + 1, x); + this.Canvas.lineTo(yHigh + arrowWidth + 1, x - arrowWidth / 2); + this.Canvas.lineTo(yHigh + arrowWidth + 1, x + arrowWidth / 2); + } + else { + this.Canvas.moveTo(x, yHigh - 1); + this.Canvas.lineTo(x - arrowWidth / 2, yHigh - arrowWidth - 1); + this.Canvas.lineTo(x + arrowWidth / 2, yHigh - arrowWidth - 1); + } + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + } + } + + this.Draw = function () { + this.PtMax = { X: null, Y: null, Value: null, Align: 'left' }; //清空最大 + this.PtMin = { X: null, Y: null, Value: null, Align: 'left' }; //清空最小 + this.ChartFrame.ChartKLine = { Max: null, Min: null }; //保存K线上 显示最大最小值坐标 + + if (this.IsShow == false) return; + + if (this.DrawType == 1) { + this.DrawCloseLine(); + return; + } + else if (this.DrawType == 2) { + this.DrawAKLine(); + } + else if (this.DrawType == 4) { + this.DrawCloseArea(); + } + else { + this.DrawKBar(); + } + + this.DrawTrade(); + + if (this.IsShowMaxMinPrice) //标注最大值最小值 + { + if (this.ChartFrame.IsHScreen === true) this.HScreenDrawMaxMinPrice(this.PtMax, this.PtMin); + else this.DrawMaxMinPrice(this.PtMax, this.PtMin); + } + } + + this.DrawMaxMinPrice = function (ptMax, ptMin) { + if (ptMax.X == null || ptMax.Y == null || ptMax.Value == null) return; + if (ptMin.X == null || ptMin.Y == null || ptMin.Value == null) return; + + var leftArrow=g_JSChartResource.KLine.MaxMin.LeftArrow; + var rightArrow=g_JSChartResource.KLine.MaxMin.RightArrow; + var highYOffset=g_JSChartResource.KLine.MaxMin.HighYOffset; + var lowYOffset=g_JSChartResource.KLine.MaxMin.LowYOffset; + + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + this.Canvas.font = this.TextFont; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.textAlign = ptMax.Align; + this.Canvas.textBaseline = 'bottom'; + var left = ptMax.X; + if (IFrameSplitOperator.IsNumber(highYOffset)) ptMax.Y+=highYOffset; + var text = ptMax.Value.toFixed(defaultfloatPrecision); + if (ptMax.Align == 'left') text = leftArrow + text; + else text = text + rightArrow; + this.Canvas.fillText(text, left, ptMax.Y); + this.ChartFrame.ChartKLine.Max = { X: left, Y: ptMax.Y, Text: { BaseLine: 'bottom' } }; + + this.Canvas.textAlign = ptMin.Align; + this.Canvas.textBaseline = 'top'; + var left = ptMin.X; + if (IFrameSplitOperator.IsNumber(lowYOffset)) ptMin.Y+=lowYOffset; + text = ptMin.Value.toFixed(defaultfloatPrecision); + if (ptMin.Align == 'left') text = leftArrow + text; + else text = text + rightArrow; + this.Canvas.fillText(text, left, ptMin.Y); + this.ChartFrame.ChartKLine.Min = { X: left, Y: ptMin.Y, Text: { BaseLine: 'top' } }; + } + + this.HScreenDrawMaxMinPrice = function (ptMax, ptMin) //横屏模式下显示最大最小值 + { + if (ptMax.X == null || ptMax.Y == null || ptMax.Value == null) return; + if (ptMin.X == null || ptMin.Y == null || ptMin.Value == null) return; + + var leftArrow=g_JSChartResource.KLine.MaxMin.LeftArrow; + var rightArrow=g_JSChartResource.KLine.MaxMin.RightArrow; + var highYOffset=g_JSChartResource.KLine.MaxMin.HighYOffset; + var lowYOffset=g_JSChartResource.KLine.MaxMin.LowYOffset; + + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + var xText = ptMax.Y; + var yText = ptMax.X; + if (IFrameSplitOperator.IsNumber(highYOffset)) xText+=highYOffset; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.font = this.TextFont; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.textAlign = ptMax.Align; + this.Canvas.textBaseline = 'bottom'; + var text = ptMax.Value.toFixed(defaultfloatPrecision); + if (ptMax.Align == 'left') text = leftArrow + text; + else text = text + rightArrow; + this.Canvas.fillText(text, 0, 0); + this.Canvas.restore(); + + + var xText = ptMin.Y; + var yText = ptMin.X; + if (IFrameSplitOperator.IsNumber(lowYOffset)) xText+=lowYOffset; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.font = this.TextFont; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.textAlign = ptMin.Align; + this.Canvas.textBaseline = 'top'; + var text = ptMin.Value.toFixed(defaultfloatPrecision); + if (ptMin.Align == 'left') text = leftArrow + text; + else text = text + rightArrow; + this.Canvas.fillText(text, 0, 0); + this.Canvas.restore(); + } + + //画某一天的信息地雷 画在底部 + this.DrawInfoDiv = function (item) { + if (!this.InfoData || this.InfoData.length <= 0) return; + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + + var infoData = this.InfoData.get(item.DayData.Date.toString()); + if (!infoData || infoData.Data.length <= 0) return; + var bHScreen = (this.ChartFrame.IsHScreen === true); + if (this.InfoDrawType === 1) { + this.Canvas.font = this.GetDynamicFont(dataWidth); + this.Canvas.fillStyle = this.InfoPointColor2; + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'top'; + if (bHScreen) { + var xText = item.YMin; + var yText = item.X; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText('▲', 0, 0); + this.Canvas.restore(); + } + else { + var left = ToFixedPoint(item.X); + this.Canvas.fillText('▲', left, item.YMin); + } + } + else { + var dataWidth = this.ChartFrame.DataWidth; + var radius = dataWidth / 2; + if (radius > 3) radius = 3; + var x = item.X; + var y = this.ChartFrame.ChartBorder.GetBottom() - 2 - radius; + if (bHScreen) y = this.ChartFrame.ChartBorder.GetLeft() + 2 + radius; + + this.Canvas.fillStyle = this.InfoPointColor; + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.arc(y, x, radius, 0, Math.PI * 2, true); + else this.Canvas.arc(ToFixedPoint(x), y, radius, 0, Math.PI * 2, true); + this.Canvas.closePath(); + this.Canvas.fill(); + } + } + + this.GetTooltipData = function (x, y, tooltip) { + return false; + } + + this.GetMaxMin = function () //计算当天显示数据的最大最小值 + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Max = null; + range.Min = null; + + if (this.IsShow == false) return range; + + if (this.DrawType==1 || this.DrawType==4 ) // 1=收盘价线 4=收盘价面积图 + { + for(var i=this.Data.DataOffset,j=0;idata.Close) range.Min=data.Close; + } + } + else + { + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + if (range.Max == null) range.Max = data.High; + if (range.Min == null) range.Min = data.Low; + + if (range.Max < data.High) range.Max = data.High; + if (range.Min > data.Low) range.Min = data.Low; + } + + } + + return range; + } +} + + + +/* + 文字输出 支持横屏 + 数组不为null的数据中输出 this.Text文本 +*/ +function ChartSingleText() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + this.TextFont = "14px 微软雅黑"; //字体 + this.Text; + this.TextAlign = 'left'; + this.Direction = 0; //0=middle 1=bottom 2=top + this.YOffset = 0; + this.Position; //指定输出位置 + + this.TextSize= + { + Max: g_JSChartResource.DRAWICON.Text.MaxSize, Min:g_JSChartResource.DRAWICON.Text.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWICON.Text.Zoom.Type , Value:g_JSChartResource.DRAWICON.Text.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWICON.Text.FontName + } + + this.ReloadResource=function(resource) + { + if (this.Name=="DRAWTEXT") + { + this.TextSize= + { + Max: g_JSChartResource.DRAWTEXT.MaxSize, Min:g_JSChartResource.DRAWTEXT.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWTEXT.Zoom.Type , Value:g_JSChartResource.DRAWTEXT.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWTEXT.FontName + } + } + else if (this.Name=="DRAWNUMBER") + { + this.TextSize= + { + Max: g_JSChartResource.DRAWNUMBER.MaxSize, Min:g_JSChartResource.DRAWNUMBER.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWNUMBER.Zoom.Type , Value:g_JSChartResource.DRAWNUMBER.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWNUMBER.FontName + } + } + } + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.Position) + { + this.DrawPosition(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true) + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + var top = this.ChartBorder.GetTopEx(); + var bottom = this.ChartBorder.GetBottomEx(); + if (isHScreen) + { + chartright = this.ChartBorder.GetBottom(); + top = this.ChartBorder.GetRightEx(); + bottom = this.ChartBorder.GetLeftEx(); + } + var xPointCount = this.ChartFrame.XPointCount; + + var isArrayText = Array.isArray(this.Text); + var text; + if (this.Direction == 1) this.Canvas.textBaseline = 'bottom'; + else if (this.Direction == 2) this.Canvas.textBaseline = 'top'; + else this.Canvas.textBaseline = 'middle'; + this.TextFont = this.GetDynamicFontEx(dataWidth,distanceWidth,this.TextSize.Max,this.TextSize.Min,this.TextSize.Zoom,this.TextSize.FontName); + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + this.Canvas.textAlign = this.TextAlign; + this.Canvas.fillStyle = this.Color; + this.Canvas.font = this.TextFont; + + if (this.YOffset > 0 && this.Direction > 0) + { + var yPrice = y; + + this.Canvas.save(); + this.Canvas.setLineDash([5, 10]); + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (isHScreen) + { + if (this.Direction == 1) + { + y = top - this.YOffset; + yPrice += 5; + } + else + { + y = bottom + this.YOffset; + yPrice -= 5; + } + this.Canvas.moveTo(ToFixedPoint(yPrice), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(y), ToFixedPoint(x)); + } + else + { + if (this.Direction == 1) + { + y = top + this.YOffset; + yPrice += 5; + } + else + { + y = bottom - this.YOffset; + yPrice -= 5; + } + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(yPrice)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(y)); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + + if (isArrayText) + { + text = this.Text[i]; + if (!text) continue; + if (isHScreen) + { + if (this.Name=='DRAWNUMBER') + { + if (this.Direction==1) y+=g_JSChartResource.DRAWABOVE.YOffset; + else if (this.Direction==2) y-=4; + } + } + else + { + if (this.Name=='DRAWNUMBER') + { + if (this.Direction==1) y-=g_JSChartResource.DRAWABOVE.YOffset; + else if (this.Direction==2) y+=4; + } + } + + this.DrawText(text, x, y, isHScreen); + } + else + { + this.DrawText(this.Text, x, y, isHScreen); + } + } + } + + this.DrawPosition=function() //绘制在指定位置上 + { + if (!this.Text) return; + var isHScreen=(this.ChartFrame.IsHScreen===true) + if (isHScreen) + { + var y=this.ChartBorder.GetRightEx()-this.ChartBorder.GetWidthEx()*this.Position.Y; + var x=this.ChartBorder.GetTop()+this.ChartBorder.GetHeight()*this.Position.X; + } + else + { + var x=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*this.Position.X; + var y=this.ChartBorder.GetTopEx()+this.ChartBorder.GetHeight()*this.Position.Y; + } + + this.Canvas.fillStyle=this.Color; + + //TYPE:0为左对齐,1为右对齐. + if (this.Position.Type==0) this.Canvas.textAlign='left'; + else if (this.Position.Type==1) this.Canvas.textAlign='right'; + else this.Canvas.textAlign='center'; + + if (this.Direction==1) this.Canvas.textBaseline='bottom'; + else if (this.Direction==2) this.Canvas.textBaseline='top'; + else this.Canvas.textBaseline='middle'; + + this.DrawText(this.Text,x,y,isHScreen); + } + + this.DrawText = function (text, x, y, isHScreen) + { + if (isHScreen) + { + this.Canvas.save(); + this.Canvas.translate(y, x); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(text, 0, 0); + this.Canvas.restore(); + } + else + { + this.Canvas.fillText(text, x, y); + } + } +} + + +//线段 +function ChartLine() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName ='ChartLine'; + this.Color = "rgb(255,193,37)"; //线段颜色 + this.LineWidth; //线段宽度 + this.DrawType = 0; //画图方式 0=无效数平滑 1=无效数不画断开 + this.IsDotLine = false; //虚线 + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + switch (this.DrawType) + { + case 0: + return this.DrawLine(); + case 1: + return this.DrawStraightLine(); + } + } + + this.DrawLine = function () + { + var bHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (bHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + this.Canvas.save(); + if (this.LineWidth > 0) this.Canvas.lineWidth = this.LineWidth; + var bFirstPoint = true; + var drawCount = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.GetYFromData(value); + + if (x > chartright) break; + + if (bFirstPoint) + { + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.moveTo(y, x); //横屏坐标轴对调 + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + } + else + { + if (bHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + } + + if (drawCount > 0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + //无效数不画 + this.DrawStraightLine = function () + { + var bHScreen = (this.ChartFrame.IsHScreen === true); + var isMinute=this.IsMinuteFrame(); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright = this.ChartBorder.GetRight(); + if (bHScreen) chartright = this.ChartBorder.GetBottom(); + if (bHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var xPointCount = this.ChartFrame.XPointCount; + + this.Canvas.save(); + if (this.LineWidth > 0) this.Canvas.lineWidth = this.LineWidth; + this.Canvas.strokeStyle = this.Color; + if (this.IsDotLine) this.Canvas.setLineDash([3, 5]); //画虚线 + + var bFirstPoint = true; + var drawCount = 0; + for(var i=this.Data.DataOffset,j=0;i 0) this.Canvas.stroke(); + bFirstPoint = true; + drawCount = 0; + continue; + } + + if (isMinute) + { + var x = this.ChartFrame.GetXFromIndex(j); + } + else + { + var left=xOffset; + var right=xOffset+dataWidth; + if (right>chartright) break; + var x=left+(right-left)/2; + } + + + var y = this.GetYFromData(value); + + if (x > chartright) break; + + if (bFirstPoint) + { + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.moveTo(y, x); //横屏坐标轴对调 + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + } + else + { + if (bHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + } + + if (drawCount > 0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.GetYFromData = function (value) + { + return this.ChartFrame.GetYFromData(value); + } +} + +//子线段 +function ChartSubLine() +{ + this.newMethod = ChartLine; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'ChartSubLine'; //类名 + this.Color = "rgb(255,193,37)"; //线段颜色 + this.LineWidth; //线段宽度 + this.DrawType = 0; //画图方式 0=无效数平滑 1=无效数不画断开 + this.IsDotLine = false; //虚线 + + this.SubFrame = { Max: null, Min: null }; + + this.Draw = function () + { + if (!this.IsShow) return; + if (!this.Data || !this.Data.Data) return; + + this.CalculateDataMaxMin(); + + switch (this.DrawType) + { + case 0: + return this.DrawLine(); + case 1: + return this.DrawStraightLine(); + } + } + + this.GetYFromData = function (value) + { + var bHScreen = (this.ChartFrame.IsHScreen === true); + + if (bHScreen) + { + if (value <= this.SubFrame.Min) return this.ChartBorder.GetLeftEx(); + if (value >= this.SubFrame.Max) return this.ChartBorder.GetRightEx(); + + var width = this.ChartBorder.GetWidthEx() * (value - this.SubFrame.Min) / (this.SubFrame.Max - this.SubFrame.Min); + return this.ChartBorder.GetLeftEx() + width; + } + else + { + if (value <= this.SubFrame.Min) return this.ChartBorder.GetBottomEx(); + if (value >= this.SubFrame.Max) return this.ChartBorder.GetTopEx(); + + var height = this.ChartBorder.GetHeightEx() * (value - this.SubFrame.Min) / (this.SubFrame.Max - this.SubFrame.Min); + return this.ChartBorder.GetBottomEx() - height; + } + } + + this.CalculateDataMaxMin = function () + { + this.SubFrame = { Max: null, Min: null }; + + var bHScreen = (this.ChartFrame.IsHScreen === true); + var chartright = this.ChartBorder.GetRight(); + if (bHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + if (x > chartright) break; + + if (this.SubFrame.Min == null || this.SubFrame.Min > value) this.SubFrame.Min = value; + if (this.SubFrame.Max == null || this.SubFrame.Max < value) this.SubFrame.Max = value; + } + } + + this.GetMaxMin = function () //数据不参与坐标轴最大最小值计算 + { + var range = { Min: null, Max: null }; + return range; + } +} + +//POINTDOT 圆点 支持横屏 +function ChartPointDot() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + this.Radius = 1; //点半径 + this.ClassName = 'ChartPointDot'; + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var bHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (bHScreen === true) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + this.Canvas.save(); + this.Canvas.fillStyle = this.Color; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.arc(y, x, this.Radius, 0, Math.PI * 2, true); + else this.Canvas.arc(x, y, this.Radius, 0, Math.PI * 2, true); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.Canvas.restore(); + } +} + +//通达信语法 STICK 支持横屏 +function ChartStick() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + this.LineWidth; //线段宽度 + this.ClassName = 'ChartStick'; + + this.DrawLine = function () + { + if (this.ChartFrame.IsMinSize) return; + if (!this.Data || !this.Data.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen === true) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + this.Canvas.save(); + if (this.LineWidth > 0) this.Canvas.lineWidth = this.LineWidth; + var bFirstPoint = true; + var drawCount = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + if (bFirstPoint) + { + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y, x); + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + } + else + { + if (isHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + } + + if (drawCount > 0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.DrawStick = function () + { + if (!this.Data || !this.Data.Data) return; + var bHScreen = (this.ChartFrame.IsHScreen === true); + var chartright = this.ChartBorder.GetRight(); + if (bHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var yBottom = this.ChartBorder.GetBottom(); + var xLeft = this.ChartBorder.GetLeft(); + + this.Canvas.save(); + this.Canvas.strokeStyle = this.Color; + if (this.LineWidth) this.Canvas.lineWidth = this.LineWidth; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + this.Canvas.beginPath(); + if (bHScreen) + { + this.Canvas.moveTo(xLeft, x); + this.Canvas.lineTo(y, x); + this.Canvas.stroke(); + } + else + { + var xFix = parseInt(x.toString()) + 0.5; + this.Canvas.moveTo(xFix, y); + this.Canvas.lineTo(xFix, yBottom); + } + this.Canvas.stroke(); + } + this.Canvas.restore(); + } + + this.Draw = function () + { + if (!this.IsShow) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + this.DrawStick(); + } +} + +//通达信语法 LINESTICK 支持横屏 +function ChartLineStick() +{ + this.newMethod = ChartStick; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'ChartLineStick'; + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + this.DrawStick(); + this.DrawLine(); + } +} + +//柱子 支持横屏 +function ChartStickLine() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName ='ChartStickLine'; + this.Color = "rgb(255,193,37)"; //线段颜色 + this.LineWidth = 2; //线段宽度 + this.BarType = 0; //柱子类型 0=实心 1=空心 + this.MinBarWidth=g_JSChartResource.MinKLineBarWidth; //最小的柱子宽度 + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + + var isMinute=this.IsMinuteFrame(); + + this.Canvas.save(); + var bFillBar = false; + var bFillKLine = false; + + if (isMinute) + { + if (this.LineWidth>1) this.Canvas.lineWidth=2; + else this.Canvas.lineWidth=1; + this.Canvas.strokeStyle=this.Color; + } + else if (this.LineWidth==50) + { + if (dataWidth >= this.MinBarWidth) + { + bFillKLine = true; + this.Canvas.fillStyle = this.Color; + this.Canvas.strokeStyle = this.Color; + } + else //太细了 画竖线 + { + this.Canvas.lineWidth = 1; + this.Canvas.strokeStyle = this.Color; + } + } + else if (this.LineWidth < 100) + { + var LineWidth = this.LineWidth; + if (dataWidth <= 4) LineWidth = 1; + else if (dataWidth < LineWidth) LineWidth = parseInt(dataWidth); + this.Canvas.lineWidth = LineWidth; + this.Canvas.strokeStyle = this.Color; + } + else + { + bFillBar = true; + this.Canvas.fillStyle = this.Color; + var fixedWidth = 2; + } + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var price = value.Value; + var price2 = value.Value2; + if (price2 == null) price2 = 0; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(price); + var y2 = this.ChartFrame.GetYFromData(price2); + + if (x > chartright) break; + + if (bFillBar) + { + var left = xOffset - fixedWidth; + if (isHScreen) + { + this.Canvas.fillRect(Math.min(y, y2), left, Math.abs(y - y2), dataWidth + distanceWidth + fixedWidth * 2); + } + else + { + var barWidth = dataWidth + distanceWidth + fixedWidth * 2; + if (left + barWidth > chartright) barWidth = chartright - left; //不要超过右边框子 + this.Canvas.fillRect(left, ToFixedRect(Math.min(y, y2)), barWidth, ToFixedRect(Math.abs(y - y2))); + } + } + else if (bFillKLine) + { + if (this.BarType == 1) //实心 + { + if (isHScreen) + { + this.Canvas.beginPath(); + this.Canvas.fillRect(ToFixedRect(Math.min(y, y2)), ToFixedRect(xOffset), ToFixedRect(Math.abs(y - y2)), ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedRect(xOffset), ToFixedRect(Math.min(y, y2)), ToFixedRect(dataWidth), ToFixedRect(Math.abs(y - y2))); + this.Canvas.stroke(); + } + } + else + { + if (isHScreen) + this.Canvas.fillRect(ToFixedRect(Math.min(y, y2)), ToFixedRect(xOffset), ToFixedRect(Math.abs(y - y2)), ToFixedRect(dataWidth)); + else + this.Canvas.fillRect(ToFixedRect(xOffset), ToFixedRect(Math.min(y, y2)), ToFixedRect(dataWidth), ToFixedRect(Math.abs(y - y2))); + } + } + else + { + if (isHScreen) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(y2, ToFixedPoint(x)); + this.Canvas.stroke(); + } + else + { + var xFix = parseInt(x.toString()) + 0.5; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix, y); + this.Canvas.lineTo(xFix, y2); + this.Canvas.stroke(); + } + } + } + + this.Canvas.restore(); + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.Data.Data[i]; + if (data == null) continue; + var value2 = data.Value2; + if (value2 == null) value2 = 0; + if (data == null || isNaN(data.Value) || isNaN(value2)) continue; + + var valueMax = Math.max(data.Value, value2); + var valueMin = Math.min(data.Value, value2); + + if (range.Max == null) range.Max = valueMax; + if (range.Min == null) range.Min = valueMin; + + if (range.Max < valueMax) range.Max = valueMax; + if (range.Min > valueMin) range.Min = valueMin; + } + + return range; + } +} + +//画矩形 +function ChartRectangle() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName ='ChartRectangle'; + this.Color = []; + this.Rect; + this.BorderColor = g_JSChartResource.FrameBorderPen; + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Color || !this.Rect) return; + if (this.Color.length <= 0) return; + + this.Canvas.strokeStyle = this.BorderColor; + var bFill = false; + if (this.Color.length == 2) + { + /* TODO 渐变下次做吧 + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + } + + let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y); + gradient.addColorStop(0, this.Color[0]); + gradient.addColorStop(1, this.Color[1]); + this.Canvas.fillStyle=gradient; + */ + + this.Canvas.fillStyle = this.Color[0]; + bFill = true; + } + else if (this.Color.length == 1) + { + if (this.Color[0]) + { + this.Canvas.fillStyle = this.Color[0]; + bFill = true; + } + } + else + { + return; + } + + var chartWidth = this.ChartBorder.GetWidth(); + var chartHeight = this.ChartBorder.GetHeightEx(); + var left = this.Rect.Left / 1000 * chartWidth; + var top = this.Rect.Top / 1000 * chartHeight; + var right = this.Rect.Right / 1000 * chartWidth; + var bottom = this.Rect.Bottom / 1000 * chartHeight; + + left = this.ChartBorder.GetLeft() + left + top = this.ChartBorder.GetTopEx() + top; + right = this.ChartBorder.GetLeft() + right; + bottom = this.ChartBorder.GetTopEx() + bottom; + var width = Math.abs(left - right); + var height = Math.abs(top - bottom); + if (bFill) this.Canvas.fillRect(left, top, width, height); + this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(top), ToFixedRect(width), ToFixedRect(height)); + this.Canvas.stroke(); + } +} + +//K线叠加 +function ChartOverlayKLine() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(65,105,225)"; + this.MainData; //主图K线数据 + this.SourceData; //叠加的原始数据 + this.Name = "ChartOverlayKLine"; + this.Title; + this.DrawType = 0; + this.ClassName ='ChartOverlayKLine'; + this.CustomDrawType = null; //图形类型 + + this.SetOption = function (option) + { + if (!option) return; + if (IFrameSplitOperator.IsNumber(option.DrawType)) this.CustomDrawType = option.DrawType; + } + + this.DrawKBar = function (firstOpen) //firstOpen 当前屏第1个显示数据 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var isFristDraw = true; + var firstOverlayOpen = null; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.Data.Data[i]; + if (!data || data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + if (firstOverlayOpen == null) firstOverlayOpen = data.Open; + + if (isFristDraw) + { + this.Canvas.strokeStyle = this.Color; + this.Canvas.fillStyle = this.Color; + this.Canvas.beginPath(); + isFristDraw = false; + } + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yLow = this.ChartFrame.GetYFromData(data.Low / firstOverlayOpen * firstOpen); + var yHigh = this.ChartFrame.GetYFromData(data.High / firstOverlayOpen * firstOpen); + var yOpen = this.ChartFrame.GetYFromData(data.Open / firstOverlayOpen * firstOpen); + var yClose = this.ChartFrame.GetYFromData(data.Close / firstOverlayOpen * firstOpen); + var y = yHigh; + + if (data.Open < data.Close) //阳线 + { + if (dataWidth >= 4) + { + if (data.High > data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(this.DrawType == 3 ? Math.max(yClose, yOpen) : yClose), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(this.DrawType == 3 ? Math.min(yClose, yOpen) : yClose)); + } + y = yClose; + } + else + { + y = yClose; + } + + if (isHScreen) { + if (Math.abs(yOpen - y) < 1) + { + this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), 1, ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType == 3) this.Canvas.rect(ToFixedPoint(y), ToFixedPoint(left), ToFixedRect(yOpen - y), ToFixedRect(dataWidth)); //空心柱 + else this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), ToFixedRect(yOpen - y), ToFixedRect(dataWidth)); + } + } + else + { + if (Math.abs(yOpen - y) < 1) + { + this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), 1); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType == 3) this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(y), ToFixedRect(dataWidth), ToFixedRect(yOpen - y)); //空心柱 + else this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), ToFixedRect(yOpen - y)); + } + } + + if (data.Open > data.Low) + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(this.DrawType == 3 ? Math.min(yClose, yOpen) : y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(this.DrawType == 3 ? Math.max(yClose, yOpen) : y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + } + } + else if (data.Open > data.Close) //阴线 + { + if (dataWidth >= 4) + { + if (data.High > data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yOpen), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yOpen)); + } + y = yOpen; + } + else + { + y = yOpen + } + + if (isHScreen) + { + if (Math.abs(yClose - y) < 1) this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), 1, ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(left), ToFixedRect(yClose - y), ToFixedRect(dataWidth)); + } + else + { + if (Math.abs(yClose - y) < 1) this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), 1); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(y), ToFixedRect(dataWidth), ToFixedRect(yClose - y)); + } + + if (data.Open > data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + } + } + else // 平线 + { + if (dataWidth >= 4) + { + if (data.High > data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(yOpen, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yOpen); + } + + y = yOpen; + } + else + { + y = yOpen; + } + + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(left)); + this.Canvas.lineTo(ToFixedPoint(y), ToFixedPoint(right)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(left), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(y)); + } + + if (data.Open > data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow), ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x), ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + } + } + + } + + if (isFristDraw == false) this.Canvas.stroke(); + } + + this.DrawAKLine = function (firstOpen) //美国线 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var firstOverlayOpen = null; + this.Canvas.strokeStyle = this.Color; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + if (firstOverlayOpen == null) firstOverlayOpen = data.Open; + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yLow = this.ChartFrame.GetYFromData(data.Low / firstOverlayOpen * firstOpen); + var yHigh = this.ChartFrame.GetYFromData(data.High / firstOverlayOpen * firstOpen); + var yOpen = this.ChartFrame.GetYFromData(data.Open / firstOverlayOpen * firstOpen); + var yClose = this.ChartFrame.GetYFromData(data.Close / firstOverlayOpen * firstOpen); + + this.Canvas.beginPath(); //最高-最低 + if (isHScreen) + { + this.Canvas.moveTo(yHigh, ToFixedPoint(x)); + this.Canvas.lineTo(yLow, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), yHigh); + this.Canvas.lineTo(ToFixedPoint(x), yLow); + } + + this.Canvas.stroke(); + + if (dataWidth >= 4) + { + this.Canvas.beginPath(); //开盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yOpen), left); + this.Canvas.lineTo(ToFixedPoint(yOpen), x); + } + else + { + this.Canvas.moveTo(left, ToFixedPoint(yOpen)); + this.Canvas.lineTo(x, ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + + this.Canvas.beginPath(); //收盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yClose), right); + this.Canvas.lineTo(ToFixedPoint(yClose), x); + } + else + { + this.Canvas.moveTo(right, ToFixedPoint(yClose)); + this.Canvas.lineTo(x, ToFixedPoint(yClose)); + } + this.Canvas.stroke(); + } + } + + } + + this.DrawCloseLine = function (firstOpen) //收盘价线 + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var firstOverlayOpen = null; + var bFirstPoint = true; + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + if (firstOverlayOpen == null) firstOverlayOpen = data.Open; + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + var yClose = this.ChartFrame.GetYFromData(data.Close / firstOverlayOpen * firstOpen); + + if (bFirstPoint) + { + if (isHScreen) this.Canvas.moveTo(yClose, x); + else this.Canvas.moveTo(x, yClose); + bFirstPoint = false; + } + else + { + if (isHScreen) this.Canvas.lineTo(yClose, x); + else this.Canvas.lineTo(x, yClose); + } + } + + if (bFirstPoint == false) this.Canvas.stroke(); + } + + this.Draw = function () + { + this.TooltipRect = []; + if (!this.MainData || !this.Data) return; + + var xPointCount = this.ChartFrame.XPointCount; + var firstOpen = null; //主线数据第1个开盘价 + for (var i = this.Data.DataOffset, j = 0; i < this.MainData.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.MainData.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + firstOpen = data.Open; + break; + } + + if (firstOpen == null) return; + + var drawTypeBackup = this.DrawType; //备份下线段类型 + if (this.CustomDrawType != null) this.DrawType = this.CustomDrawType; + + if (this.DrawType == 1) this.DrawCloseLine(firstOpen); + else if (this.DrawType == 2) this.DrawAKLine(firstOpen); + else this.DrawKBar(firstOpen); + + this.DrawType = drawTypeBackup; //还原线段类型 + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Max = null; + range.Min = null; + + if (!this.MainData || !this.Data) return range; + + var firstOpen = null; //主线数据第1个收盘价 + for (var i = this.Data.DataOffset, j = 0; i < this.MainData.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.MainData.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + firstOpen = data.Close; + break; + } + + if (firstOpen == null) return range; + + var firstOverlayOpen = null; + var high, low; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.Data.Data[i]; + if (!data || data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + if (firstOverlayOpen == null) firstOverlayOpen = data.Open; + + high = data.High / firstOverlayOpen * firstOpen; + low = data.Low / firstOverlayOpen * firstOpen; + if (range.Max == null) range.Max = high; + if (range.Min == null) range.Min = low; + + if (range.Max < high) range.Max = high; + if (range.Min > low) range.Min = low; + } + + return range; + } +} + +// 多文本集合 支持横屏 +function ChartMultiText() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName ='ChartMultiText'; + this.Texts = []; //[ {Index:, Value:, Text:, Color:, Font: , Baseline:, Line:{ Color:, Dash:[虚线点], KData:"H/L", Offset:[5,10], Width:线粗细 } } ] + this.Font = g_JSChartResource.DefaultTextFont; + this.Color = g_JSChartResource.DefaultTextColor; + this.IsHScreen = false; //是否横屏 + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length <= 0) return; + if (!this.Texts) return; + + this.IsHScreen = (this.ChartFrame.IsHScreen === true); + var xPointCount = this.ChartFrame.XPointCount; + var offset = this.Data.DataOffset; + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRight(); + + if (this.IsHScreen) + { + left = this.ChartBorder.GetTop(); + right = this.ChartBorder.GetBottom(); + } + + for (var i in this.Texts) + { + var item = this.Texts[i]; + if (!item.Text) continue; + if (!IFrameSplitOperator.IsNumber(item.Index)) continue; + + var index = item.Index - offset; + if (index >= 0 && index < xPointCount) + { + var x = this.ChartFrame.GetXFromIndex(index); + var y = this.ChartFrame.GetYFromData(item.Value); + + if (item.Color) this.Canvas.fillStyle = item.Color; + else this.Canvas.fillStyle = this.Color; + if (item.Font) this.Canvas.font = item.Font; + else this.Canvas.font = this.Font; + + var textWidth = this.Canvas.measureText(item.Text).width; + this.Canvas.textAlign = 'center'; + if (x + textWidth / 2 >= right) + { + this.Canvas.textAlign = 'right'; + x = right; + } + else if (x - textWidth / 2 < left) + { + this.Canvas.textAlign = 'left'; + x = left; + } + if (item.Baseline == 1) this.Canvas.textBaseline = 'top'; + else if (item.Baseline == 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = 'middle'; + if (this.IsHScreen) //横屏旋转 + { + this.Canvas.save(); + this.Canvas.translate(y, x); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Text, 0, 0); + this.Canvas.restore(); + } + else + { + this.Canvas.fillText(item.Text, x, y); + } + + if (item.Line) + { + var kItem=this.Data.Data[item.Index]; + var price=item.Line.KData=="H"? kItem.High:kItem.Low; + var yPrice=this.ChartFrame.GetYFromData(price); + var yText=y; + if (Array.isArray(item.Line.Offset) && item.Line.Offset.length==2) + { + if (yText>yPrice) //文字在下方 + { + yText-=item.Line.Offset[1]; + yPrice+=item.Line.Offset[0] + } + else if (yText0) this.Canvas.lineWidth=item.Line.Width; //线宽 + this.Canvas.strokeStyle = item.Line.Color; + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(yText,ToFixedPoint(x)); + this.Canvas.lineTo(yPrice,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yText); + this.Canvas.lineTo(ToFixedPoint(x),yPrice); + } + this.Canvas.stroke(); + this.Canvas.restore(); + } + } + } + } + + this.GetMaxMin = function () + { + var range = { Min: null, Max: null }; + if (!this.Texts) return range; + + var xPointCount = this.ChartFrame.XPointCount; + var start = this.Data.DataOffset; + var end = start + xPointCount; + + for (var i in this.Texts) + { + var item = this.Texts[i]; + if (item.Index >= start && item.Index < end) + { + if (range.Max == null) range.Max = item.Value; + else if (range.Max < item.Value) range.Max = item.Value; + if (range.Min == null) range.Min = item.Value; + else if (range.Min > item.Value) range.Min = item.Value; + } + } + + return range; + } +} + +// 多dom节点 +function ChartMultiHtmlDom() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiHtmlDom"; + this.Texts=[]; //[ {Index:, Value:, Text: ] Text=dom内容 + this.IsHScreen=false; //是否横屏 + this.DrawCallback; //function(op, obj) op:1=开始 2=结束 3=绘制单个数据 + this.DrawItem=[]; + + this.Draw=function() + { + this.DrawItem=[]; + if (this.DrawCallback) this.DrawCallback(1, {Self:this} ); + + this.DrawDom(); + + if (this.DrawCallback) this.DrawCallback(2, { Self:this, Draw:this.DrawItem } ); + } + + this.DrawDom=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + + for(var i in this.Texts) + { + var item=this.Texts[i]; + + if (!item.Text) continue; + if (!IFrameSplitOperator.IsNumber(item.Index)) continue; + + var index=item.Index-offset; + var kItem=this.Data.Data[item.Index]; //K线数据 + var obj={ KData:kItem, Item:item, IsShow:false, Self:this }; + if (index>=0 && index=start && item.Indexitem.Value) range.Min=item.Value; + } + } + + return range; + } +} + +// 线段集合 支持横屏 +function ChartMultiLine() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Lines = []; // [ {Point:[ {Index, Value }, ], Color: }, ] + this.IsHScreen = false; + this.LineWidth=1; + this.LineDash; + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length <= 0) return; + + this.IsHScreen = (this.ChartFrame.IsHScreen === true); + var xPointCount = this.ChartFrame.XPointCount; + var offset = this.Data.DataOffset; + + var drawLines = []; + for (var i in this.Lines) + { + var line = this.Lines[i]; + var drawPoints = { Point: [], Color: line.Color }; + for (var j in line.Point) + { + var point = line.Point[j]; + if (!IFrameSplitOperator.IsNumber(point.Index)) continue; + + var index = point.Index - offset; + if (index >= 0 && index < xPointCount) + { + var x = this.ChartFrame.GetXFromIndex(index); + var y = this.ChartFrame.GetYFromData(point.Value); + drawPoints.Point.push({ X: x, Y: y }); + } + } + + if (drawPoints.Point.length >= 2) drawLines.push(drawPoints) + } + + this.Canvas.save(); + for (var i in drawLines) + { + if (this.LineDash) this.Canvas.setLineDash(this.LineDash); + if (IFrameSplitOperator.IsPlusNumber(this.LineWidth)) this.Canvas.lineWidth=this.LineWidth; + else this.Canvas.lineWidth=1; + + var item = drawLines[i]; + this.DrawLine(item); + } + this.Canvas.restore(); + } + + this.DrawLine = function (line) + { + this.Canvas.strokeStyle = line.Color; + for (var i in line.Point) + { + var item = line.Point[i]; + if (i == 0) + { + this.Canvas.beginPath(); + if (this.IsHScreen) this.Canvas.moveTo(item.Y, item.X); + else this.Canvas.moveTo(item.X, item.Y); + } + else + { + if (this.IsHScreen) this.Canvas.lineTo(item.Y, item.X); + else this.Canvas.lineTo(item.X, item.Y); + } + } + this.Canvas.stroke(); + } + + this.GetMaxMin = function () + { + var range = { Min: null, Max: null }; + var xPointCount = this.ChartFrame.XPointCount; + var start = this.Data.DataOffset; + var end = start + xPointCount; + + for (var i in this.Lines) + { + var line = this.Lines[i]; + for (var j in line.Point) + { + var point = line.Point[j]; + if (point.Index >= start && point.Index < end) + { + if (range.Max == null) range.Max = point.Value; + else if (range.Max < point.Value) range.Max = point.Value; + if (range.Min == null) range.Min = point.Value; + else if (range.Min > point.Value) range.Min = point.Value; + } + } + } + + return range; + } +} + +// 柱子集合 支持横屏 +function ChartMultiBar() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Bars = []; // [ {Point:[ {Index, Value, Value2 }, ], Color:, Width: , Type: 0 实心 1 空心 }, ] + this.IsHScreen = false; + + this.Draw = function () + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length <= 0) return; + + this.IsHScreen = (this.ChartFrame.IsHScreen === true); + var xPointCount = this.ChartFrame.XPointCount; + var offset = this.Data.DataOffset; + var dataWidth = this.ChartFrame.DataWidth; + + var drawBars = []; + for (var i in this.Bars) + { + var item = this.Bars[i]; + var drawPoints = { Point: [], Color: item.Color, Width: dataWidth, Type: 0 }; + if (item.Type > 0) drawPoints.Type = item.Type; + if (item.Width > 0) + { + drawPoints.Width = item.Width; + if (drawPoints.Width > dataWidth) drawPoints.Width = dataWidth; + } + else + { + if (drawPoints.Width < 4) drawPoints.Width = 1; + } + + for (var j in item.Point) + { + var point = item.Point[j]; + if (!IFrameSplitOperator.IsNumber(point.Index)) continue; + + var index = point.Index - offset; + if (index >= 0 && index < xPointCount) + { + var x = this.ChartFrame.GetXFromIndex(index); + var y = this.ChartFrame.GetYFromData(point.Value); + var y2 = this.ChartFrame.GetYFromData(point.Value2); + drawPoints.Point.push({ X: x, Y: y, Y2: y2 }); + } + } + + if (drawPoints.Point.length > 0) drawBars.push(drawPoints) + } + + for (var i in drawBars) + { + var item = drawBars[i]; + if (item.Width >= 4) + { + if (item.Type == 1) this.DrawHollowBar(item); + else this.DrawFillBar(item); + } + else + { + this.DrawLineBar(item); + } + } + } + + this.DrawLineBar = function (bar) + { + this.Canvas.strokeStyle = bar.Color; + var backupLineWidth = this.Canvas.lineWidth; + this.Canvas.lineWidth = bar.Width; + for (var i in bar.Point) + { + var item = bar.Point[i]; + + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(ToFixedPoint(item.Y), ToFixedPoint(item.X)); + this.Canvas.lineTo(ToFixedPoint(item.Y2), ToFixedPoint(item.X)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(item.X), ToFixedPoint(item.Y)); + this.Canvas.lineTo(ToFixedPoint(item.X), ToFixedPoint(item.Y2)); + } + + this.Canvas.stroke(); + } + + this.Canvas.lineWidth = backupLineWidth; + } + + this.DrawFillBar = function (bar) + { + this.Canvas.fillStyle = bar.Color; + for (var i in bar.Point) + { + var item = bar.Point[i]; + var x = item.X - (bar.Width / 2); + var y = Math.min(item.Y, item.Y2); + var barWidth = bar.Width; + var barHeight = Math.abs(item.Y - item.Y2); + if (this.IsHScreen) + this.Canvas.fillRect(ToFixedRect(y), ToFixedRect(x), ToFixedRect(barHeight), ToFixedRect(barWidth)); + else + this.Canvas.fillRect(ToFixedRect(x), ToFixedRect(y), ToFixedRect(barWidth), ToFixedRect(barHeight)); + } + } + + this.DrawHollowBar = function (bar) //空心柱子 + { + this.Canvas.strokeStyle = bar.Color; + var backupLineWidth = 1; + for (var i in bar.Point) + { + var item = bar.Point[i]; + var x = item.X - (bar.Width / 2); + var y = Math.min(item.Y, item.Y2); + var barWidth = bar.Width; + var barHeight = Math.abs(item.Y - item.Y2); + this.Canvas.beginPath(); + if (this.IsHScreen) + this.Canvas.rect(ToFixedPoint(y), ToFixedPoint(x), ToFixedRect(barHeight), ToFixedRect(barWidth)); + else + this.Canvas.rect(ToFixedPoint(x), ToFixedPoint(y), ToFixedRect(barWidth), ToFixedRect(barHeight)); + + this.Canvas.stroke(); + } + + this.Canvas.lineWidth = backupLineWidth; + } + + this.GetMaxMin = function () + { + var range = { Min: null, Max: null }; + var xPointCount = this.ChartFrame.XPointCount; + var start = this.Data.DataOffset; + var end = start + xPointCount; + for (var i in this.Bars) + { + var item = this.Bars[i]; + for (var j in item.Point) + { + var point = item.Point[j]; + if (point.Index >= start && point.Index < end) + { + var minValue = Math.min(point.Value, point.Value2); + var maxValue = Math.max(point.Value, point.Value2); + if (range.Max == null) range.Max = maxValue; + else if (range.Max < maxValue) range.Max = maxValue; + if (range.Min == null) range.Min = minValue; + else if (range.Min > minValue) range.Min = minValue; + } + } + } + + return range; + } +} + +//分钟信息地雷 支持横屏 +function ChartMinuteInfo() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = "ChartMinuteInfo"; + this.Data = new Map() //Map key=date-time, value=[{Date, Time, Title, Type, ID:}] + this.SourceData; + this.ChartMinutePrice; + this.YClose; + + this.TextColor = g_JSChartResource.MinuteInfo.TextColor; + this.Font = g_JSChartResource.MinuteInfo.Font; + this.PointColor = g_JSChartResource.MinuteInfo.PointColor; + this.LineColor = g_JSChartResource.MinuteInfo.LineColor; + this.TextBGColor = g_JSChartResource.MinuteInfo.TextBGColor; + this.TextHeight = 18; + + this.TextRectCache = []; + this.InfoDrawCache = []; + this.FrameBottom; + this.FrameTop; + this.FrameLeft; + this.FrameRight; + this.YOffset = 5; + this.IsHScreen = false; + + this.SetOption = function (option) + { + if (option.TextColor) this.TextColor = option.TextColor; + if (option.TextBGColor) this.TextBGColor = option.TextBGColor; + if (option.Font) this.Font = option.Font; + if (option.PointColor) this.PointColor = option.PointColor; + if (option.LineColor) this.LineColor = option.LineColor; + if (option.TextHeight > 0) this.TextHeight = option.TextHeight; + } + + this.Draw = function () + { + if (!this.ChartMinutePrice) return; + if (!this.Data || this.Data.size <= 0) return; + + this.TextRectCache = []; + this.InfoDrawCache = []; + this.IsHScreen = (this.ChartFrame.IsHScreen === true); + + var xPointCount = this.ChartFrame.XPointCount; + var minuteCount = this.ChartFrame.MinuteCount; + + this.FrameBottom = this.ChartBorder.GetBottom(); + this.FrameTop = this.ChartBorder.GetTop(); + this.FrameLeft = this.ChartBorder.GetLeft(); + this.FrameRight = this.ChartBorder.GetRight(); + if (this.IsHScreen) + { + this.FrameRight = this.ChartBorder.GetBottom(); + this.FrameLeft = this.ChartBorder.GetTop(); + this.FrameBottom = this.ChartBorder.GetLeft(); + this.FrameTop = this.ChartBorder.GetRight(); + } + + this.YClose = this.ChartMinutePrice.YClose; + + var data = this.ChartMinutePrice.Data; + var isBeforeData = false; + if (this.ChartMinutePrice.SourceData) + { + data = this.ChartMinutePrice.SourceData; + isBeforeData = true; + } + + this.Canvas.font = this.Font; + for (var i = data.DataOffset, j = 0; i < data.Data.length && j < xPointCount; ++i, ++j) + { + var item = this.SourceData.Data[i]; + if (isBeforeData && item.Before) continue; + if (!item) continue; + + var dateTime = item.DateTime; + if (!this.Data.has(dateTime)) continue; + if (this.IsHScreen) + this.CalcuateInfoHScreenPosition(this.Data.get(dateTime), j, item); + else + this.CalcuateInfoPosition(this.Data.get(dateTime), j, item); + } + + for (var i in this.InfoDrawCache) + { + var item = this.InfoDrawCache[i]; + this.DrawInfoLines(item); + } + + for (var i in this.InfoDrawCache) + { + var item = this.InfoDrawCache[i]; + this.DrawInfoText(item); + } + + this.TextRectCache = []; + this.InfoDrawCache = []; + } + + this.CalcuateInfoPosition = function (infoItem, index, minuteItem) + { + if (!infoItem || !infoItem.Data || infoItem.Data.length <= 0) return; + + var showItem = infoItem.Data[0]; + var textWidth = this.Canvas.measureText(showItem.Title).width + 4; + var textHeight = this.TextHeight; + + var x = this.ChartFrame.GetXFromIndex(index); + var y = this.ChartFrame.GetYFromData(minuteItem.Close); + x = ToFixedPoint(x); + + var isDrawLeft = x < (this.FrameLeft + Math.abs(this.FrameLeft - this.FrameRight) / 2); + + var ARRAY_OFFSET = [2, 4, 3, 2, 3, 3, 2]; + var offset = textHeight + ARRAY_OFFSET[index % ARRAY_OFFSET.length]; + var yData = + { + Y: + [ + { Value: y + (textHeight + this.YOffset), Offset: offset }, + { Value: y - (2 * textHeight + this.YOffset), Offset: -offset } + ] + }; + + if (minuteItem.Close < this.YClose) + yData.Y = yData.Y.reverse(); + + var rtBorder = { X: x, Y: null, Width: textWidth, Height: textHeight }; + if (!isDrawLeft) rtBorder.X -= rtBorder.Width; + + this.FixTextRect(rtBorder, yData); + var InfoDrawItem = { Border: rtBorder, Start: { X: x, Y: y }, IsLeft: isDrawLeft, Title: showItem.Title }; + + this.InfoDrawCache.push(InfoDrawItem); + this.TextRectCache.push(rtBorder); + } + + this.CalcuateInfoHScreenPosition = function (infoItem, index, minuteItem) + { + if (!infoItem || !infoItem.Data || infoItem.Data.length <= 0) return; + + var showItem = infoItem.Data[0]; + var textHeight = this.Canvas.measureText(showItem.Title).width + 4; + var textWidth = this.TextHeight; + + var y = this.ChartFrame.GetXFromIndex(index); + var x = this.ChartFrame.GetYFromData(minuteItem.Close); + y = ToFixedPoint(y); + + var isDrawLeft = y < (this.FrameLeft + Math.abs(this.FrameLeft - this.FrameRight) / 2); + + var ARRAY_OFFSET = [2, 4, 3, 2, 3, 3, 2]; + var offset = textWidth + ARRAY_OFFSET[index % ARRAY_OFFSET.length]; + var xData = + { + X: + [ + { Value: x + (textWidth + this.YOffset), Offset: offset }, + { Value: x - (2 * textWidth + this.YOffset), Offset: -offset } + ] + }; + + if (minuteItem.Close > this.YClose) + xData.X = xData.X.reverse(); + + var rtBorder = { X: null, Y: y, Width: textWidth, Height: textHeight }; + if (!isDrawLeft) rtBorder.Y -= rtBorder.Height; + + this.FixHScreenTextRect(rtBorder, xData); + var InfoDrawItem = { Border: rtBorder, Start: { X: x, Y: y }, IsLeft: isDrawLeft, Title: showItem.Title }; + + this.InfoDrawCache.push(InfoDrawItem); + this.TextRectCache.push(rtBorder); + } + + this.DrawInfoLines = function (item) + { + var rtBorder = item.Border; + var isDrawLeft = item.IsLeft; + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(item.Start.X, item.Start.Y); + if (isDrawLeft) + { + this.Canvas.lineTo(rtBorder.X, rtBorder.Y); + } + else + { + if (this.IsHScreen) this.Canvas.lineTo(rtBorder.X, rtBorder.Y + rtBorder.Height); + else this.Canvas.lineTo(rtBorder.X + rtBorder.Width, rtBorder.Y); + } + this.Canvas.stroke(); + + this.Canvas.fillStyle = this.PointColor; + this.Canvas.beginPath(); + this.Canvas.arc(item.Start.X, item.Start.Y, 5, 0, 2 * Math.PI); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.DrawInfoText = function (item) + { + var rtBorder = item.Border; + var x = rtBorder.X, y = rtBorder.Y; + this.Canvas.fillStyle = this.TextBGColor; + this.Canvas.fillRect(x, y, rtBorder.Width, rtBorder.Height); + + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.beginPath(); + this.Canvas.rect(x, y, rtBorder.Width, rtBorder.Height); + this.Canvas.stroke(); + + if (this.IsHScreen) + { + this.Canvas.save(); + this.Canvas.translate(rtBorder.X, rtBorder.Y); + this.Canvas.rotate(90 * Math.PI / 180); + x = 0; y = 0; + } + + this.Canvas.textAlign = 'left' + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + if (this.IsHScreen) this.Canvas.fillText(item.Title, x + 2, y - rtBorder.Width / 2); + else this.Canvas.fillText(item.Title, x+2, y + rtBorder.Height / 2); + + if (this.IsHScreen) this.Canvas.restore(); + } + + this.FixTextRect = function (rect, yData) + { + for (var k in yData.Y) + { + var yItem = yData.Y[k]; + rect.Y = yItem.Value; + + var y; + for (var j = 0; j < 10; ++j) + { + var isOverlap = false; + for (var i in this.TextRectCache) + { + var item = this.TextRectCache[i]; + if (this.IsOverlap(item, rect)) + { + isOverlap = true; + break; + } + } + + if (isOverlap == false) return; + + y = rect.Y; + y += yItem.Offset; + if (y + rect.Height > this.FrameBottom || y < this.FrameTop) break; + + rect.Y = y; + } + } + } + + this.FixHScreenTextRect = function (rect, xData) + { + for (var k in xData.X) + { + var xItem = xData.X[k]; + rect.X = xItem.Value; + + var x; + for (var j = 0; j < 10; ++j) + { + var isOverlap = false; + for (var i in this.TextRectCache) + { + var item = this.TextRectCache[i]; + if (this.IsOverlap(item, rect)) + { + isOverlap = true; + break; + } + } + + if (isOverlap == false) return; + + x = rect.X; + x += xItem.Offset; + if (x + rect.Width < this.FrameBottom || x > this.FrameTop) break; + + rect.X = x; + } + } + } + + this.IsOverlap = function (rc1, rc2) + { + if (rc1.X + rc1.Width > rc2.X && rc2.X + rc2.Width > rc1.X && rc1.Y + rc1.Height > rc2.Y && rc2.Y + rc2.Height > rc1.Y) + return true; + else + return false; + } + + this.GetMaxMin = function () + { + var range = { Min: null, Max: null }; + return range; + } +} + +//买卖盘 +function ChartBuySell() +{ + this.newMethod = ChartSingleText; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = "ChartBuySell"; + this.TextFont = g_JSChartResource.KLineTrain.Font; //"bold 14px arial"; //买卖信息字体 + this.LastDataIcon = g_JSChartResource.KLineTrain.LastDataIcon; //{Color:'rgb(0,0,205)',Text:'↓'}; + this.BuyIcon = g_JSChartResource.KLineTrain.BuyIcon; //{Color:'rgb(0,0,205)',Text:'B'}; + this.SellIcon = g_JSChartResource.KLineTrain.SellIcon; //{Color:'rgb(0,0,205)',Text:'S'}; + this.BuySellData = new Map(); //Key=数据索引index Value:Data:[ { Op: 买/卖 0=buy 1=sell, Date:, Time, Price: Vol:}, ] + + this.AddTradeItem = function (tradeItem) + { + if (this.BuySellData.has(tradeItem.Key)) + { + var Trade = this.BuySellData.get(tradeItem.Key); + Trade.Data.push(tradeItem); + } + else + { + this.BuySellData.set(tradeItem.Key, { Data: [tradeItem] }); + } + } + + this.ClearTradeData = function () + { + this.BuySellData = new Map(); + } + + this.Draw = function () + { + if (!this.Data || !this.Data.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen === true) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var bottom = this.ChartBorder.GetBottomEx(); + var top = this.ChartBorder.GetTopEx(); + var height = this.ChartBorder.GetHeightEx(); + if (isHScreen) + { + top = this.ChartBorder.GetRightEx(); + bottom = this.ChartBorder.GetLeftEx(); + height = this.ChartBorder.GetWidthEx(); + } + + this.Canvas.font = this.TextFont; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + if (x > chartright) break; + + if (i == this.Data.Data.length - 1) + { + var x = this.ChartFrame.GetXFromIndex(j); + var yHigh = this.ChartFrame.GetYFromData(value.High); + if (this.LastDataIcon.Text) + { + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'bottom'; + this.Canvas.fillStyle = this.LastDataIcon.Color; + this.Canvas.font = this.TextFont; + this.DrawText(this.LastDataIcon.Text, x, yHigh, isHScreen); + } + else + { + var obj = + { + X: x, Top: top, Bottom: bottom, Height: height, + DataWidth: dataWidth, Color: this.LastDataIcon.Color, IsHScreen: isHScreen, + }; + this.DrawLastData(obj); + } + } + + var key = i; + if (!this.BuySellData.has(key)) continue; + + var trade = this.BuySellData.get(key); + var x = this.ChartFrame.GetXFromIndex(j); + var yHigh = this.ChartFrame.GetYFromData(value.High); + var yLow = this.ChartFrame.GetYFromData(value.Low); + var drawInfo = [false, false]; //0=buy 1=sell + for (var k in trade.Data) + { + if (drawInfo[0] == true && drawInfo[1] == true) break; //买卖图标只画一次 + + var bsItem = trade.Data[k]; + if (bsItem.Op == 0 && drawInfo[0] == false) //买 标识在最低价上 + { + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'top'; + this.Canvas.fillStyle = this.BuyIcon.Color; + this.DrawText(this.BuyIcon.Text, x, yLow, isHScreen); + drawInfo[0] = true; + } + else if (bsItem.Op == 1 && drawInfo[1] == false) //卖 标识在最高价上 + { + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'bottom'; + this.Canvas.fillStyle = this.SellIcon.Color; + this.DrawText(this.SellIcon.Text, x, yHigh, isHScreen); + drawInfo[1] = true; + } + } + } + } + + this.DrawLastData=function(obj) + { + this.Canvas.fillStyle = obj.Color; + + var width = obj.DataWidth; + if (this.LastDataIcon.Width >= 2 && this.LastDataIcon.Width < obj.DataWidth) + width = this.LastDataIcon.Width; + var left = obj.X - width / 2; + + if (obj.IsHScreen) + { + this.Canvas.fillRect(ToFixedRect(obj.Bottom), ToFixedRect(left), ToFixedRect(obj.Height), ToFixedRect(width)); + } + else + { + var left = obj.X - width/2; + this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(obj.Top), ToFixedRect(width), ToFixedRect(obj.Height)); + } + } +} + +//分钟成交量 +function ChartMinuteVolumBar() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.UpColor = g_JSChartResource.UpBarColor; + this.DownColor = g_JSChartResource.DownBarColor; + this.CustomColor=g_JSChartResource.Minute.VolBarColor; + this.YClose; //前收盘 + + this.Draw = function () + { + var isHScreen = (this.ChartFrame.IsHScreen === true) + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var yBottom = this.ChartFrame.GetYFromData(0); + var yPrice = this.YClose; //上一分钟的价格 + + if (this.CustomColor) this.Canvas.strokeStyle=this.CustomColor; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var item = this.Data.Data[i]; + if (!item || !item.Vol) continue; + + var y = this.ChartFrame.GetYFromData(item.Vol); + var x = this.ChartFrame.GetXFromIndex(i); + if (x > chartright) break; + //价格>=上一分钟价格 红色 否则绿色 + if (!this.CustomColor) this.Canvas.strokeStyle = item.Close >= yPrice ? this.UpColor : this.DownColor; + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(yBottom, ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yBottom); + } + this.Canvas.stroke(); + yPrice = item.Close; + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = 0; + range.Max = null; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var item = this.Data.Data[i]; + if (!item || !item.Vol) continue; + if (range.Max == null) range.Max = item.Vol; + if (range.Max < item.Vol) range.Max = item.Vol; + } + + return range; + } + } + +//MACD森林线 支持横屏 +function ChartMACD() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName ='ChartMACD'; + this.UpColor = g_JSChartResource.UpBarColor; + this.DownColor = g_JSChartResource.DownBarColor; + this.LineWidth=1; + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.ChartFrame.IsHScreen === true) + { + this.HScreenDraw(); + return; + } + + var isMinute=this.IsMinuteFrame(); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + + var lineWidth=this.LineWidth; + if (this.LineWidth==50) lineWidth=dataWidth; + else if (lineWidth>dataWidth) lineWidth=dataWidth; + + this.Canvas.save(); + this.Canvas.lineWidth=lineWidth; + + var bFirstPoint = true; + var drawCount = 0; + var yBottom = this.ChartFrame.GetYFromData(0); + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + } + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + var xFix = parseInt(x.toString()) + 0.5; //毛边修正 + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix, yBottom); + this.Canvas.lineTo(xFix, y); + + if (value >= 0) this.Canvas.strokeStyle = this.UpColor; + else this.Canvas.strokeStyle = this.DownColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + } + + this.Canvas.restore(); + } + + this.HScreenDraw = function () + { + var isMinute=this.IsMinuteFrame(); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var yBottom = this.ChartFrame.GetYFromData(0); + + var lineWidth=this.LineWidth; + if (this.LineWidth==50) lineWidth=dataWidth; + else if (lineWidth>dataWidth) lineWidth=dataWidth; + + this.Canvas.save(); + this.Canvas.lineWidth=lineWidth; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + } + var y = this.ChartFrame.GetYFromData(value); + + if (x > chartright) break; + + this.Canvas.beginPath(); + this.Canvas.moveTo(yBottom, ToFixedPoint(x)); + this.Canvas.lineTo(y, ToFixedPoint(x)); + + if (value >= 0) this.Canvas.strokeStyle = this.UpColor; + else this.Canvas.strokeStyle = this.DownColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + } + + this.Canvas.restore(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 等待提示 +function ChartSplashPaint() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Font = g_JSChartResource.DefaultTextFont; //字体 + this.TextColor = g_JSChartResource.DefaultTextColor; //文本颜色 + this.IsEnableSplash = false; + this.SplashTitle = '数据加载中.....'; + this.HQChart; + + this.EnableSplash=function(bEnable) + { + this.IsEnableSplash=bEnable; + if (this.HQChart) + { + var event=this.HQChart.GetEnableSplashEvent(); + if (event) + { + var data={ Enable:bEnable }; + event.Callback(event,data,this); + } + } + } + + this.SetTitle=function(title) + { + this.SplashTitle=title; + } + + this.Draw = function () + { + if (!this.IsEnableSplash) return; + + if (this.Frame.IsHScreen === true) + { + this.HScreenDraw(); + return; + } + + var xCenter = (this.Frame.ChartBorder.GetLeft() + this.Frame.ChartBorder.GetRight()) / 2; + var yCenter = (this.Frame.ChartBorder.GetTop() + this.Frame.ChartBorder.GetBottom()) / 2; + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(this.SplashTitle, xCenter, yCenter); + } + + this.HScreenDraw = function () //横屏 + { + var xCenter = (this.Frame.ChartBorder.GetLeft() + this.Frame.ChartBorder.GetRight()) / 2; + var yCenter = (this.Frame.ChartBorder.GetTop() + this.Frame.ChartBorder.GetBottom()) / 2; + + this.Canvas.save(); + this.Canvas.translate(xCenter, yCenter); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(this.SplashTitle, 0, 0); + + this.Canvas.restore(); + } +} + +//填充背景 支持横屏 +function ChartBackground() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartBackground"; + this.Color=null; + this.ColorAngle=0; //0 竖向 1 横向 + this.IsDrawFirst = true; //面积图在K线前面画,否则回挡住K线的 + this.IsHScreen=false; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Color) return; + if (this.Color.length<=0) return; + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + if (this.Color.length==2) + { + if (this.IsHScreen) + { + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + } + else + { + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + } + } + + let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y); + gradient.addColorStop(0, this.Color[0]); + gradient.addColorStop(1, this.Color[1]); + this.Canvas.fillStyle=gradient; + } + else if (this.Color.length==1) + { + this.Canvas.fillStyle=this.Color[0]; + } + else + { + return; + } + + if (this.Name=="DRAWGBK2" || this.Name=="KLINE_BG") + { + this.DrawRegion(); + return; + } + + if (this.IsHScreen) + { + var left=this.ChartBorder.GetLeftEx(); + var top=this.ChartBorder.GetTop(); + var width=this.ChartBorder.GetWidthEx(); + var height=this.ChartBorder.GetHeight(); + } + else + { + var left=this.ChartBorder.GetLeft(); + var top=this.ChartBorder.GetTopEx(); + var width=this.ChartBorder.GetWidth(); + var height=this.ChartBorder.GetHeightEx(); + } + this.Canvas.fillRect(left, top,width, height); + } + + this.DrawRegion=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + if (this.IsHScreen) + { + top=this.ChartBorder.GetRightEx(); + bottom=this.ChartBorder.GetLeftEx(); + } + + var aryPoint=[]; //点坐标 + for(var i=this.Data.DataOffset,j=0;i0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + if (this.IsHScreen) + { + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y+halfWidth); + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y-halfWidth); + } + else + { + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + color=null; + } + + if (!item) continue; + + if (firstPoint) + { + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(item.Line.X, item.Line.Y-halfWidth); + this.Canvas.lineTo(item.Line.X, item.Line.Y+halfWidth); + } + else + { + this.Canvas.moveTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + } + firstPoint=false; + color=item.Color; + } + else + { + if (this.IsHScreen) + { + this.Canvas.lineTo(item.Line.X, item.Line.Y-halfWidth); + this.Canvas.lineTo(item.Line.X, item.Line.Y+halfWidth); + } + else + { + this.Canvas.lineTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + } + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + if (this.IsHScreen) + { + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y+halfWidth); + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y-halfWidth); + } + else + { + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + } + + this.GetMaxMin=function() + { + return { Min:null, Max:null }; + } +} + +//锁 支持横屏 +function ChartLock() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + this.WidthDiv = 0.2; // 框子宽度占比 + this.LockCount = 10; // 锁最新的几个数据 + this.BGColor = g_JSChartResource.LockBGColor; + this.TextColor = g_JSChartResource.LockTextColor; + this.Font = g_JSChartResource.DefaultTextFont; + this.Title = '🔒开通权限'; + this.LockRect = null; //上锁区域 + this.LockID; //锁ID + this.Callback; //回调 + this.IndexName; //指标名字 + + this.Draw = function () + { + this.LockRect = null; + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.ChartFrame.IsHScreen === true) + { + this.HScreenDraw(); + return; + } + + var xOffset = this.ChartBorder.GetRight(); + var lOffsetWidth = 0; + if (this.ChartFrame.Data != null) + { + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + for (var i = this.ChartFrame.Data.DataOffset, j = 0; i < this.ChartFrame.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.ChartFrame.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + } + lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount; + } + if (lOffsetWidth == 0) + { + lOffsetWidth = (xOffset - this.ChartBorder.GetLeft()) * this.WidthDiv; + } + var lLeft = xOffset - lOffsetWidth; + if (lLeft < this.ChartBorder.GetLeft()) + lLeft = this.ChartBorder.GetLeft(); + var lHeight = this.ChartBorder.GetBottom() - this.ChartBorder.GetTop(); + var lWidth = this.ChartBorder.GetRight() - lLeft; + this.Canvas.fillStyle = this.BGColor; + this.Canvas.fillRect(lLeft, this.ChartBorder.GetTop(), lWidth, lHeight); + var xCenter = lLeft + lWidth / 2; + var yCenter = this.ChartBorder.GetTop() + lHeight / 2; + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(this.Title, xCenter, yCenter); + + this.LockRect = { Left: lLeft, Top: this.ChartBorder.GetTop(), Width: lWidth, Heigh: lHeight }; //保存上锁区域 + } + + this.HScreenDraw = function () + { + var xOffset = this.ChartBorder.GetBottom(); + + var lOffsetWidth = 0; + + if (this.ChartFrame.Data != null) + { + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + //求最后1个数据的位置 + for (var i = this.ChartFrame.Data.DataOffset, j = 0; i < this.ChartFrame.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var data = this.ChartFrame.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + } + lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount; + } + if (lOffsetWidth == 0) + { + lOffsetWidth = (xOffset - this.ChartBorder.GetTop()) * this.WidthDiv; + } + + var lLeft = xOffset - lOffsetWidth; + if (lLeft < this.ChartBorder.GetTop()) lLeft = this.ChartBorder.GetTop(); + var lHeight = this.ChartBorder.GetRight() - this.ChartBorder.GetLeft(); + var lWidth = this.ChartBorder.GetBottom() - lLeft; + this.Canvas.fillStyle = this.BGColor; + this.Canvas.fillRect(this.ChartBorder.GetLeft(), lLeft, lHeight, lWidth); + + var xCenter = this.ChartBorder.GetLeft() + lHeight / 2; + var yCenter = lLeft + lWidth / 2; + this.Canvas.save(); + this.Canvas.translate(xCenter, yCenter); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(this.Title, 0, 0); + this.Canvas.restore(); + + this.LockRect = { Left: this.ChartBorder.GetLeft(), Top: lLeft, Width: lHeight, Heigh: lWidth }; //保存上锁区域 + } + + //x,y是否在上锁区域 + this.GetTooltipData = function (x, y, tooltip) + { + if (this.LockRect == null) return false; + + if (this.IsPointInRect(x, y, this.LockRect.Left, this.LockRect.Top, this.LockRect.Width, this.LockRect.Heigh)) + { + tooltip.Data = { ID: this.LockID, Callback: this.Callback, IndexName: this.IndexName }; + tooltip.ChartPaint = this; + return true; + } + + return false; + } + + this.IsPointInRect = function (x, y, left, top, width, heigh) + { + if (x > left && x < left + width && y > top && y < top + heigh) return true; + return false; + } +} + +//通达信语法 VOLSTICK 支持横屏 +function ChartVolStick() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.UpColor = g_JSChartResource.UpBarColor; + this.DownColor = g_JSChartResource.DownBarColor; + this.HistoryData; //历史数据 + this.KLineDrawType = 0; + this.ClassName = 'ChartVolStick'; + this.MinBarWidth=g_JSChartResource.MinKLineBarWidth; //最小的柱子宽度 + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.ChartFrame.IsHScreen === true) + { + this.HScreenDraw(); + return; + } + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + var yBottom = this.ChartFrame.GetYFromData(0); + var isMinute=this.IsMinuteFrame(); + + if (dataWidth >= this.MinBarWidth) + { //只有K线, 分时图dataWidth=1 + yBottom = ToFixedRect(yBottom); + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + var kItem = this.HistoryData.Data[i]; + if (value == null || kItem == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + + var y = this.ChartFrame.GetYFromData(value); + var bUp = false; + if (kItem.Close >= kItem.Open) + { + this.Canvas.fillStyle = this.UpColor; + bUp = true; + } + else + { + this.Canvas.fillStyle = this.DownColor; + } + + //高度调整为整数 + var height = ToFixedRect(Math.abs(yBottom - y)>=1 ? yBottom - y : 1); + y = yBottom - height; + if (bUp && (this.KLineDrawType == 1 || this.KLineDrawType == 2 || this.KLineDrawType == 3)) //空心柱子 + { + this.Canvas.strokeStyle = this.UpColor; + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(y), ToFixedRect(dataWidth), height); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left), y, ToFixedRect(dataWidth), height); + } + } + } + else //太细了直接话线 + { + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + var kItem = this.HistoryData.Data[i]; + if (value == null || kItem == null) continue; + + var y = this.ChartFrame.GetYFromData(value); + + if (isMinute) + { + var x=this.ChartFrame.GetXFromIndex(j); + } + else + { + var left=xOffset; + var right=xOffset+dataWidth; + var x=left+(right-left)/2; + } + + if (x > chartright) break; + + if (kItem.Close > kItem.Open) + this.Canvas.strokeStyle = this.UpColor; + else + this.Canvas.strokeStyle = this.DownColor; + + var x = this.ChartFrame.GetXFromIndex(j); + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yBottom); + this.Canvas.stroke(); + } + } + } + + this.HScreenDraw = function () //横屏画法 + { + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + var chartBottom = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var yBottom = this.ChartFrame.GetYFromData(0); + + if (dataWidth >= this.MinBarWidth) + { + yBottom = ToFixedRect(yBottom); + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + var kItem = this.HistoryData.Data[i]; + if (value == null || kItem == null) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartBottom) break; + + var y = this.ChartFrame.GetYFromData(value); + var bUp = false; + + if (kItem.Close >= kItem.Open) + { + bUp = true; + this.Canvas.fillStyle = this.UpColor; + } + else + { + this.Canvas.fillStyle = this.DownColor; + } + + //高度调整为整数 + var height = ToFixedRect(y - yBottom); + if (bUp && (this.KLineDrawType == 1 || this.KLineDrawType == 2 || this.KLineDrawType == 3)) //空心柱子 + { + this.Canvas.strokeStyle = this.UpColor; + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(yBottom), ToFixedPoint(left), height, ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(yBottom, ToFixedRect(left), height, ToFixedRect(dataWidth)); + } + } + } + else //太细了直接话线 + { + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + var kItem = this.HistoryData.Data[i]; + if (value == null || kItem == null) continue; + + var y = this.ChartFrame.GetYFromData(value); + var x = this.ChartFrame.GetXFromIndex(j); + if (x > chartBottom) break; + + if (kItem.Close > kItem.Open) + this.Canvas.strokeStyle = this.UpColor; + else + this.Canvas.strokeStyle = this.DownColor; + + var x = this.ChartFrame.GetXFromIndex(j); + this.Canvas.beginPath(); + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(yBottom, ToFixedPoint(x)); + this.Canvas.stroke(); + } + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = 0; + range.Max = null; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (range.Max == null) range.Max = value; + + if (range.Max < value) range.Max = value; + } + + return range; + } +} + +function ChartText() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.TextFont = "14px 微软雅黑"; + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + + for (var i in this.Data.Data) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var price = value.Value; + var position = value.Position; + + if (position == 'Left') { + var x = this.ChartFrame.GetXFromIndex(0); + var y = this.ChartFrame.GetYFromData(price); + + if (x > chartright) continue; + + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = value.Color; + this.Canvas.font = this.TextFont; + this.Canvas.fillText(value.Message, x, y); + } + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + + for (var i in this.Data.Data) + { + var data = this.Data.Data[i]; + if (data == null || isNaN(data.Value)) continue; + + var value = data.Value; + + if (range.Max == null) range.Max = value; + if (range.Min == null) range.Min = value; + + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + + return range; + } +} + +/* 水平面积 只有1个数据 + Data 数据结构 + Value, Value2 区间最大最小值 + Color=面积的颜色 + Title=标题 TitleColor=标题颜色 + 支持横屏 +*/ +function ChartStraightArea() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + this.Font = '11px 微软雅黑'; + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + if (this.ChartFrame.IsHScreen === true) + { + this.HScreenDraw(); + return; + } + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + var bottom = this.ChartBorder.GetBottom(); + var left = this.ChartBorder.GetLeft(); + var xPointCount = this.ChartFrame.XPointCount; + + var xRight = this.ChartFrame.GetXFromIndex(xPointCount - 1); + + //画背景 + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + if (item == null || isNaN(item.Value) || isNaN(item.Value2)) continue; + if (item.Color == null) continue; + + let valueMax = Math.max(item.Value, item.Value2); + let valueMin = Math.min(item.Value, item.Value2); + + let yTop = this.ChartFrame.GetYFromData(valueMax); + let yBottom = this.ChartFrame.GetYFromData(valueMin); + + this.Canvas.fillStyle = item.Color; + this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(yTop), ToFixedRect(xRight - left), ToFixedRect(yBottom - yTop)); + } + + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + if (item == null || isNaN(item.Value) || isNaN(item.Value2)) continue; + if (item.Color == null) continue; + + let valueMax = Math.max(item.Value, item.Value2); + let valueMin = Math.min(item.Value, item.Value2); + + let yTop = this.ChartFrame.GetYFromData(valueMax); + let yBottom = this.ChartFrame.GetYFromData(valueMin); + + if (item.Title && item.TitleColor) + { + let x = xRight; + if (item.Align == 'left') + { + this.Canvas.textAlign = 'left'; + x = left; + } + else + { + this.Canvas.textAlign = 'right'; + x = xRight; + } + + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = item.TitleColor; + this.Canvas.font = this.Font; + let y = yTop + (yBottom - yTop) / 2; + this.Canvas.fillText(item.Title, x, y); + } + } + } + + this.HScreenDraw = function () + { + var bottom = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetTop(); + var height = this.ChartBorder.GetHeight(); + + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + if (item == null || isNaN(item.Value) || isNaN(item.Value2)) continue; + if (item.Color == null) continue; + + let valueMax = Math.max(item.Value, item.Value2); + let valueMin = Math.min(item.Value, item.Value2); + + var yTop = this.ChartFrame.GetYFromData(valueMax); + var yBottom = this.ChartFrame.GetYFromData(valueMin); + + this.Canvas.fillStyle = item.Color; + this.Canvas.fillRect(ToFixedRect(yBottom), ToFixedRect(top), ToFixedRect(yTop - yBottom), ToFixedRect(height)); + + if (item.Title && item.TitleColor) + { + var xText = yTop + (yBottom - yTop) / 2; + var yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = item.TitleColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(item.Title, 0, -2); + + this.Canvas.restore(); + } + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + if (item == null || isNaN(item.Value) || isNaN(item.Value2)) continue; + + let valueMax = Math.max(item.Value, item.Value2); + let valueMin = Math.min(item.Value, item.Value2); + + if (range.Max == null) range.Max = valueMax; + if (range.Min == null) range.Min = valueMin; + + if (range.Max < valueMax) range.Max = valueMax; + if (range.Min > valueMin) range.Min = valueMin; + } + return range; + } +} + +// 面积图 +function ChartBand() { + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = true; + + this.FirstColor = g_JSChartResource.Index.LineColor[0]; + this.SecondColor = g_JSChartResource.Index.LineColor[1]; + + this.Draw = function () + { + if (this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var xPointCount = this.ChartFrame.XPointCount; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + var x = 0; + var y = 0; + var y2 = 0; + var firstlinePoints = []; + var secondlinePoints = []; + var lIndex = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var value = this.Data.Data[i]; + if (value == null || value.Value == null || value.Value2 == null) continue; + x = this.ChartFrame.GetXFromIndex(j); + y = this.ChartFrame.GetYFromData(value.Value); + y2 = this.ChartFrame.GetYFromData(value.Value2); + firstlinePoints[lIndex] = { x: x, y: y }; + secondlinePoints[lIndex] = { x: x, y: y2 }; + lIndex++; + } + + if (firstlinePoints.length > 1) + { + this.Canvas.save(); + this.Canvas.beginPath(); + for (var i = 0; i < firstlinePoints.length; ++i) + { + if (i == 0) + this.Canvas.moveTo(firstlinePoints[i].x, firstlinePoints[i].y); + else + this.Canvas.lineTo(firstlinePoints[i].x, firstlinePoints[i].y); + } + for (var j = secondlinePoints.length - 1; j >= 0; --j) + { + this.Canvas.lineTo(secondlinePoints[j].x, secondlinePoints[j].y); + } + this.Canvas.closePath(); + this.Canvas.strokeStyle = "rgba(255,255,255,0)"; + this.Canvas.stroke(); + this.Canvas.clip(); + this.Canvas.beginPath(); + this.Canvas.moveTo(firstlinePoints[0].x, this.ChartBorder.GetBottom()); + for (var i = 0; i < firstlinePoints.length; ++i) + { + this.Canvas.lineTo(firstlinePoints[i].x, firstlinePoints[i].y); + } + this.Canvas.lineTo(firstlinePoints[firstlinePoints.length - 1].x, this.ChartBorder.GetBottom()); + this.Canvas.closePath(); + this.Canvas.fillStyle = this.FirstColor; + this.Canvas.fill(); + this.Canvas.beginPath(); + this.Canvas.moveTo(secondlinePoints[0].x, this.ChartBorder.GetBottom()); + for (var i = 0; i < secondlinePoints.length; ++i) + { + this.Canvas.lineTo(secondlinePoints[i].x, secondlinePoints[i].y); + } + this.Canvas.lineTo(secondlinePoints[secondlinePoints.length - 1].x, this.ChartBorder.GetBottom()); + this.Canvas.closePath(); + this.Canvas.fillStyle = this.SecondColor; + this.Canvas.fill(); + this.Canvas.restore(); + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null || value.Value == null || value.Value2 == null) continue; + var maxData = value.Value > value.Value2 ? value.Value : value.Value2; + var minData = value.Value < value.Value2 ? value.Value : value.Value2; + if (range.Max == null) + range.Max = maxData; + else if (range.Max < maxData) + range.Max = maxData; + + if (range.Min == null) + range.Min = minData; + else if (range.Min > minData) + range.Min = minData; + } + return range; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// 其他图形 +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + 饼图 +*/ +function ChartPie() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Draw = function () + { + if (!this.Data || !this.Data.Data || !(this.Data.Data.length > 0)) return this.DrawEmptyData(); + + let left = this.ChartBorder.GetLeft(); + let right = this.ChartBorder.GetRight(); + let top = this.ChartBorder.GetTop(); + let bottom = this.ChartBorder.GetBottom(); + let width = this.ChartBorder.GetWidth(); + let height = this.ChartBorder.GetHeight(); + + //圆半径 + let radius = width / 4 * 0.8; + this.Canvas.save(); + this.Canvas.translate(left + radius, top + height / 2); + + let totalValue = 0; //求和 + for (let i in this.Data.Data) { + totalValue += this.Data.Data[i].Value; + } + + let startAngle = Math.PI * 1.5; + let start = startAngle; + let end = startAngle; + //画饼图 + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + let rate = item.Value / totalValue; //占比 + + // 绘制扇形 + this.Canvas.beginPath(); + this.Canvas.moveTo(0, 0); + + end += rate * 2 * Math.PI;//终止角度 + this.Canvas.strokeStyle = "white"; + this.Canvas.fillStyle = item.Color; + this.Canvas.arc(0, 0, radius, start, end); + this.Canvas.fill(); + this.Canvas.closePath(); + this.Canvas.stroke(); + + start += rate * 2 * Math.PI;//起始角度 + } + + //画文字 + this.Canvas.restore(); + let textLeft = left + width / 2 + 5; + // let textTop = top + height / 2 + 20; + let textTop = top; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = "12px 微软雅黑"; + + for (let i = 0, j = 0; i < this.Data.Data.length; ++i) + { + let item = this.Data.Data[i]; + if (!item.Text) continue; + + this.Canvas.fillStyle = item.Color; + this.Canvas.fillRect(textLeft, textTop - 15, 13, 13); + + this.Canvas.fillStyle = 'rgb(102,102,102)'; + this.Canvas.fillText(item.Text, textLeft + 16, textTop); + // textTop += 20; + textTop += 17; + if (textTop > top + height / 2 + radius) { + ++j; + if (j >= 2) break; + + // textTop = top + height / 2 + 20; + textTop = top; + textLeft = right - (width / 4) + 5; + } + } + } + + //空数据 + this.DrawEmptyData = function () + { + console.log('[ChartPie::DrawEmptyData]') + + let left = this.ChartBorder.GetLeft(); + let right = this.ChartBorder.GetRight(); + let top = this.ChartBorder.GetTop(); + let bottom = this.ChartBorder.GetBottom(); + let width = this.ChartBorder.GetWidth(); + let height = this.ChartBorder.GetHeight(); + + //圆半径 + let radius = width / 4 * 0.8; + this.Canvas.save(); + this.Canvas.translate(left + radius, top + height / 2); + + this.Canvas.beginPath(); + this.Canvas.fillStyle = 'rgb(211,211,211)'; + this.Canvas.strokeStyle = "white"; + this.Canvas.arc(0, 0, radius * 0.8, 0, 2 * Math.PI); + this.Canvas.fill(); + this.Canvas.closePath(); + this.Canvas.stroke(); + + this.Canvas.restore(); + } +} + + +/* + 圆环 +*/ +function ChartCircle() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.BGColor = 'white'; //背景色 + this.TextHeight = 25; + + //空数据 + this.DrawEmptyData = function () + { + console.log('[ChartCircle::DrawEmptyData]') + } + + this.Draw = function () + { + if (!this.Data || !this.Data.Data || !(this.Data.Data.length > 0)) return this.DrawEmptyData(); + + let left = this.ChartBorder.GetLeft(); + let right = this.ChartBorder.GetRight(); + let top = this.ChartBorder.GetTop(); + let bottom = this.ChartBorder.GetBottom(); + let width = this.ChartBorder.GetWidth(); + let height = this.ChartBorder.GetHeight(); + + //圆半径 + let lTextHeight = this.TextHeight; + let size = width - lTextHeight; + if (size > height - lTextHeight) size = height - lTextHeight; + let radius = (size - lTextHeight) / 2; + this.Canvas.save(); + this.Canvas.translate(left + width / 2, top + height / 2 - lTextHeight / 2); + + let totalValue = 0; //求和 + for (let i in this.Data.Data) + { + totalValue += this.Data.Data[i].Value; + } + + let startAngle = Math.PI * 1.5; + let start = startAngle; + let end = startAngle; + //画饼图 + for (let i in this.Data.Data) + { + let item = this.Data.Data[i]; + let rate = item.Value / totalValue; //占比 + //console.log('[ChartPie::Draw]', i, rate, item); + + // 绘制扇形 + this.Canvas.beginPath(); + this.Canvas.moveTo(0, 0); + + end += rate * 2 * Math.PI;//终止角度 + this.Canvas.strokeStyle = "white"; + this.Canvas.fillStyle = item.Color; + this.Canvas.arc(0, 0, radius, start, end); + this.Canvas.fill(); + this.Canvas.closePath(); + this.Canvas.stroke(); + + start += rate * 2 * Math.PI;//起始角度 + } + + //中心画一个背景色的圆 + this.Canvas.beginPath(); + this.Canvas.fillStyle = this.BGColor; + this.Canvas.arc(0, 0, radius * 0.5, 0, 2 * Math.PI); + this.Canvas.fill(); + this.Canvas.closePath(); + this.Canvas.stroke(); + + this.Canvas.restore(); + + //画文字 + this.Canvas.restore(); + let textLeft = left; + let textTop = top + height / 2 - lTextHeight / 2 + radius + 5 + 20; + this.Canvas.textBaseline = "bottom"; + this.Canvas.textAlign = 'left'; + this.Canvas.font = "14px 微软雅黑"; + let textWidth = 0; + //以圆心左右显示 + for (let i = 0, j = 0; i < this.Data.Data.length; ++i) + { + let item = this.Data.Data[i]; + if (!item.Text) continue; + + this.Canvas.fillStyle = item.Color; + + if (j % 2 == 0) + { + textLeft = left + width / 2 - 10; + textWidth = this.Canvas.measureText(item.Text).width; + textLeft = textLeft - textWidth - 16; + this.Canvas.fillRect(textLeft, textTop - 15, 13, 13); + this.Canvas.fillStyle = 'rgb(102,102,102)'; + this.Canvas.fillText(item.Text, textLeft + 16, textTop); + } + else + { + textLeft = left + width / 2 + 10 + 10; + this.Canvas.fillRect(textLeft, textTop - 15, 13, 13); + this.Canvas.fillStyle = 'rgb(102,102,102)'; + this.Canvas.fillText(item.Text, textLeft + 16, textTop); + textTop += 20; + } + + if (textTop > bottom) break; + + ++j; + } + } +} + + + +// 中国地图 +function ChartChinaMap() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ImageData = null; + this.Left; + this.Top; + this.Width; + this.Height; + this.ImageWidth; + this.ImageHeight; + + this.DefaultColor = [217, 222, 239]; + + this.Color = + [ + { Name: '海南', Color: 'rgb(217,222,223)' }, + { Name: '内蒙古', Color: 'rgb(217,222,225)' }, + { Name: '新疆', Color: 'rgb(217,222,226)' }, + { Name: '青海', Color: 'rgb(217,222,227)' }, + { Name: '西藏', Color: 'rgb(217,222,228)' }, + { Name: '云南', Color: 'rgb(217,222,229)' }, + { Name: '黑龙江', Color: 'rgb(217,222,230)' }, + { Name: '吉林', Color: 'rgb(217,222,231)' }, + { Name: '辽宁', Color: 'rgb(217,222,232)' }, + { Name: '河北', Color: 'rgb(217,222,233)' }, + { Name: '山东', Color: 'rgb(217,222,234)' }, + { Name: '江苏', Color: 'rgb(217,222,235)' }, + { Name: '浙江', Color: 'rgb(217,222,236)' }, + { Name: '福建', Color: 'rgb(217,222,237)' }, + { Name: '广东', Color: 'rgb(217,222,238)' }, + { Name: '广西', Color: 'rgb(217,222,239)' }, + { Name: '贵州', Color: 'rgb(217,222,240)' }, + { Name: '湖南', Color: 'rgb(217,222,241)' }, + { Name: '江西', Color: 'rgb(217,222,242)' }, + { Name: '安徽', Color: 'rgb(217,222,243)' }, + { Name: '湖北', Color: 'rgb(217,222,244)' }, + { Name: '重庆', Color: 'rgb(217,222,245)' }, + { Name: '四川', Color: 'rgb(217,222,246)' }, + { Name: '甘肃', Color: 'rgb(217,222,247)' }, + { Name: '陕西', Color: 'rgb(217,222,248)' }, + { Name: '山西', Color: 'rgb(217,222,249)' }, + { Name: '河南', Color: 'rgb(217,222,250)' } + ]; + + this.Draw = function () + { + let left = this.ChartBorder.GetLeft() + 1; + let right = this.ChartBorder.GetRight() - 1; + let top = this.ChartBorder.GetTop() + 1; + let bottom = this.ChartBorder.GetBottom() - 1; + let width = this.ChartBorder.GetWidth() - 2; + let height = this.ChartBorder.GetHeight() - 2; + + let imageWidth = CHINA_MAP_IMAGE.width; + let imageHeight = CHINA_MAP_IMAGE.height; + + let drawImageWidth = imageWidth; + let drawImageHeight = imageHeight; + + if (height < drawImageHeight || width < drawImageWidth) + { + this.ImageData = null; + return; + } + + if (this.Left != left || this.Top != top || this.Width != width || this.Height != height || this.ImageWidth != imageWidth || this.ImageHeight != imageHeight) + { + this.ImageData = null; + + this.ImageWidth = imageWidth; + this.ImageHeight = imageHeight; + this.Left = left; + this.Top = top; + this.Width = width; + this.Height = height; + + console.log(imageWidth, imageHeight); + } + + if (this.ImageData == null) + { + this.Canvas.drawImage(CHINA_MAP_IMAGE, 0, 0, imageWidth, imageHeight, left, top, drawImageWidth, drawImageHeight); + this.ImageData = this.Canvas.getImageData(left, top, drawImageWidth, drawImageHeight); + + let defaultColorSet = new Set(); //默认颜色填充的色块 + let colorMap = new Map(); //定义颜色填充的色块 + + let nameMap = new Map(); + if (this.Data.length > 0) + { + for (let i in this.Data) + { + let item = this.Data[i]; + nameMap.set(item.Name, item.Color) + } + } + + console.log(this.Data); + for (let i in this.Color) + { + let item = this.Color[i]; + if (nameMap.has(item.Name)) + { + colorMap.set(item.Color, nameMap.get(item.Name)); + } + else + { + defaultColorSet.add(item.Color); + } + } + + var color; + for (let i = 0; i < this.ImageData.data.length; i += 4) + { + color = 'rgb(' + this.ImageData.data[i] + ',' + this.ImageData.data[i + 1] + ',' + this.ImageData.data[i + 2] + ')'; + + if (defaultColorSet.has(color)) + { + this.ImageData.data[i] = this.DefaultColor[0]; + this.ImageData.data[i + 1] = this.DefaultColor[1]; + this.ImageData.data[i + 2] = this.DefaultColor[2]; + } + else if (colorMap.has(color)) + { + let colorValue = colorMap.get(color); + this.ImageData.data[i] = colorValue[0]; + this.ImageData.data[i + 1] = colorValue[1]; + this.ImageData.data[i + 2] = colorValue[2]; + } + } + this.Canvas.clearRect(left, top, drawImageWidth, drawImageHeight); + this.Canvas.putImageData(this.ImageData, left, top, 0, 0, drawImageWidth, drawImageHeight); + } + else + { + this.Canvas.putImageData(this.ImageData, left, top, 0, 0, drawImageWidth, drawImageHeight); + } + } +} + + +// 雷达图 +function ChartRadar() +{ + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.BorderPoint = []; //边框点 + this.DataPoint = []; //数据点 + this.CenterPoint = {}; + this.StartAngle = 0; + this.Color = 'rgb(198,198,198)'; + this.AreaColor = 'rgba(242,154,118,0.4)'; //面积图颜色 + this.AreaLineColor = 'rgb(242,154,118)'; + this.TitleFont = '24px 微软雅黑'; + this.TitleColor = 'rgb(102,102,102)'; + this.BGColor = ['rgb(255,255,255)', 'rgb(224,224,224)']//背景色 + + this.DrawBorder = function () //画边框 + { + if (this.BorderPoint.length <= 0) return; + + this.Canvas.font = this.TitleFont; + this.Canvas.strokeStyle = this.Color; + + const aryBorder = [1, 0.8, 0.6, 0.4, 0.2]; + for (let j in aryBorder) + { + var rate = aryBorder[j]; + var isFirstDraw = true; + for (let i in this.BorderPoint) + { + var item = this.BorderPoint[i]; + item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180) * rate; + item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180) * rate; + if (isFirstDraw) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X, item.Y); + isFirstDraw = false; + } + else + { + this.Canvas.lineTo(item.X, item.Y); + } + + if (j == 0) this.DrawText(item); + } + + this.Canvas.closePath(); + this.Canvas.stroke(); + this.Canvas.fillStyle = this.BGColor[j % 2 == 0 ? 0 : 1]; + this.Canvas.fill(); + } + + this.Canvas.beginPath(); + for (let i in this.BorderPoint) + { + var item = this.BorderPoint[i]; + item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180); + item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180); + this.Canvas.moveTo(this.CenterPoint.X, this.CenterPoint.Y); + this.Canvas.lineTo(item.X, item.Y); + } + this.Canvas.stroke(); + } + + this.DrawArea = function () + { + if (!this.DataPoint || this.DataPoint.length <= 0) return; + + this.Canvas.fillStyle = this.AreaColor; + this.Canvas.strokeStyle = this.AreaLineColor; + this.Canvas.beginPath(); + var isFirstDraw = true; + for (let i in this.DataPoint) + { + var item = this.DataPoint[i]; + if (isFirstDraw) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X, item.Y); + isFirstDraw = false; + } + else + { + this.Canvas.lineTo(item.X, item.Y); + } + } + + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + + this.DrawText = function (item) + { + if (!item.Text) return; + + //console.log(item.Text, item.Angle); + this.Canvas.fillStyle = this.TitleColor; + var xText = item.X, yText = item.Y; + + //显示每个角度的位置 + if (item.Angle > 0 && item.Angle < 45) + { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + xText += 2; + } + else if (item.Angle >= 0 && item.Angle < 90) { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'top'; + xText += 2; + } + else if (item.Angle >= 90 && item.Angle < 135) + { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'top'; + xText -= 2; + } + else if (item.Angle >= 135 && item.Angle < 180) + { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'top'; + xText -= 2; + } + else if (item.Angle >= 180 && item.Angle < 225) { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'middle'; + xText -= 2; + } + else if (item.Angle >= 225 && item.Angle <= 270) { + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'bottom'; + } + else if (item.Angle > 270 && item.Angle < 315) + { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'bottom'; + xText += 2; + } + else + { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + xText += 2; + } + + this.Canvas.fillText(item.Text, xText, yText); + } + + this.Draw = function () + { + this.BorderPoint = []; + this.DataPoint = []; + this.InternalBorderPoint = []; + this.CenterPoint = {}; + if (!this.Data || !this.Data.Data || !(this.Data.Data.length > 0)) + this.CalculatePoints(null); + else + this.CalculatePoints(this.Data.Data); + + this.DrawBorder(); + this.DrawArea(); + } + + this.CalculatePoints = function (data) + { + let left = this.ChartBorder.GetLeft(); + let right = this.ChartBorder.GetRight(); + let top = this.ChartBorder.GetTop(); + let bottom = this.ChartBorder.GetBottom(); + let width = this.ChartBorder.GetWidth(); + let height = this.ChartBorder.GetHeight(); + + let ptCenter = { X: left + width / 2, Y: top + height / 2 }; //中心点 + let radius = Math.min(width / 2, height / 2) - 2 //半径 + let count = Math.max(5, data ? data.length : 0); + let averageAngle = 360 / count; + for (let i = 0; i < count; ++i) + { + let ptBorder = { Index: i, Radius: radius, Angle: i * averageAngle + this.StartAngle }; + let angle = ptBorder.Angle; + + if (data && i < data.length) + { + var item = data[i]; + let ptData = { Index: i, Text: item.Text }; + ptBorder.Text = item.Name; + if (!item.Value) + { + ptData.X = ptCenter.X; + ptData.Y = ptCenter.Y; + } + else + { + var value = item.Value; + if (value >= 1) value = 1; + var dataRadius = radius * value; + ptData.X = ptCenter.X + dataRadius * Math.cos(angle * Math.PI / 180); + ptData.Y = ptCenter.Y + dataRadius * Math.sin(angle * Math.PI / 180); + } + + this.DataPoint.push(ptData); + } + + this.BorderPoint.push(ptBorder); + } + this.CenterPoint = ptCenter; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//十字光标 +function ChartCorssCursor() +{ + this.Frame; + this.Canvas; //画布 + + this.HPenColor = g_JSChartResource.CorssCursorHPenColor; //水平线颜色 + this.HPenType = 0; //水平线样式 0=虚线 1=实线 + + this.VPenColor = g_JSChartResource.CorssCursorVPenColor; //垂直线颜色 + this.VPenType = 0; //垂直线颜色 0=虚线 1=实线 2=K线宽度 + + this.Font = g_JSChartResource.CorssCursorTextFont; //字体 + this.TextColor = g_JSChartResource.CorssCursorTextColor; //文本颜色 + this.TextBGColor = g_JSChartResource.CorssCursorBGColor; //文本背景色 + this.TextHeight = 15; //文本字体高度 + this.LastPoint; + this.CursorIndex; //当前数据的位置 + + this.PointX; + this.PointY; + + this.StringFormatX; + this.StringFormatY; + + this.IsShow = true; //是否显示 + this.ShowTextMode = { Left: 1, Right: 1, Bottom: 1 }; //0=不显示 1=显示在框架外 2=显示在框架内 + this.IsShowCorss = true; //是否显示十字光标 + this.IsShowClose = false; //Y轴始终显示收盘价 + this.IsFixXLastTime=false; //是否修正X轴,超出当前时间的,X轴调整到当前最后的时间. + + //内部使用 + this.Close = null; //收盘价格 + + this.GetCloseYPoint = function (index) + { + this.Close = null; + if (!this.StringFormatX.Data) return null; + var data = this.StringFormatX.Data; + if (!data.Data || data.Data.length <= 0) return null; + var dataIndex = data.DataOffset + index; + if (dataIndex >= data.Data.length) dataIndex = data.Data.length - 1; + if (dataIndex < 0) return null; + + var klineData = data.Data[dataIndex]; + if (!klineData) return null; + this.Close = klineData.Close; + var yPoint = this.Frame.GetYFromData(this.Close); + return yPoint; + } + + this.FixMinuteLastTimeXPoint=function(index) + { + if (!IFrameSplitOperator.IsNumber(index)) return null; + index=parseInt(index); + if (!this.StringFormatX.Data) return null; + var data = this.StringFormatX.Data; + if (!data.Data || data.Data.length <= 0) return null; + var dataIndex = data.DataOffset + index; + if (dataIndex 2) this.Canvas.lineWidth = barWidth; + } + + this.Canvas.beginPath(); + if (this.Frame.SubFrame.length > 0) + { + for (var i in this.Frame.SubFrame) + { + var frame = this.Frame.SubFrame[i].Frame; + top = frame.ChartBorder.GetTopTitle(); + bottom = frame.ChartBorder.GetBottom(); + this.Canvas.moveTo(ToFixedPoint(x), top); + this.Canvas.lineTo(ToFixedPoint(x), bottom); + } + } + else + { + this.Canvas.moveTo(ToFixedPoint(x), top); + this.Canvas.lineTo(ToFixedPoint(x), bottom); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + + var xValue = this.Frame.GetXData(x); + var yValueExtend = {}; + var yValue = this.Frame.GetYData(y, yValueExtend); + this.StringFormatY.RValue = yValueExtend.RightYValue; //右侧子坐标 + if (this.IsShowClose && this.Close != null) yValue = this.Close; + + this.StringFormatX.Value = this.CursorIndex; + this.StringFormatY.Value = yValue; + this.StringFormatY.FrameID = yValueExtend.FrameID; + + if (((this.ShowTextMode.Left == 1 && this.Frame.ChartBorder.Left >= 30) || this.ShowTextMode.Left == 2 || + (this.ShowTextMode.Right == 1 && this.Frame.ChartBorder.Right >= 30) || this.ShowTextMode.Right == 2) && this.StringFormatY.Operator()) + { + var text = this.StringFormatY.Text; + this.Canvas.font = this.Font; + var textWidth = this.Canvas.measureText(text).width + 4; //前后各空2个像素 + + if (this.Frame.ChartBorder.Left >= 30 && this.ShowTextMode.Left == 1) + { + this.Canvas.fillStyle = this.TextBGColor; + if (left < textWidth) //左边空白的地方太少了画布下 + { + this.Canvas.fillRect(2, y - this.TextHeight / 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, 2 + 2, y, textWidth); + } + else + { + this.Canvas.fillRect(left - 2, y - this.TextHeight / 2, -textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, left - 4, y, textWidth); + } + } + else if (this.ShowTextMode.Left == 2) + { + this.Canvas.fillStyle = this.TextBGColor; + this.Canvas.fillRect(left, y - this.TextHeight / 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, left + 2, y, textWidth); + } + + if (this.StringFormatY.RText) + { + text = this.StringFormatY.RText; + var textWidth = this.Canvas.measureText(text).width + 4; //前后各空2个像素 + } + + if (this.Frame.ChartBorder.Right >= 30 && this.ShowTextMode.Right == 1) + { + this.Canvas.fillStyle = this.TextBGColor; + if (rightWidth > textWidth) //右边不够就不画 + { + this.Canvas.fillRect(right + 2, y - this.TextHeight / 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, right + 4, y, textWidth); + } + else + { + this.Canvas.fillRect(chartRight - 2 - textWidth, y - this.TextHeight / 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, chartRight - 4, y, textWidth); + } + } + else if (this.ShowTextMode.Right == 2) + { + this.Canvas.fillStyle = this.TextBGColor; + var showLeft = right - textWidth; + this.Canvas.fillRect(showLeft, y - this.TextHeight / 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, showLeft + 2, y, textWidth); + } + } + + if (this.ShowTextMode.Bottom == 1 && this.StringFormatX.Operator()) + { + var text = this.StringFormatX.Text; + this.Canvas.font = this.Font; + + this.Canvas.fillStyle = this.TextBGColor; + var textWidth = this.Canvas.measureText(text).width + 4; //前后各空2个像素 + if (x - textWidth / 2 < 3) //左边位置不够了, 顶着左边画 + { + this.Canvas.fillRect(x - 1, bottom + 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, x + 1, bottom + 2, textWidth); + } + else if ((right - left) - x < textWidth) + { //右边位置不够用,顶着右边画 + this.Canvas.fillRect(x - textWidth, bottom + 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, x - 1, bottom + 2, textWidth); + } + else + { + this.Canvas.fillRect(x - textWidth / 2, bottom + 2, textWidth, this.TextHeight); + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, x, bottom + 2, textWidth); + } + } + } + + this.HScreenDraw = function () + { + var x = this.LastPoint.X; + var y = this.LastPoint.Y; + + y = this.Frame.GetXFromIndex(this.CursorIndex); //手机端 十字只能画在K线上 + + var left = this.Frame.ChartBorder.GetLeft(); + var right = this.Frame.ChartBorder.GetRightEx(); + var top = this.Frame.ChartBorder.GetTop(); + var bottom = this.Frame.ChartBorder.GetBottom(); + var bottomWidth = this.Frame.ChartBorder.Bottom; + + this.PointY = [[left, y], [right, y]]; + this.PointX = [[x, top], [x, bottom]]; + + if (this.IsShowCorss) //十字线 + { + this.Canvas.save(); + this.Canvas.strokeStyle = this.HPenColor; + if (this.HPenType == 0) this.Canvas.setLineDash([3, 2]); //虚线 + + //画竖线 + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), top); + this.Canvas.lineTo(ToFixedPoint(x), bottom); + this.Canvas.stroke(); + this.Canvas.restore(); + + this.Canvas.save(); + this.Canvas.strokeStyle = this.VPenColor; + if (this.VPenType == 0) + { + this.Canvas.setLineDash([3, 2]); //虚线 + } + else if (this.VPenType == 2) + { + let barWidth = this.Frame.SubFrame[0].Frame.DataWidth; //和K线一样宽度 + if (barWidth > 2) this.Canvas.lineWidth = barWidth; + } + this.Canvas.beginPath(); + //画横线 + if (this.Frame.SubFrame.length > 0) + { + for (var i in this.Frame.SubFrame) + { + var frame = this.Frame.SubFrame[i].Frame; + this.Canvas.moveTo(frame.ChartBorder.GetLeft(), ToFixedPoint(y)); + this.Canvas.lineTo(frame.ChartBorder.GetRightTitle(), ToFixedPoint(y)); + } + } + else + { + this.Canvas.moveTo(left, ToFixedPoint(y)); + this.Canvas.lineTo(right, ToFixedPoint(y)); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + + var xValue = this.Frame.GetXData(y); + var yValueExtend = {}; + var yValue = this.Frame.GetYData(x, yValueExtend); + + this.StringFormatX.Value = xValue; + this.StringFormatY.Value = yValue; + this.StringFormatY.FrameID = yValueExtend.FrameID; + + if (((this.ShowTextMode.Left == 1 && this.Frame.ChartBorder.Top >= 30) || this.ShowTextMode.Left == 2 || + (this.ShowTextMode.Right == 1 && this.Frame.ChartBorder.Bottom >= 30) || this.ShowTextMode.Right == 2) && this.StringFormatY.Operator()) { + var text = this.StringFormatY.Text; + this.Canvas.font = this.Font; + var textWidth = this.Canvas.measureText(text).width + 4; //前后各空2个像素 + + if (this.Frame.ChartBorder.Top >= 30 && this.ShowTextMode.Left == 1) { + var xText = x, yText = top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + this.Canvas.fillStyle = this.TextBGColor; + + if (top >= textWidth) { + this.Canvas.fillRect(-textWidth, -(this.TextHeight / 2), textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, -2, 0, textWidth); + } + else { + this.Canvas.fillRect((textWidth - top), -(this.TextHeight / 2), -textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, (textWidth - top) - 2, 0, textWidth); + } + this.Canvas.restore(); + } + else if (this.ShowTextMode.Left == 2) { + var xText = x; + var yText = top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle = this.TextBGColor; + this.Canvas.fillRect(0, -(this.TextHeight / 2), textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, 2, 0, textWidth); + + this.Canvas.restore(); + } + + if (this.Frame.ChartBorder.Bottom >= 30 && this.ShowTextMode.Right == 1) { + var xText = x, yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + this.Canvas.fillStyle = this.TextBGColor; + + if (bottomWidth > textWidth) { + this.Canvas.fillRect(0, -(this.TextHeight / 2), textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, 2, 0, textWidth); + } + else { + this.Canvas.fillRect((bottomWidth - textWidth), -(this.TextHeight / 2), textWidth, this.TextHeight); + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, (bottomWidth - textWidth) + 2, 0, textWidth); + } + this.Canvas.restore(); + } + else if (this.ShowTextMode.Right == 2) { + var xText = x; + var yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle = this.TextBGColor; + this.Canvas.fillRect(0, -(this.TextHeight / 2), -textWidth, this.TextHeight); + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, -2, 0, textWidth); + + this.Canvas.restore(); + } + } + + if (this.ShowTextMode.Bottom === 1 && this.StringFormatX.Operator()) { + var text = this.StringFormatX.Text; + this.Canvas.font = this.Font; + + this.Canvas.fillStyle = this.TextBGColor; + var textWidth = this.Canvas.measureText(text).width + 4; //前后各空2个像素 + if (y - textWidth / 2 < 3) //左边位置不够了, 顶着左边画 + { + var xText = left; + var yText = y; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillRect(0, 0, textWidth, this.TextHeight); + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, 0, 0, textWidth); + + this.Canvas.restore(); + } + else { + var xText = left; + var yText = y; + + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillRect(-(textWidth / 2), 0, textWidth, this.TextHeight); + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.fillText(text, 0, 0, textWidth); + + this.Canvas.restore(); + } + } + } +} + +//分钟线 +function ChartMinutePriceLine() +{ + this.newMethod = ChartLine; //派生 + this.newMethod(); + delete this.newMethod; + + this.YClose; + this.IsDrawArea = true; //是否画价格面积图 + this.AreaColor = 'rgba(0,191,255,0.1)'; + + this.Draw = function () + { + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.IsShow) return; + if (!this.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen === true) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var minuteCount = this.ChartFrame.MinuteCount; + var bottom = this.ChartBorder.GetBottomEx(); + var left = this.ChartBorder.GetLeftEx(); + + var bFirstPoint = true; + var ptFirst = {}; //第1个点 + var drawCount = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (value == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (bFirstPoint) + { + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y, x); + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + ptFirst = { X: x, Y: y }; + } + else + { + if (isHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + + if (drawCount >= minuteCount) //上一天的数据和这天地数据线段要断开 + { + bFirstPoint = true; + this.Canvas.stroke(); + if (this.IsDrawArea) //画面积 + { + if (isHScreen) + { + this.Canvas.lineTo(left, x); + this.Canvas.lineTo(left, ptFirst.X); + this.SetFillStyle(this.AreaColor, this.ChartBorder.GetRightEx(), bottom, this.ChartBorder.GetLeftEx(), bottom); + } + else + { + this.Canvas.lineTo(x, bottom); + this.Canvas.lineTo(ptFirst.X, bottom); + this.SetFillStyle(this.AreaColor, left, this.ChartBorder.GetTopEx(), left, bottom); + } + this.Canvas.fill(); + } + drawCount = 0; + } + } + + if (drawCount > 0) + { + this.Canvas.stroke(); + if (this.IsDrawArea) //画面积 + { + if (isHScreen) + { + this.Canvas.lineTo(left, x); + this.Canvas.lineTo(left, ptFirst.X); + this.SetFillStyle(this.AreaColor, this.ChartBorder.GetRightEx(), bottom, this.ChartBorder.GetLeftEx(), bottom); + } + else + { + this.Canvas.lineTo(x, bottom); + this.Canvas.lineTo(ptFirst.X, bottom); + this.SetFillStyle(this.AreaColor, left, this.ChartBorder.GetTopEx(), left, bottom); + } + this.Canvas.fill(); + } + } + } + + this.GetMaxMin = function () + { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + if (this.YClose == null) return range; + + range.Min = this.YClose; + range.Max = this.YClose; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var value = this.Data.Data[i]; + if (!value) continue; + + if (range.Max == null) range.Max = value; + if (range.Min == null) range.Min = value; + + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + + if (range.Max == this.YClose && range.Min == this.YClose) + { + range.Max = this.YClose + this.YClose * 0.1; + range.Min = this.YClose - this.YClose * 0.1; + return range; + } + + var distance = Math.max(Math.abs(this.YClose - range.Max), Math.abs(this.YClose - range.Min)); + range.Max = this.YClose + distance; + range.Min = this.YClose - distance; + + return range; + } +} + +//////////////////////////////////////////////////////////////////////////////// +//深度图十字光标 +function DepthChartCorssCursor() +{ + this.Frame; + this.Canvas; //画布 + this.Data; + this.Symbol; + this.HQChart; + + this.HPenType=0; //水平线样式 0=虚线 1=实线 + this.VPenType=0; //垂直线颜色 0=虚线 1=实线 + this.LineDash=g_JSChartResource.DepthCorss.LineDash; + + this.AskColor=g_JSChartResource.DepthCorss.AskColor.Line; //卖 + this.BidColor=g_JSChartResource.DepthCorss.BidColor.Line; //买 + this.LineWidth=g_JSChartResource.DepthCorss.LineWidth; + + this.IsShowTooltip=true; + this.Tooltip= + { + LineHeight:g_JSChartResource.DepthCorss.Tooltip.LineHeight, + Border: + { + Top:g_JSChartResource.DepthCorss.Tooltip.Border.Top, + Left:g_JSChartResource.DepthCorss.Tooltip.Border.Left, + Bottom:g_JSChartResource.DepthCorss.Tooltip.Border.Bottom, + Center: g_JSChartResource.DepthCorss.Tooltip.Border.Center + }, + Font:g_JSChartResource.DepthCorss.Tooltip.Font, + TextColor:g_JSChartResource.DepthCorss.Tooltip.TextColor, + BGColor:g_JSChartResource.DepthCorss.Tooltip.BGColor + }; // Width: Height: + + this.Font=g_JSChartResource.CorssCursorTextFont; //字体 + this.TextColor=g_JSChartResource.CorssCursorTextColor; //文本颜色 + this.TextBGColor=g_JSChartResource.CorssCursorBGColor; //文本背景色 + this.TextHeight=20; //文本字体高度 + this.LastPoint; + + this.PointX; + this.PointY; + + this.StringFormatX; + this.StringFormatY; + + this.IsShowCorss=true; //是否显示十字光标 + this.IsShow=true; + + this.GetVol=function(price, isAsk) + { + if (!this.Data) return null; + var aryData=isAsk? this.Data.Asks:this.Data.Bids; + if (!aryData || !Array.isArray(aryData) || aryData.length<=0) return null; + + for(var i in aryData) + { + var item=aryData[i]; + if (item.Price==price) return item.Vol; + } + + return null; + } + + this.Draw=function() + { + this.Status=0; + if (!this.LastPoint) return; + if (!this.Data) return; + if (!this.IsShow) return; + + var x=this.LastPoint.X; + var y=this.LastPoint.Y; + + var isInClient=false; + var rtClient = new Rect(this.Frame.ChartBorder.GetLeft(), this.Frame.ChartBorder.GetTop(), this.Frame.ChartBorder.GetWidth(), this.Frame.ChartBorder.GetHeight()); + isInClient = rtClient.IsPointIn(x, y); + + this.PointY=null; + this.PointY==null; + + if (!isInClient) return; + + if (this.Frame.IsHScreen===true) + { + return; + } + + var left=this.Frame.ChartBorder.GetLeft(); + var right=this.Frame.ChartBorder.GetRight(); + var top=this.Frame.ChartBorder.GetTopTitle(); + var bottom=this.Frame.ChartBorder.GetBottom(); + var rightWidth=this.Frame.ChartBorder.Right; + var chartRight=this.Frame.ChartBorder.GetChartWidth(); + + var xValue=this.Frame.GetXData(x); + var xInfo=this.Frame.GetXFromPrice(xValue); //调整价格到有数据的点上 + + if (!xInfo) return; + + var yVol=this.GetVol(xInfo.Price, xInfo.IsAsk); + y=this.Frame.GetYFromData(yVol); //调整Y轴, 让它在线段上 + + xInfo.Vol=yVol; + xInfo.Y=y; + + this.PointY=[[left,y],[right,y]]; + this.PointX=[[x,top],[x,bottom]]; + + if (this.IsShowCorss) + { + if (xInfo.IsAsk) this.Canvas.strokeStyle=this.AskColor; + else this.Canvas.strokeStyle=this.BidColor; + this.Canvas.save(); + this.Canvas.lineWidth=this.LineWidth; + var lineWidth=this.Canvas.lineWidth; + + if (this.HPenType==1 || this.HPenType==0) //0=实线 1=虚线 + { + if (this.HPenType==0) this.Canvas.setLineDash(this.LineDash); //虚线 + var yFix=ToFixedPoint(y); + this.Canvas.beginPath(); + this.Canvas.moveTo(left,yFix); + this.Canvas.lineTo(right,yFix); + this.Canvas.stroke(); + if (this.HPenType==0) this.Canvas.setLineDash([]); + } + + if (this.VPenType==0) this.Canvas.setLineDash(this.LineDash); //虚线 + var xFix=ToFixedPoint(xInfo.X); + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix,top); + this.Canvas.lineTo(xFix,bottom); + this.Canvas.stroke(); + if (this.VPenType==0) this.Canvas.setLineDash([]); + + this.Canvas.restore(); + } + + if (this.HQChart) + { + //JSCHART_EVENT_ID.ON_DRAW_DEPTH_TOOLTIP + var event=this.HQChart.GetEvent(25); + if (event) + { + event.Callback(event,xInfo,this); + } + } + + if (this.IsShowTooltip) this.DrawTooltip(xInfo); + } + + this.DrawTooltip=function(data) + { + var price=data.Price; + var vol=data.Vol; + var border=this.Tooltip.Border; + this.Canvas.font=this.Tooltip.Font; + var floatPrecision=2; + if (this.Symbol) floatPrecision=JSCommonCoordinateData.GetfloatPrecision(this.Symbol);//价格小数位数 + var maxText='擎擎: 9999.99亿 '; + if (floatPrecision>=5) maxText=`擎擎: ${99.99.toFixed(floatPrecision)} `; //小数位数太多了 + this.Tooltip.Width=this.Canvas.measureText(maxText).width+border.Left; + this.Tooltip.Height=this.Tooltip.LineHeight*4+border.Top+border.Bottom+border.Center; + + var chartRight=this.Frame.ChartBorder.GetRight(); + var chartTop=this.Frame.ChartBorder.GetTop(); + + var left=data.X+2; + var top=data.Y-this.Tooltip.Height-2; + if (left+this.Tooltip.Width>=chartRight) left=data.X-this.Tooltip.Width-2; + if (topxRange.Max) break; + } + + var x=this.ChartFrame.GetXFromIndex(item.Price); + var y=this.ChartFrame.GetYFromData(item.Vol); + aryPoint.push({X:x,Y:y}); + } + if (aryPoint.length<=1) return; + + var left=this.ChartBorder.GetLeft(); + var bottom=this.ChartBorder.GetBottom(); + var right=this.ChartBorder.GetRight(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, bottom); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.lineTo(item.X,item.Y); + } + + this.Canvas.lineTo(isLeft?left:right,aryPoint[aryPoint.length-1].Y); + this.Canvas.lineTo(isLeft?left:right,bottom); + this.Canvas.lineTo(aryPoint[0].X,bottom); + this.Canvas.closePath(); + this.Canvas.fillStyle = colorArea; + this.Canvas.fill(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, bottom); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.lineTo(item.X,item.Y); + } + this.Canvas.lineTo(isLeft?left:right,aryPoint[aryPoint.length-1].Y); + this.Canvas.strokeStyle=colorLine; + this.Canvas.stroke(); + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null, XMin:null, XMax:null }; + var xRange=this.ChartFrame.VerticalRange; + + for(var i in this.Data.Asks) + { + var item=this.Data.Asks[i]; + if (item.Price>xRange.Max) break; + + if (range.XMin==null || range.XMin>item.Price) range.XMin=item.Price; + if (range.XMax==null || range.XMaxitem.Vol) range.Min=item.Vol; + if (range.Max==null || range.Maxitem.Price) range.XMin=item.Price; + if (range.XMax==null || range.XMaxitem.Vol) range.Min=item.Vol; + if (range.Max==null || range.Max= this.Data.Data.length) dataIndex = this.Data.Data.length - 1; + if (dataIndex < 0) return null; + + var item = this.Data.Data[dataIndex]; + return item; + } + + this.GetDataIndex=function() + { + if (this.CursorIndex == null || !this.Data) return null; + if (this.Data.length <= 0) return null; + + var index = this.CursorIndex; + index = parseInt(index.toFixed(0)); + var dataIndex = this.Data.DataOffset + index; + if (dataIndex >= this.Data.Data.length) dataIndex = this.Data.Data.length - 1; + if (dataIndex < 0) return null; + + return dataIndex; + } + + this.SendUpdateUIMessage = function (funcName) //通知外面 标题变了 + { + if (!this.UpdateUICallback) return; + + var sendData = { + TitleName: 'K线标题', CallFunction: funcName, Stock: { Name: this.Name, Symbol: this.Symbol, }, + Rect: + { + Left: this.Frame.ChartBorder.GetLeft(), Right: this.Frame.ChartBorder.GetRight(), + Top: 0, Bottom: this.Frame.ChartBorder.GetTop(), + } + }; + + //有数据 + if (this.Data && this.Data.Data && this.Data.Data.length > 0) { + let index = this.Data.Data.length - 1; //默认最后一天的数据 + if (this.CursorIndex) { + let cursorIndex = Math.abs(this.CursorIndex - 0.5); + cursorIndex = parseInt(cursorIndex.toFixed(0)); + index = this.Data.DataOffset + cursorIndex; + if (index >= this.Data.Data.length) index = this.Data.Data.length - 1; + } + + if (index >= 0) { + let item = this.Data.Data[index]; + sendData.Stock.Data = + { + Date: item.Date, + YClose: item.YClose, Open: item.Open, High: item.High, Low: item.Low, Close: item.Close, + Vol: item.Vol, Amount: item.Amount + } + if (item.Time) sendData.Stock.Time = item.Time; //分钟K线才有时间 + } + + if (this.Data.Period != null) sendData.Stock.PeriodName = this.GetPeriodName(this.Data.Period); //周期名字 + if (this.Data.Right != null) sendData.Stock.RightName = RIGHT_NAME[this.Data.Right]; //复权名字 + } + + //console.log('[DynamicKLineTitlePainting::SendUpdateUIMessage', sendData); + this.UpdateUICallback(sendData); + } + + this.GetPeriodName = function (period) + { + var name = ''; + if (period > CUSTOM_MINUTE_PERIOD_START && period <= CUSTOM_MINUTE_PERIOD_END) + name = (period - CUSTOM_MINUTE_PERIOD_START) + g_JSChartLocalization.GetText('自定义分钟', this.LanguageID); + else if (period > CUSTOM_DAY_PERIOD_START && period <= CUSTOM_DAY_PERIOD_END) + name = (period - CUSTOM_DAY_PERIOD_START) + g_JSChartLocalization.GetText('自定义日线',this.LanguageID); + else if (period > CUSTOM_SECOND_PERIOD_START && period <= CUSTOM_SECOND_PERIOD_END) + name = (period - CUSTOM_SECOND_PERIOD_START) + g_JSChartLocalization.GetText('自定义秒', this.LanguageID); + else + name = g_JSChartLocalization.GetText(ChartData.GetPeriodName(period), this.LanguageID); + return name; + } + + this.GetRightName = function (rightID, periodID) + { + //分钟K线没有复权 + if (ChartData.IsMinutePeriod(periodID, true) || ChartData.IsSecondPeriod(periodID)) + return null; + + if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.UpperSymbol)) //A股有复权 + { + var rightName = RIGHT_NAME[rightID]; + return rightName + } + + return null; + } + + this.FullDraw=function() + { + if (!this.IsShow) return; + this.UpperSymbol=this.Symbol ? this.Symbol.toUpperCase():''; + if (this.CursorIndex == null || !this.Data || this.Data.length <= 0) + { + this.OnDrawEventCallback(null, 'DynamicKLineTitlePainting::FullDraw'); + return; + } + + this.SpaceWidth = this.Canvas.measureText(' ').width; + var index = this.CursorIndex; + index = parseInt(index.toFixed(0)); + var dataIndex = this.Data.DataOffset + index; + if (dataIndex >= this.Data.Data.length) dataIndex=-1; + if (dataIndex < 0) + { + this.OnDrawEventCallback(null, 'DynamicKLineTitlePainting::FullDraw'); + return; + } + + var item = this.Data.Data[dataIndex]; + this.OnDrawEventCallback(item, 'DynamicKLineTitlePainting::FullDraw'); + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + if (this.LineCount > 1) this.DrawMulitLine(item); + else this.DrawSingleLine(item,true); + this.Canvas.restore(); + if (!item.Time && item.Date && this.InfoData) this.HSCreenKLineInfoDraw(item.Date); + } + else + { + if (this.LineCount > 1) this.DrawMulitLine(item); + else this.DrawSingleLine(item, true); + if (!item.Time && item.Date && this.InfoData) this.KLineInfoDraw(item.Date); + } + } + + this.DrawTitle = function () + { + this.UpperSymbol=this.Symbol ? this.Symbol.toUpperCase():''; + this.SendUpdateUIMessage('DrawTitle'); + this.OnDrawEventCallback(null, 'DynamicKLineTitlePainting::DrawTitle'); + + if (!this.IsShow) return; + if (!this.IsShowName && !this.IsShowSettingInfo) return; + if (this.LineCount > 1) return; + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + this.HScreenDrawTitle(); + this.Canvas.restore(); + return; + } + + var left = this.Frame.ChartBorder.GetLeft(); + var bottom = this.Frame.ChartBorder.GetTop(); + var right = this.Frame.ChartBorder.GetRight(); + if (bottom < 5) return; + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = this.Font; + var position = { Left: left, Bottom: bottom, IsHScreen: false }; + + if (this.IsShowName && this.Name) + { + if (!this.DrawKLineText(this.Name, this.NameColor, position)) return; + } + + if (this.IsShowSettingInfo && this.Data.Period != null && this.Data.Right != null) + { + var periodName = this.GetPeriodName(this.Data.Period); + var rightName = this.GetRightName(this.Data.Right,this.Data.Period); + var text = "(" + periodName + ")"; + if (rightName) text = "(" + periodName + " " + rightName + ")"; + if (!this.DrawKLineText(text, this.SettingColor, position)) return; + } + } + + this.HScreenDrawTitle = function () + { + var xText = this.Frame.ChartBorder.GetRight(); + var yText = this.Frame.ChartBorder.GetTop(); + var right = this.Frame.ChartBorder.GetHeight(); + if (this.Frame.ChartBorder.Right < 10) return; + + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = this.Font; + + var left = 2; + var bottom = -2; + var position = { Left: left, Bottom: bottom, IsHScreen: false }; + if (this.IsShowName && this.Name) + { + if (!this.DrawKLineText(this.Name, this.NameColor, position)) return; + } + + if (this.IsShowSettingInfo && this.Data.Period != null && this.Data.Right != null) + { + var periodName = this.GetPeriodName(this.Data.Period); + var rightName = this.GetRightName(this.Data.Right,this.Data.Period); + var text = "(" + periodName + ")"; + if (rightName) text = "(" + periodName + " " + rightName + ")"; + if (!this.DrawKLineText(text, this.SettingColor, position)) return; + } + } + + this.DrawMulitLine = function (item) //画多行 + { + var isHScreen = this.Frame.IsHScreen === true; + var leftSpace = 1; + var bottomSpace = 1; + var left = this.Frame.ChartBorder.GetLeft() + leftSpace;; + var width = this.Frame.ChartBorder.GetWidth(); + var height = this.Frame.ChartBorder.GetTop(); + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol);//价格小数位数 + if (isHScreen) + { + var left = leftSpace;; + var width = this.Frame.ChartBorder.GetHeight(); + var height = this.Frame.ChartBorder.Right; + var xText = this.Frame.ChartBorder.GetChartWidth(); + var yText = this.Frame.ChartBorder.GetTop(); + + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + + var itemHeight = (height - bottomSpace) / this.LineCount; + var itemWidth = (width - leftSpace) / 4; + var bottom = itemHeight; + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = this.Font; + + var text = IFrameSplitOperator.FormatDateString(item.Date); + this.Canvas.fillStyle = this.DateTimeColor; + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + this.Canvas.textAlign = "left"; + this.Canvas.fillStyle = this.GetColor(item.Open, item.YClose); + var text = g_JSChartLocalization.GetText('Tooltip-Open', this.LanguageID) + item.Open.toFixed(defaultfloatPrecision); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + this.Canvas.fillStyle = this.GetColor(item.High, item.YClose); + var text = g_JSChartLocalization.GetText('Tooltip-High', this.LanguageID) + item.High.toFixed(defaultfloatPrecision); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + var value = (item.Close - item.YClose) / item.YClose * 100; + this.Canvas.fillStyle = this.GetColor(value, 0); + var text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID) + value.toFixed(2) + '%'; + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + bottom += itemHeight; //换行 + var left = this.Frame.ChartBorder.GetLeft() + leftSpace; + if (isHScreen) left = leftSpace; + if (ChartData.IsMinutePeriod(this.Period, true) && item.Time) + { + this.Canvas.fillStyle = this.DateTimeColor; + var text = IFrameSplitOperator.FormatTimeString(item.Time); + this.Canvas.fillText(text, left, bottom, itemWidth); + } + else if (ChartData.IsSecondPeriod(this.Period) && item.Time) + { + this.Canvas.fillStyle = this.SettingColor; + var text = IFrameSplitOperator.FormatTimeString(item.Time, 'HH:MM:SS'); + this.Canvas.fillText(text, left, bottom, itemWidth); + } + left += itemWidth; + + this.Canvas.fillStyle = this.GetColor(item.Close, item.YClose); + var text = g_JSChartLocalization.GetText('Tooltip-Close', this.LanguageID) + item.Close.toFixed(defaultfloatPrecision); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + this.Canvas.fillStyle = this.GetColor(item.Low, item.YClose); + var text = g_JSChartLocalization.GetText('Tooltip-Low', this.LanguageID) + item.Low.toFixed(defaultfloatPrecision); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + this.Canvas.fillStyle = this.AmountColor; + var text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Amount, 2, this.LanguageID); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + } + + this.DrawSingleLine = function (item,bDrawTitle) //画单行 + { + var isHScreen = this.Frame.IsHScreen === true; + var left = this.Frame.ChartBorder.GetLeft(); + //var bottom=this.Frame.ChartBorder.GetTop()-this.Frame.ChartBorder.Top/2; + var bottom = this.Frame.ChartBorder.GetTop(); + var right = this.Frame.ChartBorder.GetRight(); + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol);//价格小数位数 + + if (isHScreen) + { + right = this.Frame.ChartBorder.GetHeight(); + if (this.Frame.ChartBorder.Right < 5) return; + left = 2; + bottom = -2; + var xText = this.Frame.ChartBorder.GetRight(); + var yText = this.Frame.ChartBorder.GetTop(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + else + { + if (bottom < 5) return; + } + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = this.Font; + + var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen }; + + if (this.IsShowName) //名称 + { + if (!this.DrawKLineText(this.Name, this.NameColor, position, bDrawTitle==true)) return; + } + + if (this.IsShowSettingInfo) //周期 复权信息 + { + var periodName = this.GetPeriodName(this.Data.Period); + var rightName = this.GetRightName(this.Data.Right,this.Data.Period); + var text = "(" + periodName + ")"; + if (rightName) text = "(" + periodName + " " + rightName + ")"; + if (!this.DrawKLineText(text, this.SettingColor, position, bDrawTitle==true)) return; + } + + var text = IFrameSplitOperator.FormatDateString(item.Date); //日期 + if (!this.DrawKLineText(text, this.DateTimeColor, position)) return; + + //时间 + if (ChartData.IsMinutePeriod(this.Period, true) && IFrameSplitOperator.IsNumber(item.Time)) + { + var text = IFrameSplitOperator.FormatTimeString(item.Time); + if (!this.DrawKLineText(text, this.DateTimeColor, position)) return; + } + else if (ChartData.IsSecondPeriod(this.Period) && IFrameSplitOperator.IsNumber(item.Time)) + { + var text = IFrameSplitOperator.FormatTimeString(item.Time, "HH:MM:SS"); + if (!this.DrawKLineText(text, this.DateTimeColor, position)) return; + } + + var color = this.GetColor(item.Open, item.YClose); + var text = g_JSChartLocalization.GetText('KTitle-Open', this.LanguageID) + item.Open.toFixed(defaultfloatPrecision); + if (!this.DrawKLineText(text, color, position)) return; + + var color = this.GetColor(item.High, item.YClose); + var text = g_JSChartLocalization.GetText('KTitle-High', this.LanguageID) + item.High.toFixed(defaultfloatPrecision); + if (!this.DrawKLineText(text, color, position)) return; + + var color = this.GetColor(item.Low, item.YClose); + var text = g_JSChartLocalization.GetText('KTitle-Low', this.LanguageID) + item.Low.toFixed(defaultfloatPrecision); + if (!this.DrawKLineText(text, color, position)) return; + + var color = this.GetColor(item.Close, item.YClose); + var text = g_JSChartLocalization.GetText('KTitle-Close', this.LanguageID) + item.Close.toFixed(defaultfloatPrecision); + if (!this.DrawKLineText(text, color, position)) return; + + if (IFrameSplitOperator.IsNumber(item.Vol)) + { + var text = g_JSChartLocalization.GetText('KTitle-Vol', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Vol, 2); + if (!this.DrawKLineText(text, this.VolColor, position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + var text = g_JSChartLocalization.GetText('KTitle-Amount', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Amount, 2); + if (!this.DrawKLineText(text, this.AmountColor, position)) return; + } + + if (MARKET_SUFFIX_NAME.IsChinaFutures(this.UpperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) + { + var text = g_JSChartLocalization.GetText('KTitle-Position', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Position, 2); + if (!this.DrawKLineText(text, this.PositionColor, position)) return; + } + } + + this.OnDrawEventCallback = function (drawData, explain) + { + if (!this.OnDrawEvent || !this.OnDrawEvent.Callback) return; + var data = { Draw: drawData, Name: this.ClassName, Explain: explain }; + if (this.Data && this.Data.Data) + { + if (IFrameSplitOperator.IsNumber(this.CursorIndex)) + { + var index = this.CursorIndex; + index = parseInt(index.toFixed(0)); + var dataIndex = this.Data.DataOffset + index; + } + else + { + dataIndex=this.Data.Data.length-1; + } + + var dataCount=this.Data.Data.length; + data.DataIndex=dataIndex; + data.DataCount=dataCount; + } + this.OnDrawEvent.Callback(this.OnDrawEvent, data, this); + } + + this.Draw = function () + { + this.UpperSymbol = this.Symbol ? this.Symbol.toUpperCase() : ''; + this.SendUpdateUIMessage('Draw'); + + if (!this.IsShow) return; + if (this.CursorIndex == null || !this.Data || this.Data.length <= 0) + { + this.OnDrawEventCallback(null, 'DynamicKLineTitlePainting::Draw'); + return; + } + + this.SpaceWidth = this.Canvas.measureText(' ').width; + var index = this.CursorIndex; + index = parseInt(index.toFixed(0)); + var dataIndex = this.Data.DataOffset + index; + if (dataIndex >= this.Data.Data.length) dataIndex = this.Data.Data.length - 1; + if (dataIndex < 0) + { + this.OnDrawEventCallback(null, 'DynamicKLineTitlePainting::Draw'); + return; + } + + var item = this.Data.Data[dataIndex]; + this.OnDrawEventCallback(item, 'DynamicKLineTitlePainting::Draw'); + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + if (this.LineCount > 1) this.DrawMulitLine(item); + else this.DrawSingleLine(item); + this.Canvas.restore(); + if (!item.Time && item.Date && this.InfoData) this.HSCreenKLineInfoDraw(item.Date); + } + else + { + if (this.LineCount > 1) this.DrawMulitLine(item); + else this.DrawSingleLine(item); + + if (!item.Time && item.Date && this.InfoData) this.KLineInfoDraw(item.Date); + } + } + + + this.KLineInfoDraw = function (date) { + var info = this.InfoData.get(date.toString()); + if (!info) return; + var invesotrCount = 0; //互动易统计 + var researchCouunt = 0; + var reportCount = 0; + var blockTradeCount = 0; //大宗交易次数 + var tradeDetailCount = 0; //龙虎榜上榜次数 + var policyData = null; + var reportTitle = null, pforecastTitle = null; + //console.log(info); + for (var i in info.Data) { + var item = info.Data[i]; + switch (item.InfoType) { + case KLINE_INFO_TYPE.INVESTOR: + ++invesotrCount; + break; + case KLINE_INFO_TYPE.PFORECAST: + pforecastTitle = item.Title; + break; + case KLINE_INFO_TYPE.ANNOUNCEMENT: + ++reportCount; + break; + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4: + reportTitle = item.Title; + break; + case KLINE_INFO_TYPE.RESEARCH: + ++researchCouunt; + break; + case KLINE_INFO_TYPE.BLOCKTRADING: + ++blockTradeCount; + break; + case KLINE_INFO_TYPE.TRADEDETAIL: + ++tradeDetailCount; + break; + case KLINE_INFO_TYPE.POLICY: + policyData = item; + break; + } + } + + var isHScreen = (this.Frame.IsHScreen === true); + var right = this.Frame.ChartBorder.GetRight() - 4; + var top = this.Frame.ChartBorder.GetTopEx(); + if (isHScreen) { + right = this.Frame.ChartBorder.GetBottom() - 4; + top = this.Frame.ChartBorder.GetRightEx(); + this.Canvas.translate(top, right); + this.Canvas.rotate(90 * Math.PI / 180); + right = 0; top = 0; + } + + this.Canvas.font = this.Font; + + var aryTitle = []; + var position = { Top: top, Right: right, IsHScreen: isHScreen }; + + aryTitle.push(IFrameSplitOperator.FormatDateString(date)); + if (reportTitle) aryTitle.push(reportTitle); //季报 + if (pforecastTitle) aryTitle.push(pforecastTitle); //业绩预告 + if (reportCount > 0) aryTitle.push('公告数量:' + reportCount); + if (researchCouunt > 0) aryTitle.push('机构调研次数:' + researchCouunt); + if (tradeDetailCount > 0) aryTitle.push('龙虎榜上榜次数:' + tradeDetailCount); + if (invesotrCount > 0) aryTitle.push('互动易数量:' + invesotrCount); + if (blockTradeCount > 0) aryTitle.push('大宗交易次数:' + blockTradeCount); + if (policyData) //策略选股 + { + for (let i in policyData.ExtendData) //显示满足的策略 + { + aryTitle.push(policyData.ExtendData[i].Name); + } + } + + var maxWidth = 0, textBGHeight = 0; + for (let i in aryTitle) { + var item = aryTitle[i]; + var textWidth = this.Canvas.measureText(item).width + 2; //后空2个像素 + if (maxWidth < textWidth) maxWidth = textWidth; + textBGHeight += this.InfoTextHeight; + } + + this.Canvas.fillStyle = this.InfoTextBGColor; + if (isHScreen) this.Canvas.fillRect(position.Right - maxWidth, position.Top, maxWidth + 2, textBGHeight + 2); + else this.Canvas.fillRect(position.Right - maxWidth, position.Top, maxWidth + 2, textBGHeight + 2); + + for (let i in aryTitle) { + var item = aryTitle[i]; + this.DrawInfoText(item, position); + } + } + + this.HSCreenKLineInfoDraw = function (date) { + this.Canvas.save(); + this.KLineInfoDraw(date); + this.Canvas.restore(); + } + + this.GetColor = function (price, yclse) { + if (price > yclse) return this.UpColor; + else if (price < yclse) return this.DownColor; + else return this.UnchagneColor; + } + + this.DrawInfoText = function (title, position) { + if (!title) return true; + + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "top"; + this.Canvas.fillStyle = this.InfoTextColor; + this.Canvas.fillText(title, position.Right, position.Top); + position.Top += this.InfoTextHeight; + return true; + } + + this.DrawKLineText = function (title, color, position, isShow) + { + if (!title) return true; + + var isHScreen = this.Frame.IsHScreen === true; + var right = this.Frame.ChartBorder.GetRight(); + if (isHScreen) right = this.Frame.ChartBorder.GetHeight(); + + this.Canvas.fillStyle = color; + var textWidth = this.Canvas.measureText(title).width; + if (position.Left + textWidth > right) return false; + if (!(isShow === false)) this.Canvas.fillText(title, position.Left, position.Bottom, textWidth); + + position.Left += textWidth + this.SpaceWidth; + return true; + } + +} + +//分时图标题 +function DynamicMinuteTitlePainting() +{ + this.newMethod = DynamicKLineTitlePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.YClose; + this.IsShowDate = false; //标题是否显示日期 + this.IsShowName = true; //标题是否显示股票名字 + this.Symbol; + this.UpperSymbol; + this.LastShowData; //保存最后显示的数据 给tooltip用 + this.ClassName ='DynamicMinuteTitlePainting'; + this.SpaceWidth = 2; + this.IsShowAveragePrice=true; //是否显示均线价格 + + this.GetCurrentKLineData = function () //获取当天鼠标位置所在的K线数据 + { + if (this.LastShowData) return this.LastShowData; + if (this.CursorIndex == null || !this.Data) return null; + if (this.Data.length <= 0) return null; + + var index = Math.abs(this.CursorIndex); + index = parseInt(index.toFixed(0)); + var dataIndex = this.Data.DataOffset + index; + if (dataIndex >= this.Data.Data.length) dataIndex = this.Data.Data.length - 1; + if (dataIndex < 0) return null; + + var item = this.Data.Data[dataIndex]; + return item; + } + + this.SendUpdateUIMessage = function (funcName) //通知外面 标题变了 + { + if (!this.UpdateUICallback) return; + + var sendData = + { + TitleName: '分钟标题', CallFunction: funcName, Stock: { Name: this.Name, Symbol: this.Symbol, }, + Rect: + { + Left: this.Frame.ChartBorder.GetLeft(), Right: this.Frame.ChartBorder.GetRight(), + Top: 0, Bottom: this.Frame.ChartBorder.GetTop(), + } + }; + + //有数据 + if (this.Data && this.Data.Data && this.Data.Data.length > 0) { + let index = this.Data.Data.length - 1; //默认最后1分钟的数据 + if (this.CursorIndex) { + let cursorIndex = Math.abs(this.CursorIndex - 0.5); + cursorIndex = parseInt(cursorIndex.toFixed(0)); + index = this.Data.DataOffset + cursorIndex; + if (index >= this.Data.Data.length) index = this.Data.Data.length - 1; + } + + if (index >= 0) { + let item = this.Data.Data[index]; + this.LastShowData = item; + sendData.Stock.Data = + { + Time: item.Time, Close: item.Close, AvPrice: item.AvPrice, + Vol: item.Vol, Amount: item.Amount + } + if (item.Time) sendData.Stock.Time = item.Time; //分钟K线才有时间 + } + } + this.UpdateUICallback(sendData); + } + + this.DrawTitle = function () + { + this.UpperSymbol = this.Symbol ? this.Symbol.toUpperCase() : ''; + this.SendUpdateUIMessage('DrawTitle'); + this.OnDrawEventCallback(null, "DynamicMinuteTitlePainting::DrawTitle"); + } + + this.GetDecimal = function (symbol) + { + return JSCommonCoordinateData.GetfloatPrecision(symbol);//价格小数位数 + } + + this.DrawMulitLine = function (item) //画多行 + { + var leftSpace = 5; + var bottomSpace = 2; + var left = this.Frame.ChartBorder.GetLeft() + leftSpace;; + var right = this.Frame.ChartBorder.GetRight(); + var width = this.Frame.ChartBorder.GetWidth(); + var height = this.Frame.ChartBorder.GetTop(); + + var defaultfloatPrecision = this.GetDecimal(this.Symbol); //价格小数位数 + var itemHeight = (height - bottomSpace) / this.LineCount; + var bottom = itemHeight; + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "bottom"; + this.Canvas.font = this.Font; + this.Canvas.fillStyle = this.UnchagneColor; + + this.Canvas.fillStyle = this.UnchagneColor; + var text = IFrameSplitOperator.FormatDateTimeString(item.DateTime, this.IsShowDate ? 'YYYY-MM-DD' : 'HH-MM'); + var timeWidth = this.Canvas.measureText(text).width + 5; //后空5个像素 + this.Canvas.fillText(text, left, bottom, timeWidth); + + if (this.IsShowDate) { + var text = IFrameSplitOperator.FormatDateTimeString(item.DateTime, 'HH-MM'); + this.Canvas.fillText(text, left, bottom + itemHeight, timeWidth); + } + + var itemWidth = (width - leftSpace - timeWidth) / 2; + left += timeWidth; + + if (item.Close != null) { + this.Canvas.fillStyle = this.GetColor(item.Close, this.YClose); + var text = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID) + item.Close.toFixed(defaultfloatPrecision); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + } + + if (item.Increase != null) { + this.Canvas.fillStyle = this.GetColor(item.Increase, 0); + var text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID) + item.Increase.toFixed(2) + '%'; + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + } + + bottom += itemHeight; //换行 + var left = this.Frame.ChartBorder.GetLeft() + leftSpace + timeWidth; + + this.Canvas.fillStyle = this.VolColor; + var text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Vol, 2, this.LanguageID); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + + this.Canvas.fillStyle = this.AmountColor; + var text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Amount, 2, this.LanguageID); + this.Canvas.fillText(text, left, bottom, itemWidth); + left += itemWidth; + } + + this.DrawItem = function (item) + { + var isHScreen = this.Frame.IsHScreen === true; + var left = this.Frame.ChartBorder.GetLeft();; + var bottom = this.Frame.ChartBorder.GetTop() - this.Frame.ChartBorder.Top / 2; + var right = this.Frame.ChartBorder.GetRight(); + var defaultfloatPrecision = this.GetDecimal(this.Symbol); //价格小数位数 + + if (isHScreen) + { + if (this.Frame.ChartBorder.Right < 5) return; + var left = 2; + var bottom = this.Frame.ChartBorder.Right / 2; //上下居中显示 + var right = this.Frame.ChartBorder.GetHeight(); + var xText = this.Frame.ChartBorder.GetChartWidth(); + var yText = this.Frame.ChartBorder.GetTop(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + else + { + if (bottom < 5) return; + } + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.font = this.Font; + var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen }; + + if (this.IsShowName) + { + if (!this.DrawMinuteText(this.Name, this.NameColor, position, true)) return; + } + + this.Canvas.fillStyle = this.UnchagneColor; + var text = IFrameSplitOperator.FormatDateTimeString(item.DateTime, this.IsShowDate ? 'YYYY-MM-DD HH-MM' : 'HH-MM'); + if (!this.DrawMinuteText(text, this.DateTimeColor, position)) return; + + if (IFrameSplitOperator.IsNumber(item.Close)) + { + var color = this.GetColor(item.Close, this.YClose); + var text = g_JSChartLocalization.GetText('MTitle-Close', this.LanguageID) + item.Close.toFixed(defaultfloatPrecision); + if (!this.DrawMinuteText(text, color, position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.Increase)) + { + var color = this.GetColor(item.Increase, 0); + var text = g_JSChartLocalization.GetText('MTitle-Increase', this.LanguageID) + item.Increase.toFixed(2) + '%'; + if (!this.DrawMinuteText(text, color, position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true) + { + var color = this.GetColor(item.AvPrice, this.YClose); + var text = g_JSChartLocalization.GetText('MTitle-AvPrice', this.LanguageID) + item.AvPrice.toFixed(defaultfloatPrecision); + if (!this.DrawMinuteText(text, color, position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.Vol)) + { + var text = g_JSChartLocalization.GetText('MTitle-Vol', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Vol, 2, this.LanguageID); + if (!this.DrawMinuteText(text, this.VolColor, position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + var text = g_JSChartLocalization.GetText('MTitle-Amount', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Amount, 2, this.LanguageID); + if (!this.DrawMinuteText(text, this.AmountColor, position)) return; + } + + if (MARKET_SUFFIX_NAME.IsChinaFutures(this.UpperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) + { + var text = g_JSChartLocalization.GetText('MTitle-Position', this.LanguageID) + IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID); + if (!this.DrawMinuteText(text, this.VolColor, position)) return; + } + + } + + this.FullDraw=function() + { + this.Draw(); + } + + this.Draw = function () + { + this.UpperSymbol = this.Symbol ? this.Symbol.toUpperCase() : ''; + this.LastShowData = null; + this.SendUpdateUIMessage('Draw'); + if (!this.IsShow) return; + if (this.CursorIndex == null || !this.Data || !this.Data.Data || this.Data.Data.length <= 0) + { + this.OnDrawEventCallback(null,"DynamicMinuteTitlePainting::Draw"); + return; + } + + var index = this.CursorIndex; + index = parseInt(index.toFixed(0)); + var dataIndex = index + this.Data.DataOffset; + if (dataIndex >= this.Data.Data.length) dataIndex = this.Data.Data.length - 1; + + var item = this.Data.Data[dataIndex]; + this.LastShowData = item; + this.OnDrawEventCallback(item, "DynamicMinuteTitlePainting::Draw"); + + if (this.LineCount > 1 && !(this.Frame.IsHScreen === true)) + { + this.DrawMulitLine(item); + return; + } + + this.Canvas.save(); + this.DrawItem(item); + this.Canvas.restore(); + } + + this.DrawMinuteText = function (title, color, position, isShow) + { + if (!title) return true; + + var isHScreen = this.Frame.IsHScreen === true; + var right = this.Frame.ChartBorder.GetRight(); + if (isHScreen) right = this.Frame.ChartBorder.GetHeight(); + + this.Canvas.fillStyle = color; + var textWidth = this.Canvas.measureText(title).width; + if (position.Left + textWidth > right) return false; + if (!(isShow === false)) this.Canvas.fillText(title, position.Left, position.Bottom, textWidth); + + position.Left += textWidth + this.SpaceWidth; + return true; + } +} + +//字符串输出格式 +var STRING_FORMAT_TYPE = +{ + DEFAULT: 1, //默认 2位小数 单位自动转化 (万 亿) + ORIGINAL: 2, //原始数据 + THOUSANDS: 21, //千分位分割 +}; + +function DynamicTitleData(data, name, color) //指标标题数据 +{ + this.Data = data; + this.Name = name; + this.Color = color; //字体颜色 + this.DataType; //数据类型 + this.StringFormat = STRING_FORMAT_TYPE.DEFAULT; //字符串格式 + this.FloatPrecision = 2; //小数位数 + this.GetTextCallback; //自定义数据转文本回调 + this.IsShow=true; +} + +//指标标题 +function DynamicChartTitlePainting() +{ + this.newMethod = IChartTitlePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.IsDynamic = true; + this.Data = new Array(); + this.Explain; + this.TitleBG; //标题背景色 + this.TitleBGHeight = 20; //标题背景色高度 + this.TitleAlign = 'middle';//对其方式 + this.TitleBottomDistance = 1; //标题靠底部输出的时候 字体和底部的间距 + this.Text = new Array(); //副标题 Text:'文本', Color:'颜色' + this.EraseRect; + this.EraseColor = g_JSChartResource.BGColor; //用来擦出的背景色 + + this.TitleRect; //指标名字显示区域 + this.IsDrawTitleBG=false; //是否绘制指标名字背景色 + this.BGColor=g_JSChartResource.IndexTitleBGColor; + + this.IsShowIndexName = true; //是否显示指标名字 + this.ParamSpace = 2; //参数显示的间距 + this.OutName=null; //动态标题 + this.IsFullDraw=true; //手势离开屏幕以后是否显示最后的价格 + + this.SetDynamicOutName=function(outName, args) + { + if (!this.OutName) this.OutName=new Map(); + else this.OutName.clear(); + + var mapArgs=new Map(); + for(var i in args) + { + var item=args[i]; + mapArgs.set(`{${item.Name}}`, item); + } + + for(var i in outName) + { + var item=outName[i]; + var aryFond = item.DynamicName.match(/{\w*}/i); + if (!aryFond || aryFond.length<=0) + { + this.OutName.set(item.Name, item.DynamicName); + } + else + { + var dyName=item.DynamicName; + var bFind=true; + for(var j=0;jthis.TitleRect.Left && xthis.TitleRect.Top && y 0) text += ','; + + text += year.toString(); + switch (quarter) { + case 1: + text += '一季报 '; + break; + case 2: + text += '半年报 '; + break; + case 3: + text += '三季报 '; + break; + case 4: + text += '年报 '; + break; + } + + text += this.FormatValue(value, format); + } + + return text; + } + + this.SendUpdateUIMessage = function (funcName) //通知外面 标题变了 + { + if (!this.UpdateUICallback) return; + + var sendData = { + TitleName: '指标标题', CallFunction: funcName, + TitleData: { Title: this.Title, Identify: this.Frame.Identify, Data: [] }, + Rect: //标题的位置 + { + Top: this.Frame.ChartBorder.GetTop(), Left: this.Frame.ChartBorder.GetLeft(), + Right: this.Frame.ChartBorder.GetRight(), Bottom: this.Frame.ChartBorder.GetBottom() + } + }; + + for (var i in this.Data) { + var item = this.Data[i]; + if (!item || !item.Data || !item.Data.Data) continue; + if (item.Data.Data.length <= 0) continue; + + var titleItem = { Name: item.Name, Color: item.Color }; + if (item.DataType) titleItem.DataType = item.DataType; + + if (item.DataType == "StraightLine") //直线只有1个数据 + { + titleItem.Value = item.Data.Data[0]; + } + else { + var index = item.Data.Data.length - 1; + if (this.CursorIndex != null) { + var cursorIndex = Math.abs(this.CursorIndex - 0.5); + cursorIndex = parseInt(cursorIndex.toFixed(0)); + index = item.Data.DataOffset + cursorIndex + } + if (index >= item.Data.Data.length) index = item.Data.Data.length - 1; + + titleItem.Value = item.Data.Data[index]; + } + + sendData.TitleData.Data.push(titleItem); + } + + //console.log('[DynamicChartTitlePainting::SendUpdateUIMessage', sendData); + this.UpdateUICallback(sendData); + } + + this.FullDraw=function() + { + this.EraseRect = null; + this.TitleRect=null; + this.IsDrawTitleBG=this.Frame.IsDrawTitleBG; + if (this.Frame.ChartBorder.TitleHeight < 5) return; + if (this.Frame.IsShowTitle == false) return; + if (this.Frame.IsMinSize) return; + + this.IsShowIndexName = this.Frame.IsShowIndexName; + this.ParamSpace = this.Frame.IndexParamSpace; + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + this.DrawItem(true,true); + this.Canvas.restore(); + return; + } + + this.DrawItem(true,true); + } + + this.DrawTitle = function () + { + this.IsDrawTitleBG=this.Frame.IsDrawTitleBG; + this.EraseRect = null; + this.TitleRect=null; + this.SendUpdateUIMessage('DrawTitle'); + if (this.Frame.ChartBorder.TitleHeight < 5) return; + if (this.Frame.IsShowTitle == false) return; + + this.IsShowIndexName = this.Frame.IsShowIndexName; + this.ParamSpace = this.Frame.IndexParamSpace; + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + this.DrawItem(true,false); + this.Canvas.restore(); + return; + } + + this.DrawItem(true,false); + } + + this.EraseTitle = function () + { + if (!this.EraseRect) return; + this.Canvas.fillStyle = this.EraseColor; + this.Canvas.fillRect(this.EraseRect.Left, this.EraseRect.Top, this.EraseRect.Width, this.EraseRect.Height); + } + + this.Draw = function () + { + this.TitleRect=null; + this.SendUpdateUIMessage('Draw'); + + if (this.CursorIndex == null) return; + if (!this.Data) return; + if (this.Frame.ChartBorder.TitleHeight < 5) return; + if (this.Frame.IsShowTitle == false) return; + + this.IsShowIndexName = this.Frame.IsShowIndexName; + this.ParamSpace = this.Frame.IndexParamSpace; + + if (this.Frame.IsHScreen === true) + { + this.Canvas.save(); + this.DrawItem(false,true); + this.Canvas.restore(); + return; + } + + this.DrawItem(false,true); + } + + this.DrawItem=function(bDrawTitle, bDrawValue) + { + var isHScreen=(this.Frame.IsHScreen === true); + var left = this.Frame.ChartBorder.GetLeft() + 1; + var bottom = this.Frame.ChartBorder.GetTop() + this.Frame.ChartBorder.TitleHeight / 2; //上下居中显示 + if (this.TitleAlign == 'bottom') bottom = this.Frame.ChartBorder.GetTopEx() - this.TitleBottomDistance; + var right = this.Frame.ChartBorder.GetRight(); + var textWidth; + + if (isHScreen) + { + let xText = this.Frame.ChartBorder.GetRightTitle(); + let yText = this.Frame.ChartBorder.GetTop(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + left = 1; + bottom = -(this.Frame.ChartBorder.TitleHeight / 2); //上下居中显示 + if (this.TitleAlign == 'bottom') bottom = -this.TitleBottomDistance; + right = this.Frame.ChartBorder.GetHeight(); + } + + this.EraseTitle(); + + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = this.TitleAlign; + this.Canvas.font = this.Font; + + if (this.TitleBG && this.Title) //指标名称 + { + textWidth = this.Canvas.measureText(this.Title).width + 2; + let height = this.Frame.ChartBorder.TitleHeight; + let top = this.Frame.ChartBorder.GetTop(); + if (height > 20) + { + top += (height - 20) / 2 + (height - 45) / 2; + height = 20; + } + + if (this.TitleAlign == 'bottom') //底部输出文字 + { + top = this.Frame.ChartBorder.GetTopEx() - 20; + if (top < 0) top = 0; + } + if (bDrawTitle) + { + this.Canvas.fillStyle = this.TitleBG; + this.Canvas.fillRect(left, top, textWidth, height); + } + } + + if (this.Title && this.IsShowIndexName) //指标参数 + { + const metrics = this.Canvas.measureText(this.Title); + textWidth = metrics.width + 2; + if (bDrawTitle) + { + if (this.IsDrawTitleBG) //绘制指标名背景色 + { + var spaceSize=1; + this.Canvas.fillStyle=this.BGColor; + if (isHScreen) + { + this.TitleRect= {Left:this.Frame.ChartBorder.GetRightTitle(),Top:this.Frame.ChartBorder.GetTop(),Width:this.Frame.ChartBorder.TitleHeight ,Height:textWidth}; //保存下标题的坐标 + let drawRect={Left:left, Top:-this.Frame.ChartBorder.TitleHeight+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)}; + this.Canvas.fillRect(drawRect.Left,drawRect.Top,drawRect.Width,drawRect.Height); + } + else + { + this.TitleRect={Left:left, Top:this.Frame.ChartBorder.GetTop()+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)}; //保存下标题的坐标 + this.Canvas.fillRect(this.TitleRect.Left,this.TitleRect.Top,this.TitleRect.Width,this.TitleRect.Height); + } + } + this.Canvas.fillStyle = this.TitleColor; + this.Canvas.fillText(this.Title, left, bottom, textWidth); + } + left += textWidth; + } + + if (this.Text && this.Text.length > 0) + { + for (let i in this.Text) + { + let item = this.Text[i]; + this.Canvas.fillStyle = item.Color; + textWidth = this.Canvas.measureText(item.Text).width + 2; + this.Canvas.fillText(item.Text, left, bottom, textWidth); + left += textWidth; + } + } + + if (bDrawValue) + { + for (var i in this.Data) + { + var item = this.Data[i]; + if (!item || !item.Data || !item.Data.Data) continue; + if (item.Data.Data.length <= 0) continue; + if (item.IsShow==false) continue; + + var value = null; + var valueText = null; + if (item.DataType == "StraightLine") //直线只有1个数据 + { + value = item.Data.Data[0]; + valueText = this.FormatValue(value, item); + } + else + { + var index = this.CursorIndex - 0.5; + if (index<0) index=0; + index = parseInt(index.toFixed(0)); + if (item.Data.DataOffset + index >= item.Data.Data.length) continue; + + value = item.Data.Data[item.Data.DataOffset + index]; + if (value == null) continue; + + if (item.DataType == "HistoryData-Vol") + { + value = value.Vol; + valueText = this.FormatValue(value, item); + } + else if (item.DataType == "MultiReport") + { + valueText = this.FormatMultiReport(value, item); + } + else + { + if (item.GetTextCallback) valueText = item.GetTextCallback(value, item); + else valueText = this.FormatValue(value, item); + } + } + + this.Canvas.fillStyle = item.Color; + + var text; + if (item.Name) + { + var dyTitle=this.GetDynamicOutName(item.Name); + if (dyTitle) text=dyTitle+ ":" + valueText; + else text = item.Name + ":" + valueText; + } + else + { + text=valueText; + } + textWidth = this.Canvas.measureText(text).width + this.ParamSpace; //后空2个像素 + this.Canvas.fillText(text, left, bottom, textWidth); + left += textWidth; + } + } + else + { + left += 4; + var eraseRight = left, eraseLeft = left; + for (var i in this.Data) + { + var item = this.Data[i]; + if (!item || !item.Data || !item.Data.Data) continue; + if (item.Data.Data.length <= 0) continue; + + var indexName = '●' + item.Name; + this.Canvas.fillStyle = item.Color; + textWidth = this.Canvas.measureText(indexName).width + this.ParamSpace; + if (left + textWidth >= right) break; + this.Canvas.fillText(indexName, left, bottom, textWidth); + left += textWidth; + eraseRight = left; + } + + if (eraseRight > eraseLeft) + { + if (isHScreen) + { + this.EraseRect = + { + Left: eraseLeft, Right: eraseRight, Top: -(this.Frame.ChartBorder.TitleHeight - 1), + Width: eraseRight - eraseLeft, Height: this.Frame.ChartBorder.TitleHeight - 2 + }; + } + else + { + this.EraseRect = + { + Left: eraseLeft, Right: eraseRight, Top: (this.Frame.ChartBorder.GetTop() + 1), + Width: eraseRight - eraseLeft, Height: this.Frame.ChartBorder.TitleHeight - 2 + }; + } + } + } + } +} + + + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonChartTitle: + { + IChartTitlePainting: IChartTitlePainting, + DynamicKLineTitlePainting: DynamicKLineTitlePainting, + DynamicMinuteTitlePainting: DynamicMinuteTitlePainting, + DynamicChartTitlePainting: DynamicChartTitlePainting, + DynamicTitleData: DynamicTitleData, + STRING_FORMAT_TYPE: STRING_FORMAT_TYPE, + }, + + //单个类导出 + JSCommonChartTitle_IChartTitlePainting: IChartTitlePainting, + JSCommonChartTitle_DynamicKLineTitlePainting: DynamicKLineTitlePainting, + JSCommonChartTitle_DynamicMinuteTitlePainting: DynamicMinuteTitlePainting, + JSCommonChartTitle_DynamicChartTitlePainting: DynamicChartTitlePainting, + JSCommonChartTitle_DynamicTitleData: DynamicTitleData, + JSCommonChartTitle_STRING_FORMAT_TYPE: STRING_FORMAT_TYPE, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.complier.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.complier.wechat.js new file mode 100644 index 0000000..60b650c --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.complier.wechat.js @@ -0,0 +1,10644 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 分析家语法编译器 +*/ + +//日志 +import { JSConsole } from "./umychart.console.wechat.js" +import { JSCommonData } from "./umychart.data.wechat.js"; //行情数据结构体 及涉及到的行情算法(复权,周期等) +//配色资源 +import { + JSCommonResource_Global_JSChartResource as g_JSChartResource, + JSCommonResource_JSCHART_LANGUAGE_ID as JSCHART_LANGUAGE_ID, + JSCommonResource_Global_JSChartLocalization as g_JSChartLocalization, +} from './umychart.resource.wechat.js' + +import +{ + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, +} from './umychart.framesplit.wechat.js' + + +var g_JSComplierResource= +{ + Domain : "https://opensource.zealink.com", //API域名 + CacheDomain : "https://opensourcecache.zealink.com", //缓存域名 + + CustomFunction: //定制函数 + { + Data:new Map() //自定义函数 key=函数名, Value:{ID:函数名, Callback: } + }, + + CustomVariant: //自定义变量 + { + Data:new Map() //自定义函数 key=变量名, Value:{ Name:变量名, Description:描述信息 } + }, + + IsCustomFunction:function(name) + { + if (g_JSComplierResource.CustomFunction.Data.has(name)) return true; + return false; + }, + + IsCustomVariant:function(name) + { + if (g_JSComplierResource.CustomVariant.Data.has(name)) return true; + return false; + } +} + +var Messages = { + BadGetterArity: 'Getter must not have any formal parameters', + BadSetterArity: 'Setter must have exactly one formal parameter', + BadSetterRestParameter: 'Setter function argument must not be a rest parameter', + ConstructorIsAsync: 'Class constructor may not be an async method', + ConstructorSpecialMethod: 'Class constructor may not be an accessor', + DeclarationMissingInitializer: 'Missing initializer in %0 declaration', + DefaultRestParameter: 'Unexpected token =', + DuplicateBinding: 'Duplicate binding %0', + DuplicateConstructor: 'A class may only have one constructor', + DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', + ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer', + GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts', + IllegalBreak: 'Illegal break statement', + IllegalContinue: 'Illegal continue statement', + IllegalExportDeclaration: 'Unexpected token', + IllegalImportDeclaration: 'Unexpected token', + IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list', + IllegalReturn: 'Illegal return statement', + InvalidEscapedReservedWord: 'Keyword must not contain escaped characters', + InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence', + InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInForIn: 'Invalid left-hand side in for-in', + InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', + InvalidModuleSpecifier: 'Unexpected token', + InvalidRegExp: 'Invalid regular expression', + LetInLexicalBinding: 'let is disallowed as a lexically bound name', + MissingFromClause: 'Unexpected token', + MultipleDefaultsInSwitch: 'More than one default clause in switch statement', + NewlineAfterThrow: 'Illegal newline after throw', + NoAsAfterImportNamespace: 'Unexpected token', + NoCatchOrFinally: 'Missing catch or finally after try', + ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', + Redeclaration: '%0 \'%1\' has already been declared', + StaticPrototype: 'Classes may not have static property named prototype', + StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', + StrictDelete: 'Delete of an unqualified identifier in strict mode.', + StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block', + StrictFunctionName: 'Function name may not be eval or arguments in strict mode', + StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', + StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + StrictModeWith: 'Strict mode code may not include a with statement', + StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', + StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', + StrictReservedWord: 'Use of future reserved word in strict mode', + StrictVarName: 'Variable name may not be eval or arguments in strict mode', + TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', + UnexpectedEOS: 'Unexpected end of input', + UnexpectedIdentifier: 'Unexpected identifier', + UnexpectedNumber: 'Unexpected number', + UnexpectedReserved: 'Unexpected reserved word', + UnexpectedString: 'Unexpected string', + UnexpectedTemplate: 'Unexpected quasi %0', + UnexpectedToken: 'Unexpected token %0', + UnexpectedTokenIllegal: 'Unexpected token ILLEGAL', + UnknownLabel: 'Undefined label \'%0\'', + UnterminatedRegExp: 'Invalid regular expression: missing /' +}; + +var Regex = { + // Unicode v8.0.0 NonAsciiIdentifierStart: + NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, + // Unicode v8.0.0 NonAsciiIdentifierPart: + NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ +} + +var Character = +{ + FromCodePoint: function (cp) { + return (cp < 0x10000) ? String.fromCharCode(cp) : + String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); + }, + + //是否是空格 https://tc39.github.io/ecma262/#sec-white-space + IsWhiteSpace:function(cp) + { + return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) || + (cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0); + }, + + //是否换行 https://tc39.github.io/ecma262/#sec-line-terminators + IsLineTerminator:function(cp) + { + return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029); + }, + + // https://tc39.github.io/ecma262/#sec-names-and-keywords + IsIdentifierStart:function(cp) + { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp === 0x5C) || + //【】 + (cp===0x3010 || cp===0x3011) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(Character.FromCodePoint(cp))); + }, + + IsIdentifierPart: function (cp) + { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp >= 0x30 && cp <= 0x39) || + (cp === 0x5C) || + //【】 + (cp===0x3010 || cp===0x3011) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(Character.FromCodePoint(cp))); + }, + + // https://tc39.github.io/ecma262/#sec-literals-numeric-literals + IsDecimalDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x39); // 0..9 + }, + + IsHexDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x39) || (cp >= 0x41 && cp <= 0x46) || (cp >= 0x61 && cp <= 0x66); // a..f + }, + + isOctalDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x37); // 0..7 + } +} + +var TOKEN_NAME={}; +TOKEN_NAME[1 /* BooleanLiteral */] = 'Boolean'; +TOKEN_NAME[2 /* EOF */] = ''; +TOKEN_NAME[3 /* Identifier */] = 'Identifier'; +TOKEN_NAME[4 /* Keyword */] = 'Keyword'; +TOKEN_NAME[5 /* NullLiteral */] = 'Null'; +TOKEN_NAME[6 /* NumericLiteral */] = 'Numeric'; +TOKEN_NAME[7 /* Punctuator */] = 'Punctuator'; +TOKEN_NAME[8 /* StringLiteral */] = 'String'; +TOKEN_NAME[9 /* RegularExpression */] = 'RegularExpression'; +TOKEN_NAME[10 /* Template */] = 'Template'; + +//编译异常, 错误类 +function ErrorHandler() +{ + this.Error=[]; + + this.RecordError=function(error) + { + this.Error.push(error); + } + + this.ConstructError=function(msg,column) + { + let error=new Error(msg); + //通过自己抛异常并自己截获 来获取调用堆栈信息 + try + { + throw error; + } + catch(base) + { + if (Object.create && Object.defineProperties) + { + error=Object.create(base); + error.Column=column; + } + } + + return error; + } + + this.CreateError=function(index, line, col, description) + { + let msg='Line ' + line + ': ' + description; + let error=this.ConstructError(msg,col); + error.Index=index; + error.LineNumber=line; + error.Description=description; + return error; + } + + this.ThrowError=function(index, line, col, description) + { + let error=this.CreateError(index,line,col,description); + throw error; + } +} + +//扫描类 +function Scanner(code, ErrorHandler) +{ + this.Source=code; + this.ErrorHandler=ErrorHandler; + this.Length=code.length; + this.Index=0; + this.LineNumber=(code.length>0)?1:0; + this.LineStart=0; + this.CurlyStack=[]; + + this.SaveState=function() //保存当前扫描状态 + { + return { Index:this.Index, LineNumber:this.LineNumber, LineStart:this.LineStart }; + } + + this.RestoreState=function(state) //还原扫描状态 + { + this.Index=state.Index; + this.LineNumber=state.LineNumber; + this.LineStart=state.LineStart; + } + + this.IsEOF=function() //否是已经结束 + { + return this.Index>=this.Length; + } + + this.IsKeyword=function(id) + { + return false; + } + + this.CodePointAt = function (i) + { + let cp = this.Source.charCodeAt(i); + if (cp >= 0xD800 && cp <= 0xDBFF) + { + let second = this.Source.charCodeAt(i + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { + var first = cp; + cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + return cp; + } + + this.Lex=function() + { + if (this.IsEOF()) return { Type:2/*EOF*/, Value:'', LineNumber:this.LineNumber, LineStart:this.LineStart, Start:this.Index, End:this.Index }; + let cp=this.Source.charCodeAt(this.Index); + + //变量名 或 关键字 + if (Character.IsIdentifierStart(cp)) return this.ScanIdentifier(); + + //( ) ; 开头 操作符扫描 + if (cp === 0x28 || cp === 0x29 || cp === 0x3B) return this.ScanPunctuator(); + + //' " 开头 字符串扫描 + if (cp === 0x27 || cp === 0x22) return this.ScanStringLiteral(); + + //. 开头 浮点型 + if (cp==0x2E) + { + if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index + 1))) + return this.ScanNumericLiteral(); + + return this.ScanPunctuator(); + } + + //数字 + if (Character.IsDecimalDigit(cp)) return this.ScanNumericLiteral(); + + if (cp >= 0xD800 && cp < 0xDFFF) + { + if (Character.IsIdentifierStart(this.CodePointAt(this.Index))) return this.ScanIdentifier(); + } + + return this.ScanPunctuator(); + + } + + //关键字 变量名 https://tc39.github.io/ecma262/#sec-names-and-keywords + this.ScanIdentifier=function() + { + let type; + let start=this.Index; + //0x5C 反斜杠 + let id=(this.Source.charCodeAt(start)=== 0x5C) ? this.GetComplexIdentifier() : this.GetIdentifier(); + + if (id.length) type=3; //Identifier + else if (this.IsKeyword(id)) type=4; //Keyword + else if (id==null) type=5; //NullLiteral + else if (id=='true' || id=='false') type=1; //BooleanLiteral + else type=3; //Identifier + + if (type!=3 && (start+id.length!=this.Index)) + { + let restore=this.Index; + this.Index=start; + throw Messages.InvalidEscapedReservedWord; + this.Index=restore; + } + + if (id=='AND' || id=='OR') type=7 /*Punctuator*/; + + return { Type:type, Value:id, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index}; + } + + this.GetIdentifier=function() + { + let start=this.Index++; //start 保存进来的位置 + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + if (ch==0x5C) + { + this.Index=start; + return this.GetComplexIdentifier(); + } + else if (ch >= 0xD800 && ch < 0xDFFF) + { + this.Index=start; + return this.GetComplexIdentifier(); + } + + if (Character.IsIdentifierPart(ch)) ++this.Index; + else break; + } + + return this.Source.slice(start,this.Index); + } + + //操作符 https://tc39.github.io/ecma262/#sec-punctuators + this.ScanPunctuator=function() + { + let start=this.Index; + let str=this.Source[this.Index]; + switch(str) + { + case '(': + ++this.Index; + break; + case ')': + case ';': + case ',': + ++this.Index; + break; + case '.': + ++this.Index; + /*if (this.Source[this.Index] === '.' && this.Source[this.Index + 1] === '.') + { + //Spread operator: ... + this.Index += 2; + str = '...'; + } + */ + break; + default: + str=this.Source.substr(this.Index,3); + if (str=='AND') + { + this.Index+=3; + } + else + { + str = this.Source.substr(this.Index, 2); + if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '<>' || str === '<=' || str === '>=' || str === '=>' || str==':=' || str=='OR') + { + this.Index += 2; + } + else + { + str=this.Source[this.Index]; + if ('<>=!+-*%&|^/:'.indexOf(str) >= 0) ++this.Index; + } + } + } + + if (this.Index==start) + this.ThrowUnecpectedToken(); + + return { Type:7/*Punctuator*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index }; + } + + //字符串 https://tc39.github.io/ecma262/#sec-literals-string-literals + this.ScanStringLiteral=function() + { + let start=this.Index; + let quote=this.Source[this.Index]; + + ++this.Index; + var octal=false; + let str=''; + while(!this.IsEOF()) + { + let ch=this.Source[this.Index++]; + if (ch==quote) + { + quote=''; + break; + } + else if (ch=='\\') //字符串转义 + { + throw "not complete"; + } + else if (Character.IsLineTerminator(ch.charCodeAt(0))) + { + break; + } + else + { + str+=ch; + } + } + + if (quote!='') + { + this.Index=start; + this.ThrowUnecpectedToken(); + } + + return {Type:8/*StringLiteral*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index}; + } + + this.ScanNumericLiteral=function() + { + let start=this.Index; + let ch=this.Source[this.Index]; + let num=''; + if (ch!='.') + { + num=this.Source[this.Index++]; + ch=this.Source[this.Index]; + // Hex number starts with '0x'. 16进制 + if (num=='0') + { + if (ch=='x' || ch=='X') + { + ++this.Index; + return this.ScanHexLiteral(start); + } + } + + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + + ch=this.Source[this.Index]; + } + + if (ch=='.') + { + num+=this.Source[this.Index++]; + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + ch=this.Source[this.Index]; + } + + //科学计数法 + if (ch=='e' || ch=='E') + { + num+=this.Source[this.Index++]; + ch=this.Source[this.Index]; + if (ch=='+' || ch=='-') num+=this.Source[this.Index]; + if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + } + else + { + this.ThrowUnecpectedToken(); + } + } + + if (Character.IsIdentifierStart(this.Source.charCodeAt(this.Index))) + { + this.ThrowUnecpectedToken(); + } + + return { Type:6/*NumericLiteral*/, Value:parseFloat(num), LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index }; + } + + //空格 或 注释 + this.ScanComments=function() + { + let comments; + let start=(this.Index==0); + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + if (Character.IsWhiteSpace(ch)) //过滤掉空格 + { + ++this.Index; + } + else if (Character.IsLineTerminator(ch)) + { + ++this.Index; + if (ch==0x0D && this.Source.charCodeAt(this.Index)==0x0A) ++this.Index; //回车+换行 + ++this.LineNumber; + this.LineStart=this.Index; + start=true; + } + else if (ch==0x2F) // //注释 + { + ch=this.Source.charCodeAt(this.Index+1); + if (ch==0x2F) + { + this.Index+=2; + let comment=this.SkipSingleLineComment(2); + start=true; + } + else + { + break; + } + } + else if (ch == 0x7B) //{ } 注释 + { + this.Index += 1; + let comment = this.SkipMultiLineComment(); + } + else + { + break; + } + } + + return comments; + } + + this.SkipMultiLineComment = function () + { + var comments = []; + while (!this.IsEOF()) + { + var ch = this.Source.charCodeAt(this.Index); + if (Character.IsLineTerminator(ch)) + { + ++this.LineNumber; + ++this.Index; + this.LineStart = this.Index; + } + else if (ch == 0x7D) + { + this.Index += 1; + return comments; + } + else + { + ++this.Index; + } + } + + return comments; + } + + //单行注释 https://tc39.github.io/ecma262/#sec-comments + this.SkipSingleLineComment=function(offset) + { + let comments=[]; + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + ++this.Index; + if (Character.IsLineTerminator(ch)) + { + if (ch === 13 && this.Source.charCodeAt(this.Index) === 10) + ++this.Index; + + ++this.LineNumber; + this.LineStart=this.Index; + return comments; + } + } + + return comments; + } + + this.ThrowUnecpectedToken=function(message) + { + if (!message) message = Messages.UnexpectedTokenIllegal; + return this.ErrorHandler.ThrowError(this.Index, this.LineNumber, this.Index - this.LineStart + 1, message); + } + +} + +function Tokenizer(code) +{ + this.ErrorHandler=new ErrorHandler(); //错误信息处理类 + this.Scanner=new Scanner(code,this.ErrorHandler); + this.Buffer=[]; + + this.GetNextToken=function() + { + if (this.Buffer.length==0) + { + let comments=this.Scanner.ScanComments(); + if (!this.Scanner.IsEOF()) + { + let token=this.Scanner.Lex(); + + let entry={ Type:TOKEN_NAME[token.Type], Value:this.Scanner.Source.slice(token.Start, token.End)}; + + this.Buffer.push(entry); + } + } + + return this.Buffer.shift(); + } +} + +var Syntax = { + AssignmentExpression: 'AssignmentExpression', + AssignmentPattern: 'AssignmentPattern', + ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AwaitExpression: 'AwaitExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExportAllDeclaration: 'ExportAllDeclaration', + ExportDefaultDeclaration: 'ExportDefaultDeclaration', + ExportNamedDeclaration: 'ExportNamedDeclaration', + ExportSpecifier: 'ExportSpecifier', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForOfStatement: 'ForOfStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', + ImportSpecifier: 'ImportSpecifier', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + MetaProperty: 'MetaProperty', + MethodDefinition: 'MethodDefinition', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', + Program: 'Program', + Property: 'Property', + RestElement: 'RestElement', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SpreadElement: 'SpreadElement', + Super: 'Super', + SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' +}; + + +function Node() +{ + this.IsNeedIndexData=false; //是否需要大盘数据 + this.IsNeedLatestData=false; //是否需要最新的个股行情数据 + this.IsNeedSymbolData=false; //是否需要下载股票数据 + this.IsNeedMarginData = new Set(); + this.IsNeedNewsAnalysisData = new Set(); //新闻统计数据 + this.IsNeedBlockIncreaseData = new Set(); //是否需要市场涨跌股票数据统计 + this.IsNeedSymbolExData = new Set(); //下载股票行情的其他数据 + + this.FunctionData=[]; //{ID:, Args:, FunctionName: } + //FINVALUE(ID),FINONE(ID,Y,MMDD), FINANCE(ID) + + this.IsAPIData = [] //加载API数据 + + this.GetDataJobList=function() //下载数据任务列表 + { + let jobs=[]; + if (this.IsNeedSymbolData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA}); + if (this.IsNeedIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA}); + if (this.IsNeedLatestData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA}); + + //涨跌停家数统计 + for (var blockSymbol of this.IsNeedBlockIncreaseData) + { + jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA, Symbol: blockSymbol }); + } + + //加载融资融券 + for (var jobID of this.IsNeedMarginData) + { + jobs.push({ID:jobID}); + } + + //加载新闻统计 + for (var jobID of this.IsNeedNewsAnalysisData) + { + jobs.push({ID:jobID}); + } + + for (var i in this.IsAPIData) + { + var item = this.IsAPIData[i]; + jobs.push(item); + } + + //行情其他数据 + for (var jobID of this.IsNeedSymbolExData) + { + jobs.push({ ID:jobID }); + } + + for(var i in this.FunctionData) + { + var item=this.FunctionData[i]; + jobs.push(item); + } + + return jobs; + } + + this.VerifySymbolVariable = function (varName, token) + { + let setIndexName = new Set(['INDEXA', 'INDEXC', 'INDEXH', 'INDEXL', "INDEXO", "INDEXV", 'INDEXDEC', 'INDEXADV']); + if (setIndexName.has(varName)) + { + this.IsNeedIndexData=true; + return; + } + + let setSymbolDataName=new Set(['CLOSE','C','VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT']); + if (setSymbolDataName.has(varName)) + { + this.IsNeedSymbolData=true; + return; + } + + if (varName === 'VOLR') + { + if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA)) + this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA); + return; + } + + //CAPITAL流通股本(手), EXCHANGE 换手率, TOTALCAPITAL 总股本(手) + let setVariantName=new Set( + [ + "CAPITAL","TOTALCAPITAL","EXCHANGE", + "HYBLOCK","DYBLOCK","GNBLOCK","FGBLOCK","ZSBLOCK","ZHBLOCK","ZDBLOCK","HYZSCODE", + "GNBLOCKNUM","FGBLOCKNUM","ZSBLOCKNUM","ZHBLOCKNUM","ZDBLOCKNUM", + "HYSYL","HYSJL" + ]); + if (setVariantName.has(varName)) + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:varName }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (g_JSComplierResource.IsCustomVariant(varName)) //自定义函数 + { + var item={ VariantName:varName, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + } + + this.VerifySymbolFunction = function (callee, args, token) + { + //自定义函数 可以覆盖系统内置函数 + if (g_JSComplierResource.IsCustomFunction(callee.Name)) + { + var item={FunctionName:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA, Args:args} + if (token) item.Token={ Index:token.Start, Line:token.LineNumber}; + this.FunctionData.push(item); + return; + } + + if (callee.Name=='DYNAINFO') + { + this.IsNeedLatestData=true; + return; + } + + //财务函数 + if (callee.Name=='FINANCE') + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="FINVALUE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="FINONE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=='GPJYVALUE') + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name === 'MARGIN') + { + let jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(args[0].Value); + if (jobID && !this.IsNeedMarginData.has(jobID)) this.IsNeedMarginData.add(jobID); + return; + } + + if (callee.Name === 'NEWS') + { + let jobID = JS_EXECUTE_JOB_ID.GetNewsAnalysisID(args[0].Value); + if (jobID && !this.IsNeedNewsAnalysisData.has(jobID)) this.IsNeedNewsAnalysisData.add(jobID); + return; + } + + if (callee.Name == 'COST' || callee.Name == 'WINNER') //筹码都需要换手率 + { + //下载流通股 + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:[7], FunctionName:"FINANCE", FunctionName2:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="INBLOCK") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:"INBLOCK" }; //下载所有板块 + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name === 'BETA') //beta需要下载上证指数 + { + this.IsNeedIndexData = true; + return; + } + + if (callee.Name == 'UPCOUNT' || callee.Name == 'DOWNCOUNT') //上涨下跌个数 + { + var blockSymbol = args[0].Value; + if (!this.IsNeedBlockIncreaseData.has(blockSymbol)) this.IsNeedBlockIncreaseData.add(blockSymbol); + return; + } + + if (callee.Name == "LOADAPIDATA") //加载API数据 + { + var item = { Name: callee.Name, ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA, Args: args }; + if (token) item.Token = { Index: token.Start, Line: token.LineNumber }; + + this.IsAPIData.push(item); + return; + } + } + + this.ExpressionStatement=function(expression) + { + return { Type:Syntax.ExpressionStatement, Expression:expression }; + } + + this.Script=function(body) + { + return {Type:Syntax.Program, Body:body, SourceType:'通达信脚本' }; + } + + this.SequenceExpression=function(expression) + { + return {Type:Syntax.SequenceExpression, Expression:expression }; + } + + this.BinaryExpression=function(operator, left, right) + { + let logical = (operator === '||' || operator === '&&' || operator=='AND' || operator=='OR'); + let type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression; + + return { Type:type, Operator:operator, Left:left, Right:right }; + } + + this.Literal=function(value,raw) + { + return { Type:Syntax.Literal, Value:value, Raw:raw }; + } + + this.Identifier = function (name, token) + { + this.VerifySymbolVariable(name, token); + + return { Type:Syntax.Identifier, Name:name}; + } + + this.AssignmentExpression=function (operator, left, right) + { + return { Type:Syntax.AssignmentExpression, Operator:operator, Left:left, Right:right }; + } + + this.UnaryExpression=function(operator, argument) + { + return { Type:Syntax.UnaryExpression, Operator:operator, Argument:argument, Prefix:true }; + } + + this.EmptyStatement=function() + { + return { Type:Syntax.EmptyStatement }; + } + + this.CallExpression = function (callee, args, token) + { + this.VerifySymbolFunction(callee, args, token); + + return { Type:Syntax.CallExpression, Callee:callee, Arguments:args }; + } + + this.StaticMemberExpression = function (object, property) + { + return { Type: Syntax.MemberExpression, Computed: false, Object: object, Property: property }; + } +} + + + +function JSParser(code) +{ + this.ErrorHandler=new ErrorHandler(); + this.Scanner=new Scanner(code, this.ErrorHandler); + this.Node=new Node(); //节点创建 + + this.LookAhead={Type:2, Value:'', LineNumber:this.Scanner.LineNumber, LineStart:0, Start:0, End:0 }; + this.HasLineTerminator=false; + this.Context = { + IsModule: false, + await: false, + allowIn: true, + allowStrictDirective: true, + allowYield: true, + FirstCoverInitializedNameError: null, + IsAssignmentTarget: false, + IsBindingElement: false, + InFunctionBody: false, + inIteration: false, + inSwitch: false, + labelSet: {}, + Strict: false + }; + + this.PeratorPrecedence = + { + ')': 0, + ';': 0, + ',': 0, + ']': 0, + '||': 1, + 'OR':1, + '&&': 2, + 'AND':2, + '|': 3, + '^': 4, + '&': 5, + '=': 6, + '==': 6, + '!=': 6, + '<>': 6, + '===': 6, + '!==': 6, + '<': 7, + '>': 7, + '<=': 7, + '>=': 7, + '<<': 8, + '>>': 8, + '>>>': 8, + '+': 9, + '-': 9, + '*': 11, + '/': 11, + '%': 11 + }; + + this.StartMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 }; + this.LastMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 }; + + this.Initialize=function() + { + this.NextToken(); + this.LastMarker={ Index:this.Scanner.Index, Line:this.Scanner.LineNumber, Column:this.Scanner.Index-this.Scanner.LineStart }; + } + + + this.CreateNode=function() + { + return { Index:this.StartMarker.Index, Line:this.StartMarker.Line, Column:this.StartMarker.Column }; + } + + this.StartNode=function(token, lastLineStart) + { + if (lastLineStart==void 0) { lastLineStart=0; } + + let column = token.Start - token.LineStart; + let line = token.LineNumber; + if (column < 0) + { + column += lastLineStart; + line--; + } + + return { Index: token.Start, Line: line, Column: column }; + } + + this.Match=function(value) + { + return this.LookAhead.Type==7 /*Punctuator*/ && this.LookAhead.Value==value; + } + + this.Expect=function(value) + { + let token=this.NextToken(); + if (token.Type!=7 /*Punctuator*/ || token.Value!=value) + this.ThrowUnexpectedToken(token); + } + + //是否是赋值操作符 + this.MatchAssign=function() + { + if (this.LookAhead.Type!=7 /*Punctuator*/) return false; + let op=this.LookAhead.Value; + + return op==':' || op==':='; + } + + this.GetTokenRaw=function(token) + { + return this.Scanner.Source.slice(token.Start, token.End); + } + + this.NextToken=function() + { + let token=this.LookAhead; + this.LastMarker.Index=this.Scanner.Index; + this.LastMarker.Line=this.Scanner.LineNumber; + this.LastMarker.Column=this.Scanner.Index-this.Scanner.LineStart; + this.CollectComments(); //过滤注释 空格 + + if (this.Scanner.Index !== this.StartMarker.Index) + { + this.StartMarker.Index = this.Scanner.Index; + this.StartMarker.Line = this.Scanner.LineNumber; + this.StartMarker.Column = this.Scanner.Index - this.Scanner.LineStart; + } + + let next=this.Scanner.Lex(); + this.HasLineTerminator=(token.LineNumber!=next.LineNumber); + if (next && this.Context.Strict && next.Type==3/*Identifier */) + { + //TODO: + } + + this.LookAhead=next; + + return token; + } + + this.CollectComments=function() + { + this.Scanner.ScanComments(); + } + + this.ParseScript=function() + { + let node=this.CreateNode(); + let body=this.ParseDirectivePrologues(); + + while(this.LookAhead.Type!=2 /*EOF*/) + { + body.push(this.ParseStatementListItem()) + } + + return this.Finalize(node,this.Node.Script(body)); + } + + //https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive + this.ParseDirective=function() + { + let token=this.LookAhead; + let node=this.CreateNode(); + let expr=this.ParseExpression(); + } + + this.ParseDirectivePrologues=function() + { + let firstRestricted=null; + let body=[]; + while(true) + { + let token=this.LookAhead; + if (token.Type!=8 /*StringLiteral*/) break; + + let statement=this.ParseDirective(); + body.push(statement); + } + + return body; + } + + // https://tc39.github.io/ecma262/#sec-block + this.ParseStatementListItem=function() + { + let statement; + this.Context.IsAssignmentTarget=true; + this.Context.IsBindingElement=true; + if (this.LookAhead.Type==4 /*Keyword*/) + { + + } + else + { + statement=this.ParseStatement(); + } + + return statement; + } + + // https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations + this.ParseStatement=function() + { + let statement; + switch(this.LookAhead.Type) + { + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + case 10 /* Template */: + case 9 /* RegularExpression */: + statement = this.ParseExpressionStatement(); + break; + case 7 /* Punctuator */: + let value = this.LookAhead.Value; + if (value === '(') statement = this.ParseExpressionStatement(); + else if (value === ';') statement = this.ParseEmptyStatement(); + else statement = this.ParseExpressionStatement(); + break; + case 3 /* Identifier */: + statement = this.ParseLabelledStatement(); + break; + case 4 /* Keyword */: + break; + default: + statement="error"; + } + + return statement; + } + + // https://tc39.github.io/ecma262/#sec-empty-statement + this.ParseEmptyStatement=function() + { + let node=this.CreateNode(); + this.Expect(';'); + return this.Finalize(node, this.Node.EmptyStatement()); + } + + //https://tc39.github.io/ecma262/#sec-labelled-statements + this.ParseLabelledStatement=function() + { + let node=this.CreateNode(); + let expr=this.ParseExpression(); + this.ConsumeSemicolon(); + let statement = new this.Node.ExpressionStatement(expr); + + return this.Finalize(node, statement); + } + + // https://tc39.github.io/ecma262/#sec-comma-operator + this.ParseExpression=function() + { + let startToken=this.LookAhead; + let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + if (this.Match(',')) + { + let expressions=[]; + expressions.push(expr); + while(this.LookAhead.Type!=2 /*EOF*/) + { + if (!this.Match(',')) break; + this.NextToken(); + expressions.push(this.IsolateCoverGrammar(this.ParseAssignmentExpression)); + } + + expr=this.Finalize(this.StartNode(startToken),this.Node.SequenceExpression(expressions)); + } + + return expr; + } + + this.ParseAssignmentExpression=function() + { + let expr; + + let startToken=this.LookAhead; + let token=startToken; + expr=this.ParseConditionalExpression(); + + if (this.MatchAssign()) + { + if (!this.Context.IsAssignmentTarget) + { + let marker=expr.Marker; + this.ThrowUnexpectedError(marker.Index,marker.Line,marker.Column,Messages.InvalidLHSInAssignment); + } + + if (!this.Match('=') && !this.Match(':')) + { + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + } + else + { + this.ReinterpretExpressionAsPattern(expr); + } + + token=this.NextToken(); + let operator=token.Value; + let right=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + expr=this.Finalize(this.StartNode(startToken), this.Node.AssignmentExpression(operator, expr, right)); + this.Context.FirstCoverInitializedNameError=null; + } + + return expr; + } + + this.ParseConditionalExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseBinaryExpression); + + return expr; + } + + this.ParseBinaryExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseExponentiationExpression); + let token=this.LookAhead; + var prec=this.BinaryPrecedence(token); + if (prec>0) + { + this.NextToken(); + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + let markers=[startToken,this.LookAhead]; + let left=expr; + let right=this.IsolateCoverGrammar(this.ParseExponentiationExpression); + let stack=[left,token.Value,right]; + let precedences = [prec]; + while(true) + { + prec=this.BinaryPrecedence(this.LookAhead); + if (prec<=0) break; + + while(stack.length>2 && prec<=precedences[precedences.length-1]) + { + right=stack.pop(); + let operator=stack.pop(); + precedences.pop(); + left=stack.pop(); + markers.pop(); + let node=this.StartNode(markers[markers.length - 1]); + stack.push(this.Finalize(node, this.Node.BinaryExpression(operator, left, right))); + } + + //Shift + stack.push(this.NextToken().Value); + precedences.push(prec); + markers.push(this.LookAhead); + stack.push(this.IsolateCoverGrammar(this.ParseExponentiationExpression)); + } + + let i=stack.length-1; + expr=stack[i]; + let lastMarker=markers.pop(); + while(i>1) + { + let marker=markers.pop(); + let lastLineStart=lastMarker && lastMarker.LineStart; + let node=this.StartNode(marker, lastLineStart); + let operator=stack[i-1]; + expr=this.Finalize(node, this.Node.BinaryExpression(operator, stack[i - 2], expr)); + i-=2; + lastMarker=marker; + } + } + + return expr; + } + + this.ParseExponentiationExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseUnaryExpression); + + return expr; + } + + this.ParseUnaryExpression=function() + { + let expr; + if (this.Match('+') || this.Match('-')) + { + let node=this.StartNode(this.LookAhead); + let token=this.NextToken(); + expr=this.InheritCoverGrammar(this.ParseUnaryExpression); + expr=this.Finalize(node, this.Node.UnaryExpression(token.Value, expr)); + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + } + else + { + expr=this.ParseUpdateExpression(); + } + + return expr; + } + + // https://tc39.github.io/ecma262/#sec-update-expressions + this.ParseUpdateExpression=function() + { + let expr; + let startToken=this.LookAhead; + expr=this.InheritCoverGrammar(this.ParseLeftHandSideExpressionAllowCall); + + return expr; + } + + this.ParseLeftHandSideExpressionAllowCall=function() + { + let startToken=this.LookAhead; + let expr; + expr=this.InheritCoverGrammar(this.ParsePrimaryExpression); + + while(true) + { + if (this.Match('.')) + { + this.Context.IsBindingElement = false; + this.Context.IsAssignmentTarget = true; + this.Expect('.'); + const property = this.ParseIdentifierName(); + expr = this.Finalize(this.StartNode(startToken), this.Node.StaticMemberExpression(expr, property)); + } + else if (this.Match('(')) + { + this.Context.IsBindingElement=false; + this.Context.IsAssignmentTarget=false; + var args=this.ParseArguments(); //解析 调用参数 + expr = this.Finalize(this.StartNode(startToken), this.Node.CallExpression(expr, args, startToken)); + } + else + { + break; + } + } + + return expr; + } + + /* + BooleanLiteral = 1, + EOF=2, + Identifier=3, + Keyword=4, + NullLiteral=5, + NumericLiteral=6, + Punctuator=7, + StringLiteral=9, + RegularExpression=9, + Template=10 + */ + this.IsIdentifierName = function (token) + { + return token.Type === 3 //Identifier + || token.Type === 4 //Keyword + || token.Type === 1 //BooleanLiteral + || token.Type === 5;//NullLiteral; + } + + this.ParseIdentifierName = function () + { + const node = this.CreateNode(); + const token = this.NextToken(); + if (!this.IsIdentifierName(token)) + { + this.ThrowUnexpectedToken(token); + } + + return this.Finalize(node, this.Node.Identifier(token.Value, token)); + } + + // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions + this.ParseArguments=function() + { + this.Expect('('); + var args=[]; + if (!this.Match(')')) + { + while(true) + { + let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + args.push(expr); + + if (this.Match(')')) break; + + this.ExpectCommaSeparator(); + + if (this.Match(')')) break; + } + } + + this.Expect(')'); + return args; + } + + // Quietly expect a comma when in tolerant mode, otherwise delegates to expect(). + this.ExpectCommaSeparator=function() + { + this.Expect(','); + } + + // https://tc39.github.io/ecma262/#sec-primary-expression + this.ParsePrimaryExpression=function() + { + let node=this.CreateNode(); + let expr; + var token, raw; + switch(this.LookAhead.Type) + { + case 3:/* Identifier */ + token = this.NextToken(); + expr = this.Finalize(node, this.Node.Identifier(token.Value, token)); + break; + case 6:/* NumericLiteral */ + case 8:/* StringLiteral */ + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + token=this.NextToken(); + raw=this.GetTokenRaw(token); + expr=this.Finalize(node, this.Node.Literal(token.Value,raw)); + break; + case 7:/* Punctuator */ + switch(this.LookAhead.Value) + { + case '(': + this.Context.IsBindingElement=false; + expr=this.InheritCoverGrammar(this.ParseGroupExpression); + break; + default: + expr=this.ThrowUnexpectedToken(this.NextToken()) + } + break; + default: + expr = this.ThrowUnexpectedToken(this.NextToken()); + } + + return expr; + } + + this.ParseGroupExpression=function() + { + let expr; + this.Expect('('); + if (this.Match(')')) + { + this.NextToken(); + } + else + { + let startToken=this.LookAhead; + let params=[]; + let arrow=false; + this.Context.IsBindingElement=true; + expr=this.InheritCoverGrammar(this.ParseAssignmentExpression); + if (this.Match(',')) + { + let expressions=[]; + this.Context.IsAssignmentTarget=false; + expressions.push(expr); + while(this.LookAhead.Type!=2 /* EOF */) + { + if (!this.Match(',')) break; + + this.NextToken(); + if (this.Match(')')) + { + + } + } + } + + if (!arrow) + { + this.Expect(')'); + this.Context.IsBindingElement=false; + } + } + + return expr; + } + + // https://tc39.github.io/ecma262/#sec-expression-statement + this.ParseExpressionStatement=function() + { + let node=this.CreateNode(); + let expr=this.ParseExpression(); + this.ConsumeSemicolon(); + + return this.Finalize(node,this.Node.ExpressionStatement(expr)); + } + + this.ConsumeSemicolon=function() + { + if (this.Match(';')) + { + this.NextToken(); + } + else if (!this.HasLineTerminator) + { + //if (this.LookAhead.Type!=2/*EOF*/ && !this.Match('}')) + + this.LastMarker.Index=this.StartMarker.Index; + this.LastMarker.Line=this.StartMarker.Line; + this.LastMarker.Column=this.StartMarker.Column; + } + } + + this.ReinterpretExpressionAsPattern=function(expr) + { + switch(expr.Type) + { + case Syntax.Identifier: + case Syntax.MemberExpression: + case Syntax.AssignmentExpression: + break; + default: + break; + } + } + + this.Finalize=function(marker,node) + { + node.Marker={ Line:marker.Line, Column:marker.Column, Index:marker.Index }; + return node; + } + + this.BinaryPrecedence = function (token) + { + let op = token.Value; + let precedence; + + if (token.Type === 7 /* Punctuator */) precedence = this.PeratorPrecedence[op] || 0; + else precedence = 0; + + return precedence; + }; + + this.IsolateCoverGrammar=function(parseFunction) + { + let previousIsBindingElement=this.Context.IsBindingElement; + let previousIsAssignmentTarget=this.Context.IsAssignmentTarget; + let previousFirstCoverInitializedNameError=this.Context.FirstCoverInitializedNameError; + + this.Context.IsBindingElement=true; + this.Context.IsAssignmentTarget=true; + this.Context.FirstCoverInitializedNameError=null; + let result=parseFunction.call(this); + + if (this.Context.FirstCoverInitializedNameError!=null) + { + //错误 this.throwUnexpectedToken(this.context.firstCoverInitializedNameError); + } + + this.Context.IsBindingElement=previousIsBindingElement; + this.Context.IsAssignmentTarget=previousIsAssignmentTarget; + this.Context.FirstCoverInitializedNameError=previousFirstCoverInitializedNameError; + + return result; + } + + this.InheritCoverGrammar = function (parseFunction) + { + let previousIsBindingElement = this.Context.IsBindingElement; + let previousIsAssignmentTarget = this.Context.IsAssignmentTarget; + let previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError; + this.Context.IsBindingElement = true; + this.Context.IsAssignmentTarget = true; + this.Context.FirstCoverInitializedNameError = null; + + let result = parseFunction.call(this); + + this.Context.IsBindingElement = this.Context.IsBindingElement && previousIsBindingElement; + this.Context.IsAssignmentTarget = this.Context.IsAssignmentTarget && previousIsAssignmentTarget; + this.Context.FirstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.Context.FirstCoverInitializedNameError; + + return result; + }; + + this.ThrowUnexpectedToken=function(token,message) + { + throw this.UnexpectedTokenError(token,message); + } + + this.ThrowUnexpectedError=function(index,line,column,message) + { + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(index,line,column,msg); + } + + this.UnexpectedTokenError=function(token,message) + { + let msg=message || Messages.UnexpectedToken; + let value='ILLEGAL'; + if (token) + { + if (!message) + { + + } + value=token.Value; + } + + msg=msg.replace("%0",value); + if (token && typeof(token.LineNumber)=='number') + { + let index=token.Start; + let line=token.LineNumber; + let lastMarkerLineStart=this.LastMarker.Index-this.LastMarker.Column; + let column=token.Start-lastMarkerLineStart+1; + return this.ErrorHandler.CreateError(index,line,column,msg); + } + else + { + let index=this.LastMarker.Index; + let line=this.LastMarker.Line; + let column=this.LastMarker.Column+1; + return this.ErrorHandler.CreateError(index,line,column,msg); + } + } +} + + +/* + 算法类 +*/ +function JSAlgorithm(errorHandler, symbolData) +{ + this.ErrorHandler=errorHandler; + this.SymbolData = symbolData; //股票数据 + + //相加 + this.Add=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值相加 + if (isNumber && isNumber2) return data+data2; + + //都是数组相加 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;idata2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;idata2[i] ? 1:0); + } + } + + return result; + } + + if (isNumber) //单数据-数组 + { + for(let i in data2) + { + result[i]=null; + if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>data2[i] ? 1 : 0); + } + } + else //数组-单数据 + { + for(let i in data) + { + result[i]=null; + if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>data2 ? 1 : 0); + } + } + + return result; + } + + //大于等于 + this.GTE=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值比较 + if (isNumber && isNumber2) return (data>=data2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;i=data2[i] ? 1:0); + } + } + + return result; + } + + if (isNumber) //单数据-数组 + { + for(let i in data2) + { + result[i]=null; + if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>=data2[i] ? 1 : 0); + } + } + else //数组-单数据 + { + for(let i in data) + { + result[i]=null; + if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>=data2 ? 1 : 0); + } + } + + return result; + } + + //小于 + this.LT=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值比较 + if (isNumber && isNumber2) return (data=data2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;iOPEN,HIGH,LOW)表示该周期收阴则返回最高值,否则返回最低值 + */ + this.IFN=function(data,trueData,falseData) + { + return this.IF(data,falseData,trueData); + } + + //指标函数 函数名全部大写 + this.REF=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(0,data.length-n); + + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>0 && value<=i) result[i]=data[i-value]; + else if (i) result[i]=result[i-1]; + else result[i]=data[i]; + } + } + + return result; + } + + //引用若干周期前的数据(未作平滑处理). + //用法: REFV(X,A),引用A周期前的X值.A可以是变量. + //平滑处理:当引用不到数据时进行的操作. + //例如: REFV(CLOSE,BARSCOUNT(C)-1)表示第二根K线的收盘价. + this.REFV=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(0,data.length-n); + + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value<=i) result[i]=data[i-value]; + } + } + + return result; + } + + //属于未来函数,引用若干周期后的数据(平滑处理). + //用法: REFX(X,A),引用A周期后的X值.A可以是变量. + //平滑处理:当引用不到数据时进行的操作.此函数中,平滑时使用上一个周期的引用值. + //例如: TT:=IF(C>O,1,2); + // REFX(CLOSE,TT);表示阳线引用下一周期的收盘价,阴线引用日后第二周期的收盘价. + this.REFX=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(n,data.length); + + //平滑处理 + var lastData=data[data.length-1]; + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value+i=data.length) return result; + + result=data.slice(n,data.length); + + //平滑处理 + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value+i= 0; --j) + { + var value = data[i - j]; + if (!this.IsNumber(value)) + { + value = preValue; //空数据就取上一个数据 + data[i - j] = value; + } + else + { + preValue = value; + } + sum += value; + } + + result[i] = sum / dayCount; + } + + return result; + } + + //指数平均数指标 EMA(close,10) + this.EMA=function(data,dayCount) + { + var result = []; + + var offset=0; + if (offset>=data.length) return result; + + //取首个有效数据 + for(;offset= 0 && j < data.length) + { + if (this.IsNumber(data[j])) + { + sum += data[j]; + ++count; + } + } + } + + if (count != 0) result[i] = (sum / count); + else result[i] = null; + + sum = 0; + count = 0; + } + + return result; + } + + /* + SMA 移动平均 + 返回移动平均。 + 用法: SMA(X,N,M) X的M日移动平均,M为权重,如Y=(X*M+Y'*(N-M))/N + */ + this.SMA=function(data,n,m) + { + var result = []; + + var i=0; + var lastData=null; + for(;i= 0; --j) + { + var value = data[i - j]; + if (!this.IsNumber(value)) + { + value = preValue; + data[i - j] = value; + } + else + preValue = value; + + count += dayCount - j; + sum += value * (dayCount - j); + } + result[i] = sum / count; + } + return result; + } + + /* + 返回平滑移动平均 + 用法:MEMA(X,N):X的N日平滑移动平均,如Y=(X+Y'*(N-1))/N + MEMA(X,N)相当于SMA(X,N,1) + */ + this.MEMA = function (data, dayCount) + { + let result = []; + if (!data || !data.length) return result; + var i = 0, j = 0; + for (j = 0; j < data.length && !this.IsNumber(data[j]); ++j) + { + result[j] = null; + } + i = j; + if (dayCount < 1 || i + dayCount >= data.length) return result; + var sum = 0; + var data = data.slice(0); + for (; i < j + dayCount; ++i) + { + result[i] = null; + if (!this.IsNumber(data[i]) && i - 1 >= 0) + data[i] = data[i - 1]; + sum += data[i]; + } + result[i - 1] = sum / dayCount; + for (; i < data.length; ++i) + { + if (this.IsNumber(result[i - 1]) && this.IsNumber(data[i])) + result[i] = (data[i] + result[i - 1] * (dayCount - 1)) / dayCount; + else if (i - 1 > -1 && this.IsNumber(result[i - 1])) + result[i] = result[i - 1]; + else + result[i] = null; + } + return result; + } + + /* + 加权移动平均 + 返回加权移动平均 + 用法:EXPMA(X,M):X的M日加权移动平均 + EXPMA[i]=buffer[i]*para+(1-para)*EXPMA[i-1] para=2/(1+__para) + */ + this.EXPMA=function(data,dayCount) + { + let result=[]; + if (dayCount>=data.length) return result; + + let i=dayCount; + for(;i=data.length) return result; + + var index=0; + for(;index= start; --i) + { + for (j = i, total = 0; j >= start && total < data2[i]; --j) + total += data[j]; + if (j < start) result[i] = null; + else result[i] = i - j; + } + for (i = start + 1; i < data.length; ++i) + { + if (result[i] == null) + result[i] = result[i - 1]; + } + return result; + } + + /* + 求相反数. + 用法:REVERSE(X)返回-X. + 例如:REVERSE(CLOSE)返回-CLOSE + */ + this.REVERSE = function (data) + { + var result = []; + var i = 0; + for (; i < data.length && !this.isNumber(data[i]); ++i) + { + result[i] = null; + } + for (; i < data.length; ++i) + { + if (!this.isNumber(data[i])) + result[i] = null; + else + result[i] = 0 - data[i]; + } + return result; + } + + this.COUNT=function(data,n) + { + if (Array.isArray(n)) + { + var start=null; + var dataCount=data.length; + for(var i=0;i=0 && k= n.length) continue; + + max = null; + var count = n[i]; + if (count > 0 && count <= i) + { + for (j = i - count; j <= i; ++j) + { + if (max == null || max < data[j]) max = data[j]; + } + } + else + { + count = i; + for (j = 0; j <= i; ++j) + { + if (max == null || max < data[j]) max = data[j]; + } + } + + result[i] = max; + } + } + else + { + if (n > data.length) return result; + if (n <= 0) n = data.length - 1; + + var nMax = 0; + for (nMax = 0; nMax < data.length; ++nMax) + { + if (this.IsNumber(data[nMax])) break; + } + + if (nMax < data.length) result[nMax] = data[nMax]; + for (var i = nMax + 1, j = 2; i < data.length && j < n; ++i, ++j) + { + if (data[i] >= data[nMax]) nMax = i; + result[i] = data[nMax]; + } + + for (; i < data.length; ++i) + { + if (i - nMax < n) + { + nMax = data[i] < data[nMax] ? nMax : i; + } + else + { + for (j = nMax = (i - n + 1); j <= i; ++j) + { + nMax = data[j] < data[nMax] ? nMax : j; + } + } + + result[i] = data[nMax]; + } + } + + return result; + } + + /* + LLV 最低值 + 求最低值。 + 用法: LLV(X,N) 求N周期内X最低值,N=0则从第一个有效值开始。 + 例如: LLV(LOW,0) 表示求历史最低价。 + */ + this.LLV=function(data,n) + { + var result = []; + if (Array.isArray(n)) + { + for (var i = 0; i < data.length; ++i) + { + result[i] = null; + if (i >= n.length) continue; + + var min = null; + var count = n[i]; + if (count > 0 && count <= i) + { + for (var j = i - count; j <= i; ++j) + { + if (min == null || min > data[j]) min = data[j]; + } + } + else + { + count = i; + for (var j = 0; j <= i; ++j) + { + if (min == null || min > data[j]) min = data[j]; + } + } + + result[i] = min; + } + } + else + { + if (n>data.length) return result; + if (n<=0) n=data.length-1; + + var nMin=0; + for(nMin=0;nMindata[nMin]?nMin:i; + } + else + { + for(j=nMin=(i-n+1);j<=i;++j) + { + nMin=data[j]>data[nMin]?nMin:j; + } + } + + result[i]=data[nMin]; + } + } + + return result; + } + + this.STD=function(data,n) + { + var result=[]; + + var total=0; + var averageData=[]; //平均值 + for(var i=n-1;i data2[index] && data[index - 1] < data2[index - 1]) ? 1 : 0; + } + } + else if (Array.isArray(data) && typeof (data2) == 'number') + { + var index = 0; + for (; index < data.length; ++index) + { + if (this.IsNumber(data[index])) break; + } + + for (++index; index < data.length; ++index) + { + result[index] = (data[index] > data2 && data[index - 1] < data2) ? 1 : 0; + } + } + else if (typeof (data) == 'number' && Array.isArray(data2)) + { + var index = 0; + for (; index < data2.length; ++index) + { + if (this.IsNumber(data2[index])) break; + } + + for (++index; index < data2.length; ++index) + { + result[index] = (data2[index] < data && data2[index - 1] > data) ? 1 : 0; + } + } + + return result; + } + + //累乘 + this.MULAR=function(data,n) + { + var result=[]; + if(data.lengthdatanum) return result; + for(E=0; i < datanum && j < num; ++i,++j) + E += data[i]/num; + if (j == num) + { + DEV = 0; + for(i--; k < num; k++) + DEV += (data[i-k]-E) * (data[i-k]-E); + result[i] = DEV; + i++; + } + for(; i < datanum; ++i) + { + E += (data[i] - data[i-num]) / num; + for(DEV=0, k = 0; k < num; ++k) + DEV += (data[i-k]-E) * (data[i-k]-E); + result[i] = DEV; + } + return result; + } + + //NOT 取反 + //求逻辑非。 + //用法: NOT(X) 返回非X,即当X=0时返回1,否则返回0。 + //例如: NOT(ISUP) 表示平盘或收阴。 + this.NOT=function(data) + { + let isNumber=typeof(data)=='number'; + if (isNumber) return data? 0:1; + + let result=[]; + for(let i in data) + { + result[i]=null; + if (this.IsNumber(data[i])) result[i]=data[i]?0:1; + } + + return result; + } + + //FORCAST 线性回归预测值 + //FORCAST(X,N)  返回线性回归预测值。 + this.FORCAST=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num < 1 || num >= datanum) + return result; + var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope; + var i, j,x; + for(j = 0; j < datanum && !this.IsNumber(data[j]); ++j) + { + result[j] = null; + } + for(i = j+num-1; i < datanum; ++i) + { + Ex = Ey = Sxy = Sxx = 0; + for (j = 0, x = num; j < num && j <= i; ++j,--x) + { + Ex +=x; + Ey += data[i - j]; + } + Ex /= num; + Ey /= num; + for (j = 0, x = num; j < num && j <= i; ++j, --x) + { + Sxy += (x-Ex)*(data[i-j]-Ey); + Sxx += (x-Ex)*(x-Ex); + } + Slope = Sxy / Sxx; + Const = Ey - Ex*Slope; + result[i] = Slope * num + Const; + } + + return result; + } + + //SLOPE 线性回归斜率 + //SLOPE(X,N)  返回线性回归斜率。 + this.SLOPE=function(data,n) + { + let result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + if (n<1 || !data.length) return result; + if (n>=data.length) return result; + + let start=0; + for(let i=0;i= datanum) + return result; + var i = 0, j = 0; + for(i = 0; i < datanum && !this.isNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX = 0, SigmaX = 0, MidResult; + for (; i < datanum && j < num; ++i, ++j) + { + SigmaPowerX += data[i] * data[i]; + SigmaX += data[i]; + } + if (j == num) + { + MidResult = num*SigmaPowerX - SigmaX*SigmaX; + result[i-1] = Math.sqrt(MidResult) / num; + } + for(; i < datanum; ++i) + { + SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num]; + SigmaX += data[i] - data[i-num]; + MidResult = num*SigmaPowerX - SigmaX*SigmaX; + result[i] = Math.sqrt(MidResult) / num; + } + } + + //VAR 估算样本方差 + //VAR(X,N)  返回估算样本方差。 + this.VAR=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num <= 1 || num >= datanum) + return result; + var i, j; + for (i = 0; i < datanum && !this.IsNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX, SigmaX; + for (j = 0, i = i+num-1; i < datanum; ++i) + { + SigmaPowerX = SigmaX = 0; + for(j=0; j < num && j <= i; ++j) + { + SigmaPowerX += data[i-j] * data[i-j]; + SigmaX += data[i-j]; + } + result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / num * (num -1); + } + + return result; + } + + //VARP 总体样本方差 + //VARP(X,N)  返回总体样本方差 。 + this.VARP=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num < 1 || num >= datanum) + return result; + var i = 0, j = 0; + for (i = 0; i < datanum && !this.IsNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX = 0, SigmaX = 0; + for (; i < datanum && j < num; ++i, ++j) + { + SigmaPowerX += data[i] * data[i]; + SigmaX += data[i]; + } + if (j == num) + result[i-1] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num); + for(; i < datanum; ++i) + { + SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num]; + SigmaX += data[i] - data[i-num]; + result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num); + } + + return result; + } + + //RANGE(A,B,C)表示A>B AND AMath.min(range,range2) && data=range.length) continue; + + rangeValue=range[i]; + } + else + { + rangeValue=range; + } + if (!this.IsNumber(rangeValue)) continue; + + if (!isNumber3) + { + if (i>=range2.length) continue; + + rangeValue2=range2[i]; + } + else + { + rangeValue2=range2; + } + if (!this.IsNumber(rangeValue2)) continue; + + + result[i]= (value>Math.min(rangeValue,rangeValue2) && value0) latestID==i; + + if (i-latestIDOPEN,5)查找阳线,5天内再次出现的阳线不被记录在内 + */ + this.FILTER = function (data, n) + { + var result = []; + for (let i = 0, j = 0; i < data.length; ++i) + { + if (data[i]) + { + result[i] = 1; + for (j = 0; j < n && j + i + 1 < data.length; ++j) + { + result[j + i + 1] = 0; + } + i += n; + } + else + { + result[i] = 0; + } + } + + return result; + } + + this.BARSLAST=function(data) + { + var result=[]; + if (!data) return result; + + let day=null; + for(let i=0;i0) day=0; + else if (day!=null) ++day; + + if (day!=null) result[i]=day; + } + + return result; + } + + /* + N周期内第一个条件成立到当前的周期数. + 用法: + BARSSINCEN(X,N):N周期内第一次X不为0到现在的天数,N为常量 + 例如: + BARSSINCEN(HIGH>10,10)表示10个周期内股价超过10元时到当前的周期数 + */ + this.BARSSINCEN = function (data, n) + { + var result=[]; + if (this.IsNumber(n) && Array.isArray(data)) + { + var nPeriod=n; + if (nPeriod<1) nPeriod=data.length; + var i=this.GetFirstVaildIndex(data); + if (i>=data.length) return result; + var j=0; + if (i <= nPeriod - 1) j = nPeriod - 1; + else j = i; + + result[j] = j - i; + + for (; j < data.length; ++j) + { + if (this.IsNumber(result[j - 1])) + { + if (result[j - 1] + 1 < nPeriod) + { + result[j] = result[j - 1] + 1; + } + else + { + for (var k = j - nPeriod+1; k <= j; ++k) + { + if (!(Math.abs(data[k]) < 0.000001)) + { + result[j] = j - k; + break; + } + } + } + } + else + { + if (!(Math.abs(data[j]) < 0.000001)) + result[j] = 0; + } + } + } + + return result; + } + + /* + 第一个条件成立到当前的周期数. + 用法: + BARSSINCE(X):第一次X不为0到现在的天数 + 例如: + BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数 + */ + this.BARSSINCE = function (data) + { + var result = []; + var day = null; + + for (let i = 0; i < data.length; ++i) + { + result[i] = null; + if (day == null) + { + if (data[i]) day = 0; + } + else + { + ++day; + } + + if (day) result[i] = day; + } + + return result; + } + + /*三角函数调用 func 三角函数 + 反正切值. 用法: ATAN(X)返回X的反正切值 + 余弦值. 用法: COS(X)返回X的余弦值 + 正弦值. 用法: SIN(X)返回X的正弦值 + 正切值. 用法: TAN(X)返回X的正切值 + + 求自然对数. 用法: LN(X)以e为底的对数 例如: LN(CLOSE)求收盘价的对数 + 求10为底的对数. 用法: LOG(X)取得X的对数 例如: LOG(100)等于2 + 指数. 用法: EXP(X)为e的X次幂 例如: EXP(CLOSE)返回e的CLOSE次幂 + 开平方. 用法: SQRT(X)为X的平方根 例如: SQRT(CLOSE)收盘价的平方根 + */ + this.Trigonometric = function (data, func) + { + if (!Array.isArray(data)) + { + if (this.IsNumber(data)) return func(data); + + return null; + } + else + { + var result = []; + for (let i in data) + { + var item = data[i]; + if (this.IsNumber(item)) result[i] = func(item); + else result[i] = null; + } + + return result; + } + } + + //反正弦值. 用法: ASIN(X)返回X的反正弦值 + this.ASIN = function (data) + { + if (!Array.isArray(data)) + { + if (this.IsNumber(data)) return Math.acos(data); + return null; + } + else + { + var result = []; + for (let i in data) + { + var item = data[i]; + result[i] = null; + if (this.IsNumber(item)) + { + if (item >= -1 && item <= 1) + { + result[i] = Math.asin(item); + } + else if (i - 1 >= 0) + { + var preItem = result[i - 1]; + if (this.IsNumber(preItem)) result[i] = preItem; + } + } + } + + return result; + } + } + + + //反余弦值. 用法: ACOS(X)返回X的反余弦值 + this.ACOS = function (data) + { + if (!Array.isArray(data)) + { + if (this.IsNumber(data)) return Math.acos(data); + + return null; + } + else + { + var result = []; + for (let i in data) + { + var item = data[i]; + result[i] = null; + if (this.IsNumber(item)) + { + if (item >= -1 && item <= 1) + { + result[i] = Math.acos(item); + } + else if (i - 1 >= 0) //超出范围使用上一个数值 + { + var preItem = result[i - 1]; + if (this.IsNumber(preItem)) result[i] = preItem; + } + } + } + + return result; + } + } + + /* + LAST(X,A,B):持续存在. + 用法: + LAST(CLOSE>OPEN,10,5) + 表示从前10日到前5日内一直阳线 + 若A为0,表示从第一天开始,B为0,表示到最后日止 + */ + this.LAST = function (data, n, n2) + { + var result = []; + if (n2 <= 0) n2 = data.length - 1; + if (n2 > n) return result; + + var day = 0; + + for (let i = 0, j = 0; i < data.length; ++i) { + result[i] = 0; + day = 0; + var start = i - n; + var end = i - n2; + if (start < 0 || end < 0) continue; + + for (j = start; j < data.length && j <= end; ++j, ++day) { + if (!data[j]) break; + } + + if (day == end - start + 1) //[start,end] + result[i] = 1; + } + + return result; + } + + /* + 属于未来函数,之字转向. + 用法: ZIG(K,N),当价格变化量超过N%时转向,K表示0:开盘价,1:最高价,2:最低价,3:收盘价,其余:数组信息 + 例如: ZIG(3,5)表示收盘价的5%的ZIG转向 + */ + this.ZIG=function(data,n) + { + var hisData=this.SymbolData.Data; + var result=[]; + if (typeof(data)=='number') + { + switch(data) + { + case 0: + data=hisData.GetOpen(); + break; + case 1: + data=hisData.GetHigh(); + break; + case 2: + data=hisData.GetLow(); + break; + case 3: + data=hisData.GetClose(); + break; + default: + return result; + } + } + + return this.ZIG_Calculate(data,n); + } + + this.ZIG_Calculate=function(data,dRate) + { + var dest=[]; + var nDataCount=data.length; + var m=this.GetFirstVaildIndex(data); + var i = 0, lLastPos = 0, lState = 0, j = 0; + var dif = 0; + for (i = m + 1, lLastPos = lState = m; i= dRate*data[m] ? (data[i]>data[m] ? i : -i) : m; + } + + for (; i= data[i - 1] && data[i] >= data[i + 1]) + { + if (lState<0) + { + if ((data[i] - data[-lState]) * 100= lLastPos; j--) + dest[j]=data[-lState] + (-lState - j)*dif; + lLastPos = -lState; + lState = i; + } + } + else if (data[i]>data[lState]) lState = i; + } + else if (data[i] <= data[i - 1] && data[i] <= data[i + 1]) + { + if (lState>0) + { + if ((data[lState] - data[i]) * 100= nDataCount - 2) + { + if (lState>0 && data[nDataCount - 1] >= data[lState]) lState = nDataCount - 1; + if (lState<0 && data[nDataCount - 1] <= data[-lState]) lState = 1 - nDataCount; + } + + if (lState>0) + { + dif = (data[lState] - data[j = lLastPos]) / (lState - lLastPos ); + dest[j++]=data[lLastPos]; + for (; j <= lState; ++j) + dest[j]=data[lLastPos] + (j - lLastPos)*dif; + } + else + { + dif = (data[lLastPos] - data[j = -lState]) / (-lState - lLastPos); + dest[j--]=data[-lState]; + for (; j >= lLastPos; j--) + dest[j]=(data[-lState] + (-lState - j)*dif); + } + if ((lState = Math.abs(lState))= data[lState]) + { + dif = (data[nDataCount - 1] - data[j = lState]) / (nDataCount - lState); + dest[j++]=(data[lState]); + for (; j= lState; j--) + dest[j]=(data[nDataCount - 1] + (nDataCount - j)*dif); + } + } + + return dest; + } + + + this.GetFirstVaildIndex=function(data) + { + for (var i = 0; i subItem) findData = { ID: i, Value: subItem }; + } + } + + secondData.Value = findData.Value; + secondData.ID = findData.ID; + + var lineCache = { Start: { ID: firstData.ID, Value: firstData.Value }, End: { ID: secondData.ID, Value: secondData.Value } }; + var lineData = this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值 + for (var i in lineData) + { + var lineItem = lineData[i]; + result[lineItem.ID] = lineItem.Value; + } + + if (thridData.ID == data.length - 1) //最后一组数据 + { + //最后2个点的数据连成线 + lineCache = { Start: { ID: secondData.ID, Value: secondData.Value }, End: { ID: thridData.ID, Value: thridData.Value } }; + lineData = this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值 + for (var i in lineData) + { + var lineItem = lineData[i]; + result[lineItem.ID] = lineItem.Value; + } + } + else + { + firstData.ID = secondData.ID; + firstData.Value = secondData.Value; + + secondData.ID = thridData.ID; + secondData.Value = thridData.Value; + secondData.Up = firstData.Value < secondData.Value; + } + } + + /* + 属于未来函数,前M个ZIG转向波谷到当前距离. + 用法: + TROUGHBARS(K,N,M)表示之字转向ZIG(K,N)的前M个波谷到当前的周期数,M必须大于等于1 + 例如: + TROUGHBARS(2,5,2)表示%5最低价ZIG转向的前2个波谷到当前的周期数 + */ + this.TROUGHBARS=function(data,n,n2) + { + var zigData=this.ZIG(data,n); //计算ZIG + var dest=[]; + + var lEnd =n2; + if (lEnd<1) return dest; + + var nDataCount = zigData.length; + var trough = []; + for(var i=0;izigData[i - 1]; ++i); + + for (; izigData[i-1]; ++i); + + for(; izigData[i - 1]; ++i); + + for (peak[0] = --i; izigData[i + 1]) + { + if (lFlag) + { + if (lEnd) + { + var tempPeak=peak.slice(0); + for(var j=0;jzigData[i - 1]; ++i); + + for (peak[0] = --i; izigData[i + 1]) + { + if (lFlag) + { + if (lEnd) + { + var tempPeak=peak.slice(0); + for(var j=0;jOPEN,N) + 表示N日内一直阳线(N应大于0,小于总周期数,N支持变量) + */ + this.EVERY = function (data, n) + { + var result = []; + if (n < 1) return result; + var i = 0; + for (; i < data.length; ++i) + { + result[i] = null; + if (this.IsNumber(data[i])) break; + } + + var flag = 0; + for (; i < data.length; ++i) + { + if (data[i]) flag += 1; + else flag = 0; + + if (flag == n) + { + result[i] = 1; + --flag; + } + else + { + result[i] = 0; + } + } + + return result; + } + + /* + 成本分布情况. + 用法: + COST(10),表示10%获利盘的价格是多少,即有10%的持仓量在该价格以下,其余90%在该价格以上,为套牢盘 + 该函数仅对日线分析周期有效 + */ + this.COST = function (data, node) + { + var result=[]; + var rate=data/100; + if(rate<0.000001 || rate>1) return result; + + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low; + for(var i=0;i 2000 || dMinPrice < 0 || dMaxPrice>2000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'COST() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + //去掉小数 + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var i=0;i= aryCapital.length) continue; + if (aryCapital[i]>1) + { + var kItem=kData[i] + dHSL = kItem.Vol/aryCapital[i]; + + for( var j=0;j=dTotalVol*rate) + { + dCost=(dMaxPrice-dMinPrice)*j/lSpeed+dMinPrice; + break; + } + } + } + + result[i]=dCost; + } + + return result; + } + + /* + 获利盘比例. + 用法: + WINNER(CLOSE),表示以当前收市价卖出的获利盘比例,例如返回0.1表示10%获利盘;WINNER(10.5)表示10.5元价格的获利盘比例 + 该函数仅对日线分析周期有效 + !!!!计算比较耗时间 + */ + this.WINNER = function (data,node) + { + var result=[]; + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low; + for(var i=0;i 1000 || dMinPrice < 0 || dMaxPrice>1000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'WINNER() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + //去掉小数 + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var i=0;i= aryCapital.length) continue; + if (!(aryCapital[i]>1)) continue; + var kItem=kData[i] + dHSL = kItem.Vol/aryCapital[i]; + + for( var j=0;j 0) result[i]=dVol / dTotalVol; + else if (i - 1 >= 0) result[i] = result[i - 1]; + } + + return result; + } + + //计算截至到某一天的历史所有筹码 + this.CalculateChip = function (index, exchangeData, hisData, dRate) + { + var result = { Min: null, Max: null, Data: [] }; + var seed = 1;//筹码历史衰减换手系数 + var max = null, min = null; + for (let i = index; i >= 0; --i) + { + let item = {}; //Vol:量 High:最高 Low:最低 + var kData = hisData[i]; + if (i == index) item.Vol = kData.Vol * exchangeData[i]; + else item.Vol = kData.Vol * seed; + + item.Date = kData.Date; + item.High = kData.High; + item.Low = kData.Low; + + if (max == null) max = item.High; + else if (max < item.High) max = item.High; + if (min == null) min = item.Low; + else if (min < item.Low) min = item.Low; + + result.Data[i] = item; + + seed *= (1 - (exchangeData[i] / 100) * dRate); //换手率累乘 + } + + result.Max = max; + result.Min = min; + + return result; + } + + /* + 返回是否连涨周期数. + 用法: + UPNDAY(CLOSE,M) + 表示连涨M个周期,M为常量 + */ + this.UPNDAY = function (data, n) + { + var result = []; + if (n < 1) return result; + if (data == null || n > data.length) return result; + + var days = 0; + for (let i = 0; i < data.length; ++i) + { + result[i] = 0; + if (i - 1 < 0) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data[i - 1])) //无效数都不算连涨 + { + days = 0; + continue; + } + + if (data[i] > data[i - 1])++days; + else days = 0; + + if (days == n) + { + result[i] = 1; + --days; + } + } + + return result; + } + + /* + 返回是否连跌周期. + 用法: + DOWNNDAY(CLOSE,M) + 表示连跌M个周期,M为常量 + */ + this.DOWNNDAY = function (data, n) + { + var result = []; + if (n < 1) return result; + if (data == null || n > data.length) return result; + + var days = 0; + for (let i = 0; i < data.length; ++i) + { + result[i] = 0; + if (i - 1 < 0) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data[i - 1])) //无效数都不算连涨 + { + days = 0; + continue; + } + + if (data[i] < data[i - 1])++days; + else days = 0; + + if (days == n) + { + result[i] = 1; + --days; + } + } + + return result; + } + + /* + 返回是否持续存在X>Y + 用法: + NDAY(CLOSE,OPEN,3) + 表示连续3日收阳线 + */ + this.NDAY = function (data, data2, n) + { + var result = []; + if (n < 1) return result; + if (!Array.isArray(data) && !Array.isArray(data2)) return result; + if (data == null || data2 == null) return result; + + if (Array.isArray(data) && Array.isArray(data2)) + { + if (n >= data.length || n >= data2.length) return result; + var count = Math.max(data.length, data2.length); + var days = 0; + for (let i = 0; i < count; ++i) + { + result[i] = 0; + if (i >= data.length || i >= data2.length) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i])) + { + days = 0; + continue; + } + + if (data[i] > data2[i])++days; + else days = 0; + + if (days == n) + { + result[i] = 1; + --days; + } + } + } + else if (Array.isArray(data) && !Array.isArray(data2)) + { + if (n >= data.length || !this.IsNumber(data2)) return; + var days = 0; + for (let i in data) + { + result[i] = 0; + if (!this.IsNumber(data[i])) + { + days = 0; + continue; + } + + if (data[i] > data2)++days; + else days = 0; + + if (days == n) + { + result[i] = 1; + --days; + } + } + } + else if (!Array.isArray(data) && Array.isArray(data2)) + { + if (n >= data2.length || !this.IsNumber(data)) return; + var days = 0; + for (let i in data2) + { + result[i] = 0; + if (!this.IsNumber(data2[i])) + { + days = 0; + continue; + } + + if (data > data2[i])++days; + else days = 0; + + if (days == n) + { + result[i] = 1; + --days; + } + } + } + + return result; + } + + /* + 两条线维持一定周期后交叉. + 用法:LONGCROSS(A,B,N)表示A在N周期内都小于B,本周期从下方向上穿过B时返回1,否则返回0 + */ + this.LONGCROSS = function (data, data2, n) + { + var result = []; + var count = Math.max(data.length, data2.length); + for (let i = 0; i < count; ++i) + { + result[i] = 0; + if (i - 1 < 0) continue; + if (i >= data.length || i >= data2.length) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]) || !this.IsNumber(data[i - 1]) || !this.IsNumber(data2[i - 1])) continue; + + if (data[i] > data2[i] && data[i - 1] < data2[i - 1]) result[i] = 1; + } + + for (let i = 0, j = 0; i < count; ++i) + { + if (!result[i]) continue; + + for (j = 1; j <= n && i - j >= 0; ++j) + { + if (data[i - j] >= data2[i - j]) + { + result[i] = 0; + break; + } + } + } + + return result; + } + + /* + EXISTR(X,A,B):是否存在(前几日到前几日间). + 例如: EXISTR(CLOSE>OPEN,10,5) + 表示从前10日内到前5日内存在着阳线 + 若A为0,表示从第一天开始,B为0,表示到最后日止 + */ + this.EXISTR = function (data, n, n2) + { + var result = []; + if (!Array.isArray(data)) return result; + + n = parseInt(n); + n2 = parseInt(n2); + if (n <= 0) n = data.length; + if (n2 <= 0) n2 = 1; + if (n2 > n) return result; + + var result = []; + var value; + for (let i = 0, j = 0; i < data.length; ++i) + { + result[i] = null; + if (i - n < 0 || i - n2 < 0) continue; + + result[i] = 0; + for (j = n; j >= n2; --j) + { + var value = data[i - j]; + if (this.IsNumber(value) && value) + { + result[i] = 1; + break; + } + } + } + + return result; + } + + /* + RELATE(X,Y,N) 返回X和Y的N周期的相关系数 + RELATE(X,Y,N)=(∑[(Xi-Avg(X))(Yi-Avg(y))])/N ÷ √((∑(Xi-Avg(X))^2)/N * (∑(Yi-Avg(Y))^2)/N) + 其中 avg(x)表示x的N周期均值: avg(X) = (∑Xi)/N + √(...)表示开平方 + */ + this.RELATE = function (data, data2, n) + { + var result = []; + if (n < 1) n = 1; + + if (!Array.isArray(data) || !Array.isArray(data2)) return result; + + var dataAverage = this.CalculateAverage(data, n); + var data2Average = this.CalculateAverage(data2, n); + + var count = Math.max(data.length, data2.length); + for (let i = 0, j = 0; i < count; ++i) + { + result[i] = null; + + if (i >= data.length || i >= data2.length || i >= dataAverage.length || i >= data2Average.length) continue; + + var average = dataAverage[i]; + var average2 = data2Average[i]; + + var total = 0, total2 = 0, total3 = 0; + for (j = i - n + 1; j <= i; ++j) + { + total += (data[j] - average) * (data2[j] - average2); //∑[(Xi-Avg(X))(Yi-Avg(y))]) + total2 += Math.pow(data[j] - average, 2); //∑(Xi-Avg(X))^2 + total3 += Math.pow(data2[j] - average2, 2); //∑(Yi-Avg(Y))^2) + } + + result[i] = (total / n) / (Math.sqrt(total2 / n) * Math.sqrt(total3 / n)); + } + + return result; + } + + //计算数组n周期内的均值 + this.CalculateAverage = function (data, n) + { + var result = []; + if (n < 1) return result; + + var total = 0; + + for (var i = 0; i < data.length; ++i) //去掉开始的无效数 + { + if (this.IsNumber(data[i])) break; + } + + for (; i < data.length && i < n; ++i) //计算第1个周期的数据 + { + result[i] = null; + var value = data[i]; + if (!this.IsNumber(value)) continue; + total += value; + } + result[i - 1] = total / n; + + for (; i < data.length; ++i) //计算后面的周期数据 + { + var value = data[i]; + var preValue = data[i - n]; //上一个周期的第1个数据 + if (!this.IsNumber(value)) value = 0; + if (!this.IsNumber(preValue)) preValue = 0; + + total = total - preValue + value; //当前周期的数据 等于上一个周期数据 去掉上一个周期的第1个数据 加上这个周期的最后1个数据 + result[i] = total / n; + } + + return result; + } + + /* + COVAR(X,Y,N) 返回X和Y的N周期的协方差 + */ + this.COVAR = function (data, data2, n) + { + var result = []; + if (n < 1) n = 1; + + if (!Array.isArray(data) || !Array.isArray(data2)) return result; + + var dataAverage = this.CalculateAverage(data, n); + var data2Average = this.CalculateAverage(data2, n); + + var count = Math.max(data.length, data2.length); + + var count = Math.max(data.length, data2.length); + for (let i = 0, j = 0; i < count; ++i) + { + result[i] = null; + + if (i >= data.length || i >= data2.length || i >= dataAverage.length || i >= data2Average.length) continue; + + var average = dataAverage[i]; + var average2 = data2Average[i]; + + var total = 0; + for (j = i - n + 1; j <= i; ++j) + { + total += (data[j] - average) * (data2[j] - average2); + } + + result[i] = (total / n); + } + + return result; + } + + /* + 求上一高点到当前的周期数. + 用法: + HHVBARS(X,N):求N周期内X最高值到当前周期数,N=0表示从第一个有效值开始统计 + 例如: + HHVBARS(HIGH,0)求得历史新高到到当前的周期数 + */ + this.HHVBARS = function (data, n) + { + var result = []; + if (!Array.isArray(data)) return result; + if (Array.isArray(n)) + { + for(var i=0;i=data[nMax]) nMax=j; + } + + if (nMax!=null) + result[i]=(i-nMax); + } + } + else + { + if (n < 1) n = data.length; + + var nMax = null; //最大值索引 + for (var i = 0; i < data.length; ++i) + { + result[i] = null; + if (this.IsNumber(data[i])) { + nMax = i; + break; + } + } + + var j = 0; + for (i = nMax + 1; i < data.length && j < n; ++i, ++j) //求第1个最大值 + { + if (data[i] >= data[nMax]) nMax = i; + if (n == data.length) result[i] = (i - nMax); + } + + for (; i < data.length; ++i) + { + if (i - nMax < n) + { + if (data[i] >= data[nMax]) nMax = i; + } + else + { + nMax = i - n + 1; + for (j = nMax; j <= i; ++j) //计算区间最大值 + { + if (data[j] >= data[nMax]) nMax = j; + } + } + + result[i] = i - nMax; + } + } + + return result; + } + + /* + 求上一低点到当前的周期数. + 用法: LLVBARS(X,N):求N周期内X最低值到当前周期数,N=0表示从第一个有效值开始统计 + 例如: LLVBARS(HIGH,20)求得20日最低点到当前的周期数 + */ + this.LLVBARS = function (data, n) + { + var result = []; + if (!Array.isArray(data)) return result; + if (Array.isArray(n)) + { + for(var i=0;i 0 && stockItem.YClose > 0) stockProfit[i] = (stockItem.Close - stockItem.YClose) / stockItem.YClose; + if (indexItem.Close > 0 && indexItem.YClose > 0) indexProfit[i] = (indexItem.Close - indexItem.YClose) / indexItem.YClose; + } + + //计算均值数组 + var averageStockProfit = this.CalculateAverage(stockProfit, n); + var averageIndexProfit = this.CalculateAverage(indexProfit, n); + + for (var i = 0, j = 0; i < stockData.Data.length; ++i) + { + result[i] = null; + if (i >= stockProfit.length || i >= indexProfit.length || i >= averageStockProfit.length || i >= averageIndexProfit.length) continue; + + var averageStock = averageStockProfit[i]; + var averageIndex = averageIndexProfit[i]; + + var covariance = 0; //协方差 + var variance = 0; //方差 + for (j = i - n + 1; j <= i; ++j) + { + var value = (indexProfit[j] - averageIndex); + var value2 = (stockProfit[j] - averageStock); + covariance += value * value2; + variance += value * value; + } + + if (this.IsDivideNumber(variance) && this.IsNumber(covariance)) + result[i] = covariance / variance; //(covariance/n)/(variance/n)=covariance/variance; + } + + return result; + } + + /* + 用法:BETA2(X,Y,N)为X与Y的N周期相关放大系数,表示Y变化1%,则X将变化N% + 例如:BETA2(CLOSE,INDEXC,10)表示收盘价与大盘指数之间的10周期相关放大率 + */ + this.BETA2 = function (x, y, n) + { + var result = []; + if (n <= 0) n = 1; + + var xProfit = [null]; //x数据的涨幅 + var yProfit = [null]; //y数据的涨幅 + + var count = Math.max(x.length, y.length); + + var lastItem = { X: x[0], Y: y[0] }; + for (var i = 1; i < count; ++i) + { + xProfit[i] = 0; + yProfit[i] = 0; + + var xItem = x[i]; + var yItem = y[i]; + + if (lastItem.X > 0) xProfit[i] = (xItem - lastItem.X) / lastItem.X; + if (lastItem.Y > 0) yProfit[i] = (yItem - lastItem.Y) / lastItem.Y; + + lastItem = { X: xItem, Y: yItem }; + } + + //计算均值数组 + var averageXProfit = this.CalculateAverage(xProfit, n); + var averageYProfit = this.CalculateAverage(yProfit, n); + + for (var i = 0, j = 0; i < count; ++i) + { + result[i] = null; + + if (i >= xProfit.length || i >= yProfit.length || i >= averageXProfit.length || i >= averageYProfit.length) continue; + + var averageX = averageXProfit[i]; + var averageY = averageYProfit[i]; + + var covariance = 0; //协方差 + var variance = 0; //方差 + for (j = i - n + 1; j <= i; ++j) + { + var value = (xProfit[j] - averageX); + var value2 = (yProfit[j] - averageY); + covariance += value * value2; + variance += value * value; + } + + if (this.IsDivideNumber(variance) && this.IsNumber(covariance)) + result[i] = covariance / variance; //(covariance/n)/(variance/n)=covariance/variance; + } + + return result; + } + + /* + 抛物转向. + 用法: + SAR(N,S,M),N为计算周期,S为步长,M为极值 + 例如: + SAR(10,2,20)表示计算10日抛物转向,步长为2%,极限值为20% + */ + this.SAR = function (n, step, exValue) + { + var result = []; + var stockData = this.SymbolData.Data; + if (n >= stockData.Data.length) return result; + + var high = null, low = null; + for (var i = 0; i < n; ++i) + { + var item = stockData.Data[i]; + if (high == null) high = item.High; + else if (high < item.High) high = item = high; + if (low == null) low = item.Low; + else if (low > item.Low) low = item.Low; + } + + const SAR_LONG = 0, SAR_SHORT = 1; + var position = SAR_LONG; + result[n - 1] = low; + var nextSar = low, sip = stockData.Data[0].High, af = exValue / 100; + for (var i = n; i < stockData.Data.length; ++i) + { + var ysip = sip; + var item = stockData.Data[i]; + var yitem = stockData.Data[i - 1]; + + if (position == SAR_LONG) + { + if (item.Low < result[i - 1]) + { + position = SAR_SHORT; + sip = item.Low; + af = step / 100; + nextSar = Math.max(item.High, yitem.High); + nextSar = Math.max(nextSar, ysip + af * (sip - ysip)); + } + else + { + position = SAR_LONG; + if (item.High > ysip) + { + sip = item.High; + af = Math.min(af + step / 100, exValue / 100); + } + nextSar = Math.min(item.Low, yitem.Low); + nextSar = Math.min(nextSar, result[i - 1] + af * (sip - result[i - 1])); + } + } + else if (position == SAR_SHORT) + { + if (item.High > result[i - 1]) + { + position = SAR_LONG; + sip = item.High; + af = step / 100; + nextSar = Math.min(item.Low, yitem.Low); + nextSar = Math.min(nextSar, result[i - 1] + af * (sip - ysip)); + } + else + { + position = SAR_SHORT; + if (item.Low < ysip) + { + sip = item.Low; + af = Math.min(af + step / 100, exValue / 100); + } + nextSar = Math.max(item.High, yitem.High); + nextSar = Math.max(nextSar, result[i - 1] + af * (sip - result[i - 1])); + } + } + + result[i] = nextSar; + } + + return result; + } + + /* + 抛物转向点. + 用法: + SARTURN(N,S,M),N为计算周期,S为步长,M为极值,若发生向上转向则返回1,若发生向下转向则返回-1,否则为0 + 其用法与SAR函数相同 + */ + this.SARTURN = function (n, step, exValue) + { + var result = []; + var sar = this.SAR(n, step, exValue); + var stockData = this.SymbolData.Data; + var index = 0; + for (index = 0; index < sar.length; ++index) + { + if (this.IsNumber(sar[index])) break; + } + var flag = 0; + if (index < stockData.Data.length) flag = stockData.Data[index].Close > sar[index]; + + for (var i = index + 1; i < stockData.Data.length; ++i) + { + var item = stockData.Data[i]; + if (item.Close < sar[i] && flag) result[i] = -1; + else result[i] = (item.Close > sar[i] && !flag) ? 1 : 0; + + flag = item.Close > sar[i]; + } + + return result; + } + + /* + 属于未来函数,将当前位置到若干周期前的数据设为1. + 用法: + BACKSET(X,N),若X非0,则将当前位置到N周期前的数值设为1. + 例如: + BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0 + */ + this.BACKSET = function (condition, n) + { + var result = []; + if (!condition) return result; + var dataCount = condition.length; + if (!this.IsNumber(dataCount) || dataCount <= 0) return result; + if (Array.isArray(n)) + { + for(var i=0;i=0 && k= 0; --i) + { + var value = condition[i]; + if (this.IsNumber(value) && value) + { + for (j = i; j > i - num; --j) + { + result[j] = 1; + } + } + } + + if (condition[i]) + { + for (j = i; j >= pos; --j) result[j] = 1; + } + } + + return result; + } + + //STRCAT(A,B):将两个字符串A,B(非序列化)相加成一个字符串C. + //用法: STRCAT('多头','开仓')将两个字符串'多头','开仓'相加成一个字符串'多头开仓' + this.STRCAT = function (str1, str2) + { + var result=[]; + if (this.IsString(str1) && this.IsString(str2)) + result=str1+str2; + return result; + } + + //VARCAT(A,B):将两个字符串A,B相加成一个字符串C. + //用法: DRAWTEXT(CLOSE>OPEN,LOW,VARCAT('多头',VAR2STR(C,2))) 将两个字符串相加成一个字符串并按条件显示出来 + this.VARCAT=function(data,data2) + { + var result=[]; + if (Array.isArray(data) && Array.isArray(data2)) + { + var nCount=Math.max(data.length, data2.length); + var strValue=""; + for(var i=0;i= 0; --i) + { + var item = data[i]; + if (this.IsNumber(item)) + { + result = item.toFixed(n); + return result; + } + } + } + else + { + if (this.IsNumber(data)) + result = data.toFixed(n); + } + + return result; + } + + //VAR2STR(A,N):取A的每一个值转为字符串,小数位数N. + //用法: VAR2STR(C,3)表示取收盘价,以3位小数转为字符串 + this.VAR2STR=function(data,n) + { + var result=[]; + if (Array.isArray(data)) + { + for(var i=0;iOPEN)表示统计连续收阳的周期数 + */ + this.BARSLASTCOUNT=function(data) + { + var result=null; + if (Array.isArray(data)) + { + result=[]; + if (data.length>0) + { + var count=0; + for(var i=data.length-1;i>=0;--i) + { + count=0; + for(var j=i;j>=0;--j) + { + if (data[j]) ++count; + else break; + } + result[i]=count; + } + } + } + else + { + if (data) result=1; + else result=0; + } + return result; + } + + //取整. + //用法: INTPART(A)返回沿A绝对值减小方向最接近的整数 + //例如:INTPART(12.3)求得12,INTPART(-3.5)求得-3 + this.INTPART=function(data) + { + var result=null; + if (Array.isArray(data)) + { + result=[]; + for(var i in data) + { + var item=data[i]; + if (this.IsNumber(item)) result[i]=parseInt(item); + else result[i]=null; + } + } + else if (this.IsNumber(data)) + { + result=parseInt(data); + } + + return result; + } + + //函数调用 + this.CallFunction=function(name,args,node) + { + switch(name) + { + case 'MAX': + return this.MAX(args[0], args[1]); + case 'MIN': + return this.MIN(args[0], args[1]); + case 'REF': + return this.REF(args[0], args[1]); + case "REFV": + return this.REFV(args[0], args[1]); + case 'REFX': + return this.REFX(args[0], args[1]); + case "REFXV": + return this.REFXV(args[0], args[1]); + case 'ABS': + return this.ABS(args[0]); + case 'MA': + return this.MA(args[0], args[1]); + case "EMA": + return this.EMA(args[0], args[1]); + case "SMA": + return this.SMA(args[0], args[1],args[2]); + case "DMA": + return this.DMA(args[0], args[1]); + case "XMA": + return this.XMA(args[0], args[1]); + case 'EXPMA': + return this.EXPMA(args[0], args[1]); + case 'EXPMEMA': + return this.EXPMEMA(args[0], args[1]); + case 'COUNT': + return this.COUNT(args[0], args[1]); + case 'LLV': + return this.LLV(args[0], args[1]); + case 'LLVBARS': + return this.LLVBARS(args[0], args[1]); + case 'HHV': + return this.HHV(args[0], args[1]); + case 'HHVBARS': + return this.HHVBARS(args[0], args[1]); + case 'MULAR': + return this.MULAR(args[0], args[1]); + case 'CROSS': + return this.CROSS(args[0], args[1]); + case 'LONGCROSS': + return this.LONGCROSS(args[0], args[1], args[2]); + case 'AVEDEV': + return this.AVEDEV(args[0], args[1]); + case 'STD': + return this.STD(args[0], args[1]); + case 'IF': + case 'IFF': + return this.IF(args[0], args[1], args[2]); + case 'IFN': + return this.IFN(args[0], args[1], args[2]); + case 'NOT': + return this.NOT(args[0]); + case 'SUM': + return this.SUM(args[0], args[1]); + case 'RANGE': + return this.RANGE(args[0],args[1],args[2]); + case 'EXIST': + return this.EXIST(args[0],args[1]); + case 'EXISTR': + return this.EXISTR(args[0], args[1], args[2]); + case 'FILTER': + return this.FILTER(args[0], args[1]); + case 'TFILTER': + return this.TFILTER(args[0],args[1],args[2]); + case 'SLOPE': + return this.SLOPE(args[0],args[1]); + case 'BARSLAST': + return this.BARSLAST(args[0]); + case 'BARSCOUNT': + return this.BARSCOUNT(args[0]); + case 'BARSSINCEN': + return this.BARSSINCEN(args[0], args[1]); + case 'BARSSINCE': + return this.BARSSINCE(args[0]); + case 'LAST': + return this.LAST(args[0], args[1], args[2]); + case 'EVERY': + return this.EVERY(args[0], args[1]); + case 'ZIG': + return this.ZIG(args[0], args[1]); + case 'TROUGHBARS': + return this.TROUGHBARS(args[0], args[1], args[2]); + case "TROUGH": + return this.TROUGH(args[0],args[1],args[2]); + case 'PEAKBARS': + return this.PEAKBARS(args[0], args[1], args[2]); + case 'PEAK': + return this.PEAK(args[0],args[1],args[2]); + case 'COST': + return this.COST(args[0], node); + case 'WINNER': + return this.WINNER(args[0], node); + case 'UPNDAY': + return this.UPNDAY(args[0], args[1]); + case 'DOWNNDAY': + return this.DOWNNDAY(args[0], args[1]); + case 'NDAY': + return this.NDAY(args[0], args[1], args[2]); + case 'DEVSQ': + return this.DEVSQ(args[0], args[1]); + case 'FORCAST': + return this.FORCAST(args[0], args[1]); + case 'STDP': + return this.STDP(args[0], args[1]); + case 'VAR': + return this.VAR(args[0], args[1]); + case 'VARP': + return this.VARP(args[0], args[1]); + case 'RELATE': + return this.RELATE(args[0], args[1], args[2]); + case 'COVAR': + return this.COVAR(args[0], args[1], args[2]); + case 'BETA': + return this.BETA(args[0]); + case 'BETA2': + return this.BETA2(args[0], args[1], args[2]); + case 'WMA': + return this.WMA(args[0], args[1]); + case 'MEMA': + return this.MEMA(args[0], args[1]); + case 'SUMBARS': + return this.SUMBARS(args[0], args[1]); + case 'REVERSE': + return this.REVERSE(args[0]); + case 'SAR': + return this.SAR(args[0], args[1], args[2]); + case 'SARTURN': + return this.SARTURN(args[0], args[1], args[2]); + case 'BACKSET': + return this.BACKSET(args[0], args[1]); + case 'STRCAT': + return this.STRCAT(args[0], args[1]); + case "VARCAT": + return this.VARCAT(args[0], args[1]); + case "VAR2STR": + return this.VAR2STR(args[0], args[1]); + case 'CON2STR': + return this.CON2STR(args[0], args[1]); + case "STRSPACE": + return this.STRSPACE(args[0]); + case 'DTPRICE': + return this.DTPRICE(args[0], args[1]); + case 'ZTPRICE': + return this.ZTPRICE(args[0], args[1]); + case 'FRACPART': + return this.FRACPART(args[0]); + case 'BARSLASTCOUNT': + return this.BARSLASTCOUNT(args[0]); + case 'INTPART': + return this.INTPART(args[0]); + //三角函数 + case 'ATAN': + return this.Trigonometric(args[0], Math.atan); + case 'ACOS': + return this.ACOS(args[0]); + case 'ASIN': + return this.ASIN(args[0]); + case 'COS': + return this.Trigonometric(args[0], Math.cos); + case 'SIN': + return this.Trigonometric(args[0], Math.sin); + case 'TAN': + return this.Trigonometric(args[0], Math.tan); + case 'LN': + return this.Trigonometric(args[0], Math.log); + case 'LOG': + return this.Trigonometric(args[0], Math.log10); + case 'EXP': + return this.Trigonometric(args[0], Math.exp); + case 'SQRT': + return this.Trigonometric(args[0], Math.sqrt); + default: + this.ThrowUnexpectedNode(node,'函数'+name+'不存在'); + } + } + + //调用自定义函数 返回数据格式{Out:输出数据, Draw:绘图数据(可选)} + this.CallCustomFunction=function(name, args, symbolData, node) + { + var functionInfo=g_JSComplierResource.CustomFunction.Data.get(name); + var dwonloadData=symbolData.GetStockCacheData({ CustomName:name, Node:node }); + if (!functionInfo.Invoke) + return { Out: dwonloadData } + + JSConsole.Complier.Log('[JSAlgorithm::CallCustomFunction] call custom function functionInfo=',functionInfo); + + var self=this; + var obj= + { + Name:name, + Args:args, + Symbol:symbolData.Symbol, Period:symbolData.Period, Right:symbolData.Right, + KData:symbolData.Data, //K线数据 + DownloadData:dwonloadData, + ThrowError:function(error) + { + self.ThrowUnexpectedNode(node, error); + } + }; + + return functionInfo.Invoke(obj); + } + + this.ThrowUnexpectedNode=function(node,message) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg); + + } +} + +//是否有是有效的数字 +JSAlgorithm.prototype.IsNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return true; +} + +//是否是整形 +JSAlgorithm.prototype.IsInteger=function(x) +{ + return (typeof x === 'number') && (x % 1 === 0); +} + + +//是否有是有效的除数 +JSAlgorithm.prototype.IsDivideNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + if (value==0) return false; + + return true; +} + +//是否是字符串 +JSAlgorithm.prototype.IsString=function(value) +{ + if (value && typeof(value)=='string') return true; + return false; +} + +/* + 绘图函数 +*/ +function JSDraw(errorHandler, symbolData) +{ + this.ErrorHandler=errorHandler; + this.SymbolData = symbolData; + + this.DRAWTEXT=function(condition,price,text) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'DRAWTEXT',Text:text}; + + if (Array.isArray(condition)) + { + var IsNumber=this.IsNumber(price); + for(var i in condition) + { + drawData[i]=null; + + if (isNaN(condition[i]) || !condition[i]) continue; + + if (IsNumber) + { + drawData[i]=price; + } + else + { + if (this.IsNumber(price[i])) drawData[i]=price[i]; + } + } + } + else if (this.IsNumber(condition) && condition) + { + var IsNumber=this.IsNumber(price); + for(var i=0;i=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1) 表示在创20天新高与创20天新低之间画直线并且向右延长。 + */ + this.DRAWLINE=function(condition,data,condition2,data2,expand) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'DRAWLINE', Expand:expand}; + + if(condition.length<=0) return result; + let count=Math.max(condition.length,condition2.length); + + let bFirstPoint=false; + let bSecondPont=false; + let lineCache={Start:{ },End:{ }, List:new Array()}; + + for(let i=0;i=0;--i) + { + if (this.IsNumber(drawData[i])) + { + x2=i; + break; + } + } + //y3=(y1-y2)*(x3-x1)/(x2-x1) + if (x2!=null && x2-1>=0) + { + var x1=x2-1; + for(var i=x2+1;iVAL2时,在VAL1和VAL2之间填充COLOR1;当VAL1=HHV(HIGH,20),HIGH)表示在创20天新高点之间画折线。 + */ + this.POLYLINE = function (condition, data) + { + let drawData = []; + let result = { DrawData: drawData, DrawType: 'POLYLINE' }; + let isNumber = typeof (data) == 'number'; + + let bFirstPoint = false; + let bSecondPont = false; + if (isNumber) + { + for (let i in condition) + { + drawData[i] = null; + if (bFirstPoint == false) + { + if (!condition[i]) continue; + + drawData[i] = data; + bFirstPoint = true; + } + else { + drawData[i] = data; + } + } + } + else + { + let lineCache = { Start: {}, End: {}, List: new Array() }; + for (let i in condition) + { + drawData[i] = null; + if (bFirstPoint == false && bSecondPont == false) + { + if (condition[i] == null || !condition[i]) continue; + if (i >= data.length || !this.IsNumber(data[i])) continue; + + bFirstPoint = true; + lineCache.Start = { ID: parseInt(i), Value: data[i] }; //第1个点 + } + else if (bFirstPoint == true && bSecondPont == false) + { + if (condition[i] == null || !condition[i]) continue; + if (i >= data.length || !this.IsNumber(data[i])) continue; + + lineCache.End = { ID: parseInt(i), Value: data[i] }; //第2个点 + //根据起始点和结束点 计算中间各个点的数据 + let lineData = this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值 + + for (let j in lineData) + { + let item = lineData[j]; + drawData[item.ID] = item.Value; + } + + let start = { ID: lineCache.End.ID, Value: lineCache.End.Value }; + lineCache = { Start: start, End: {} }; + } + } + } + + return result + } + + /* + 画出数字. + 用法: + DRAWNUMBER(COND,PRICE,NUMBER),当COND条件满足时,在PRICE位置书写数字NUMBER. + 例如: + DRAWNUMBER(CLOSE/OPEN>1.08,LOW,C)表示当日实体阳线大于8%时在最低价位置显示收盘价. + */ + this.DRAWNUMBER = function (condition, data, data2) + { + let drawData = { Value: new Array(), Text: new Array() }; + let result = { DrawData: drawData, DrawType: 'DRAWNUMBER' }; + var isArrayData=Array.isArray(data); + let isNumber = typeof (data2) == 'number'; + let text; + if (isNumber) + { + if (this.IsInteger(data2)) text=data2.toString(); + else text=data2.toFixed(2); + } + + for (let i in condition) + { + drawData.Value[i] = null; + if (!condition[i]) continue; + if (isArrayData) + { + if (i >= data.length || !this.IsNumber(data[i])) continue; + + if (isNumber) + { + drawData.Value[i] = data[i]; + drawData.Text[i] = text; + } + else + { + if (i >= data2.length || !data2[i]) continue; + drawData.Value[i] = data[i]; + if (typeof(data2[i])=='number') + drawData.Text[i] = data2[i].toFixed(2); + else + drawData.Text[i] = data2[i].toString(); + } + } + else if (this.IsNumber(data)) + { + if (isNumber) + { + drawData.Value[i]=data; + drawData.Text[i]=text; + } + else + { + if (i>=data2.length || !data2[i]) continue; + drawData.Value[i]=data; + if (this.IsNumber(data2[i])) + drawData.Text[i] = data2[i].toFixed(2); + else + drawData.Text[i] = data2[i].toString(); + } + } + + } + + return result; + } + + /* + 在图形上绘制小图标. + 用法: + DRAWICON(COND,PRICE,TYPE),当COND条件满足时,在PRICE位置画TYPE号图标(TYPE为1--41). + 例如: + DRAWICON(CLOSE>OPEN,LOW,1)表示当收阳时在最低价位置画1号图标. + */ + this.DRAWICON = function (condition, data, type) + { + //图标对应的字符代码 + let mapIcon = new Map([ + [1, { Symbol: '↑', Color: 'rgb(238,44,44)' }], [2, { Symbol: '↓', Color: 'rgb(0,139,69)' }], + [3, { Symbol: '😧' }], [4, { Symbol: '😨' }], [5, { Symbol: '😁' }], [6, { Symbol: '😱' }], + [7, { Symbol: '◼', Color: 'rgb(238,44,44)' }], [8, { Symbol: '◆', Color: 'rgb(0,139,69)' }], + [9, { Symbol: '💰' }], [10, { Symbol: '📪' }], [11, { Symbol: '👆' }], [12, { Symbol: '👇' }], + [13, { Symbol: 'B', Color: 'rgb(178,34,34)' },], [14, { Symbol: 'S', Color: 'rgb(0,139,69)' }], + [36, { Symbol: 'Χ', Color: 'rgb(238,44,44)' }], [37, { Symbol: 'X', Color: 'rgb(0,139,69)' }], + [38, { Symbol: '▲', Color: 'rgb(238,44,44)' }], [39, { Symbol: '▼', Color: 'rgb(0,139,69)' }], + ]); + + let icon = mapIcon.get(type); + if (!icon) icon = { Symbol: '●', Color: 'rgb(0,139,69)'}; + let drawData = []; + let result = { DrawData: drawData, DrawType: 'DRAWICON', Icon: icon }; + if (condition.length <= 0) return result; + + var IsNumber = typeof (data) == "number"; + if (typeof (condition) == 'number') + { + if (!condition) return result; + + for (var i = 0; i < this.SymbolData.Data.Data.length; ++i) + { + if (IsNumber) + { + drawData[i] = data; + } + else + { + if (i < data.length && this.IsNumber(data[i])) drawData[i] = data[i]; + else drawData[i] = null; + } + } + return result; + } + + for (var i in condition) + { + drawData[i] = null; + + if (!condition[i]) continue; + + if (IsNumber) + { + drawData[i] = data; + } + else + { + if (this.IsNumber(data[i])) drawData[i] = data[i]; + } + } + + return result; + } + + // 相对位置上画矩形. + //用法: DRAWRECTREL(LEFT,TOP,RIGHT,BOTTOM,COLOR),以图形窗口(LEFT,TOP)为左上角,(RIGHT,BOTTOM)为右下角绘制矩形,坐标单位是窗口沿水平和垂直方向的1/1000,取值范围是0—999,超出范围则可能显示在图形窗口外,矩形中间填充颜色COLOR,COLOR为0表示不填充. + //例如: DRAWRECTREL(0,0,500,500,RGB(255,255,0))表示在图形最左上部1/4位置用黄色绘制矩形 + this.DRAWRECTREL = function (left, top, right, bottom, color) + { + + let drawData = + { + Rect: + { + Left: Math.min(left,right), Top: Math.min(top,bottom), + Right: Math.max(left,right), Bottom: Math.max(top,bottom) + }, + Color: color + }; + if (color == 0) drawData.Color = null; + let result = { DrawData: drawData, DrawType: 'DRAWRECTREL' }; + + return result; + } + + //填充背景. + //用法: DRAWGBK(COND,COLOR1,COLOR2,colorAngle) colorAngle=渐近色角度 + //例如: DRAWGBK(O>C,RGB(0,255,0),RGB(255,0,0),0); + this.DRAWGBK=function(condition, color, color2, colorAngle) + { + let drawData={ Color:[], Angle:colorAngle }; + if (color) drawData.Color.push(color); + if (color2) drawData.Color.push(color2); + + let result={DrawData:null, DrawType:'DRAWGBK'}; + if (Array.isArray(condition)) + { + for(var i in condition) + { + var item=condition[i]; + if (item) + { + result.DrawData=drawData; + break; + } + } + } + else + { + if (condition) result.DrawData=drawData; + } + + return result; + } + + this.DRAWGBK2=function(condition, color, color2, colorAngle) + { + let drawData={ Color:[], Angle:colorAngle }; + if (color) drawData.Color.push(color); + if (color2) drawData.Color.push(color2); + + let result={DrawData:null, DrawType:'DRAWGBK2'}; + if (Array.isArray(condition)) + { + drawData.Data=[]; + for(var i in condition) + { + var item=condition[i]; + drawData.Data[i]=item ? 1:0; + } + + result.DrawData=drawData; + } + else + { + if (condition) + { + result.DrawData=drawData; + result.DrawType="DRAWGBK"; + } + } + + return result; + } + + this.RGB = function (r, g, b) + { + var rgb = `rgb(${r},${g},${b})`; + return rgb; + } + + this.RGBA = function (r, g, b, a) + { + var rgba = `rgba(${r},${g},${b},${a})`; + return rgba; + } +} + + +JSDraw.prototype.CalculateDrawLine=function(lineCache) +{ + lineCache.List=[]; + for(let i=lineCache.Start.ID; i<=lineCache.End.ID; ++i) lineCache.List.push(i); + + let height=Math.abs(lineCache.Start.Value-lineCache.End.Value); + let width=lineCache.List.length-1; + + var result=[]; + result.push({ID:lineCache.Start.ID, Value:lineCache.Start.Value}); //第1个点 + + if (lineCache.Start.Value>lineCache.End.Value) + { + for(let i=1;i0) this.MaxReqeustDataCount=option.MaxReqeustDataCount; + if (option.MaxRequestMinuteDayCount>0) this.MaxRequestMinuteDayCount=option.MaxRequestMinuteDayCount; + if (option.KLineApiUrl) this.KLineApiUrl=option.KLineApiUrl; + if (option.NetworkFilter) this.NetworkFilter = option.NetworkFilter; + } + + //最新行情 + this.GetLatestData=function() + { + if (this.LatestData) return this.Execute.RunNextJob(); + + var self=this; + wx.request({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"], + "symbol": [this.Symbol] + }, + method:"POST", + dataType: "json", + success: function (recvData) + { + self.RecvLatestData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + this.RecvLatestData = function (recvData) + { + let data=recvData.data; + if (!data.stock || data.stock.length!=1) return; + + let stock=data.stock[0]; + this.LatestData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time, + YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount, + Increase:stock.increase, Exchangerate:stock.exchangerate, Amplitude:stock.amplitude}; + + JSConsole.Complier.Log('[JSSymbolData::RecvLatestData]', this.LatestData); + } + + this.GetLatestCacheData=function(dataname) + { + if (!this.LatestData) return null; + + switch(dataname) + { + case DYNAINFO_ARGUMENT_ID.YCLOSE: + return this.LatestData.YClose; + case DYNAINFO_ARGUMENT_ID.OPEN: + return this.LatestData.Open; + case DYNAINFO_ARGUMENT_ID.HIGH: + return this.LatestData.High; + case DYNAINFO_ARGUMENT_ID.LOW: + return this.LatestData.Low; + case DYNAINFO_ARGUMENT_ID.VOL: + return this.LatestData.Vol; + case DYNAINFO_ARGUMENT_ID.AMOUNT: + return this.LatestData.Amount; + case DYNAINFO_ARGUMENT_ID.INCREASE: + return this.LatestData.Increase; + case DYNAINFO_ARGUMENT_ID.EXCHANGERATE: + return this.LatestData.Exchangerate; + case DYNAINFO_ARGUMENT_ID.AMPLITUDE: + return this.LatestData.Amplitude; + case DYNAINFO_ARGUMENT_ID.CLOSE: + return this.LatestData.Price; + default: + return null; + } + } + + this.GetVolRateData = function (job, node) { + var volrKey = job.ID.toString() + '-VolRate-' + this.Symbol; + if (this.ExtendData.has(volrKey)) return this.Execute.RunNextJob(); + + var self = this; + wx.request({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name", "symbol", "avgvol5", 'date'], + "symbol": [this.Symbol] + }, + method: "POST", + dataType: "json", + async: true, + success: function (recvData) + { + self.RecvVolRateData(recvData, volrKey); + self.Execute.RunNextJob(); + }, + error: function (request) + { + self.RecvError(request); + } + }); + } + + this.RecvVolRateData = function (recvData, key) + { + var data=recvData.data; + if (!data.stock || data.stock.length != 1) return; + var avgVol5 = data.stock[0].avgvol5; + var date = data.stock[0].date; + var item = { AvgVol5: avgVol5, Date: date }; + this.ExtendData.set(key, item); + + JSConsole.Complier.Log('[JSSymbolData::RecvVolRateData]', item); + } + + this.GetVolRateCacheData = function (node) + { + var key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA.toString() + '-VolRate-' + this.Symbol; + if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node, '不支持VOLR'); + + var result = []; + var value = this.ExtendData.get(key); + var avgVol5 = value.AvgVol5 / 241; + var totalVol = 0; + //5日成交总量只取了最新一天的,历史的暂时没有取,所以数据计算的时候只计算最新的一天, 其他都空 + for (var i = 0, j = 0; i < this.Data.Data.length; ++i) + { + result[i] = null; + var item = this.Data.Data[i]; + var dateTime = item.DateTime; //日期加时间 + if (!dateTime) continue; + var aryValue = dateTime.split(' '); + if (aryValue.length != 2) continue; + var date = parseInt(aryValue[0]); + if (date != value.Date) continue; + + totalVol += item.Vol; + if (avgVol5 > 0) result[i] = totalVol / (j + 1) / avgVol5 * 100; + ++j; + } + + return result; + } + + //获取大盘指数数据 + this.GetIndexData=function() + { + if (this.IndexData) return this.Execute.RunNextJob(); + + var self=this; + if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 + { + wx.request({ + url: self.KLineApiUrl, + data: + { + "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", 'up', 'down', 'stop', 'unchanged'], + "symbol": '000001.sh', + "start": -1, + "count": self.MaxReqeustDataCount+500 //多请求2年的数据 确保股票剔除停牌日期以后可以对上 + }, + method: 'POST', + dataType: "json", + success: function (recvData) + { + self.RecvIndexHistroyData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据 + { + wx.request({ + url: self.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": '000001.sh', + "start": -1, + "count": self.MaxRequestMinuteDayCount+5 + }, + method: 'POST', + dataType: "json", + success: function (data) + { + self.RecvIndexMinuteHistroyData(data); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + } + + this.RecvIndexHistroyData=function(recvData) + { + let data = recvData.data; + JSConsole.Complier.Log('[JSSymbolData::RecvIndexHistroyData] recv data' , data); + + let hisData=this.JsonDataToHistoryData(data); + this.IndexData = new JSCommonData.ChartData(); + this.IndexData.DataType=0; /*日线数据 */ + this.IndexData.Data=hisData; + + var aryOverlayData = this.SourceData.GetOverlayData(this.IndexData.Data); //和主图数据拟合以后的数据 + this.IndexData.Data=aryOverlayData; + + if (JSCommonData.ChartData.IsDayPeriod(this.Period, false)) //周期数据 + { + let periodData=this.IndexData.GetPeriodData(this.Period); + this.IndexData.Data=periodData; + } + } + + this.RecvIndexMinuteHistroyData = function (recvData) + { + let data = recvData.data; + JSConsole.Complier.Log('[JSSymbolData::RecvIndexMinuteHistroyData] recv data' , data); + + let hisData=this.JsonDataToMinuteHistoryData(data); + this.IndexData = new JSCommonData.ChartData(); + this.IndexData.DataType=1; /*分钟线数据 */ + this.IndexData.Data=hisData; + + if (JSCommonData.ChartData.IsMinutePeriod(this.Period, false)) //周期数据 + { + let periodData=this.IndexData.GetPeriodData(this.Period); + this.IndexData.Data=periodData; + } + } + + //获取大盘指数缓存数据 + this.GetIndexCacheData=function(dataName) + { + if (!this.IndexData) return new Array(); + + switch(dataName) + { + case 'INDEXA': + return this.IndexData.GetAmount(); + case 'INDEXC': + return this.IndexData.GetClose(); + case 'INDEXH': + return this.IndexData.GetHigh(); + case 'INDEXL': + return this.IndexData.GetLow(); + case 'INDEXO': + return this.IndexData.GetOpen(); + case 'INDEXV': + return this.IndexData.GetVol(); + case 'INDEXADV': + return this.IndexData.GetUp(); + case 'INDEXDEC': + return this.IndexData.GetDown(); + } + } + + //分钟涨幅股票个数统计数据下载 + this.GetIndexIncreaseData = function (job) + { + var upKey = job.ID.toString() + '-UpCount-' + job.Symbol; + var downKey = job.ID.toString() + '-DownCount-' + job.Symbol; + if (this.ExtendData.has(upKey) && this.ExtendData.has(downKey)) return this.Execute.RunNextJob(); + + var symbol = job.Symbol; + symbol = symbol.replace('.CI', '.ci'); + var self = this; + var apiUrl = g_JSComplierResource.CacheDomain + '/cache/analyze/increaseanalyze/' + symbol + '.json'; + JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] Get url=', apiUrl); + wx.request({ + url: apiUrl, + method: "GET", + dataType: "json", + success: function (data) + { + self.RecvMinuteIncreaseData(data, { UpKey: upKey, DownKey: downKey }); + self.Execute.RunNextJob(); + }, + error: function (request) + { + self.RecvError(request); + } + }); + } + + this.RecvMinuteIncreaseData = function (recvData, key) + { + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteIncreaseData] recv data', recvData); + var data=recvData.data; + if (!data.minute) return; + var minuteData = data.minute; + if (!minuteData.time || !minuteData.up || !minuteData.down) return; + var upData = [], downData = []; + for (var i = 0; i < minuteData.time.length; ++i) { + upData[i] = minuteData.up[i]; + downData[i] = minuteData.down[i]; + } + + this.ExtendData.set(key.UpKey, upData); + this.ExtendData.set(key.DownKey, downData); + } + + //分钟涨幅股票个数统计数据 + this.GetIndexIncreaseCacheData = function (funcName, symbol, node) { + var key; + if (funcName == 'UPCOUNT') key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString() + '-UpCount-' + symbol; + else if (funcName == 'DOWNCOUNT') key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString() + '-DownCount-' + symbol; + + if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node, '不支持函数' + funcName + '(' + symbol + ')'); + + return this.ExtendData.get(key); + } + + this.GetSymbolData=function() + { + if (this.Data) return this.Execute.RunNextJob(); + + let self=this; + + if (this.DataType === 2) //当天分钟数据 + { + wx.request({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", "amount", "date", "minute", "time", "minutecount"], + "symbol": [self.Symbol], + "start": -1 + }, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) { + self.RecvMinuteData(recvData); + self.Execute.RunNextJob(); + } + }); + return; + } + + if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 + { + wx.request({ + url: self.KLineApiUrl, + data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxReqeustDataCount + }, + method: 'POST', + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvHistroyData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据 + { + wx.request({ + url: this.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount + }, + method: 'POST', + dataType: "json", + async:true, + success: function (data) + { + self.RecvMinuteHistroyData(data); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + } + + this.RecvHistroyData=function(recvData) + { + let data=recvData.data; + JSConsole.Complier.Log('[JSSymbolData::RecvHistroyData] recv data' , data); + + let hisData=this.JsonDataToHistoryData(data); + this.Data=new JSCommonData.ChartData(); + this.Data.DataType=0; /*日线数据 */ + this.Data.Data=hisData; + this.SourceData = new JSCommonData.ChartData; + this.SourceData.Data = hisData; + + if (this.Right>0) //复权 + { + let rightData=this.Data.GetRightDate(this.Right); + this.Data.Data=rightData; + } + + if (JSCommonData.ChartData.IsDayPeriod(this.Period, false)) //周期数据 + { + let periodData=this.Data.GetPeriodData(this.Period); + this.Data.Data=periodData; + } + + this.Name = data.name; + } + + this.RecvMinuteHistroyData = function (recvData) + { + let data = recvData.data; + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteHistroyData] recv data' , data); + + let hisData=this.JsonDataToMinuteHistoryData(data); + this.Data = new JSCommonData.ChartData(); + this.Data.DataType=1; /*分钟线数据 */ + this.Data.Data=hisData; + this.SourceData = new JSCommonData.ChartData; + this.SourceData.Data = hisData; + + if (JSCommonData.ChartData.IsMinutePeriod(this.Period, false)) //周期数据 + { + let periodData=this.Data.GetPeriodData(this.Period); + this.Data.Data=periodData; + } + + this.Name = data.name; + } + + //最新的分钟数据走势图 + this.RecvMinuteData = function (recvData) + { + let data = recvData.data; + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteData] recv data', data); + + var aryMinuteData = this.JsonDataToMinuteData(data); + this.Data = new JSCommonData.ChartData(); + this.Data.DataType = 2; /*分钟走势图数据 */ + this.Data.Data = aryMinuteData; + + this.Name = data.stock[0].name; + } + + this.GetSymbolCacheData=function(dataName) + { + if (!this.Data) return new Array(); + + switch(dataName) + { + case 'CLOSE': + case 'C': + return this.Data.GetClose(); + case 'VOL': + case 'V': + return this.Data.GetVol(); + case 'OPEN': + case 'O': + return this.Data.GetOpen(); + case 'HIGH': + case 'H': + return this.Data.GetHigh(); + case 'LOW': + case 'L': + return this.Data.GetLow(); + case 'AMOUNT': + case 'AMO': + return this.Data.GetAmount(); + case 'VOLINSTK': + return this.Data.GetPosition(); + } + } + + this.GetCurrBarsCount=function() + { + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return new Array(); + + let lCount=this.Data.Data.length; + let result=[]; + for(let i=lCount-1;i>=0;--i) + result.push(i); + + return result; + } + + this.GetIsLastBar = function () + { + let result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result + + let lCount = this.Data.Data.length; + for (let i = 0; i < lCount; ++i) + { + if (i == lCount - 1) result.push(1); + else result.push(0); + } + + return result; + } + + //融资融券函数 + this.GetMarginCacheData = function (id, node) + { + let jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(id); + if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持MARGIN(' + id + ')'); + if (this.MarginData.has(jobID)) return this.MarginData.get(jobID); + + return []; + } + + //下融资融券 + this.GetMarginData = function (jobID) + { + if (this.MarginData.has(jobID)) return this.Execute.RunNextJob(); + + JSConsole.Complier.Log('[JSSymbolData::GetMarginData] jobID=', jobID); + var self = this; + let fieldList = ["name", "date", "symbol"]; + + switch (jobID) + { + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE: //融资融券余额 + fieldList.push("margin.balance"); + break; + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE: //融资占比 + fieldList.push("margin.rate"); + break; + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入 + fieldList.push("margin.buy"); + break; + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出 + fieldList.push("margin.sell"); + break; + } + + //请求数据 + wx.request({ + url: this.StockHistoryDayApiUrl, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "orderfield": "date" + }, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) { + self.RecvMarginData(recvData, jobID); + self.Execute.RunNextJob(); + } + }); + } + + this.RecvMarginData = function (recvData, jobID) + { + var data = recvData.data; + //JSConsole.Complier.Log(data); + if (!data.stock || data.stock.length != 1) return; + + let stock = data.stock[0]; + var aryData = new Array(); + var aryData2 = [], aryData3 = [], aryData4 = []; //其他3个数据 + for (let i in stock.stockday) + { + var item = stock.stockday[i]; + var marginData = item.margin; + if (!marginData) continue; + + let indexData = new JSCommonData.SingleData(); + indexData.Date = item.date; + + switch (jobID) + { + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE: + if (!this.IsNumber(marginData.balance)) continue; + indexData.Value = marginData.balance; //融资融券余额 + aryData.push(indexData); + break; + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE: + if (!this.IsNumber(marginData.rate)) continue; + indexData.Value = marginData.rate; //融资占比 + aryData.push(indexData); + break; + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入 + var buyData = marginData.buy; + if (!buyData) continue; + if (!this.IsNumber(buyData.balance) || !this.IsNumber(buyData.amount) || !this.IsNumber(buyData.repay) || !this.IsNumber(buyData.net)) continue; + + indexData.Value = buyData.balance; + var indexData2 = new JSCommonData.SingleData(); + indexData2.Date = item.date; + indexData2.Value = buyData.amount; + var indexData3 = new JSCommonData.SingleData(); + indexData3.Date = item.date; + indexData3.Value = buyData.repay; + var indexData4 = new JSCommonData.SingleData(); + indexData4.Date = item.date; + indexData4.Value = buyData.net; + + aryData.push(indexData); + aryData2.push(indexData2); + aryData3.push(indexData3); + aryData4.push(indexData4); + break; + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出 + var sellData = marginData.sell; + if (!sellData) continue; + if (!this.IsNumber(sellData.balance) || !this.IsNumber(sellData.volume) || !this.IsNumber(sellData.repay) || !this.IsNumber(sellData.net)) continue; + + indexData.Value = buyData.balance; + var indexData2 = new JSCommonData.SingleData(); + indexData2.Date = item.date; + indexData2.Value = buyData.volume; + var indexData3 = new JSCommonData.SingleData(); + indexData3.Date = item.date; + indexData3.Value = buyData.repay; + var indexData4 = new JSCommonData.SingleData(); + indexData4.Date = item.date; + indexData4.Value = buyData.net; + + aryData.push(indexData); + aryData2.push(indexData2); + aryData3.push(indexData3); + aryData4.push(indexData4); + break; + default: + continue; + } + } + + var allData = []; + if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE) + { + allData.push({ JobID: jobID, Data: aryData }); + } + else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT || + jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET) + { + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE, Data: aryData }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT, Data: aryData2 }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY, Data: aryData3 }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET, Data: aryData4 }); + } + else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME || + jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET) + { + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE, Data: aryData }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME, Data: aryData2 }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY, Data: aryData3 }); + allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET, Data: aryData4 }); + } + + for (let i in allData) + { + let aryFixedData = this.Data.GetFittingData(allData[i].Data); + + var bindData = new JSCommonData.ChartData(); + bindData.Data = aryFixedData; + bindData.Period = this.Period; //周期 + + if (bindData.Period > 0) //周期数据 + { + var periodData = bindData.GetPeriodSingleData(bindData.Period); + bindData.Data = periodData; + } + + let data = bindData.GetValue(); + this.MarginData.set(allData[i].JobID, data); + } + } + + this.GetNewsAnalysisCacheData = function (id, node) + { + + let jobID = JS_EXECUTE_JOB_ID.GetNewsAnalysisID(id); + if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持NEWS(' + id + ')'); + if (this.NewsAnalysisData.has(jobID)) return this.NewsAnalysisData.get(jobID); + + return []; + } + + //下载新闻统计 + this.GetNewsAnalysisData = function (jobID) + { + if (this.NewsAnalysisData.has(jobID)) return this.Execute.RunNextJob(); + + var self = this; + var mapFolder = new Map([ + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE, "negative"], + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH, 'research'], + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT, 'interact'], + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE, 'holderchange'], //NEWS(4) 股东增持 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2, 'holderchange'], //NEWS(5) 股东减持 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER, 'trustholder'], //NEWS(6) 信托持股 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING, 'Blocktrading'], //NEWS(7) 大宗交易 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS, 'companynews'], //NEWS(8) 官网新闻 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS, 'topmanagers'], //NEWS(9) 高管要闻 + [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE, 'Pledge'], //NEWS(10) 股权质押 + ]); + + if (!mapFolder.has(jobID)) + { + this.Execute.RunNextJob(); + return; + } + + var folderName = mapFolder.get(jobID); + var url = this.StockNewsAnalysisApiUrl + '/' + folderName + '/' + this.Symbol + '.json'; + + //请求数据 + wx.request({ + url: url, + method: 'GET', + dataType: "json", + async: true, + success: function (recvData) + { + if (recvData.statusCode==200) + self.RecvNewsAnalysisData(recvData, jobID); + else + self.RecvNewsAnalysisDataError(recvData, jobID); + self.Execute.RunNextJob(); + }, + fail: function (request, textStatus) + { + //self.RecvNewsAnalysisDataError(request, textStatus, jobID); + self.Execute.RunNextJob(); + } + }); + } + + this.RecvNewsAnalysisDataError = function (recvData, jobID) + { + JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisDataError] request error.', recvData.statusCode); + + //没有新闻使用0数据填充 + var aryData = []; + for (var i = 0; i < this.Data.Data.length; ++i) + { + var item = new JSCommonData.SingleData(); + item.Date = this.Data.Data[i].Date; + item.Value = 0 + aryData.push(item); + } + + var bindData = new JSCommonData.ChartData(); + bindData.Data = aryData; + this.NewsAnalysisData.set(jobID, bindData.GetValue()); + } + + this.RecvNewsAnalysisData = function (recvData, jobID) + { + var data=recvData.data; + if (!data.data || !data.date) return; + if (data.data.length <= 0 || data.data.length != data.date.length) return; + + JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisData] jobID', jobID, data.update); + if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE || jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2) + { + var aryData = [], aryData2 = []; + for (var i = 0; i < data.data.length; ++i) + { + var item = new JSCommonData.SingleData(); + item.Date = data.date[i]; + item.Value = data.data[i]; + if (this.IsNumber(item.Value)) aryData.push(item); + + if (i < data.data2.length) + { + item = new JSCommonData.SingleData(); + item.Date = data.date[i]; + item.Value = data.data2[i]; + if (this.IsNumber(item.Value)) aryData2.push(item); + } + } + + let aryFixedData = this.Data.GetFittingData2(aryData, 0); + var bindData = new JSCommonData.ChartData(); + bindData.Data = aryFixedData; + this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE, bindData.GetValue()); + + aryFixedData = this.Data.GetFittingData2(aryData2, 0); + bindData = new JSCommonData.ChartData(); + bindData.Data = aryFixedData; + this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2, bindData.GetValue()); + } + else + { + var aryData = []; + for (var i = 0; i < data.data.length; ++i) + { + var item = new JSCommonData.SingleData(); + item.Date = data.date[i]; + item.Value = data.data[i]; + aryData.push(item); + } + + let aryFixedData = this.Data.GetFittingData2(aryData, 0); + + var bindData = new JSCommonData.ChartData(); + bindData.Data = aryFixedData; + + this.NewsAnalysisData.set(jobID, bindData.GetValue()); + } + } + + this.GetStockDataKey=function(jobItem, aryArgs) + { + var key=jobItem.FunctionName; + if (aryArgs.length>0) + { + key+="("; + for(var i=0;i0) key+=","; + key+=aryArgs[i].toString(); + } + key+=")"; + } + return key; + } + + this.GetFinOne=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 3); + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetFinOne', //类名:: + Explain:'财务数据FINONE(ID,Y,MMDD)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,1); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadFinOneData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,1); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetFinValue=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 1); + var lID=aryArgs[0]; + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetFinValue', //类名:: + Explain:'财务数据FINVALUE(ID)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,0); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadFinValueData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + + this.GetFinance=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 1); + var lID=aryArgs[0]; + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetFinance', //类名:: + Explain:'财务数据FINANCE(ID)', + JobID:jobItem.ID, + Request:{ Url:self.RealtimeApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,0); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadFinanceData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetVariantData=function(jobItem) + { + var key=jobItem.VariantName; + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetVariantData', //类名:: + Explain:'变量数据下载', + JobID:jobItem.ID, + Request:{ Url:"www.121287.com", Type:'POST', Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) + { + self.AddStockValueError(key,recvData.Error); + } + else + { + var dataType=0; + if (IFrameSplitOperator.IsNumber(recvData.DataType)) dataType=recvData.DataType; + self.RecvStockValue(recvData.Data,jobItem,key,dataType); + } + + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var errorCallback=function(strError) + { + self.AddStockValueError(key,strError); + }; + + var apiDownload; + if (jobItem.VariantName=="CAPITAL" || jobItem.VariantName=="TOTALCAPITAL" || jobItem.VariantName=="EXCHANGE") + { + var callback=function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }; + + apiDownload=new DownloadFinanceData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:[jobItem.VariantName], + DataKey:key, + Callback:callback, + ErrorCallback:errorCallback + }); + } + else if (jobItem.VariantName=="HYBLOCK" || jobItem.VariantName=="DYBLOCK" || jobItem.VariantName=="GNBLOCK") + { + var callback=function(recvData, jobItem, key, dataType) + { + self.RecvStockValue(recvData, jobItem, key, dataType); + self.Execute.RunNextJob(); + }; + + apiDownload=new DownloadGroupData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:[jobItem.VariantName], + DataKey:key, + Callback:callback, + ErrorCallback:errorCallback + }); + } + else if (jobItem.VariantName=="INBLOCK") + { + var errorMessage=`${jobItem.VariantName}, 请对接外部数据.`; + this.AddStockValueError(key,errorMessage); + this.Execute.RunNextJob(); + return; + } + else + { + var errorMessage=`不支持变量${jobItem.VariantName}, 请对接外部数据.`; + this.AddStockValueError(key,errorMessage); + this.Execute.RunNextJob(); + return; + } + + apiDownload.Download(); + } + + this.GetGPJYValue=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 3); + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + //TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理 + var dataType=aryArgs[2]==1?0:2; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetGPJYValue', //类名:: + Explain:'股票交易类数据GPJYVALUE(ID,N,TYPE)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,dataType); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadGPJYValue( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,dataType); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + //自定义变量数据下载 + this.GetCustomVariantData=function(jobItem) + { + var key=jobItem.VariantName; + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var variantInfo=g_JSComplierResource.CustomVariant.Data.get(key); + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetCustomVariantData', //类名::函数名 + Explain:'自定义变量数据下载', + JobID:jobItem.ID, + Request:{ Url:"www.121287.com", Type:'POST', Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + VariantInfo:variantInfo, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) self.AddStockValueError(key,recvData.Error); + else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType); + self.Execute.RunNextJob(); + }); + } + else + { + this.AddStockValueError(key, `自定义变量${key}下载失败`); + this.Execute.RunNextJob(); + } + } + + this.GetCustomFunctionData=function(jobItem) + { + var key=jobItem.FunctionName; + var functionInfo=g_JSComplierResource.CustomFunction.Data.get(key); + if (!functionInfo.IsDownload) return this.Execute.RunNextJob(); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); //一个函数只能缓存一个数据, 保存多个外部自己保存 + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetCustomFunctionData', //类名::函数名 + Explain:'自定义函数数据下载', + JobID:jobItem.ID, + Request: + { + Url:"www.121287.com", Type:'POST', + Data: + { + FunctionName:jobItem.FunctionName, + symbol: this.Symbol, daterange:dateRange, + JobItem:jobItem //函数编译信息 + } + }, + Self:this, + FunctionInfo:functionInfo, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) self.AddStockValueError(key,recvData.Error); + else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType); + self.Execute.RunNextJob(); + }); + } + else + { + this.AddStockValueError(key, `自定义函数${key}下载失败`); + this.Execute.RunNextJob(); + } + } + + this.RecvStockValue=function(recvData,jobItem,key,dataType) + { + if (!recvData) + { + //JSConsole.Complier.Log(`[JSSymbolData::RecvStockValue] key=${key} data is null`); + return; + } + + if (dataType==0) + { + if (Array.isArray(recvData)) + { + var kdata=this.Data; //K线 + var aryFittingData; + if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) + aryFittingData=kdata.GetFittingFinanceData(recvData); //数据和主图K线拟合 + else if (JSCommonData.ChartData.IsMinutePeriod(this.Period,true)) + aryFittingData=kdata.GetMinuteFittingFinanceData(recvData); //数据和主图K线拟合 + else + return; + + var bindData=new JSCommonData.ChartData(); + bindData.Data=aryFittingData; + var result=bindData.GetValue(); + + if (key=="EXCHANGE") //计算换手率=成交量/流通股本*100 + { + for(var i in result) + { + var kitem=kdata.Data[i]; + if (result[i]>0) + result[i]=kitem.Vol/result[i] * 100; + } + } + + this.StockData.set(key,{ Data:result }); + } + else + { + this.StockData.set(key,{ Data:recvData.Value }); + } + } + else if (dataType==1) //单数值 + { + this.StockData.set(key,{ Data:recvData.Value }); + } + else if (dataType==2) //数据不做平滑处理 + { + var kdata=this.Data; //K线 + var aryFittingData; + if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) + aryFittingData=kdata.GetFittingTradeData(recvData, 0, false); //数据和主图K线拟合 + else if (JSCommonData.ChartData.IsMinutePeriod(this.Period,true)) + aryFittingData=kdata.GetMinuteFittingTradeData(recvData, 0, false); //数据和主图K线拟合 + else + return; + + var bindData=new JSCommonData.ChartData(); + bindData.Data=aryFittingData; + var result=bindData.GetValue(); + + this.StockData.set(key,{ Data:result }); + } + } + + this.AddStockValueError=function(key, message) + { + this.StockData.set(key,{ Error:message }); + } + + this.GetStockCacheData=function(obj) + { + var key; + if (obj.FunctionName) + key=this.GetStockDataKey({FunctionName:obj.FunctionName}, obj.Args); + else if (obj.VariantName) + key=obj.VariantName; + else if (obj.CustomName) + key=obj.CustomName; //自定义名字 + else + return null; + + if (!this.StockData.has(key)) return null; + var data=this.StockData.get(key); + + if (data.Error) this.Execute.ThrowUnexpectedNode(obj.Node, data.Error); + return data.Data; + } + + this.IsInBlock=function(blockName, node) + { + var data=this.GetStockCacheData({ VariantName:"INBLOCK", Node:node }); + if (!data) return 0; + var aryBlock=data.split('|'); + for(var i=0; i up) item.Up = list[i][up]; + if (list[i].length > down) item.Down = list[i][down]; + if (list[i].length > stop) item.Stop = list[i][stop]; + if (list[i].length > unchanged) item.Unchanged = list[i][unchanged]; + + aryDayData.push(item); + } + + return aryDayData; + } + + this.JsonDataToMinuteHistoryData=function(data) + { + var list = data.data; + var aryDayData=new Array(); + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8; + for (var i = 0; i < list.length; ++i) + { + let item = new JSCommonData.HistoryData(); + + item.Date = list[i][date]; + item.Open = list[i][open]; + item.YClose = list[i][yclose]; + item.Close = list[i][close]; + item.High = list[i][high]; + item.Low = list[i][low]; + item.Vol = list[i][vol]; //原始单位股 + item.Amount = list[i][amount]; + item.Time=list[i][time]; + + // if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除 + aryDayData.push(item); + } + + // 无效数据处理 + for(let i = 0; i < aryDayData.length; ++i) + { + var minData = aryDayData[i]; + if (minData == null) coninue; + if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0 + || isNaN(minData.Close) || minData.Close <= 0 || isNaN(minData.YClose) || minData.YClose <= 0) + { + if (i == 0) + { + if (minData.YClose > 0) + { + minData.Open = minData.YClose; + minData.High = minData.YClose; + minData.Low = minData.YClose; + minData.Close = minData.YClose; + } + } + else // 用前一个有效数据填充 + { + for(let j = i-1; j >= 0; --j) + { + var minData2 = aryDayData[j]; + if (minData2 == null) coninue; + if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0) + { + if (minData.YClose <= 0) minData.YClose = minData2.Close; + minData.Open = minData2.Open; + minData.High = minData2.High; + minData.Low = minData2.Low; + minData.Close = minData2.Close; + break; + } + } + } + } + } + return aryDayData; + } + + //API 返回数据 转化为array[] + this.JsonDataToMinuteData = function (data) + { + var aryMinuteData = new Array(); + for (var i in data.stock[0].minute) + { + var jsData = data.stock[0].minute[i]; + var item = new JSCommonData.MinuteData(); + + item.Close = jsData.price; + item.Open = jsData.open; + item.High = jsData.high; + item.Low = jsData.low; + item.Vol = jsData.vol; //股 + item.Amount = jsData.amount; + if (i == 0) //第1个数据 写死9:25 + item.DateTime = data.stock[0].date.toString() + " 0925"; + else + item.DateTime = data.stock[0].date.toString() + " " + jsData.time.toString(); + item.Date = data.stock[0].date; + item.Time = jsData.time; + item.Increate = jsData.increate; + item.Risefall = jsData.risefall; + item.AvPrice = jsData.avprice; + + aryMinuteData[i] = item; + } + + return aryMinuteData; + } + + //CODELIKE 模糊股票代码 + this.CODELIKE=function(value) + { + if (this.Symbol.indexOf(value)==0) return 1; + + return 0; + } + + this.NAMELIKE = function (value) + { + if (this.Name && this.Name.indexOf(value) == 0) return 1; + return 0; + } + + /* + SETCODE 市场类型 + 0:深圳 1:上海,47:中金所期货 28:郑州商品 29:大连商品 30:上海商品,27:香港指数 31:香港主板,48:香港创业板... + */ + this.SETCODE=function() + { + if (this.Symbol.indexOf('.sh')) return 1; + if (this.Symbol.indexOf('.sz')) return 0; + + return 0; + } + + this.GetSymbol = function () { return this.Symbol; } + + this.GetName = function () { return this.Name; } + + this.TIME=function() + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + if (this.IsNumber(item.Time)) + result[i]=item.Time; + else + result[i]=0; + } + + return result; + } + + this.DATE = function () + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for (let i in this.Data.Data) + { + var item = this.Data.Data[i]; + result[i] = item.Date - 19000000;; + } + + return result; + } + + this.YEAR = function () + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for (let i in this.Data.Data) + { + var item = this.Data.Data[i]; + if (this.IsNumber(item.Date)) + result[i] = parseInt(item.Date / 10000); + else + result[i] = null; + } + + return result; + } + + this.MONTH = function () + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for (let i in this.Data.Data) + { + var item = this.Data.Data[i]; + if (this.IsNumber(item.Date)) + result[i] = parseInt(item.Date % 10000 / 100); + else + result[i] = null; + } + + return result; + } + + //星期 1-7 + this.WEEK = function () + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + var tempDate = new Date(); + for (let i in this.Data.Data) + { + var item = this.Data.Data[i]; + result[i] = null; + if (!this.IsNumber(item.Date)) continue; + + var year = parseInt(item.Date / 10000); + var month = parseInt(item.Date % 10000 / 100); + var day = item.Date % 100; + + tempDate.setFullYear(year); + tempDate.setMonth(month - 1); + tempDate.setDate(day); + + result[i] = tempDate.getDay(); + } + + return result; + } + + this.REFDATE = function (data, date) + { + var result = null; + + var findDate=null; + if (Array.isArray(date)) + { + if (date.length>0) findDate=date[date.length-1]; + } + else if (this.IsNumber(date)) + { + findDate=date; + } + if (findDate==null) return null; + if (findDate<5000000) findDate+=19000000; + + var index = null; + for (let i in this.Data.Data) //查找日期对应的索引 + { + if (this.Data.Data[i].Date == findDate) + { + index = parseInt(i); + break; + } + } + + if (index == null || index >= data.length) return null; + + return data[index]; + } + + //用法:结果从0到11,依次分别是1/5/15/30/60分钟,日/周/月,多分钟,多日,季,年 + this.PERIOD=function() + { + //Period周期 0=日线 1=周线 2=月线 3=年线 9=季线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 + const PERIOD_MAP=[5,6,7,11, 0,1,2,3,4,5, 9]; + if (this.Period >= 0 && this.Period <= PERIOD_MAP.length - 1) + return PERIOD_MAP[this.Period]; + + return this.Period; + } + + this.GetDrawNull = function () + { + var result = []; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for (let i in this.Data.Data) + { + result[i] = null; + } + + return result; + } +} + +//是否有是有效的数字 +JSSymbolData.prototype.IsNumber = function (value) +{ + if (value == null) return false; + if (isNaN(value)) return false; + + return true; +} + +JSSymbolData.prototype.IsDivideNumber = function (value) +{ + if (value == null) return false; + if (isNaN(value)) return false; + if (value == 0) return false; + + return true; +} + +JSSymbolData.prototype.JsonDataToFinance = function (data) + { + var financeData; + + for (let i = 1; i <= 4; ++i) + { + switch (i) + { + case 1: + var finance = data.finance1; + var announcement = data.announcement1; + break; + case 2: + var finance = data.finance2; + var announcement = data.announcement2; + break; + case 3: + var finance = data.finance3; + var announcement = data.announcement3; + break; + case 4: + var finance = data.finance4; + var announcement = data.announcement4; + break; + default: + break; + } + + if (!finance || !announcement || !this.IsNumber(announcement.year) || !this.IsNumber(announcement.quarter)) continue; + if (financeData) //如果存在1天公布多个报告期数据 只取最新的一个公告期数据 + { + if (financeData.Announcement.year < announcement.year) + financeData = { Date: item.date, Finance: finance, Announcement: announcement }; + } + else + { + financeData = { Date: data.date, Finance: finance, Announcement: announcement }; + } + + } + + return financeData; +} + +var JS_EXECUTE_DEBUG_LOG=false; + +var JS_EXECUTE_JOB_ID= +{ + JOB_DOWNLOAD_SYMBOL_DATA:1, //下载股票的K线数据 + JOB_DOWNLOAD_INDEX_DATA:2, //下载大盘的K线数据 + JOB_DOWNLOAD_SYMBOL_LATEST_DATA:3, //最新的股票行情数据 + JOB_DOWNLOAD_INDEX_INCREASE_DATA: 4, //涨跌股票个数统计数据 + JOB_DOWNLOAD_VOLR_DATA: 5, //5日量比均量下载量比数据 + + JOB_DOWNLOAD_FINVALUE:301, //引用专业财务数据 FINVALUE(ID),ID为数据编号 + JOB_DOWNLOAD_FINONE:302, //引用指定年和月日的某类型的财务数据 FINONE(ID,Y,MMDD),ID为数据编号,Y和MMDD表示年和月日. + JOB_DOWNLOAD_FINANCE:303, //FINANCE(ID) 基础财务数据 + JOB_DOWNLOAD_GPJYVALUE:304, //引用股票交易类数据 GPJYVALUE(ID,N,TYPE),ID为数据编号,N表示第几个数据,TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理 + JOB_DOWNLOAD_VARIANT:305, //CAPITAL , TOTALCAPITAL, EXCHANGE + + JOB_CUSTOM_FUNCTION_DATA:6000, //自定义函数 + JOB_CUSTOM_VARIANT_DATA:6001, //自定义变量 + + JOB_DOWNLOAD_MARGIN_BALANCE: 1000, //融资融券余额 + JOB_DOWNLOAD_MARGIN_RATE: 1001, //融资占比 + + JOB_DOWNLOAD_MARGIN_BUY_BALANCE: 1010, //买入信息-融资余额 + JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: 1011, //买入信息-买入额 + JOB_DOWNLOAD_MARGIN_BUY_REPAY: 1012, //买入信息-偿还额 + JOB_DOWNLOAD_MARGIN_BUY_NET: 1013, //买入信息-融资净买入 + + JOB_DOWNLOAD_MARGIN_SELL_BALANCE: 1020, //卖出信息-融券余量 + JOB_DOWNLOAD_MARGIN_SELL_VOLUME: 1021, //卖出信息-卖出量 + JOB_DOWNLOAD_MARGIN_SELL_REPAY: 1022, //卖出信息-偿还量 + JOB_DOWNLOAD_MARGIN_SELL_NET: 1023, //卖出信息-融券净卖出 + + JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: 2000, //负面新闻统计 + JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: 2001, //机构调研 + JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: 2002, //互动易 + JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: 2003, //股东增持 + JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: 2004, //股东减持 + JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: 2005, //信托持股 + JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: 2006, //大宗交易 + JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: 2007, //官网新闻 + JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: 2008, //高管要闻 + JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: 2009, //股权质押 + + JOB_CUSTOM_FUNCTION_DATA: 6000, //自定义函数 + JOB_CUSTOM_VARIANT_DATA: 6001, //自定义变量 + + JOB_DOWNLOAD_CUSTOM_API_DATA: 30000, //自定义数据 + + JOB_RUN_SCRIPT:10000, //执行脚本 + + //融资融券 + GetMarginJobID: function (value) + { + let dataMap = new Map([ + [1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE], //MARGIN(1) 融资融券余额 + [2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE], //MARGIN(2) 融资占比 + + [3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE], //MARGIN(3) 买入信息-融资余额 + [4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT], //MARGIN(4) 买入信息-买入额 + [5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY], //MARGIN(5) 买入信息-偿还额 + [6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET], //MARGIN(6) 买入信息-融资净买入 + + [7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE], //MARGIN(7) 卖出信息-融券余量 + [8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME], //MARGIN(8) 卖出信息-卖出量 + [9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY], //MARGIN(9) 卖出信息-偿还量 + [10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET], //MARGIN(10) 卖出信息-融券净卖出 + ]); + + if (dataMap.has(value)) return dataMap.get(value); + + return null; + }, + + GetNewsAnalysisID: function (value) + { + let dataMap = new Map([ + [1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE], //NEWS(1) 负面新闻统计 + [2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH], //NEWS(2) 机构调研统计 + [3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT], //NEWS(3) 互动易 + [4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE], //NEWS(4) 股东增持 + [5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2], //NEWS(5) 股东减持 + [6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER], //NEWS(6) 信托持股 + [7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING], //NEWS(7) 大宗交易 + [8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS], //NEWS(8) 官网新闻 + [9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS], //NEWS(9) 高管要闻 + [10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE], //NEWS(10) 股权质押 + ]); + + if (dataMap.has(value)) return dataMap.get(value); + + return null; + } + +}; + +function JSExecute(ast,option) +{ + this.AST=ast; //语法树 + + this.ErrorHandler=new ErrorHandler(); + this.VarTable=new Map(); //变量表 + this.OutVarTable=new Array(); //输出变量 + this.Arguments=[]; + + //脚本自动变量表, 只读 + this.ConstVarTable=new Map([ + //个股数据 + ['CLOSE', null], ['VOL', null], ['OPEN', null], ['HIGH', null], ['LOW', null], ['AMOUNT', null], ['AMO', null], ['VOLINSTK',null], + ['C', null], ['V', null], ['O', null], ['H', null], ['L', null], ['VOLR', null], + + //日期类 + ['DATE', null], ['YEAR', null], ['MONTH', null], ['PERIOD', null], ['WEEK', null],["TIME",null], + + //大盘数据 + ['INDEXA',null],['INDEXC',null],['INDEXH',null],['INDEXL',null],['INDEXO',null],['INDEXV',null], + ['INDEXADV', null], ['INDEXDEC', null], + + ['CURRBARSCOUNT', null], //到最后交易日的周期数 + ['ISLASTBAR', null], //判断是否为最后一个周期 + + ["TOTALCAPITAL",null], //总股本 + ['CAPITAL', null], //流通股本(手) + ['EXCHANGE', null], //换手率 + ['SETCODE', null], //市场类型 + ['CODE', null], //品种代码 + ['STKNAME', null], //品种名称 + + ['HYBLOCK', null], //所属行业板块 + ['DYBLOCK', null], //所属地域板块 + ['GNBLOCK', null], //所属概念 + ["FGBLOCK",null], //所属风格板块 + ["ZSBLOCK",null], //所属指数板块 + ["ZHBLOCK",null], //所属组合板块 + ["ZDBLOCK",null], //所属自定义板块 + ["HYZSCODE",null], + + ["GNBLOCKNUM",null], //所属概念板块的个数 + ["FGBLOCKNUM",null], //所属风格板块的个数 + ["ZSBLOCKNUM",null], //所属指数板块的个数 + ["ZHBLOCKNUM",null], //所属组合板块的个数 + ["ZDBLOCKNUM",null], //所属自定义板块的个数 + + ["HYSYL",null], //指数市盈率或个股所属行业的市盈率 + ["HYSJL",null], //指数市净率或个股所属行业的市净率 + + ['DRAWNULL', null] + ]); + + this.SymbolData=new JSSymbolData(this.AST,option,this); + this.Algorithm = new JSAlgorithm(this.ErrorHandler, this.SymbolData); + this.Draw = new JSDraw(this.ErrorHandler, this.SymbolData); + this.JobList=[]; //执行的任务队列 + + this.UpdateUICallback=null; //回调 + this.CallbackParam=null; + + if (option) + { + if (option.Callback) this.UpdateUICallback=option.Callback; + if (option.CallbackParam) this.CallbackParam=option.CallbackParam; + if (option.Arguments) this.Arguments=option.Arguments; + } + + this.Execute=function() + { + JSConsole.Complier.Log('[JSExecute::Execute] JobList', this.JobList); + this.RunNextJob(); + } + + this.RunNextJob=function() + { + if (this.JobList.length<=0) return; + + var jobItem=this.JobList.shift(); + + switch (jobItem.ID) + { + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA: + return this.SymbolData.GetSymbolData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA: + return this.SymbolData.GetIndexData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA: + return this.SymbolData.GetIndexIncreaseData(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA: + return this.SymbolData.GetLatestData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA: //量比 + return this.SymbolData.GetVolRateData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE: + return this.SymbolData.GetFinOne(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE: + return this.SymbolData.GetFinValue(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE: + return this.SymbolData.GetFinance(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE: + return this.SymbolData.GetGPJYValue(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT: //CAPITAL, TOTALCAPITAL + return this.SymbolData.GetVariantData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA: + return this.SymbolData.GetCustomVariantData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA: + return this.SymbolData.GetCustomFunctionData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE: + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE: + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出 + return this.SymbolData.GetMarginData(jobItem.ID); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: //负面新闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: //机构调研 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: //互动易 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: //股东增持 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: //股东减持 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: //信托持股 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: //大宗交易 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: //官网新闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: //高管要闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: //股权质押 + return this.SymbolData.GetNewsAnalysisData(jobItem.ID); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA: + return this.SymbolData.DownloadCustomAPIData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT: + return this.Run(); + } + } + + this.ReadSymbolData=function(name,node) + { + switch(name) + { + case 'CLOSE': + case 'C': + case 'VOL': + case 'V': + case 'OPEN': + case 'O': + case 'HIGH': + case 'H': + case 'LOW': + case 'L': + case 'AMOUNT': + case 'VOLINSTK': + return this.SymbolData.GetSymbolCacheData(name); + case 'VOLR': + return this.SymbolData.GetVolRateCacheData(node); + + //大盘数据 + case 'INDEXA': + case 'INDEXC': + case 'INDEXH': + case 'INDEXO': + case 'INDEXV': + case 'INDEXL': + case 'INDEXADV': + case 'INDEXDEC': + return this.SymbolData.GetIndexCacheData(name); + + case 'CURRBARSCOUNT': + return this.SymbolData.GetCurrBarsCount(); + case 'ISLASTBAR': + return this.SymbolData.GetIsLastBar(); + case "TOTALCAPITAL": + case 'CAPITAL': + case 'EXCHANGE': + + case "HYBLOCK": + case "DYBLOCK": + case "GNBLOCK": + case "FGBLOCK": + case "ZSBLOCK": + case "ZHBLOCK": + case "ZDBLOCK": + case "HYZSCODE": + + case "GNBLOCKNUM": + case "FGBLOCKNUM": + case "ZSBLOCKNUM": + case "ZHBLOCKNUM": + case "ZDBLOCKNUM": + + case "HYSYL": + case "HYSJL": + return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node }); + case 'SETCODE': + return this.SymbolData.SETCODE(); + + case 'CODE': + return this.SymbolData.GetSymbol(); + case 'STKNAME': + return this.SymbolData.GetName(); + + case 'TIME': + return this.SymbolData.TIME(); + case 'DATE': + return this.SymbolData.DATE(); + case 'YEAR': + return this.SymbolData.YEAR(); + case 'MONTH': + return this.SymbolData.MONTH(); + case 'WEEK': + return this.SymbolData.WEEK(); + case 'PERIOD': + return this.SymbolData.PERIOD(); + + case 'DRAWNULL': + return this.SymbolData.GetDrawNull(); + } + } + + this.ReadCustomVariant=function(name,node) + { + return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node }); + } + + //读取变量 + this.ReadVariable=function(name,node) + { + if (this.ConstVarTable.has(name)) + { + let data=this.ConstVarTable.get(name); + + if (data==null) //动态加载,用到再加载 + { + data=this.ReadSymbolData(name,node); + this.ConstVarTable.set(name,data); + } + + return data; + } + + if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量 + + if (this.VarTable.has(name)) return this.VarTable.get(name); + + this.ThrowUnexpectedNode(node, '变量'+name+'不存在'); + return null; + } + + this.ReadMemberVariable = function (node) + { + var obj = node.Object; + var member = node.Property; + + let maiObj; + if (obj.Type == Syntax.BinaryExpression || obj.Type == Syntax.LogicalExpression) + maiObj = this.VisitBinaryExpression(obj); + else if (obj.Type == Syntax.CallExpression) + maiObj = this.VisitCallExpression(obj); + else + maiObj = this.GetNodeValue(obj); + + if (!maiObj) return null; + var value = maiObj[member.Name]; + if (value) return value; + + return null; + } + + //单数据转成数组 个数和历史数据一致 + this.SingleDataToArrayData = function (value) + { + let count = this.SymbolData.Data.Data.length; + let result = []; + for (let i = 0; i < count; ++i) + { + result[i] = value; + } + + return result; + } + + this.RunAST=function() + { + //预定义的变量 + for(let i in this.Arguments) + { + let item =this.Arguments[i]; + this.VarTable.set(item.Name,item.Value); + } + + if (!this.AST) this.ThrowError(); + if (!this.AST.Body) this.ThrowError(); + + for(let i in this.AST.Body) + { + let item =this.AST.Body[i]; + this.VisitNode(item); + + //输出变量 + if (item.Type==Syntax.ExpressionStatement && item.Expression) + { + if (item.Expression.Type==Syntax.AssignmentExpression && item.Expression.Operator==':' && item.Expression.Left) + { + let assignmentItem=item.Expression; + let varName=assignmentItem.Left.Name; + let outVar=this.VarTable.get(varName); + var type=0; + if (!Array.isArray(outVar)) + { + if (typeof (outVar) == 'string') + { + var floatValue=parseFloat(outVar); + if (IFrameSplitOperator.IsNumber(floatValue)) + { + outVar=this.SingleDataToArrayData(floatValue); + } + else + { + outVar=this.SingleDataToArrayData(outVar); + type=1001; + } + } + else outVar = this.SingleDataToArrayData(outVar); + } + + this.OutVarTable.push({Name:varName, Data:outVar,Type:type}); + } + else if (item.Expression.Type==Syntax.CallExpression) + { + let callItem=item.Expression; + if (this.Draw.IsDrawFunction(callItem.Callee.Name)) + { + let draw=callItem.Draw; + draw.Name=callItem.Callee.Name; + this.OutVarTable.push({Name:draw.Name, Draw:draw, Type:1}); + } + else + { + let outVar=callItem.Out; + varName=`__temp_c_${callItem.Callee.Name}_${i}__`; + var type=0; + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + this.OutVarTable.push({Name:varName, Data:outVar,Type:type,NoneName:true}); + } + } + else if (item.Expression.Type==Syntax.Identifier) + { + let varName=item.Expression.Name; + let outVar=this.ReadVariable(varName,item.Expression); + var type=0; + if (!Array.isArray(outVar)) + { + if (typeof(outVar)=='string') outVar=this.SingleDataToArrayData(parseFloat(outVar)); + else outVar=this.SingleDataToArrayData(outVar); + } + + varName="__temp_i_"+i+"__"; + this.OutVarTable.push({Name:varName, Data:outVar, Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.BinaryExpression) + { + var varName="__temp_b_"+i+"__"; + let outVar=item.Expression.Out; + var type=0; + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.LogicalExpression) //逻辑语句 如 T1 AND T2 + { + var varName="__temp_l_"+i+"__"; + let outVar=item.Expression.Out; + var type=0; + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.SequenceExpression) + { + let varName; + let draw; + let color; + let lineWidth; + let colorStick=false; + let pointDot=false; + let circleDot=false; + let lineStick=false; + let stick=false; + let volStick=false; + let isShow = true; + let isExData = false; + let isDotLine = false; + let isOverlayLine = false; //叠加线 + let isNoneName=false; + var isShowTitle=true; + //显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如: + //DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE; + var isDrawAbove=false; + for(let j in item.Expression.Expression) + { + let itemExpression=item.Expression.Expression[j]; + if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left) + { + varName = itemExpression.Left.Name; + let varValue = this.VarTable.get(varName); + if (!Array.isArray(varValue)) + { + varValue = this.SingleDataToArrayData(varValue); + this.VarTable.set(varName, varValue); //把常量放到变量表里 + } + } + else if (itemExpression.Type==Syntax.Identifier) + { + let value=itemExpression.Name; + if (value==='COLORSTICK') colorStick=true; + else if (value==='POINTDOT') pointDot=true; + else if (value==='CIRCLEDOT') circleDot=true; + else if (value == 'DOTLINE') isDotLine = true; + else if (value==='LINESTICK') lineStick=true; + else if (value==='STICK') stick=true; + else if (value==='VOLSTICK') volStick=true; + else if (value==="DRAWABOVE") isDrawAbove=true; + else if (value.indexOf('COLOR')==0) color=value; + else if (value.indexOf('LINETHICK')==0) lineWidth=value; + else if (value.indexOf('NODRAW') == 0) isShow = false; + else if (value.indexOf('EXDATA') == 0) isExData = true; //扩展数据, 不显示再图形里面 + else if (value.indexOf('LINEOVERLAY') == 0) isOverlayLine = true; + else if (value.indexOf("NOTEXT")==0 || value.indexOf("NOTITLE")==0) isShowTitle=false; //标题不显示 + else + { + varName=itemExpression.Name; + let varValue=this.ReadVariable(varName,itemExpression); + if (!Array.isArray(varValue)) varValue=this.SingleDataToArrayData(varValue); + varName="__temp_si_"+i+"__"; + isNoneName=true; + this.VarTable.set(varName,varValue); //放到变量表里 + } + } + else if (itemExpression.Type == Syntax.Literal) //常量 + { + let aryValue = this.SingleDataToArrayData(itemExpression.Value); + varName = itemExpression.Value.toString(); + this.VarTable.set(varName, aryValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.CallExpression) + { + if (this.Draw.IsDrawFunction(itemExpression.Callee.Name)) + { + draw=itemExpression.Draw; + draw.Name=itemExpression.Callee.Name; + } + else + { + let varValue=itemExpression.Out; + varName=`__temp_sc_${itemExpression.Callee.Name}_${i}__`; + isNoneName=true; + this.VarTable.set(varName,varValue); + } + } + else if (itemExpression.Type==Syntax.BinaryExpression) + { + varName="__temp_sb_"+i+"__"; + let aryValue=itemExpression.Out; + isNoneName=true; + this.VarTable.set(varName,aryValue); + } + } + + if (pointDot && varName) //圆点 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Radius:g_JSChartResource.POINTDOT.Radius, Type:3}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth = lineWidth; + this.OutVarTable.push(value); + } + else if (circleDot && varName) //圆点 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Radius:g_JSChartResource.CIRCLEDOT.Radius, Type:3}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth = lineWidth; + this.OutVarTable.push(value); + } + else if (lineStick && varName) //LINESTICK 同时画出柱状线和指标线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:4}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (stick && varName) //STICK 画柱状线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:5}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (volStick && varName) //VOLSTICK 画彩色柱状线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:6}; + if (color) value.Color=color; + this.OutVarTable.push(value); + } + else if (colorStick && varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Color:color, Type:2}; + if (lineWidth) value.LineWidth=lineWidth; + if (color) value.Color=color; + this.OutVarTable.push(value); + } + else if (varName && color) + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Color:color, Type:0}; + if (lineWidth) value.LineWidth=lineWidth; + if (isShow == false) value.IsShow = false; + if (isExData == true) value.IsExData = true; + if (isDotLine == true) value.IsDotLine = true; + if (isOverlayLine == true) value.IsOverlayLine = true; + if (isNoneName==true) value.NoneName=true; + if (isShowTitle==false) value.IsShowTitle=false; + this.OutVarTable.push(value); + } + else if (draw) + { + var outVar = { Name: draw.Name, Draw: draw, Type: 1 }; + if (color) outVar.Color = color; + if (lineWidth) outVar.LineWidth = lineWidth; + if (isDrawAbove) outVar.IsDrawAbove=true; + this.OutVarTable.push(outVar); + } + else if (varName) + { + let outVar = this.VarTable.get(varName); + let value = { Name: varName, Data: outVar, Type: 0 }; + if (color) value.Color = color; + if (lineWidth) value.LineWidth = lineWidth; + if (isShow == false) value.IsShow = false; + if (isExData == true) value.IsExData = true; + if (isDotLine == true) value.IsDotLine = true; + if (isOverlayLine == true) value.IsOverlayLine = true; + if (isShowTitle==false) value.IsShowTitle=false; + this.OutVarTable.push(value); + } + } + } + } + + JSConsole.Complier.Log('[JSExecute::Run]', this.VarTable); + + return this.OutVarTable; + } + + this.Run=function() + { + let data=this.RunAST();//执行脚本 + JSConsole.Complier.Log('[JSComplier.Run] execute finish', data); + + if (this.UpdateUICallback) + { + JSConsole.Complier.Log('[JSComplier.Run] invoke UpdateUICallback.'); + this.UpdateUICallback(data,this.CallbackParam); + } + } + + this.VisitNode=function(node) + { + switch(node.Type) + { + case Syntax.SequenceExpression: + this.VisitSequenceExpression(node); + break; + case Syntax.ExpressionStatement: + this.VisitNode(node.Expression); + break; + case Syntax.AssignmentExpression: + this.VisitAssignmentExpression(node); + break; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + this.VisitBinaryExpression(node); + break; + case Syntax.CallExpression: + this.VisitCallExpression(node); + break; + } + } + + this.VisitSequenceExpression=function(node) + { + for(let i in node.Expression) + { + let item =node.Expression[i]; + this.VisitNode(item); + } + } + + //函数调用 + this.VisitCallExpression=function(node) + { + let funcName=node.Callee.Name; + let args=[]; + for(let i in node.Arguments) + { + let item=node.Arguments[i]; + let value; + if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(item); + else if (item.Type==Syntax.CallExpression) + value=this.VisitCallExpression(item); + else + value=this.GetNodeValue(item); + args.push(value); + } + + //if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitCallExpression]' , funcName, '(', args.toString() ,')'); + if (g_JSComplierResource.IsCustomFunction(funcName)) //自定义函数 + { + var data=this.Algorithm.CallCustomFunction(funcName, args, this.SymbolData, node); + node.Out=[]; + node.Draw=null; + + if (data) + { + if (data.Out) node.Out=data.Out; + if (data.Draw) node.Draw=data.Draw; + } + + return node.Out; + } + + switch(funcName) + { + case 'DYNAINFO': //行情最新数据 + node.Out=this.SymbolData.GetLatestCacheData(args[0]); + break; + case 'STICKLINE': + node.Draw=this.Draw.STICKLINE(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'DRAWTEXT': + node.Draw=this.Draw.DRAWTEXT(args[0],args[1],args[2]); + node.Out=[]; + break; + case 'SUPERDRAWTEXT': + node.Draw = this.Draw.SUPERDRAWTEXT(args[0], args[1], args[2], args[3], args[4]); + node.Out = []; + break; + case 'DRAWTEXT_FIX': + node.Draw=this.Draw.DRAWTEXT_FIX(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'DRAWICON': + node.Draw = this.Draw.DRAWICON(args[0], args[1], args[2]); + node.Out = []; + break; + case 'DRAWLINE': + node.Draw=this.Draw.DRAWLINE(args[0],args[1],args[2],args[3],args[4]); + node.Out=node.Draw.DrawData; + break; + case 'DRAWBAND': + node.Draw=this.Draw.DRAWBAND(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'DRAWKLINE': + node.Draw = this.Draw.DRAWKLINE(args[0], args[1], args[2], args[3]); + node.Out = []; + break; + case 'DRAWKLINE_IF': + node.Draw = this.Draw.DRAWKLINE_IF(args[0], args[1], args[2], args[3], args[4]); + node.Out = []; + break; + case 'PLOYLINE': + case 'POLYLINE': + node.Draw = this.Draw.POLYLINE(args[0], args[1]); + node.Out = node.Draw.DrawData; + break; + case 'DRAWNUMBER': + node.Draw = this.Draw.DRAWNUMBER(args[0], args[1], args[2]); + node.Out = node.Draw.DrawData.Value; + break; + case 'RGB': + node.Out = this.Draw.RGB(args[0], args[1], args[2]); + break; + case 'RGBA': + node.Out = this.Draw.RGBA(args[0], args[1], args[2],args[3]); + break; + case 'DRAWRECTREL': + node.Draw = this.Draw.DRAWRECTREL(args[0], args[1], args[2], args[3], args[4]); + node.Out = []; + break; + case 'DRAWGBK': + node.Draw=this.Draw.DRAWGBK(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'DRAWGBK2': + node.Draw=this.Draw.DRAWGBK2(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'CODELIKE': + node.Out=this.SymbolData.CODELIKE(args[0]); + break; + case 'NAMELIKE': + node.Out = this.SymbolData.NAMELIKE(args[1]); + break; + case 'REFDATE': + node.Out = this.SymbolData.REFDATE(args[0], args[1]); + break; + case 'FINANCE': + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } ); + break; + case "FINVALUE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } ); + break; + case "FINONE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); + break; + case "GPJYVALUE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); + break; + case "MARGIN": + node.Out = this.SymbolData.GetMarginCacheData(args[0], node); + break; + case "NEWS": + node.Out = this.SymbolData.GetNewsAnalysisCacheData(args[0], node); + break; + case 'UPCOUNT': + case 'DOWNCOUNT': + node.Out = this.SymbolData.GetIndexIncreaseCacheData(funcName, args[0], node); + break; + case 'LOADAPIDATA': + node.Out = this.SymbolData.GetCustomApiData(args); + break; + case "INBLOCK": + node.Out=this.SymbolData.IsInBlock(args[0],node); + break; + default: + node.Out=this.Algorithm.CallFunction(funcName, args,node); + break; + } + + return node.Out; + } + + //赋值 + this.VisitAssignmentExpression=function(node) + { + let left=node.Left; + if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node); + + let varName=left.Name; + + let right=node.Right; + let value=null; + if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(right); + else if (right.Type==Syntax.CallExpression) + value=this.VisitCallExpression(right); + else if (right.Type==Syntax.Literal) + value=right.Value; + else if (right.Type==Syntax.Identifier) //右值是变量 + value=this.ReadVariable(right.Name,right); + else if (right.Type == Syntax.MemberExpression) + value = this.ReadMemberVariable(right); + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitAssignmentExpression]' , varName, ' = ',value); + this.VarTable.set(varName,value); + } + + //逻辑运算 + this.VisitBinaryExpression=function(node) + { + let stack=[]; + stack.push(node); + let temp=null; + + while(stack.length!=0) + { + temp=stack[stack.length-1]; + if (temp.Left && node!=temp.Left && node!=temp.Right) + { + stack.push(temp.Left); + } + else if (temp.Right && node!=temp.Right) + { + stack.push(temp.Right); + } + else + { + let value=stack.pop(); + if (value.Type==Syntax.BinaryExpression) //只遍历操作符就可以 + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '-': + value.Out=this.Algorithm.Subtract(leftValue,rightValue); + break; + case '*': + value.Out=this.Algorithm.Multiply(leftValue,rightValue); + break; + case '/': + value.Out=this.Algorithm.Divide(leftValue,rightValue) + break; + case '+': + value.Out=this.Algorithm.Add(leftValue,rightValue); + break; + case '>': + value.Out=this.Algorithm.GT(leftValue,rightValue); + break; + case '>=': + value.Out=this.Algorithm.GTE(leftValue,rightValue); + break; + case '<': + value.Out=this.Algorithm.LT(leftValue,rightValue); + break; + case '<=': + value.Out=this.Algorithm.LTE(leftValue,rightValue); + break; + case '==': + case '=': + value.Out=this.Algorithm.EQ(leftValue,rightValue); + break; + case '!=': + case '<>': + value.Out = this.Algorithm.NEQ(leftValue, rightValue); + break; + } + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value); + } + else if (value.Type==Syntax.LogicalExpression) + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '&&': + case 'AND': + value.Out=this.Algorithm.And(leftValue,rightValue); + break; + case '||': + case 'OR': + value.Out=this.Algorithm.Or(leftValue,rightValue); + break; + } + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value); + } + + node=temp; + } + } + + return node.Out; + + } + + this.GetNodeValue=function(node) + { + switch(node.Type) + { + case Syntax.Literal: //数字 + return node.Value; + case Syntax.UnaryExpression: + if (node.Operator=='-') + { + let value=this.GetNodeValue(node.Argument); + return this.Algorithm.Subtract(0,value); + } + return node.Argument.Value; + case Syntax.Identifier: + let value=this.ReadVariable(node.Name,node); + return value; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + return node.Out; + case Syntax.CallExpression: + return this.VisitCallExpression(node); + default: + this.ThrowUnexpectedNode(node); + } + } + + this.ThrowUnexpectedNode=function(node,message) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg); + + } + + this.ThrowError=function() + { + + } +} + +//对外导出类 +function JSComplier() +{ + +} + + +//词法分析 +JSComplier.Tokenize=function(code) +{ + JSConsole.Complier.Log('[JSComplier.Tokenize]', code); + let tokenizer=new Tokenizer(code); + let tokens=[]; + try + { + while(true) + { + let token=tokenizer.GetNextToken(); + if (!token) break; + + tokens.push(token); + } + } + catch(e) + { + + } + + return tokens; +} + +//语法解析 生成抽象语法树(Abstract Syntax Tree) +JSComplier.Parse=function(code) +{ + JSConsole.Complier.Log('[JSComplier.Parse]',code); + + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + let ast=program; + return ast; +} + +/* + 执行 + option.Symbol=股票代码 + option.Name=股票名称 + option.Data=这个股票的ChartData + option.Right=复权 + option.MaxReqeustDataCount=请求数据的最大个数 +*/ + +function timeout(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + +JSComplier.Execute=function(code,option,errorCallback) +{ + //异步调用 + var asyncExecute= function() + { + try + { + JSConsole.Complier.Log('[JSComplier.Execute] code ',code); + + JSConsole.Complier.Log('[JSComplier.Execute] parser .....'); + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + + let ast=program; + JSConsole.Complier.Log('[JSComplier.Execute] parser finish.', ast); + + JSConsole.Complier.Log('[JSComplier.Execute] execute .....'); + let execute=new JSExecute(ast,option); + execute.JobList=parser.Node.GetDataJobList(); + execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT}); + + let result=execute.Execute(); + + }catch(error) + { + JSConsole.Complier.Log(error); + + if (errorCallback) errorCallback(error); + } + } + + asyncExecute(); + + JSConsole.Complier.Log('[JSComplier.Execute] async execute.'); +} + +JSComplier.SetDomain = function (domain, cacheDomain) +{ + if (domain) g_JSComplierResource.Domain = domain; + if (cacheDomain) g_JSComplierResource.CacheDomain = cacheDomain; +} + +JSComplier.AddFunction=function(obj) //添加函数 { Name:函数名, Description:描述信息, IsDownload:是否需要下载数据, Invoke:函数执行(可选) } +{ + if (!obj || !obj.Name) return; + + var ID=obj.Name.toUpperCase(); + g_JSComplierResource.CustomFunction.Data.set(ID, obj); +} + +JSComplier.AddVariant=function(obj) //{ Name:变量名, Description:描述信息 } +{ + if (!obj || !obj.Name) return; + + var ID=obj.Name.toUpperCase(); + g_JSComplierResource.CustomVariant.Data.set(ID, obj); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +//数据下载 +function DownloadFinanceData(obj) +{ + this.Url=obj.Url; + this.RealtimeUrl=obj.RealtimeUrl; + this.Job=obj.Job; + this.Symbol=obj.Symbol; + this.Args=obj.Args; + this.DataKey=obj.DataKey; + this.RecvCallback=obj.Callback; + this.ErrorCallback=obj.ErrorCallback; + + this.Download=function() + { + var id=this.Args[0]; + switch(id) + { + case 1: //FINANCE(1) 总股本(随时间可能有变化) 股 + case 7: //FINANCE(7) 流通股本(随时间可能有变化) 股 + case "EXCHANGE": //换手率 + this.DownloadHistoryData(id); + break; + + case 9: //FINANCE(9) 资产负债率 + case 18: //FINANCE(18) 每股公积金 + case 30: //FINANCE(30) 净利润 + case 32: //FINANCE(32) 每股未分配利润 + case 33: //FINANCE(33) 每股收益(折算为全年收益),对于沪深品种有效 + case 34: //FINANCE(34) 每股净资产 + case 38: //FINANCE(38) 每股收益(最近一期季报) + case 40: //FINANCE(40) 流通市值 + case 41: //FINANCE(41) 总市值 + case 42: //FINANCE(42) 上市的天数 + case 43: //FINANCE(43) 利润同比 + + case "CAPITAL": + case "TOTALCAPITAL": + + //定制 + case 100: //股东人数 + this.DownloadRealtimeData(id); + break; + + default: + this.DownloadRealtimeData(id); + break; + } + } + + //最新一期数据 + this.DownloadRealtimeData=function(id) + { + var self=this; + var fieldList=this.GetFieldList(); + if (!fieldList) + { + if (this.Job.FunctionName2) message=`${this.Job.FunctionName2} can't support.`; + else if (this.Job.FunctionName) message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`; + else message=`${this.Args[0]} can't support.`; + this.ErrorCallback(message); + self.RecvCallback(null, self.Job, self.DataKey); + return; + } + + //请求数据 + wx.request({ + url: this.RealtimeUrl, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "condition":[ ] , + "start": 0, + "end": 10 + }, + method: 'POST', + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.RealtimeDataToHQChartData(recvData.data); + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + } + + //历史数据 + this.DownloadHistoryData=function(id) + { + var self=this; + var fieldList=this.GetFieldList(); + if (!fieldList) + { + message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`; + this.ErrorCallback(message); + self.RecvCallback(null, self.Job, self.DataKey); + return; + } + + //请求数据 + wx.request({ + url: this.Url, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "condition":[ ] , + "start": 0, + "end": 200 + }, + method: 'POST', + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.ToHQChartData(recvData.data); + if (data) //排序 + data.sort(function (a, b) { return (a.Date - b.Date) }); + + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + } + + this.GetFieldList=function() + { + var id=this.Args[0]; + switch(id) + { + case 1: + return ["capital.total", "capital.date"]; + case 7: + return ["capital.a", "capital.date"]; + case "EXCHANGE": + return ["capital.a", "capital.date"]; + + case 9: + return ["finance.peruprofit","symbol","date"]; + case 18: + return ["finance.percreserve","symbol","date"]; + case 30: + return ["finance.nprofit","symbol","date"]; + case 32: + return ["finance.peruprofit","symbol","date"]; + case 33: + return ["finance.persearning","symbol","date"]; + case 34: + return ["finance.pernetasset","symbol","date"]; + case 38: + return ["finance.persearning","symbol","date"]; + case 40: + return ["capital.a", "capital.date","symbol","date", "price"]; + case 41: + return ["capital.total", "capital.date","symbol","date","price"]; + case "CAPITAL": + return ["capital.a", "capital.date","symbol","date"]; + case "TOTALCAPITAL": + return ["capital.total", "capital.date","symbol","date"]; + case 42: + return ["company.releasedate","symbol","date"]; + case 43: + return ["dividendyield","symbol","date"]; + case 100: + return ["shareholder","symbol","date"] + default: + return null; + } + } + + //最新报告期数据 + this.RealtimeDataToHQChartData=function(recvData,id) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + var stock=recvData.stock[0]; + var id=this.Args[0]; + var date=stock.date; + switch(id) + { + case 9: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.peruprofit }; + case 18: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.percreserve }; + case 30: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.nprofit }; + case 32: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.peruprofit }; + case 33: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.persearning }; + case 34: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.pernetasset }; + case 38: + if (!stock.finance) return null; + return { Date:date, Value:stock.finance.persearning }; + case 40: //FINANCE(40) 流通市值 + if (!stock.capital) return null; + return { Date:date, Value:stock.capital.a*stock.price }; //流通股*最新价格 + case 41: //FINANCE(41) 总市值 + if (!stock.capital) return null; + return { Date:date, Value:stock.capital.total*stock.price }; //总股本*最新价格 + case 42: //FINANCE(42) 上市的天数 + if (!stock.company) return null; + { + var releaseDate=stock.company.releasedate; + var year=parseInt(releaseDate/10000); + var month=parseInt((releaseDate%10000)/100); + var day=releaseDate%100; + var firstDate=new Date(year, month-1, day); + var nowDate=new Date(); + var days=parseInt((nowDate.getTime()-firstDate.getTime())/(1000 * 60 * 60 * 24)); + return { Date:date, Value:days+1 }; + } + case 43: + if (!stock.dividendyield) return null; + return { Date:date, Value:stock.dividendyield.quarter4 }; + case 100: + if (!stock.shareholder) return null; + return { Date:date, Value:stock.shareholder.count }; + case "CAPITAL": + if (!stock.capital) return null; + return { Date:date, Value:stock.capital.a/100 }; //当前流通股本 手 + case "TOTALCAPITAL": + if (!stock.capital) return null; + return { Date:date, Value:stock.capital.total/100 }; //当前流通股本 手 + } + } + + //历史数据转 + this.ToHQChartData=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + + var aryData=[]; + var setDate=new Set(); //有重复数据 去掉 + var stock=recvData.stock[0]; + var id=this.Args[0]; + for(var i in stock.stockday) + { + var item=stock.stockday[i]; + + var hqchartItem=this.ToHQChartItemData(item,id); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + return aryData; + } + + this.ToHQChartItemData=function(item, id) + { + if (!item) return null; + var date=item.date; + switch(id) + { + case 1: + if (!item.capital) return null; + return { Date:date, Value:item.capital.total }; + case 7: + case "EXCHANGE": //换手率 历史流通股本 + if (!item.capital) return null; + return { Date:date, Value:item.capital.a }; + + default: + return null; + } + } +} + + +////////////////////////////////////////////////////////////////////////////////////////////// +//内置财务数据下载 +// +function DownloadFinValueData(obj) +{ + this.Url=obj.Url; + this.Job=obj.Job; + this.Symbol=obj.Symbol; + this.Args=obj.Args; + this.DataKey=obj.DataKey; + this.RecvCallback=obj.Callback; + this.ErrorCallback=obj.ErrorCallback; + + this.Download=function() + { + var self=this; + var fieldList=this.GetFieldList(); + if (!fieldList) + { + message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`; + this.ErrorCallback(message); + self.RecvCallback(null, self.Job, self.DataKey); + return; + } + + //请求数据 + wx.request({ + url: this.Url, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "condition":[ {"item":["finance","doc","exists","true"]}] , + "start": 0, + "end": 200 + }, + method: 'POST', + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.ToHQChartData(recvData.data); + if (data) //排序 + data.sort(function (a, b) { return (a.Date - b.Date) }); + + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + } + + this.ToHQChartData=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + + var aryData=[]; + var setDate=new Set(); //有重复数据 去掉 + var stock=recvData.stock[0]; + for(var i in stock.stockday) + { + var item=stock.stockday[i]; + if (item.announcement1) + { + var hqchartItem=this.ToHQChartItemData(item.announcement1, item.finance1, item); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + if (item.announcement2) + { + var hqchartItem=this.ToHQChartItemData(item.announcement2, item.finance2, item); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + if (item.announcement3) + { + var hqchartItem=this.ToHQChartItemData(item.announcement3, item.finance3, item); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + if (item.announcement4) + { + var hqchartItem=this.ToHQChartItemData(item.announcement4, item.finance4, item); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + } + + return aryData; + } + + //{ Date:日期 , Value:数值 } + this.ToHQChartItemData=function(announcement, finance, sourceItem) + { + var id=this.Args[0]; + var date=announcement.year*10000; + var quarter=announcement.quarter; + switch(quarter) + { + case 1: + date+=331; + break; + case 2: + date+=630; + break; + case 3: + date+=930; + break; + case 4: + date+=1231; + break; + default: + return null; + } + + var result={ Date:date, Value:0 }; + switch(id) + { + case 0: + result.Value=date%1000000; //0--返回报告期(YYMMDD格式),150930表示为2015年第三季 + break; + case 1: + result.Value=finance.persearning; + break; + case 3: + result.Value=finance.peruprofit; + break; + case 4: + result.Value=finance.pernetasset; + break; + case 5: + result.Value=finance.percreserve; + break; + case 6: + result.Value=finance.woewa; + break; + case 7: + result.Value=finance.perccfo; + break; + case 8: + result.Value=finance.monetaryfunds; + break; + case 11: + result.Value=finance.areceivable; + break; + } + + return result; + } + + this.GetFieldList=function() + { + var id=this.Args[0]; + switch(id) + { + case 0: + return ["finance.date"]; + case 1: //persearning 每股收益 + return ["finance.persearning"]; + case 3: //peruprofit 每股未分配利润 + return ["finance.peruprofit"]; + case 4: //pernetasset 每股净资产 + return ["finance.pernetasset"]; + case 5: //percreserve 每股资本公积金 + return ["finance.percreserve"]; + case 6: //woewa 加权平均净资产收益 + return ["finance.woewa"]; + case 7: //perccfo 每股经营性现金流 + return ["finance.perccfo"]; + case 8: //monetaryfunds 货币资金 + return ["finance.monetaryfunds"]; + case 11: //areceivable 应收账款 + return ["finance.areceivable"]; + default: + return null; + } + } +} + + +///////////////////////////////////////////////////////// +// 内置财务数据下载 某一期的数据 +// +function DownloadFinOneData(obj) +{ + this.newMethod=DownloadFinValueData; //派生 + this.newMethod(obj); + delete this.newMethod; + + this.Download=function() + { + var self=this; + var fieldList=this.GetFieldList(); + if (!fieldList) + { + message=`${this.Job.FunctionName}(${this.Args[0]}, ${this.Args[1]}, ${this.Args[2]}) can't support.`; + this.ErrorCallback(message); + self.RecvCallback(null, self.Job, self.DataKey); + return; + } + + var aryCondition=[ {"item":["finance","doc","exists","true"] } ]; + + var year=this.Args[1]; + var month=this.Args[2]; + var dataIndex=0; + var dataEnd=3; + var preYear=null; + if (year==0 && month==0) //如果Y和MMDD都为0,表示最新的财报; + { + + } + else if (year==0 && month<300) //如果Y为0,MMDD为小于300的数字,表示最近一期向前推MMDD期的数据,如果是331,630,930,1231这些,表示最近一期的对应季报的数据; + { + dataIndex=month; + dataEnd=200; + } + else if (month==0 && year<1000) //如果Y为0,MMDD为小于300的数字,表示最近一期向前推MMDD期的数据,如果是331,630,930,1231这些,表示最近一期的对应季报的数据; + { + preYear=year; + } + else if (year>1909) + { + if (month==331) + { + aryCondition= + [ + {"item":["announcement1.year","int32","eq",year]}, + {"item":["finance1","doc","exists","true"]} + ]; + + fieldList.push("announcement1.year"); + fieldList.push("announcement1.quarter"); + } + else if (month==630) + { + aryCondition= + [ + {"item":["announcement2.year","int32","eq",year]}, + {"item":["finance2","doc","exists","true"]} + ]; + + fieldList.push("announcement2.year"); + fieldList.push("announcement2.quarter"); + } + else if (month==930) + { + aryCondition= + [ + {"item":["announcement3.year","int32","eq",year]}, + {"item":["finance3","doc","exists","true"]} + ]; + + fieldList.push("announcement4.year"); + fieldList.push("announcement4.quarter"); + } + else + { + aryCondition= + [ + {"item":["announcement4.year","int32","eq",year]}, + {"item":["finance4","doc","exists","true"]} + ]; + + fieldList.push("announcement4.year"); + fieldList.push("announcement4.quarter"); + } + } + + //请求数据 + wx.request({ + url: this.Url, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "condition":aryCondition, + "start": 0, + "end": dataEnd + }, + method: 'POST', + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.ToHQChartData(recvData.data); + var result=null; + if (data && data.length>0) + { + data.sort(function (a, b) { return (b.Date-a.Date) }); + if (preYear==null) + result=data[dataIndex]; //返回一个数据 + else + result=self.GetPreYearData(data, preYear); + } + self.RecvCallback(result, self.Job, self.DataKey); + } + }); + } + + this.GetPreYearData=function(data, preYear) + { + //331,630,930,1231这些,表示最近一期的对应季报的数据; + if (preYear==331 || preYear==630|| preYear==930 || preYear==1231) + { + for(var i in data) + { + var item=data[i]; + if (item.Date%10000==preYear) return item; + } + } + else + { + //如果MMDD为0,Y为一数字,表示最近一期向前推Y年的同期数据; + var month=data[0].Date%1000; + for(var i=1, j=0; i0) + { + data.sort(function (a, b) { return (a.Date-b.Date) }); + } + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + } + + this.GetFieldList=function() + { + var id=this.Args[0]; + switch(id) + { + case 1: //1--股东人数 股东户数(户) + return ["shareholder", "date", "symbol"]; + case 2: //2--龙虎榜 买入总计(万元) 卖出总计(万元) + return ["tradedetail.buy","tradedetail.sell", "date", "symbol"]; + case 3: //3--融资融券1 融资余额(万元) 融券余量(股) + return ["margin","date", "symbol"]; + case 4: //4--大宗交易 成交均价(元) 成交额(万元) + return ["blocktrading.amount","blocktrading.price","date", "symbol"] + default: + return null; + } + } + + this.ToHQChartData=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + + var aryData=[]; + var setDate=new Set(); //有重复数据 去掉 + var stock=recvData.stock[0]; + var id=this.Args[0]; + var subID=this.Args[1]; + for(var i in stock.stockday) + { + var item=stock.stockday[i]; + + var hqchartItem=this.ToHQChartItemData(item,id,subID); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + return aryData; + } + + this.ToHQChartItemData=function(item, id, subID) + { + if (!item) return null; + var date=item.date; + switch(id) + { + case 1: + if (!item.shareholder) return null; + return { Date:date, Value:item.shareholder.count }; + case 2: + if (!item.tradedetail && item.tradedetail[0]) return null; + if (subID==0) + return { Date:date, Value:item.tradedetail[0].buy }; + else + return { Date:date, Value:item.tradedetail[0].sell }; + case 3: + if (!item.margin) return null; + if (subID==0) + { + if (item.margin.buy) + return { Date:date, Value:item.margin.buy.balance }; + } + else + { + if (item.margin.sell) + return { Date:date, Value:item.margin.sell.balance }; + } + return null; + case 4: + if (!item.blocktrading) return null; + if (subID==0) + return { Date:date, Value:item.blocktrading.price }; + else + return { Date:date, Value:item.blocktrading.amount }; + default: + return null; + } + } +} + +function DownloadGroupData(obj) +{ + this.Url=obj.Url; + this.RealtimeUrl=obj.RealtimeUrl; + this.Job=obj.Job; + this.Symbol=obj.Symbol; + this.Args=obj.Args; + this.DataKey=obj.DataKey; + this.RecvCallback=obj.Callback; + this.ErrorCallback=obj.ErrorCallback; + + this.Download=function() + { + var varName=this.Args[0]; + switch(varName) + { + case "HYBLOCK": + case "DYBLOCK": + case "GNBLOCK": + this.DownloadGroupName(varName); + break; + } + } + + this.DownloadGroupName=function(blockType) + { + var self=this; + var field=["name","symbol"]; + if (blockType=="HYBLOCK") field.push("industry"); + else if (blockType=="DYBLOCK") field.push("region"); + else if (blockType=="GNBLOCK") field.push("concept"); + + wx.request({ + url: self.RealtimeUrl, + data: + { + "field": field, + "symbol": [this.Symbol] + }, + method:"post", + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.RecvGroupName(recvData.data); + self.RecvCallback(data, self.Job, self.DataKey, 1); + }, + error: function(request) + { + self.ErrorCallback(request); + } + }); + } + + this.RecvGroupName=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + var stock=recvData.stock[0]; + var varName=this.Args[0]; + var value=null; + if (varName=="HYBLOCK") + { + var industry=stock.industry; + if (!industry) return null; + var value; + for(var i in industry) + { + var item=industry[i]; + value=item.name; + } + } + else if (varName=="DYBLOCK") + { + var region=stock.region; + if (!region) return null; + for(var i in region) + { + var item=region[i]; + value=item.name; + } + } + else if (varName=="GNBLOCK") + { + var concept=stock.concept; + if (!concept) return null; + value=""; + for(var i in concept) + { + var item=concept[i]; + if (value.length>0) value+=' '; + value+=item.name; + } + + } + + return { Value:value }; + } +} + +/* 测试例子 +var code1='VARHIGH:IF(VAR1<=REF(HH,-1),REF(H,BARSLAST(VAR1>=REF(HH,1))),DRAWNULL),COLORYELLOW;'; +var code2='VAR1=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);'; +var code3='mm1=1-2*-9+20;'; + +JSConsole.Complier.Log(code1+code2) +var tokens=JSComplier.Tokenize(code1+code2); +var ast=JSComplier.Parse(code2+code1); + +JSConsole.Complier.Log(ast); +*/ + + +module.exports = +{ + JSCommonComplier: + { + JSComplier: JSComplier, + }, + + //单个类导出 + JSCommonComplier_ErrorHandler: ErrorHandler, + JSCommonComplier_JSComplier:JSComplier, + JSCommonComplier_JSParser:JSParser, + JSCommonComplier_Syntax:Syntax, + JS_EXECUTE_JOB_ID:JS_EXECUTE_JOB_ID, + g_JSComplierResource:g_JSComplierResource, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.console.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.console.wechat.js new file mode 100644 index 0000000..9bdea83 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.console.wechat.js @@ -0,0 +1,15 @@ +//日志输出类 +var JSConsole= +{ + Chart:{ Log:console.log, Warn:console.warn }, //图形日志 + Complier:{ Log:console.log, Warn:console.warn }, //编译器日志 +}; + +module.exports = +{ + JSConsole: + { + Chart: JSConsole.Chart, + Complier:JSConsole.Complier + } +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.coordinatedata.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.coordinatedata.wechat.js new file mode 100644 index 0000000..2eff188 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.coordinatedata.wechat.js @@ -0,0 +1,2443 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 各个品种分钟走势图坐标信息 +*/ + +function GetLocalTime(i) //得到标准时区的时间的函数 +{ + if (typeof i !== 'number') return; + var d = new Date(); + //得到1970年一月一日到现在的秒数 + var len = d.getTime(); + //本地时间与GMT时间的时间偏移差 + var offset = d.getTimezoneOffset() * 60000; + //得到现在的格林尼治时间 + var utcTime = len + offset; + return new Date(utcTime + 3600000 * i); +} + +var MARKET_SUFFIX_NAME= +{ + SH:'.SH', + SZ:'.SZ', + SHSZ_C_Index:'.CI', //自定义指数 + SHO:'.SHO', //上海交易所 股票期权 + HK:'.HK', + FHK: '.FHK', //港股期货 + SHFE: '.SHF', //上期所 (Shanghai Futures Exchange) + SHFE2: '.SHFE', //上期所 (Shanghai Futures Exchange) + CFFEX: '.CFE', //中期所 (China Financial Futures Exchange) + DCE: '.DCE', //大连商品交易所(Dalian Commodity Exchange) + CZCE: '.CZC', //郑州期货交易所 + USA: '.USA', //美股 + FTSE: '.FTSE', //富时中国 + + BIT: '.BIT', //数字货币 如比特币 + BIZ: '.BIZ', //数字货币 + + NYMEX: '.NYMEX', //纽约商品期货交易所(New York Mercantile Exchange) + COMEX: ".COMEX", //纽约商品期货交易所(New York Mercantile Exchange) + NYBOT: ".NYBOT", //美國紐約商品交易所 + CBOT: ".CBOT", //芝商所 + + LME: ".LME", //伦敦金属交易所 + + ET: '.ET', //其他未知的品种 + + IsET: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.ET) > 0; + }, + + IsETShowAvPrice: function (upperSymbol) //是否显示均价 + { + return false; + }, + + IsNYMEX: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.NYMEX) > 0; + }, + + IsCOMEX: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.COMEX) > 0; + }, + + IsNYBOT: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.NYBOT) > 0; + }, + + IsCBOT: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.CBOT) > 0; + }, + + IsLME: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.LME) > 0; + }, + + IsFTSE: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.FTSE) > 0; + }, + + IsFHK: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.FHK) > 0; + }, + + IsBIT: function (upperSymbol) + { + if (!upperSymbol) return false; + if (upperSymbol.indexOf(this.BIT) > 0) return true; + if (upperSymbol.indexOf(this.BIZ) > 0) return true; + return false; + }, + + IsUSA: function (upperSymbol) //是否是美股 + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.USA) > 0; + }, + + IsSH: function (upperSymbol) + { + //需要精确匹配最后3位 + var pos = upperSymbol.length-this.SH.length; + var find = upperSymbol.indexOf(this.SH); + return find == pos; + }, + + IsSZ: function (upperSymbol) + { + var pos = upperSymbol.length - this.SZ.length; + var find = upperSymbol.indexOf(this.SZ); + return find == pos; + }, + + //自定义指数 + IsSHSZCustomIndex:function(upperSymbol) + { + var pos = upperSymbol.length - this.SHSZ_C_Index.length; + var find = upperSymbol.indexOf(this.SHSZ_C_Index); + return find == pos; + }, + + IsSHO: function (upperSymbol) + { + var pos = upperSymbol.length - this.SHO.length; + var find = upperSymbol.indexOf(this.SHO); + return find == pos; + }, + + IsHK: function (upperSymbol) + { + var pos = upperSymbol.length - this.HK.length; + var find = upperSymbol.indexOf(this.HK); + return find == pos; + }, + + IsSHFE: function (upperSymbol) + { + if (!upperSymbol) return false; + if (upperSymbol.indexOf(this.SHFE) > 0) return true; + if (upperSymbol.indexOf(this.SHFE2) > 0) return true; + return false; + }, + + IsCFFEX: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.CFFEX) > 0; + }, + + IsDCE: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.DCE) > 0; + }, + + IsCZCE: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.CZCE) > 0; + }, + + IsChinaFutures: function (upperSymbol) //是否是国内期货 + { + return this.IsCFFEX(upperSymbol) || this.IsCZCE(upperSymbol) || this.IsDCE(upperSymbol) || this.IsSHFE(upperSymbol); + }, + + IsFutures: function (upperSymbol) //是否是期货 包含国外的 + { + return this.IsChinaFutures(upperSymbol) || + this.IsNYMEX(upperSymbol) || this.IsCOMEX(upperSymbol) || this.IsNYBOT(upperSymbol) || this.IsCBOT(upperSymbol) || + this.IsLME(upperSymbol); + }, + + IsSHSZ: function (upperSymbol) //是否是沪深的股票 + { + return this.IsSZ(upperSymbol) || this.IsSH(upperSymbol) || this.IsSHSZCustomIndex(upperSymbol); + }, + + IsSHSZFund: function (upperSymbol) //是否是交易所基金 + { + if (!upperSymbol) return false; + + if (this.IsSH(upperSymbol)) //51XXXX.SH + { + if (upperSymbol.charAt(0) == '5' && upperSymbol.charAt(1) == '1') return true; + } + else if (this.IsSZ(upperSymbol)) //15XXXX.sz, 16XXXX.sz, 17XXXX.sz, 18XXXX.sz + { + if (upperSymbol.charAt(0) == '1' && + (upperSymbol.charAt(1) == '5' || upperSymbol.charAt(1) == '6' || upperSymbol.charAt(1) == '7' || upperSymbol.charAt(1) == '8')) return true; + } + + return false; + }, + + IsSHSZIndex: function (symbol) //是否是沪深指数代码 + { + if (!symbol) return false; + + var upperSymbol = symbol.toUpperCase(); + if (this.IsSH(upperSymbol)) + { + var temp = upperSymbol.replace('.SH', ''); + if (upperSymbol.charAt(0) == '0' && parseInt(temp) <= 3000) return true; + + } + else if (this.IsSZ(upperSymbol)) + { + if (upperSymbol.charAt(0) == '3' && upperSymbol.charAt(1) == '9') return true; + } + else if (this.IsSHSZCustomIndex(upperSymbol)) //自定义指数 + { + return true; + } + + return false; + }, + + IsSHSZStockA: function (symbol) //是否是沪深A股 + { + if (!symbol) return false; + var upperSymbol = symbol.toUpperCase(); + if (this.IsSH(upperSymbol)) + { + var temp = upperSymbol.replace('.SH', ''); + if (upperSymbol.charAt(0) == '6') return true; + } + else if (this.IsSZ(upperSymbol)) + { + if (upperSymbol.charAt(0) == '0') + { + if (upperSymbol.charAt(1) == '0' && upperSymbol.charAt(2) == '2') return true; //002 中小板 + if (upperSymbol.charAt(1) != '7' && upperSymbol.charAt(1) != '8') return true; + } + } + + return false; + }, + + IsSHStockSTAR: function (symbol) // 是否是科创板 Sci-Tech innovAtion boaRd (STAR Market) + { + if (!symbol) return false; + var upperSymbol = symbol.toUpperCase(); + if (!this.IsSH(upperSymbol)) return false; + if (upperSymbol.charAt(0) == '6' && upperSymbol.charAt(1) == '8' && upperSymbol.charAt(2) == '8') + return true; + + return false; + }, + + GetMarketStatus: function (symbol) //获取市场状态 0=闭市 1=盘前 2=盘中 3=盘后 + { + if (!symbol) return 0; + var upperSymbol = symbol.toUpperCase(); + var nowDate = new Date(); + var day = nowDate.getDay(); + var time = nowDate.getHours() * 100 + nowDate.getMinutes(); + + if (this.IsUSA(upperSymbol)) + { + var usaDate = GetLocalTime(-4); + var day = usaDate.getDay(); + var time = usaDate.getHours() * 100 + usaDate.getMinutes(); + if (day == 6 || day == 0) return 0; //周末 + + //9:30 - 16:00 考虑夏令时间时间增加1小时 9:30 - 17:00 + if (time > 1730) return 3; + if (time < 930) return 1; + + return 2; + } + else if (this.IsBIT(upperSymbol)) //数字货币24小时 + { + return 2; + } + else if (this.IsFTSE(upperSymbol)) //富时中国 9:00-16:30 17:00-04:45 + { + if (day == 6 || day == 0) return 0; //周末 + if (time >= 830 && time <= 2359) return 2; + if (time >= 0 && time <= 500) return 2; + return 0; + } + else if (this.IsFHK(upperSymbol)) //港股指数期货 9:15-12:00 13:00-16:30 17:15-01:00 + { + if (day == 6 || day == 0) return 0; //周末 + if (time >= 900 && time <= 2359) return 2; + if (time >= 0 && time <= 120) return 2; + return 0; + } + else if (this.IsET(upperSymbol)) + { + return this.GetETMarketStatus(symbol); + } + else if (this.IsHK(upperSymbol)) //港股 + { + if (day == 6 || day == 0) return 0; //周末 + if (time > 1630) return 3; + if (time < 925) return 1; + return 2; + } + else if (this.IsNYMEX(upperSymbol)) + { + return this.GetNYMEXMarketStatus(upperSymbol); + } + else if (this.IsCOMEX(upperSymbol)) + { + return this.GetCOMEXMarketStatus(upperSymbol); + } + else if (this.IsNYBOT(upperSymbol)) + { + return this.GetNYBOTMarketStatus(upperSymbol); + } + else if (this.IsCBOT(upperSymbol)) + { + return this.GetCBOTMarketStatus(upperSymbol); + } + else if (this.IsChinaFutures(upperSymbol)) //国内期货 + { + if(day == 6 || day== 0) return 0; //周末 + + //21:00-2:30 + if(time>=2100) return 2; + if (time<=240) return 2; + + //8:55-11:30, 13:00-15:00 + if(time>=830 && time<=1510) return 2; + + return 1; + } + else //9:30 - 15:40 + { + if (day == 6 || day == 0) return 0; //周末 + if (time > 1540) return 3; + if (time < 925) return 1; + return 2; + } + + }, + + GetDefaultDecimal:function(symbol) + { + return 2; + }, + + GetFHKDecimal: function (symbol) //港股指数期货 小数位数 + { + return 0; + }, + + GetFTSEDecimal: function (symbol) //富时中国A50期货 小数位数 + { + return 0; + }, + + GetBITDecimal: function (symbol) + { + return 2; + }, + + GetETDecimal: function (symbol) + { + return 2; + }, + + GetSHODecimal: function (symbol) + { + return 4; + }, + + GetNYMEXDecimal: function (symbol) //纽约期货交易所 + { + return g_NYMEXTimeData.GetDecimal(symbol); + }, + + GetCOMEXDecimal:function(symbol) + { + return g_COMEXTimeData.GetDecimal(symbol); + }, + + GetNYBOTDecimal:function(symbol) + { + return g_NYBOTTimeData.GetDecimal(symbol); + }, + + GetCBOTDecimal:function(symbol) + { + return g_CBOTTimeData.GetDecimal(symbol); + }, + + GetLMEDecimal:function(symbol) + { + return g_LMETimeData.GetDecimal(symbol); + }, + + GetETMarketStatus: function (symbol) + { + // 0=闭市 1=盘前 2=盘中 3=盘后 + return 2; + }, + + GetCOMEXMarketStatus:function(symbol) + { + return g_COMEXTimeData.GetMarketStatus(symbol); + }, + + GetNYBOTMarketStatus:function(symbol) + { + return g_NYBOTTimeData.GetMarketStatus(symbol); + }, + + GetCBOTMarketStatus:function(symbol) + { + return g_CBOTTimeData.GetMarketStatus(symbol); + }, + + GetNYMEXMarketStatus: function (symbol) + { + return g_NYMEXTimeData.GetMarketStatus(symbol); + }, + + GetLimitPriceRange:function(symbol, name) //涨停范围 + { + if (!this.IsSHSZStockA(symbol)) return null; + if (this.IsSHStockSTAR(symbol)) return {Max:0.2 , Min:-0.2}; //科创板 [20%- -20%] + + if (!name) return null; + if (name.indexOf('ST')>=0) return { Max:0.05, Min:-0.05 }; //ST 股票 [5% - -5%] + + return {Max:0.1 , Min:-0.1}; //[10% - -10%] + }, +} + + +//走势图分钟数据对应的时间 +function MinuteTimeStringData() +{ + this.SHSZ = null; //上海深证交易所时间 + this.HK = null; //香港交易所时间 + this.Futures=new Map(); //期货交易时间 key=时间名称 Value=数据 + this.USA = null; //美股交易时间 + this.FTSE = null; //富时中国 + this.FHK = null; //港股指数期货 + this.BIT=null; //数字货币 + + this.Initialize = function () //初始化 默认只初始化沪深的 其他市场动态生成 + { + //this.SHSZ = this.CreateSHSZData(); + //this.HK = this.CreateHKData(); + } + + this.GetET = function (upperSymbol) //当天所有的分钟 + { + throw { Name: 'MinuteTimeStringData::GetET', Error: 'not implement' }; + } + + this.GetSHSZ=function() //动态创建 + { + if (!this.SHSZ) this.SHSZ=this.CreateSHSZData(); + return this.SHSZ; + } + + this.GetSHO = function () + { + if (!this.SHO) this.SHO = this.CreateSHOData(); + return this.SHO; + } + + this.GetHK=function() + { + if (!this.HK) this.HK = this.CreateHKData(); + return this.HK; + } + + this.GetFutures=function(splitData) + { + if (!this.Futures.has(splitData.Name)) + { + var data = this.CreateTimeData(splitData.Data); + this.Futures.set(splitData.Name,data); + } + + return this.Futures.get(splitData.Name); + } + + this.GetUSA=function() + { + if (!this.USA) this.USA=this.CreateUSAData(); + return this.USA; + } + + this.GetFTSE = function () + { + if (!this.FTSE) this.FTSE = this.CreateFTSEData(); + return this.FTSE; + } + + this.GetFHK = function () + { + if (!this.FHK) this.FHK = this.CreateFHKData(); + return this.FHK; + } + + this.GetBIT=function(upperSymbol) + { + if (!this.BIT) this.BIT=this.CreateBITData(); + return this.BIT; + } + + this.CreateSHSZData = function () + { + const TIME_SPLIT = + [ + { Start: 925, End: 925 }, + { Start: 930, End: 1130 }, + { Start: 1300, End: 1500 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateSHOData = function () + { + const TIME_SPLIT = + [ + { Start: 930, End: 1129 }, + { Start: 1300, End: 1500 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateHKData = function () + { + const TIME_SPLIT = + [ + { Start: 930, End: 1200 }, + { Start: 1300, End: 1600 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateUSAData=function() + { + //美国夏令时 + const TIME_SUMMER_SPLIT = + [ + { Start: 2130, End: 2359 }, + { Start: 0, End: 400 } + ]; + + //非夏令时 + const TIME_SPLIT = + [ + { Start: 2230, End: 2359 }, + { Start: 0, End: 500 } + ]; + + //使用美国本地时间 + const TIME_LOCAL_SPLIT = + [ + { Start: 930, End: 1600 } //美国东部时间9:30到16:00 + ]; + + return this.CreateTimeData(TIME_LOCAL_SPLIT); + } + + this.CreateFTSEData = function () + { + const TIME_SPLIT = + [ + { Start: 1700, End: 2359 }, + { Start: 0, End: 445 }, + { Start: 900, End: 1630 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateFHKData = function () + { + //港股指数期货 9:15-12:00 13:00-16:30 17:15-01:00 + const TIME_SPLIT = + [ + { Start: 1715, End: 2359 }, + { Start: 0, End: 100 }, + { Start: 915, End: 1200 }, + { Start: 1300, End: 1630 }, + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateBITData=function() + { + //数字货币 7:00 - 6:59 + const TIME_SPLIT= + [ + { Start:600, End:2359 }, + { Start:0, End:559 }, + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateTimeData = function (timeSplit) + { + var data = []; + for (var i in timeSplit) + { + var item = timeSplit[i]; + for (var j = item.Start; j <= item.End; ++j) + { + if (j % 100 >= 60) continue; //大于60分钟的数据去掉 + data.push(j); + } + } + return data; + } + + this.GetTimeData = function (symbol) + { + if (!symbol) return this.SHSZ; + + var upperSymbol = symbol.toLocaleUpperCase(); //转成大写 + if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol) || MARKET_SUFFIX_NAME.IsSHSZIndex(upperSymbol)) return this.GetSHSZ(); + if (MARKET_SUFFIX_NAME.IsHK(upperSymbol)) return this.GetHK(); + if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol)) + { + var splitData = g_FuturesTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) return this.GetFTSE(); + if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) return this.GetFHK(); + if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) return this.GetET(upperSymbol); + if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) return this.GetBIT(upperSymbol); + + if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol)) //纽约期货交易所 + { + var splitData = g_NYMEXTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol)) //纽约期货交易所 + { + var splitData = g_COMEXTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol)) //纽约期货交易所 + { + var splitData = g_NYBOTTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol)) //芝商所 + { + var splitData = g_CBOTTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsLME(upperSymbol)) //伦敦LME + { + var splitData = g_LMETimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + } +} + +//走势图刻度分钟线 +function MinuteCoordinateData() +{ + //沪深走势图时间刻度 + const SHZE_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [31, 0, "RGB(200,200,200)", "10:00"], + [61, 0, "RGB(200,200,200)", "10:30"], + [91, 0, "RGB(200,200,200)", "11:00"], + [122, 1, "RGB(200,200,200)", "13:00"], + [152, 0, "RGB(200,200,200)", "13:30"], + [182, 0, "RGB(200,200,200)", "14:00"], + [212, 0, "RGB(200,200,200)", "14:30"], + [242, 1, "RGB(200,200,200)", "15:00"], // 15:00 + ], + Simple: //简洁模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [61, 0, "RGB(200,200,200)", "10:30"], + [122, 1, "RGB(200,200,200)", "13:00"], + [182, 0, "RGB(200,200,200)", "14:00"], + [242, 1, "RGB(200,200,200)", "15:00"] + ], + Min: //最小模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [122, 1, "RGB(200,200,200)", "13:00"], + [242, 1, "RGB(200,200,200)", "15:00"] + ], + + Count: 243, + MiddleCount: 122, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 400) return this.Simple; + + return this.Full; + } + }; + + //上海股票期权时间刻度 + const SHO_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [30, 0, "RGB(200,200,200)", "10:00"], + [60, 0, "RGB(200,200,200)", "10:30"], + [90, 0, "RGB(200,200,200)", "11:00"], + [120, 1, "RGB(200,200,200)", "13:00"], + [150, 0, "RGB(200,200,200)", "13:30"], + [180, 0, "RGB(200,200,200)", "14:00"], + [210, 0, "RGB(200,200,200)", "14:30"], + [240, 1, "RGB(200,200,200)", "15:00"], // 15:00 + ], + Simple: //简洁模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [60, 0, "RGB(200,200,200)", "10:30"], + [120, 1, "RGB(200,200,200)", "13:00"], + [180, 0, "RGB(200,200,200)", "14:00"], + [240, 1, "RGB(200,200,200)", "15:00"] + ], + Min: //最小模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [120, 1, "RGB(200,200,200)", "13:00"], + [240, 1, "RGB(200,200,200)", "15:00"] + ], + + Count: 241, + MiddleCount: 120, + + GetData: function (width) { + if (width < 200) return this.Min; + else if (width < 400) return this.Simple; + + return this.Full; + } + }; + + //港股走势图时间刻度 + const HK_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [30, 0, "RGB(200,200,200)", "10:00"], + [60, 1, "RGB(200,200,200)", "10:30"], + [90, 0, "RGB(200,200,200)", "11:00"], + [120, 1, "RGB(200,200,200)", "11:30"], + [151, 0, "RGB(200,200,200)", "13:00"], + [181, 1, "RGB(200,200,200)", "13:30"], + [211, 0, "RGB(200,200,200)", "14:00"], + [241, 1, "RGB(200,200,200)", "14:30"], + [271, 0, "RGB(200,200,200)", "15:00"], + [301, 1, "RGB(200,200,200)", "15:30"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [60, 1, "RGB(200,200,200)", "10:30"], + [120, 1, "RGB(200,200,200)", "11:30"], + [211, 0, "RGB(200,200,200)", "14:00"], + [271, 0, "RGB(200,200,200)", "15:00"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [151, 0, "RGB(200,200,200)", "13:00"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + + Count: 332, + MiddleCount: 151, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + }; + + //富时中国 + const FTSE_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + //[60, 0, "RGB(200,200,200)", "18:00"], + [120, 1, "RGB(200,200,200)", "19:00"], + //[180, 0, "RGB(200,200,200)", "20:00"], + [240, 1, "RGB(200,200,200)", "21:00"], + //[300, 0, "RGB(200,200,200)", "22:00"], + [360, 1, "RGB(200,200,200)", "23:00"], + //[420, 0, "RGB(200,200,200)", "00:00"], + [480, 1, "RGB(200,200,200)", "01:00"], + //[540, 0, "RGB(200,200,200)", "02:00"], + [600, 1, "RGB(200,200,200)", "03:00"], + //[660, 1, "RGB(200,200,200)", "04:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + //[766, 1, "RGB(200,200,200)", "10:00"], + [826, 1, "RGB(200,200,200)", "11:00"], + //[886, 1, "RGB(200,200,200)", "12:00"], + [946, 1, "RGB(200,200,200)", "13:00"], + //[1006, 1, "RGB(200,200,200)", "14:00"], + [1066, 1, "RGB(200,200,200)", "15:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + //[60, 0, "RGB(200,200,200)", "18:00"], + //[120, 1, "RGB(200,200,200)", "19:00"], + //[180, 0, "RGB(200,200,200)", "20:00"], + [240, 1, "RGB(200,200,200)", "21:00"], + //[300, 0, "RGB(200,200,200)", "22:00"], + //[360, 1, "RGB(200,200,200)", "23:30"], + //[420, 0, "RGB(200,200,200)", "00:00"], + [480, 1, "RGB(200,200,200)", "01:00"], + //[540, 0, "RGB(200,200,200)", "02:00"], + //[600, 1, "RGB(200,200,200)", "03:00"], + //[660, 1, "RGB(200,200,200)", "04:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + //[766, 1, "RGB(200,200,200)", "10:00"], + //[826, 1, "RGB(200,200,200)", "11:00"], + //[886, 1, "RGB(200,200,200)", "12:00"], + [946, 1, "RGB(200,200,200)", "13:00"], + //[1006, 1, "RGB(200,200,200)", "14:00"], + //[1066, 1, "RGB(200,200,200)", "15:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + + Count: 1157, + MiddleCount: 707, + + GetData: function (width) { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + }; + + //港股指数期货 + const FHK_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + //[45, 0, "RGB(200,200,200)", "18:00"], + [105, 1, "RGB(200,200,200)", "19:00"], + //[165, 0, "RGB(200,200,200)", "20:00"], + [225, 1, "RGB(200,200,200)", "21:00"], + //[285, 0, "RGB(200,200,200)", "22:00"], + [345, 1, "RGB(200,200,200)", "23:00"], + //[405, 0, "RGB(200,200,200)", "00:00"], + [466, 0, "RGB(200,200,200)", "09:15"], + //[511, 1, "RGB(200,200,200)", "10:00"], + [571, 1, "RGB(200,200,200)", "11:00"], + //[632, 1, "RGB(200,200,200)", "13:00"], + [692, 1, "RGB(200,200,200)", "14:00"], + //[752, 1, "RGB(200,200,200)", "15:00"], + [843, 1, "RGB(200,200,200)", "16:30"], + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + //[45, 0, "RGB(200,200,200)", "18:00"], + //[105, 1, "RGB(200,200,200)", "19:00"], + //[165, 0, "RGB(200,200,200)", "20:00"], + [225, 1, "RGB(200,200,200)", "21:00"], + //[285, 0, "RGB(200,200,200)", "22:00"], + //[345, 1, "RGB(200,200,200)", "23:00"], + //[405, 0, "RGB(200,200,200)", "00:00"], + [466, 0, "RGB(200,200,200)", "09:15"], + //[511, 1, "RGB(200,200,200)", "10:00"], + //[571, 1, "RGB(200,200,200)", "11:00"], + [632, 1, "RGB(200,200,200)", "13:00"], + //[692, 1, "RGB(200,200,200)", "14:00"], + //[752, 1, "RGB(200,200,200)", "15:00"], + [843, 1, "RGB(200,200,200)", "16:30"], + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + [466, 0, "RGB(200,200,200)", "09:15"], + [843, 1, "RGB(200,200,200)", "16:30"], + ], + + Count: 843, + MiddleCount: 466, + + GetData: function (width) { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + }; + + this.GetCoordinateData = function (symbol, width) + { + var data = null; + if (!symbol) + { + data = SHZE_MINUTE_X_COORDINATE; //默认沪深股票 + } + else + { + var upperSymbol = symbol.toLocaleUpperCase(); //转成大写 + if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol)) + data = this.GetSHSZData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) + data = this.GetSHOData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsHK(upperSymbol)) + data = HK_MINUTE_X_COORDINATE; + else if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol)) + return this.GetChinatFuturesData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsUSA(upperSymbol)) + data = this.GetUSAData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol, width)) + data = this.GetFTSEData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol, width)) + data = this.GetFHKData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) + data = this.GetETData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol, width)) + return data = this.GetNYMEXData(upperSymbol, width); + else if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol,width)) + return data=this.GetCOMEXData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol,width)) + return data=this.GetNYBOTData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol,width)) + return data=this.GetCBOTData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsLME(upperSymbol,width)) + return data=this.GetLMEData(upperSymbol,width); + else if ((MARKET_SUFFIX_NAME.IsBIT(upperSymbol,width))) + data=this.GetBITData(upperSymbol,width); + } + + //console.log('[MiuteCoordinateData]', width); + var result = { Count: data.Count, MiddleCount: data.MiddleCount, Data: data.GetData(width) }; + return result; + } + + this.GetSHSZData = function (upperSymbol, width) + { + var result = SHZE_MINUTE_X_COORDINATE; + return result; + } + + this.GetFuturesData = function (upperSymbol,width,timeData) + { + var splitData = timeData.GetSplitData(upperSymbol); + if (!splitData) return null; + var stringData = g_MinuteTimeStringData.GetFutures(splitData); + if (!stringData) return null; + var result = { Count: stringData.length }; + var coordinate=null; + var minWidth=200, simpleWidth=480; + /* + if (splitData.Name =='21:00-1:00,9:00-10:15,10:30-11:30,13:30-15:00') + { + minWidth=250; + simpleWidth=500; + } + */ + + if (width < minWidth) coordinate = splitData.Coordinate.Min; + else if (width < simpleWidth) coordinate = splitData.Coordinate.Simple; + else coordinate = splitData.Coordinate.Full; + + var data=[]; + for(var i=0;i430 && time<730) return 1; + + return 2; + } +} + +function COMEXTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + this.FUTURES_LIST= + [ + { Symbol:"GC", Decimal:1, Time:0 }, //COMEX黄金 + { Symbol:"QO", Decimal:2, Time:0 }, //迷你黄金 + { Symbol:"MG", Decimal:1, Time:0 }, //微型黄金 + { Symbol:"QI", Decimal:4, Time:0 }, //迷你白银 + { Symbol:"SI", Decimal:3, Time:0 }, //COMEX白银 + { Symbol:"QI", Decimal:4, Time:0 }, //迷你白银 + { Symbol:"HG", Decimal:4, Time:0 } //COMEX铜 + ] + + this.MarketSuffix=".COMEX"; +} + +function NYBOTTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //美国标准时间 + this.TIME_SPLIT= + [ + { + Name:'9:00-2:20', + Data: + [ + //9:00-2:20 + { Start: 900, End: 2359 }, + { Start: 0, End: 220 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 100, Text: '1:00' } + ] + } + }, + { + Name:'15:30-1:00', + Data: + [ + //9:00-2:20 + { Start: 1530, End: 2359 }, + { Start: 0, End: 100 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1600, Text: '16:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' } + ], + Simple: //简洁模式 + [ + { Value: 1600, Text: '16:00' }, + //{ Value: 1600, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + //{ Value: 100, Text: '1:00' } + ], + Min: //最小模式 + [ + { Value: 1600, Text: '16:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 100, Text: '1:00' } + ] + } + } + ] + + //美国夏时令 + this.TIME_SPLIT2= + [ + { + Name:'10:00-3:20', + Data: + [ + //9:00-2:20 + { Start: 1000, End: 2359 }, + { Start: 0, End: 320 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 1000, Text: '10:00' }, + //{ Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + //{ Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 1000, Text: '10:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'16:30-2:00', + Data: + [ + { Start: 1630, End: 2359 }, + { Start: 0, End: 200 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1700, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 1700, Text: '17:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 1700, Text: '17:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 200, Text: '2:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"SB", Decimal:2, Time:1 }, //11号白糖 + { Symbol:"CT", Decimal:2, Time:0 }, //棉花 + //{ Symbol:"KC", Decimal:2, Time:0 }, //咖啡 + //{ Symbol:"DX", Decimal:2, Time:0 }, //美元指数 + //{ Symbol:"CC", Decimal:2, Time:0 } //可可 + ] + + this.MarketSuffix=".NYBOT"; + + this.GetMarketStatus=function(upperSymbol) // 0=闭市 1=盘前 2=盘中 3=盘后 + { + var usaDate=GetLocalTime(-4); //需要转成美国时间的 周6 周日 + var day = usaDate.getDay(); + var time = usaDate.getHours() * 100 + usaDate.getMinutes(); + if(day == 6 || day== 0) return 0; //周末 + + var find=this.GetFuturesInfo(upperSymbol); + if (!find) return 2; + + if (find.Symbol=="SB") //Sugar No. 11 Futures 03:30 - 13:00 + { + if (time>300 && time<1400) return 2; + } + else if (find.Symbol=="CT") //美棉 21:00-14:20 + { + if( (time>=0 && time<=1500 ) || (time>=2000 && time<=2359) ) return 2; + return 1; + } + + return 0; + } +} + +//芝商所 +function CBOTTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //夏令时间 + this.TIME_SPLIT= + [ + { + Name:'8:00-2:20', + Data: + [ + //6:00 - 5:00 + { Start: 800, End: 2359 }, + { Start: 0, End: 220 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'8:00-2:45', + Data: + [ + //6:00 - 5:00 + { Start: 800, End: 2359 }, + { Start: 0, End: 245 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'6:00-5:00', + Data: + [ + //6:00 - 5:00 + { Start: 600, End: 2359 }, + { Start: 0, End: 500 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 600, Text: '6:00' }, + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' }, + { Value: 400, Text: '4:00' }, + ], + Simple: //简洁模式 + [ + { Value: 600, Text: '6:00' }, + //{ Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + //{ Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + //{ Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + //{ Value: 400, Text: '4:00' }, + ], + Min: //最小模式 + [ + { Value: 600, Text: '6:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 500, Text: '5:00' } + ] + } + } + ] + + //标准时间 + this.TIME_SPLIT2= + [ + { + Name:'9:00-3:20', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 320 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' } + //{ Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 300, Text: '3:00' } + ] + } + }, + { + Name:'9:00-3:45', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 345 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' } + //{ Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 300, Text: '3:00' } + ] + } + }, + { + Name:'7:00-6:00', + Data: + [ + { Start: 700, End: 2359 }, + { Start: 0, End: 600 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 700, Text: '7:00' }, + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' }, + { Value: 500, Text: '5:00' } + ], + Simple: //简洁模式 + [ + { Value: 700, Text: '7:00' }, + //{ Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + //{ Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + //{ Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + //{ Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + //{ Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + //{ Value: 500, Text: '5:00' } + ], + Min: //最小模式 + [ + { Value: 700, Text: '7:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 500, Text: '5:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"ZC", Decimal:2, Time:0 }, //玉米 + { Symbol:"XC", Decimal:2, Time:1 }, //迷你玉米 + { Symbol:"ZS", Decimal:2, Time:0 }, //大豆 + { Symbol:"XK", Decimal:2, Time:1 }, //迷你大豆 + { Symbol:"ZL", Decimal:2, Time:0 }, //豆油 + { Symbol:"ZR", Decimal:2, Time:0 }, //稻谷 + { Symbol:"ZO", Decimal:2, Time:0 }, //燕麦 + { Symbol:"ZW", Decimal:2, Time:0 }, //小麦 + { Symbol:"XW", Decimal:2, Time:1 }, //迷你小麦 + { Symbol:"ZM", Decimal:1, Time:0 }, //豆粕 + + { Symbol:"EH", Decimal:3, Time:2 }, //乙醇 + + { Symbol:"YM", Decimal:0, Time:2 }, //小型道指 + { Symbol:"ES", Decimal:2, Time:2 }, //小型标普 + { Symbol:"NQ", Decimal:2, Time:2 }, //小型纳指 + + { Symbol:"TY", Decimal:4, Time:2 }, //10年美国债 + { Symbol:"TU", Decimal:4, Time:2 }, //2年美国债 + { Symbol:"FV", Decimal:4, Time:2 }, //5年美国债 + { Symbol:"US", Decimal:4, Time:2 }, //30年美国债 + { Symbol:"UL", Decimal:4, Time:2 }, //超国债 + ] + + this.MarketSuffix=".CBOT"; +} + +function LMETimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //标准时间 + this.TIME_SPLIT= + [ + { + Name:'LME 9:00-3:00', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 300 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + // { Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 300, Text: '3:00' } + ] + } + } + ] + + //夏令 + this.TIME_SPLIT= + [ + { + Name:'LME 8:00-2:00', + Data: + [ + { Start: 800, End: 2359 }, + { Start: 0, End: 200 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"SND", Decimal:0, Time:0 }, //综合锡03 + { Symbol:"AHD", Decimal:2, Time:0 }, //综合铝03 + { Symbol:"PBD", Decimal:2, Time:0 }, //综合铅03 + { Symbol:"ZSD", Decimal:2, Time:0 }, //综合锌03 + { Symbol:"CAD", Decimal:2, Time:0 }, //综合铜03 + { Symbol:"NID", Decimal:0, Time:0 }, //综合镍03 + ] + + this.MarketSuffix=".LME"; +} + +var g_MinuteTimeStringData = new MinuteTimeStringData(); +var g_MinuteCoordinateData = new MinuteCoordinateData(); +var g_FuturesTimeData = new FuturesTimeData(); +var g_NYMEXTimeData = new NYMEXTimeData(); +var g_COMEXTimeData=new COMEXTimeData(); +var g_NYBOTTimeData=new NYBOTTimeData(); +var g_CBOTTimeData=new CBOTTimeData(); +var g_LMETimeData=new LMETimeData(); + + +function GetfloatPrecision(symbol) //获取小数位数 +{ + var defaultfloatPrecision = 2; //默认2位 + if (!symbol) return defaultfloatPrecision; + var upperSymbol = symbol.toUpperCase(); + + //全部由外部控制 + if (typeof(MARKET_SUFFIX_NAME.GetCustomDecimal)=='function') return MARKET_SUFFIX_NAME.GetCustomDecimal(upperSymbol); + + if (MARKET_SUFFIX_NAME.IsSHSZFund(upperSymbol)) defaultfloatPrecision = 3; //基金3位小数 + else if (MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) defaultfloatPrecision = MARKET_SUFFIX_NAME.GetSHODecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) defaultfloatPrecision = g_FuturesTimeData.GetDecimal(upperSymbol); //期货小数位数读配置 + else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) defaultfloatPrecision = MARKET_SUFFIX_NAME.GetFHKDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) defaultfloatPrecision = MARKET_SUFFIX_NAME.GetFTSEDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) defaultfloatPrecision = MARKET_SUFFIX_NAME.GetBITDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) defaultfloatPrecision = MARKET_SUFFIX_NAME.GetETDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol)) defaultfloatPrecision=g_NYMEXTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol)) defaultfloatPrecision=g_COMEXTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol)) defaultfloatPrecision=g_NYBOTTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol)) defaultfloatPrecision=g_CBOTTimeData.GetDecimal(upperSymbol); + else defaultfloatPrecision=MARKET_SUFFIX_NAME.GetDefaultDecimal(upperSymbol); + + return defaultfloatPrecision; +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonCoordinateData: + { + MinuteCoordinateData: g_MinuteCoordinateData, + MinuteTimeStringData: g_MinuteTimeStringData, + MARKET_SUFFIX_NAME: MARKET_SUFFIX_NAME, + GetfloatPrecision: GetfloatPrecision + }, + + JSCommonCoordinateData_MARKET_SUFFIX_NAME: MARKET_SUFFIX_NAME, + JSCommonCoordinateData_Global_FuturesTimeData: g_FuturesTimeData, + JSCommonCoordinateData_Global_NYMEXTimeData: g_NYMEXTimeData, + JSCommonCoordinateData_Global_COMEXTimeData: g_COMEXTimeData, + JSCommonCoordinateData_Global_NYBOTTimeData: g_NYBOTTimeData, + JSCommonCoordinateData_Global_LMETimeData: g_LMETimeData, + JSCommonCoordinateData_Global_CBOTTimeData: g_CBOTTimeData, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.data.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.data.wechat.js new file mode 100644 index 0000000..e56670d --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.data.wechat.js @@ -0,0 +1,1796 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 行情数据结构 及计算方法 +*/ + +import +{ + JSCommonCoordinateData_MARKET_SUFFIX_NAME as MARKET_SUFFIX_NAME +} from "./umychart.coordinatedata.wechat.js"; + +function Guid() +{ + function S4() + { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + } + + return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); +} + +//历史K线数据 +function HistoryData() +{ + this.Date; + this.YClose; + this.Open; + this.Close; + this.High; + this.Low; + this.Vol; + this.Amount; + this.Time; //分钟 HHMM / 秒HHMMSS + this.FlowCapital = 0; //流通股本 + this.Position = null; //持仓量 + + //指数才有的数据 + this.Stop; //停牌家数 + this.Up; //上涨 + this.Down; //下跌 + this.Unchanged; //平盘 +} + +//数据复制 +HistoryData.Copy=function(data) +{ + var newData=new HistoryData(); + newData.Date=data.Date; + newData.YClose=data.YClose; + newData.Open=data.Open; + newData.Close=data.Close; + newData.High=data.High; + newData.Low=data.Low; + newData.Vol=data.Vol; + newData.Amount=data.Amount; + newData.Time=data.Time; + newData.FlowCapital = data.FlowCapital; + newData.Position = data.Position; + + //指数才有的数据 + newData.Stop = data.Stop; + newData.Up = data.Up; + newData.Down = data.Down; + newData.Unchanged = data.Unchanged; + + return newData; +} + +//把数据 src 复制到 dest中 +HistoryData.CopyTo = function (dest, src) +{ + dest.Date = src.Date; + dest.YClose = src.YClose; + dest.Open = src.Open; + dest.Close = src.Close; + dest.High = src.High; + dest.Low = src.Low; + dest.Vol = src.Vol; + dest.Amount = src.Amount; + dest.Time = src.Time; + dest.FlowCapital = src.FlowCapital; + + dest.Stop = src.Stop; + dest.Up = src.Up; + dest.Down = src.Down; + dest.Unchanged = src.Unchanged; +} + +//数据复权拷贝 +HistoryData.CopyRight=function(data,seed) +{ + var newData=new HistoryData(); + newData.Date=data.Date; + newData.YClose=data.YClose*seed; + newData.Open=data.Open*seed; + newData.Close=data.Close*seed; + newData.High=data.High*seed; + newData.Low=data.Low*seed; + + newData.Vol=data.Vol; + newData.Amount=data.Amount; + + newData.FlowCapital = data.FlowCapital; + newData.Position = data.Position; + + return newData; +} + +//分钟数据 +function MinuteData() +{ + this.Close; + this.Open; + this.High; + this.Low; + this.Vol; + this.Amount; + this.DateTime; + this.Increase; + this.Risefall; + this.AvPrice; + this.Date; + this.Time; + this.Position = null; //持仓量 +} + +//单指标数据 +function SingleData() +{ + this.Date; //日期 + this.Value; //数据 +} + + +function DataPlus() { }; //外部数据计算方法接口 +DataPlus.GetMinutePeriodData = null; +/* +DataPlus.GetMinutePeriodData=function(period,data,self) +{ + +} +*/ + +////////////////////////////////////////////////////////////////////// +// 数据集合 +function ChartData() +{ + this.Data=new Array(); + this.DataOffset=0; //数据偏移 + this.Period=0; //周期 0 日线 1 周线 2 月线 3年线 + this.Right=0; //复权 0 不复权 1 前复权 2 后复权 + this.Symbol; //股票代码 + + this.Data2=new Array(); //第1组数据 走势图:历史分钟数据 + + this.GetCloseMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Close; + } + result[i]=sum / dayCount; + } + return result; + } + + this.GetVolMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Vol; + } + result[i]=sum / dayCount; + } + return result; + } + + this.GetAmountMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Amount; + } + result[i]=sum / dayCount; + } + return result; + } + + //获取收盘价 + this.GetClose=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Close; + } + + return result; + } + + this.GetYClose=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].YClose; + } + + return result; + } + + this.GetHigh=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].High; + } + + return result; + } + + this.GetLow=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Low; + } + + return result; + } + + this.GetOpen=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Open; + } + + return result; + } + + this.GetVol=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Vol; + } + + return result; + } + + this.GetAmount=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Amount; + } + + return result; + } + + this.GetPosition = function () + { + var result = new Array(); + for (var i in this.Data) + { + result[i] = this.Data[i].Position; + } + + return result; + } + + this.GetDate = function () + { + var result = []; + for (var i in this.Data) + { + result[i] = this.Data[i].Date; + } + + return result; + } + + this.GetTime = function () + { + var result = []; + for (var i in this.Data) + { + result[i] = this.Data[i].Time; + } + + return result; + } + + this.GetUp = function () //上涨家数 + { + var result = []; + for (var i in this.Data) { + result[i] = this.Data[i].Up; + } + + return result; + } + + this.GetDown = function () //下跌家数 + { + var result = []; + for (var i in this.Data) { + result[i] = this.Data[i].Down; + } + + return result; + } + + this.GetYear=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=parseInt(this.Data[i].Date/10000); + } + + return result; + } + + this.GetMonth=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=parseInt(this.Data[i].Date%10000/100); + } + + return result; + } + + //分时图均价 + this.GetAvPrice=function() + { + var result=new Array(); + for(var i in this.Data) + { + var value=this.Data[i].AvPrice; + if (ChartData.IsNumber(value)) + result[i]=value; + else + result[i]=0; + } + + return result; + } + + //获取数据日期和时间范围 + this.GetDateRange=function() + { + if (!this.Data || this.Data.length<=0) return null; + + var start=this.Data[0]; + var end=this.Data[this.Data.length-1]; + var range={ Start:{Date:start.Date}, End:{Date:end.Date} }; + if (ChartData.IsNumber(start.Time)) range.Start.Time=start.Time; + if (ChartData.IsNumber(end.Time)) range.End.Time=end.Time; + + return range; + } + + + this.GetDateIndex = function (data) //日期转化 对应数据索引 + { + for (var i = 0, j = 0; i < this.Data.length;) + { + var date = this.Data[i].Date; + + if (j >= data.length) break; + + var dateItem = data[j]; + + if (dateItem.Date == date) + { + dateItem.Index = i; + ++j; + } + else if (dateItem.Date < date) + { + ++j; + } + else + { + ++i; + } + } + } + + + this.GetDateTimeIndex = function (data) //日期 时间转化 对应数据索引 + { + for (var i = 0, j = 0; i < this.Data.length;) + { + var date = this.Data[i].Date; + var time = this.Data[i].Time; + + if (j >= data.length) break; + + var dateTimeItem = data[j]; + + if (dateTimeItem.Date == date && dateTimeItem.Time == time) + { + dateTimeItem.Index = i; + ++j; + } + else if (dateTimeItem.Date < date || (dateTimeItem.Date == date && dateTimeItem.Time < time)) + { + ++j; + } + else + { + ++i; + } + } + } + + this.GetMinutePeriodData=function(period) + { + if (DataPlus.GetMinutePeriodData) return DataPlus.GetMinutePeriodData(period, this.Data, this); + + if (period > CUSTOM_MINUTE_PERIOD_START && period <= CUSTOM_MINUTE_PERIOD_END) + return this.GetMinuteCustomPeriodData(period - CUSTOM_MINUTE_PERIOD_START); + + var result = []; + var periodDataCount = 5; + if (period == 5) + periodDataCount = 5; + else if (period == 6) + periodDataCount = 15; + else if (period == 7) + periodDataCount = 30; + else if (period == 8) + periodDataCount = 60; + else if (period == 11) + periodDataCount = 120; + else if (period == 12) + periodDataCount = 240; + else + return this.Data; + var bFirstPeriodData = false; + var newData = null; + var preTime = null; //上一次的计算时间 + for (var i = 0; i < this.Data.length; ) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var minData = this.Data[i]; + if (minData == null) + { + ++j; + continue; + } + if (minData.Time == 925 && (preTime == null || preTime != 924)) //9:25, 9:30 不连续就不算个数 + { + } + else if (minData.Time == 930 && (preTime == null || preTime != 929)) + { + } + else if (minData.Time == 1300 && (preTime == null || preTime != 1259)) //1点的数据 如果不是连续的 就不算个数 + { + + } + else + ++j; + newData.Date = minData.Date; + newData.Time = minData.Time; + preTime = newData.Time; + if (minData.Open==null || minData.Close==null) + continue; + if (newData.Open==null || newData.Close==null) + { + newData.Open=minData.Open; + newData.High=minData.High; + newData.Low=minData.Low; + newData.YClose=minData.YClose; + newData.Close=minData.Close; + newData.Vol=minData.Vol; + newData.Amount=minData.Amount; + newData.Position = minData.Position; + newData.FlowCapital = minData.FlowCapital; + } + else + { + if (newData.HighminData.Low) + newData.Low=minData.Low; + newData.Close=minData.Close; + newData.Vol+=minData.Vol; + newData.Amount+=minData.Amount; + newData.Position = minData.Position; + newData.FlowCapital = minData.FlowCapital; + } + + if (i + 1 < this.Data.length) //判断下一个数据是否是不同日期的 + { + var nextItem = this.Data[i + 1]; + if (nextItem && nextItem.Date != minData.Date) //不同日期的, 周期结束 + { + ++i; + break; + } + } + } + } + return result; + } + + //自定义分钟 + this.GetMinuteCustomPeriodData = function (count) + { + var result = new Array(); + var periodDataCount = count; + var bFirstPeriodData = false; + var newData = null; + for (var i = 0; i < this.Data.length;) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var minData = this.Data[i]; + if (minData == null) continue; + + newData.Date = minData.Date; + newData.Time = minData.Time; + if (minData.Open == null || minData.Close == null) continue; + if (newData.Open == null || newData.Close == null) + { + newData.Open = minData.Open; + newData.High = minData.High; + newData.Low = minData.Low; + newData.YClose = minData.YClose; + newData.Close = minData.Close; + newData.Vol = minData.Vol; + newData.Amount = minData.Amount; + newData.FlowCapital = minData.FlowCapital; + newData.Position = minData.Position; + } + else + { + if (newData.High < minData.High) newData.High = minData.High; + if (newData.Low > minData.Low) newData.Low = minData.Low; + newData.Close = minData.Close; + newData.Vol += minData.Vol; + newData.Amount += minData.Amount; + newData.FlowCapital = minData.FlowCapital; + newData.Position = minData.Position; + } + } + } + return result; + } + + this.GetDayPeriodData=function(period) + { + if (period > CUSTOM_DAY_PERIOD_START && period <= CUSTOM_DAY_PERIOD_END) //自定义周期 + return this.GetDayCustomPeriodData(period - CUSTOM_DAY_PERIOD_START); + + var isBit = MARKET_SUFFIX_NAME.IsBIT(this.Symbol); + var result=[]; + var index=0; + var startDate=0; + var weekCount = 2; + var newData=null; + for(var i in this.Data) + { + var isNewData=false; + var dayData=this.Data[i]; + + switch(period) + { + case 1: //周线 + if (isBit) var fridayDate = ChartData.GetSunday(dayData.Date); + else var fridayDate=ChartData.GetFirday(dayData.Date); + if (fridayDate!=startDate) + { + isNewData=true; + startDate=fridayDate; + } + break; + case 21: //双周 + if (isBit) var fridayDate = ChartData.GetSunday(dayData.Date); + else var fridayDate = ChartData.GetFirday(dayData.Date); + if (fridayDate != startDate) + { + ++weekCount; + if (weekCount >= 2) + { + isNewData = true; + weekCount = 0; + } + startDate = fridayDate; + } + break; + case 2: //月线 + if (parseInt(dayData.Date/100)!=parseInt(startDate/100)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 3: //年线 + if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 9: //季线 + var now = ChartData.GetQuarter(dayData.Date); + now = parseInt(dayData.Date / 10000) * 10 + now; + var last = ChartData.GetQuarter(startDate); + last = parseInt(startDate / 10000) * 10 + last; + if (now != last) + { + isNewData = true; + startDate = dayData.Date; + } + break; + } + + if (isNewData) + { + newData=new HistoryData(); + newData.Date=dayData.Date; + result.push(newData); + + if (dayData.Open==null || dayData.Close==null) continue; + + newData.Open=dayData.Open; + newData.High=dayData.High; + newData.Low=dayData.Low; + newData.YClose=dayData.YClose; + newData.Close=dayData.Close; + newData.Vol=dayData.Vol; + newData.Amount=dayData.Amount; + newData.FlowCapital = dayData.FlowCapital; + newData.Position = dayData.Position; + } + else + { + if (newData==null) continue; + if (dayData.Open==null || dayData.Close==null) continue; + + if (newData.Open==null || newData.Close==null) + { + newData.Open=dayData.Open; + newData.High=dayData.High; + newData.Low=dayData.Low; + newData.YClose=dayData.YClose; + newData.Close=dayData.Close; + newData.Vol=dayData.Vol; + newData.Amount=dayData.Amount; + newData.FlowCapital = dayData.FlowCapital; + newData.Position = dayData.Position; + } + else + { + if (newData.HighdayData.Low) newData.Low=dayData.Low; + + newData.Close=dayData.Close; + newData.Vol+=dayData.Vol; + newData.Amount+=dayData.Amount; + newData.Date=dayData.Date; + newData.FlowCapital = dayData.FlowCapital; + newData.Position = dayData.Position; + } + } + } + + return result; + } + + this.GetDayCustomPeriodData = function (count) //自定义日线周期 + { + var result = []; + var periodDataCount = count; + var bFirstPeriodData = false; + var newData = null; + for (var i = 0; i < this.Data.length;) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var dayData = this.Data[i]; + if (dayData == null) continue; + + newData.Date = dayData.Date; + + if (dayData.Open == null || dayData.Close == null) continue; + if (newData.Open == null || newData.Close == null) + { + newData.Open = dayData.Open; + newData.High = dayData.High; + newData.Low = dayData.Low; + newData.YClose = dayData.YClose; + newData.Close = dayData.Close; + newData.Vol = dayData.Vol; + newData.Amount = dayData.Amount; + newData.FlowCapital = dayData.FlowCapital; + newData.Position = dayData.Position; + } + else + { + if (newData.High < dayData.High) newData.High = dayData.High; + if (newData.Low > dayData.Low) newData.Low = dayData.Low; + newData.Close = dayData.Close; + newData.Vol += dayData.Vol; + newData.Amount += dayData.Amount; + newData.Position = dayData.Position; + newData.FlowCapital = dayData.FlowCapital; + } + } + } + return result; + } + + //周期数据 1=周 2=月 3=年 9=季 + this.GetPeriodData=function(period) + { + if (period == 1 || period == 2 || period == 3 || period == 9 || period == 21 || (period > CUSTOM_DAY_PERIOD_START && period <= CUSTOM_DAY_PERIOD_END)) return this.GetDayPeriodData(period); + if (period == 5 || period == 6 || period == 7 || period == 8 || period == 11 || period == 12 ||(period > CUSTOM_MINUTE_PERIOD_START && period <= CUSTOM_MINUTE_PERIOD_END)) return this.GetMinutePeriodData(period); + } + + //复权 0 不复权 1 前复权 2 后复权 + this.GetRightDate=function(right) + { + var result=[]; + if (this.Data.length<=0) return result; + + if (right==1) + { + var index=this.Data.length-1; + var seed=1; //复权系数 + var yClose=this.Data[index].YClose; + + result[index]=HistoryData.Copy(this.Data[index]); + + for(--index; index>=0; --index) + { + if (yClose!=this.Data[index].Close) break; + result[index]=HistoryData.Copy(this.Data[index]); + yClose=this.Data[index].YClose; + } + + for(; index>=0; --index) + { + if(yClose!=this.Data[index].Close) + seed *= yClose/this.Data[index].Close; + + result[index]=HistoryData.CopyRight(this.Data[index],seed); + + yClose=this.Data[index].YClose; + } + } + else if (right==2) + { + var index=0; + var seed=1; + var close=this.Data[index].Close; + result[index]=HistoryData.Copy(this.Data[index]); + + for(++index;index=overlayData.length) + { + result[i]=new HistoryData(); + result[i].Date=date; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + result[i]=new HistoryData(); + result[i].Date=overlayData[j].Date; + result[i].YClose=overlayData[j].YClose; + result[i].Open=overlayData[j].Open; + result[i].High=overlayData[j].High; + result[i].Low=overlayData[j].Low; + result[i].Close=overlayData[j].Close; + result[i].Vol=overlayData[j].Vol; + result[i].Amount=overlayData[j].Amount; + + //涨跌家数数据 + result[i].Stop = overlayData[j].Stop; + result[i].Up = overlayData[j].Up; + result[i].Down = overlayData[j].Down; + result[i].Unchanged = overlayData[j].Unchanged; + + ++j; + ++i; + } + else if (overlayDate=overlayData.length) + { + result[i]=null; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + var item=new SingleData(); + item.Date=overlayData[j].Date; + item.Value=overlayData[j].Value; + result[i]=item; + ++j; + ++i; + } + else if (overlayDate= overlayData.length) + { + result[i] = new SingleData(); + result[i].Date = date; + result[i].Value = emptyValue; + ++i; + continue;; + } + + var overlayDate = overlayData[j].Date; + + if (overlayDate == date) + { + var item = new SingleData(); + item.Date = overlayData[j].Date; + item.Value = overlayData[j].Value; + result[i] = item; + ++j; + ++i; + } + else if (overlayDate < date) + { + ++j; + } + else + { + result[i] = new SingleData(); + result[i].Date = date; + result[i].Value = emptyValue; + ++i; + } + } + + return result; + } + + + this.GetMinuteFittingData = function (overlayData) // 分钟数据拟合 + { + var result = []; + for (var i = 0, j = 0; i < this.Data.length;) + { + var date = this.Data[i].Date; + var time = this.Data[i].Time; + + if (j >= overlayData.length) + { + result[i] = null; + ++i; + continue;; + } + + var overlayDate = overlayData[j].Date; + var overlayTime = overlayData[j].Time; + const overlayItem = overlayData[j]; + + if (overlayDate == date && overlayTime == time) + { + var item = new SingleData(); + item.Date = overlayItem.Date; + item.Time = overlayItem.Time; + item.Value = overlayItem.Value; + result[i] = item; + ++j; + ++i; + } + else if (overlayDate < date || (overlayDate == date && overlayTime < time)) + { + ++j; + } + else + { + var item = new SingleData(); + item.Date = date; + item.Time = time; + result[i] = item; + ++i; + } + } + + return result; + } + + //把财报数据拟合到主图数据,返回 SingleData 数组 + this.GetFittingFinanceData = function (financeData) + { + var result = []; + + for (var i = 0, j = 0; i < this.Data.length;) + { + var date = this.Data[i].Date; + + if (jfirstItem.Date || (date==firstItem.Date && time>=firstItem.Time)) + { + break; + } + } + + for(var j=0;i= overlayData.length) { + result[i] = null; + ++i; + continue;; + } + + var overlayDate = overlayData[j].Date; + + if (overlayDate == date) { + var item = new SingleData(); + item.Date = overlayData[j].Date; + item.Value = overlayData[j].Value; + item.Text = overlayData[j].Text; + result[i] = item; + ++j; + ++i; + } + else if (preDate != null && preDate < overlayDate && date > overlayDate) { + var item = new SingleData(); + item.Date = date; + item.OverlayDate = overlayData[j].Date; + item.Value = overlayData[j].Value; + item.Text = overlayData[j].Text; + result[i] = item; + ++j; + ++i; + } + else if (overlayDate < date) { + ++j; + } + else { + result[i] = new SingleData(); + result[i].Date = date; + ++i; + } + + preDate = date; + } + + return result; + } + + + this.GetValue=function() + { + var result=new Array(); + for(var i in this.Data) + { + if (this.Data[i]==null || this.Data[i].Value == null) + { + result[i] = null; + } + else + { + // console.log(this.Data[i].Value); + // console.log(i); + if (!isNaN(this.Data[i].Value)) + result[i] = this.Data[i].Value; + else if (this.Data[i].Value instanceof Array) //支持数组 + result[i] = this.Data[i].Value; + else + result[i] = null; + } + } + + return result; + } + + this.GetPeriodSingleData=function(period) + { + var result=new Array(); + var index=0; + var startDate=0; + var newData=null; + for(var i in this.Data) + { + var isNewData=false; + var dayData=this.Data[i]; + if (dayData==null || dayData.Date==null) continue; + + switch(period) + { + case 1: //周线 + var fridayDate=ChartData.GetFirday(dayData.Date); + if (fridayDate!=startDate) + { + isNewData=true; + startDate=fridayDate; + } + break; + case 2: //月线 + if (parseInt(dayData.Date/100)!=parseInt(startDate/100)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 3: //年线 + if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + } + + if (isNewData) + { + newData=new SingleData(); + newData.Date=dayData.Date; + newData.Value=dayData.Value; + result.push(newData); + } + else + { + if (newData==null) continue; + if (dayData.Value==null || isNaN(dayData.Value)) continue; + if (newData.Value==null || isNaN(newData.Value)) newData.Value=dayData.Value; + } + } + + return result; + } + + /* + 分钟数据方法 + this.GetClose() 每分钟价格 + this.GetVol() 每分钟成交量 + */ + + //分钟均线 + this.GetMinuteAvPrice=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].AvPrice; + } + + return result; + } + + this.MergeMinuteData = function (data) //合并数据 + { + var sourceFirstItem = this.Data[0]; + var firstItemID = 0; + var firstItem = null; + for (var i = 0; i < data.length; ++i) //查找比原始数据起始位置大的数据位置 + { + var item = data[i]; + if (item.Date >sourceFirstItem.Date) + { + firstItemID = i; + firstItem = item; + break; + } + + if (item.Date == sourceFirstItem.Date && item.Time >= sourceFirstItem.Time) + { + firstItemID = i; + firstItem = item; + break; + } + } + if (firstItem == null) return false; + + var index = null; + var bFind = false; //第1个数据是否完全匹配 + for (var i = this.Data.length - 1; i >= 0; --i) + { + var date = this.Data[i].Date; + var time = this.Data[i].Time; + + if (firstItem.Date > date || (firstItem.Date == date && firstItem.Time >= time) ) + { + index = i; + if (firstItem.Date == date && firstItem.Time == time) bFind = true; + break; + } + } + + if (index == null) return false; + var j = index; + var i = firstItemID; + if (bFind == true) //第1个数据匹配,覆盖 + { + var item = data[i]; + if (j - 1 > 0 && !item.YClose) item.YClose = this.Data[j - 1].Close; //前收盘如果没有就是上一记录的收盘 + var newItem = HistoryData.Copy(item); + this.Data[j] = newItem; + ++j; + ++i; + } + else + { + ++j; + } + + for (; i < data.length;) + { + var item = data[i]; + if (j >= this.Data.length - 1) + { + if (j - 1 > 0 && !item.YClose) item.YClose = this.Data[j - 1].YClose; //前收盘如果没有就是上一记录的收盘 + var newItem = HistoryData.Copy(item); + this.Data[j] = newItem; + ++j; + ++i; + } + else + { + var oldItem = this.Data[j]; + if (oldItem.Date == item.Date && oldItem.Time == item.Time) //更新数据 + { + HistoryData.CopyTo(oldItem, item); + ++j; + ++i; + } + else + { + ++j; + } + } + } + + //console.log('[ChartData::MergeMinuteData] ', this.Data, data); + return true; + } + + //日线拟合交易数据, 不做平滑处理 + this.GetFittingTradeData=function(tradeData, nullValue, bExactMatch) + { + var result=[]; + var bMatch=false; + for(var i=0,j=0;idate) + { + var item=new SingleData(); + item.Date=date; + item.Value=nullValue; + result[i]=item; + ++i; + continue; + } + } + + if (j+1date || (tradeData[j].Date==date && tradeData[j].Time>time)) + { + var item=new SingleData(); + item.Date=date; + item.Time=time; + item.Value=nullValue; + result[i]=item; + ++i; + continue; + } + } + + if (j+1 0) + { + var prevTimestamp = (24 * 60 * 60 * 1000) * (7 - day); + timestamp += prevTimestamp; + } + + date.setTime(timestamp); + var sundayDate = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate(); + var week = date.getDay(); + return sundayDate; +} + +ChartData.GetQuarter = function (value) +{ + var month = parseInt(value % 10000 / 100); + if (month == 1 || month == 2 || month == 3) return 1; + else if (month == 4 || month == 5 || month == 6) return 2; + else if (month == 7 || month == 8 || month == 9) return 3; + else if (month == 10 || month == 11 || month == 12) return 4; + else return 0; +} + +//是否是日线周期 0=日线 1=周线 2=月线 3=年线 9=季线 21=双周 [40001-50000) 自定义日线 (isIncludeBase 是否包含基础日线周期) +var CUSTOM_DAY_PERIOD_START = 40000, CUSTOM_DAY_PERIOD_END = 49999; +ChartData.IsDayPeriod = function (period, isIncludeBase) +{ + if (period == 1 || period == 2 || period == 3 || period == 9 || period==21 ) return true; + if (period > CUSTOM_DAY_PERIOD_START && period <= CUSTOM_DAY_PERIOD_END) return true; + if (period == 0 && isIncludeBase == true) return true; + + return false; +} + +//是否是分钟周期 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 11=2h 12=4h[20001-30000) 自定义分钟 (isIncludeBase 是否包含基础1分钟周期) +var CUSTOM_MINUTE_PERIOD_START = 20000, CUSTOM_MINUTE_PERIOD_END = 29999; +ChartData.IsMinutePeriod = function (period, isIncludeBase) +{ + if (period == 5 || period == 6 || period == 7 || period == 8 || period == 11 || period == 12) return true; + if (period > CUSTOM_MINUTE_PERIOD_START && period <= CUSTOM_MINUTE_PERIOD_END) return true; + if (period == 4 && isIncludeBase == true) return true; + + return false; +} + +//是否是秒周期 [30001-32000) +var CUSTOM_SECOND_PERIOD_START = 30000, CUSTOM_SECOND_PERIOD_END = 32000; +ChartData.IsSecondPeriod = function (period) +{ + if (period > CUSTOM_SECOND_PERIOD_START && period <= CUSTOM_SECOND_PERIOD_END) return true; + return false; +} + + +//是否是分笔图 +ChartData.IsTickPeriod = function (period) +{ + return period == 10; +} + +//获取周期名字 +ChartData.GetPeriodName = function (period) +{ + var mapName = new Map( + [ + [0, '日线'], [1, '周线'], [2, '月线'], [3, '年线'], [9, '季线'], [21, '双周'], + [4, '1分'], [5, '5分'], [6, '15分'], [7, '30分'], [8, '60分'], [11, '2小时'], [12, '4小时'], + [10, '分笔'] + ]); + + if (mapName.has(period)) return mapName.get(period); + + return ''; +} + +function Rect(x, y, width, height) +{ + this.Left = x, + this.Top = y; + this.Right = x + width; + this.Bottom = y + height; + + this.IsPointIn = function (x, y) + { + if (x >= this.Left && x <= this.Right && y >= this.Top && y <= this.Bottom) return true; + return false; + } +} + +//修正线段有毛刺 +function ToFixedPoint(value) +{ + //return value; + return parseInt(value) + 0.5; +} + +function ToFixedRect(value) +{ + var rounded; + return rounded = (0.5 + value) << 0; +} + +var JSCHART_EVENT_ID = +{ + RECV_INDEX_DATA: 2, //接收指标数据 + RECV_HISTROY_DATA: 3,//接收到历史数据 + RECV_TRAIN_MOVE_STEP: 4, //接收K线训练,移动一次K线 + CHART_STATUS: 5, //每次Draw() 以后会调用 + BARRAGE_PLAY_END: 6, //单个弹幕播放完成 + RECV_START_AUTOUPDATE: 9, //开始自动更新 + RECV_STOP_AUTOUPDATE: 10, //停止自动更新 + ON_TITLE_DRAW: 12, //标题信息绘制事件 + RECV_MINUTE_DATA: 14, //分时图数据到达 + ON_CLICK_INDEXTITLE:15, //点击指标标题事件 + RECV_KLINE_UPDATE_DATA: 16, //K线日,分钟更新数据到达 + ON_INDEXTITLE_DRAW: 19, //指标标题重绘事件 + ON_CUSTOM_VERTICAL_DRAW: 20, //自定义X轴绘制事件 + ON_ENABLE_SPLASH_DRAW:22, //开启/关闭过场动画事件 + + ON_DRAW_DEPTH_TOOLTIP:25, //绘制深度图tooltip事件 + ON_PHONE_TOUCH:27, //手势点击事件 包含 TouchStart 和 TouchEnd + + ON_SPLIT_YCOORDINATE:29, //分割Y轴及格式化刻度文字 +} + +function PhoneDBClick() +{ + this.Start=[]; + + this.Clear=function() + { + this.Start=[]; + } + + this.AddTouchStart=function(x, y, time) + { + if (this.Start.length>0) + { + var item=this.Start[this.Start.length-1]; + var spanTime=time-item.Time; + if (spanTime>0 && spanTime<300) + { + this.Start.push({ X:x, Y:y, Time:time }); + } + else + { + this.Start=[]; + } + } + else + { + this.Start.push({ X:x, Y:y, Time:time }); + } + } + + this.IsVaildDBClick=function() + { + if (this.Start.length==2) return true; + + return false; + } + + this.AddTouchEnd=function(time) + { + if (this.Start.length<=0) return; + + var item=this.Start[this.Start.length-1]; + var spanTime=time-item.Time; + if (spanTime>=0 && spanTime<150) + { + + } + else + { + this.Start=[]; + } + } +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonData: + { + HistoryData: HistoryData, + ChartData: ChartData, + SingleData: SingleData, + MinuteData: MinuteData, + Rect: Rect, + DataPlus: DataPlus, + JSCHART_EVENT_ID:JSCHART_EVENT_ID, + PhoneDBClick:PhoneDBClick, + }, + + //单个类导出 + JSCommon_ChartData: ChartData, + JSCommon_HistoryData: HistoryData, + JSCommon_SingleData: SingleData, + JSCommon_MinuteData: MinuteData, + JSCommon_CUSTOM_DAY_PERIOD_START: CUSTOM_DAY_PERIOD_START, + JSCommon_CUSTOM_DAY_PERIOD_END: CUSTOM_DAY_PERIOD_END, + JSCommon_CUSTOM_MINUTE_PERIOD_START: CUSTOM_MINUTE_PERIOD_START, + JSCommon_CUSTOM_MINUTE_PERIOD_END: CUSTOM_MINUTE_PERIOD_END, + JSCommon_CUSTOM_SECOND_PERIOD_START: CUSTOM_SECOND_PERIOD_START, + JSCommon_CUSTOM_SECOND_PERIOD_END: CUSTOM_SECOND_PERIOD_END, + JSCommon_Rect: Rect, + JSCommon_DataPlus: DataPlus, + JSCommon_Guid: Guid, + JSCommon_ToFixedPoint: ToFixedPoint, + JSCommon_ToFixedRect: ToFixedRect, + JSCommon_JSCHART_EVENT_ID:JSCHART_EVENT_ID, + JSCommon_PhoneDBClick:PhoneDBClick, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.explainer.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.explainer.wechat.js new file mode 100644 index 0000000..825b53d --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.explainer.wechat.js @@ -0,0 +1,1030 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 分析家脚本翻译器 +*/ + +import { JSConsole } from "./umychart.console.wechat.js" + +import { + JSCommonComplier_ErrorHandler as ErrorHandler, + JSCommonComplier_JSComplier as JSComplier, + JSCommonComplier_JSParser as JSParser, + JSCommonComplier_Syntax as Syntax, + JS_EXECUTE_JOB_ID as JS_EXECUTE_JOB_ID, + g_JSComplierResource as g_JSComplierResource, +} from "./umychart.complier.wechat"; + +import +{ + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, +} from './umychart.framesplit.wechat.js' + + +//脚本说明 +function JSExplainer(ast,option) +{ + this.AST=ast; + this.ErrorHandler=new ErrorHandler(); + this.ErrorCallback; //执行错误回调 + this.UpdateUICallback; + this.CallbackParam; + this.JobList=[]; //执行的任务队列 + this.VarTable=new Map(); //变量表 + this.OutVarTable=[]; //输出变量 + + //脚本自动变量表, 只读 + this.ConstVarTable=new Map( + [ + //个股数据 + ['CLOSE',"收盘价"],['VOL',"成交量"],['OPEN',"开盘价"],['HIGH',"最高价"],['LOW',"最低价"],['AMOUNT',"成交量"], + ['C',"收盘价"],['V',"成交量"],['O',"开盘价"],['H',"最高价"],['L',"最低价"],['AMO',"成交量"], + ['VOLR',"量比"], ['VOLINSTK',"持仓量"], ["OPI","持仓量"], ["ZSTJJ","均价"], ["QHJSJ","结算价"], ["SETTLE", "结算价"], + + //日期类 + ['DATE',"日期"],['YEAR',"年份"],['MONTH',"月份"],['PERIOD', "周期"],['WEEK',"星期"],["TIME","时间"], + + //大盘数据 + ['INDEXA',"大盘成交额"],['INDEXC',"大盘收盘价"],['INDEXH',"大盘最高价"],['INDEXL',"大盘最低价"],['INDEXO',"大盘开盘价"],['INDEXV',"大盘成交量"], + ['INDEXADV',"大盘上涨家数"],['INDEXDEC',"´大盘下跌家数"], + + ["ADVANCE","上涨家数"], ['DECLINE', "下跌家数"], + + ['FROMOPEN',"当前离开盘分钟数"], + ['TOTALFZNUM', "总分钟数"], + + ['CURRBARSCOUNT',"到最后交易的周期"], //到最后交易日的周期数 + ['TOTALBARSCOUNT',"总的周期数"], + ['ISLASTBAR',"是否是最后一个周期"], //判断是否为最后一个周期 + ['BARSTATUS',"数据位置状态"], //BARSTATUS返回数据位置信息,1表示第一根K线,2表示最后一个数据,0表示中间位置. + + ['CAPITAL',"当前流通股本(手)"], ["TOTALCAPITAL","当前总股本(手)"], + ['EXCHANGE',"换手率"], //换手率 + ['SETCODE', "市场类型"], //市场类型 + ['CODE',"品种代码"], //品种代码 + ['STKNAME',"品种名称"], //品种名称 + ["TQFLAG","当前复权状态"], //TQFLAG 当前的复权状态,0:无复权 1:前复权 2:后复权 + + ['HYBLOCK',"所属行业"], //所属行业板块 + ['DYBLOCK',"所属地域"], //所属地域板块 + ['GNBLOCK',"所属概念"], //所属概念 + ["FGBLOCK","所属风格板块"], + ["ZSBLOCK","所属指数板块"], + ["ZHBLOCK",'所属组合板块'], + ["ZDBLOCK",'所属自定义板块'], + ["HYZSCODE","所属行业的板块指数代码"], + + ["GNBLOCKNUM","所属概念板块的个数"], + ["FGBLOCKNUM","所属风格板块的个数"], + ["ZSBLOCKNUM","所属指数板块的个数"], + ["ZHBLOCKNUM","所属组合板块的个数"], + ["ZDBLOCKNUM","所属自定义板块的个数"], + + ["HYSYL","指数市盈率或个股所属行业的市盈率"], + ["HYSJL","指数市净率或个股所属行业的市净率"], + + ['DRAWNULL',"无效数据"] + + ]); + + if (option) + { + if (option.Callback) this.UpdateUICallback=option.Callback; + if (option.CallbackParam) this.CallbackParam=option.CallbackParam; + if (option.Arguments) this.Arguments=option.Arguments; + } + + this.Run=function() + { + try + { + this.OutVarTable=[]; + this.VarTable=new Map(); + JSConsole.Complier.Log('[JSExecute::JSExplainer] Load Arguments', this.Arguments); + for(let i in this.Arguments) //预定义的变量 + { + let item =this.Arguments[i]; + this.VarTable.set(item.Name,item.Value); + } + + let data=this.RunAST();//执行脚本 + JSConsole.Complier.Log('[JSExplainer.Run] explain finish', data); + if (this.UpdateUICallback) //回调发送结果, 可以支持异步 + { + JSConsole.Complier.Log('[JSExplainer.Run] invoke UpdateUICallback.'); + this.UpdateUICallback(data); + } + } + catch(error) + { + JSConsole.Complier.Log('[JSExplainer.Run] throw error ', error); + if (this.ErrorCallback) + { + this.ErrorCallback(error, this.OutVarTable); + } + } + } + + + this.RunAST=function() + { + if (!this.AST) this.ThrowError(); + if (!this.AST.Body) this.ThrowError(); + + for(let i in this.AST.Body) + { + let item =this.AST.Body[i]; + this.VisitNode(item); + + //输出变量 + if (item.Type==Syntax.ExpressionStatement && item.Expression) + { + if (item.Expression.Type==Syntax.AssignmentExpression) + { + if (item.Expression.Operator==':' && item.Expression.Left) + { + let assignmentItem=item.Expression; + let varName=assignmentItem.Left.Name; + let outVar=`输出${varName}: ${this.VarTable.get(varName)}`; + this.OutVarTable.push({ Name:varName, Data:outVar,Type:0}); + } + else if (item.Expression.Operator==':=' && item.Expression.Left) + { + let assignmentItem=item.Expression; + let varName=assignmentItem.Left.Name; + let outVar=`赋值${varName}: ${this.VarTable.get(varName)}`; + this.OutVarTable.push({ Name:varName, Data:outVar,Type:0, IsOut:false }); + } + } + else if (item.Expression.Type==Syntax.CallExpression) + { + let callItem=item.Expression; + if (this.IsDrawFunction(callItem.Callee.Name)) + { + let outVar=callItem.Out; + var drawName=callItem.Callee.Name; + this.OutVarTable.push({Name:drawName, Draw:`输出: ${outVar}`, Type:1}); + } + else + { + let outVar=callItem.Out; + varName=`__temp_c_${callItem.Callee.Name}_${i}__`; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + } + else if (item.Expression.Type==Syntax.Identifier) + { + let varName=item.Expression.Name; + let outVar=this.ReadVariable(varName,item.Expression); + varName="__temp_i_"+i+"__"; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`, Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.Literal) //常量 + { + let outVar=item.Expression.Value; + if (IFrameSplitOperator.IsString(outVar) && outVar.indexOf("$")>0) + outVar=this.GetOtherSymbolExplain({ Literal:outVar }, item); + varName="__temp_li_"+i+"__"; + var type=0; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`, Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.BinaryExpression) // CLOSE+OPEN; + { + var varName="__temp_b_"+i+"__"; + let outVar=item.Expression.Out; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.LogicalExpression) //逻辑语句 如 T1 AND T2 + { + var varName="__temp_l_"+i+"__"; + let outVar=item.Expression.Out; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.SequenceExpression) + { + let varName; + let drawName; + let draw; + let color; + let lineWidth; + let colorStick=false; + let pointDot=false; + let circleDot=false; + let lineStick=false; + let stick=false; + let volStick=false; + let isShow=true; + let isExData=false; + let isDotLine=false; + let isOverlayLine=false; //叠加线 + var isNoneName=false; + //显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如: + //DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE; + var isDrawAbove=false; + for(let j in item.Expression.Expression) + { + let itemExpression=item.Expression.Expression[j]; + if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left) + { + varName=itemExpression.Left.Name; + let varValue=this.VarTable.get(varName); + this.VarTable.set(varName,varValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.Identifier) + { + let value=itemExpression.Name; + if (value==='COLORSTICK') colorStick=true; + else if (value==='POINTDOT') pointDot=true; + else if (value==='CIRCLEDOT') circleDot=true; + else if (value==='DOTLINE') isDotLine=true; + else if (value==='LINESTICK') lineStick=true; + else if (value==='STICK') stick=true; + else if (value==='VOLSTICK') volStick=true; + else if (value==="DRAWABOVE") isDrawAbove=true; + else if (value.indexOf('COLOR')==0) color=value; + else if (value.indexOf('LINETHICK')==0) lineWidth=value; + else if (value.indexOf('NODRAW')==0) isShow=false; + else if (value.indexOf('EXDATA')==0) isExData=true; //扩展数据, 不显示再图形里面 + else if (value.indexOf('LINEOVERLAY')==0) isOverlayLine=true; + else + { + varName=itemExpression.Name; + let varValue=this.ReadVariable(varName,itemExpression); + varName="__temp_si_"+i+"__"; + isNoneName=true; + this.VarTable.set(varName,varValue); //放到变量表里 + } + } + else if(itemExpression.Type==Syntax.Literal) //常量 + { + let aryValue=itemExpression.Value; + varName=itemExpression.Value.toString(); + isNoneName=true; + this.VarTable.set(varName,aryValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.CallExpression) + { + if (this.IsDrawFunction(itemExpression.Callee.Name)) + { + draw=itemExpression.Out; + drawName=itemExpression.Callee.Name; + } + else + { + let varValue=itemExpression.Out; + varName=`__temp_sc_${itemExpression.Callee.Name}_${i}__`; + isNoneName=true; + this.VarTable.set(varName,varValue); + } + } + else if (itemExpression.Type==Syntax.BinaryExpression) + { + varName="__temp_sb_"+i+"__"; + let aryValue=itemExpression.Out; + isNoneName=true; + this.VarTable.set(varName,aryValue); + } + } + + var outValue; + if (draw) outValue=`输出: ${draw}`; + else if (isNoneName) outValue=`输出: ${this.VarTable.get(varName)}`; + else outValue=`输出${varName}: ${this.VarTable.get(varName)}`; + + if (color) outValue+=`,颜色${this.GetColorExplain(color)}`; + if (lineWidth) outValue+=`,线段粗细${this.GetLineWidthExplain(lineWidth)}`; + if (isShow==false) outValue+=",不显示"; + if (isDotLine==true) outValue+=",画虚线"; + if (isDrawAbove==true) outValue+=',显示在位置之上'; + + if (pointDot && varName) //圆点 + { + outValue+=",画小圆点线"; + let value={Name:varName, Data:outValue, Radius:g_JSChartResource.POINTDOT.Radius, Type:3}; + this.OutVarTable.push(value); + } + else if (circleDot && varName) //圆点 + { + outValue+=",画小圆圈线"; + let value={Name:varName, Data:outValue, Radius:g_JSChartResource.CIRCLEDOT.Radius, Type:3}; + this.OutVarTable.push(value); + } + else if (lineStick && varName) //LINESTICK 同时画出柱状线和指标线 + { + outValue+=",画出柱状线和指标线"; + let value={Name:varName, Data:outValue, Type:4}; + this.OutVarTable.push(value); + } + else if (stick && varName) //STICK 画柱状线 + { + outValue+=",画柱状线"; + let value={Name:varName, Data:outValue, Type:5}; + this.OutVarTable.push(value); + } + else if (volStick && varName) //VOLSTICK 画彩色柱状线 + { + outValue+=",画成交量柱状线"; + let value={Name:varName, Data:outValue, Type:6}; + this.OutVarTable.push(value); + } + else if (varName && color) + { + let value={Name:varName, Data:outValue, Color:color, Type:0}; + this.OutVarTable.push(value); + } + else if (draw) //画图函数 + { + var outVar={ Name:drawName, Data:outValue, Type:1 }; + this.OutVarTable.push(outVar); + } + else if (colorStick && varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子 + { + outValue+=",画彩色柱状线"; + let value={Name:varName, Data:outValue, Color:color, Type:2}; + this.OutVarTable.push(value); + } + else if (varName) + { + let value={Name:varName, Data:outValue,Type:0}; + this.OutVarTable.push(value); + } + } + } + } + + JSConsole.Complier.Log('[JSExplainer::Run]', this.VarTable); + return this.OutVarTable; + } + + this.VisitNode=function(node) + { + switch(node.Type) + { + case Syntax.SequenceExpression: + this.VisitSequenceExpression(node); + break; + case Syntax.ExpressionStatement: + this.VisitNode(node.Expression); + break; + case Syntax.AssignmentExpression: + this.VisitAssignmentExpression(node); + break; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + this.VisitBinaryExpression(node); + break; + case Syntax.CallExpression: + this.VisitCallExpression(node); + break; + } + } + + this.VisitSequenceExpression=function(node) + { + for(let i in node.Expression) + { + let item =node.Expression[i]; + this.VisitNode(item); + } + } + + //函数调用 + this.VisitCallExpression=function(node) + { + let funcName=node.Callee.Name; + let args=[]; + for(let i in node.Arguments) + { + let item=node.Arguments[i]; + let value; + if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(item); + else if (item.Type==Syntax.CallExpression) + value=this.VisitCallExpression(item); + else + value=this.GetNodeValue(item); + args.push(value); + } + + JSConsole.Complier.Log('[JSExplainer::VisitCallExpression]' , funcName, '(', args.toString() ,')'); + + if (g_JSComplierResource.IsCustomFunction(funcName)) + { + var data=this.Algorithm.CallCustomFunction(funcName, args, this.SymbolData, node); + node.Out=[]; + node.Draw=null; + + if (data) + { + if (data.Out) node.Out=data.Out; + if (data.Draw) node.Draw=data.Draw; + } + + return node.Out; + } + + node.Out=this.CallFunctionExplain(funcName, args, node); + return node.Out; + } + + this.FUNCTION_INFO_LIST=new Map( + [ + ["REF", { Name:"REF", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日前的${args[0]}`; } } ], + ["REFX", { Name:"REFX", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日后的${args[0]}`; } } ], + ["REFV", { Name:"REFV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日前的(未作平滑处理)${args[0]}`; } } ], + ["REFXV", { Name:"REFXV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日后的(未作平滑处理)${args[0]}`; } } ], + + ["REFDATE", { Name:"REFDATE", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日${args[0]}`; } } ], + ["COUNT", { Name:"COUNT", Param:{ Count:2 }, ToString:function(args) { return `统计${args[1]}日中满足${args[0]}的天数`; } } ], + ["BARSLASTCOUNT", { Name:"BARSLASTCOUNT", Param:{ Count:1 }, ToString:function(args) { return `条件${args[0]}连续成立次数`; } } ], + ["BARSCOUNT", { Name:"BARSCOUNT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}有效数据周期数`; } } ], + ["BARSLAST", { Name:"BARSLAST", Param:{ Count:1 }, ToString:function(args) { return `上次${args[0]}不为0距今天数`; } } ], + ["BARSNEXT", { Name:"BARSNEXT", Param:{ Count:1 }, ToString:function(args) { return `下次${args[0]}不为0距今天数`; } } ], + ["BARSSINCEN", { Name:"BARSSINCEN", Param:{ Count:2 }, ToString:function(args) { return `在${args[1]}周期内首次${args[0]}距今天数`; } } ], + ["BARSSINCE", { Name:"BARSSINCE", Param:{ Count:1 }, ToString:function(args) { return `首次${args[0]}距今天数`; } } ], + ["HHV", { Name:"HHV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的最高值`; } } ], + ["LLV", { Name:"LLV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的最低值`; } } ], + + ["HOD", { Name:"HOD", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的高值名次`; } } ], + ["LOD", { Name:"LOD", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的低值名次`; } } ], + ["REVERSE", { Name:"REVERSE", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的相反数`; } } ], + ["FILTER", { Name:"FILTER", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日过滤`; } } ], + ["FILTERX", { Name:"FILTERX", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日反向过滤`; } } ], + ["SUMBARS", { Name:"SUMBARS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}累加至${args[1]}的天数`; } } ], + ["MA", { Name:"MA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日简单移动平均`; } } ], + ["SMA", { Name:"SMA", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}的${args[1]}日[${args[2]}日权重]移动平均`; } } ], + ["MEMA", { Name:"MEMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日平滑移动平均`; } } ], + ["EMA", { Name:"EMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日指数移动平均`; } } ], + ["EXPMA", { Name:"EXPMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日指数移动平均`; } } ], + ["WMA", { Name:"WMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日加权移动平均`; } } ], + ["DMA", { Name:"DMA", Param:{ Count:2 }, ToString:function(args) { return `以${args[1]}为权重${args[0]}的动态移动平均`; } } ], + ["XMA", { Name:"XMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日偏移移动平均`; } } ], + + ["RANGE", { Name:"RANGE", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}位于${args[1]}和${args[2]}之间`; } } ], + ["CONST", { Name:"CONST", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的最后一日值`; } } ], + ["TOPRANGE", { Name:"TOPRANGE", Param:{ Count:1 }, ToString:function(args) { return `当前值是近${args[0]}周期的最大值`; } } ], + ["LOWRANGE", { Name:"LOWRANGE", Param:{ Count:1 }, ToString:function(args) { return `当前值是近${args[0]}周期的最小值`; } } ], + ["FINDHIGH", { Name:"FINDHIGH", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最高价`; } } ], + ["FINDHIGHBARS", { Name:"FINDHIGHBARS", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最高价到当前周期的周期数`; } } ], + ["FINDLOW", { Name:"FINDLOW", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最低价`; } } ], + ["FINDLOWBARS", { Name:"FINDLOWBARS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最低价到当前周期的周期数`; } } ], + ["SUM", { Name:"SUM", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}${args[1]}日累加`; } } ], + ["MULAR", { Name:"MULAR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}日累乘`; } } ], + ["AMA", { Name:"AMA", Param:{ Count:2 }, ToString:function(args) { return `以${args[1]}为权重${args[0]}的自适应均线`; } } ], + ["TMA", { Name:"TMA", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}的${args[1]}日[${args[2]}日权重]移动平均`; } } ], + ["CROSS", { Name:"CROSS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}上穿${args[1]}`; } } ], + ["LONGCROSS", { Name:"LONGCROSS", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}小于${args[1]}保持${args[2]}个交易日后交叉上穿`; } } ], + ["UPNDAY", { Name:"UPNDAY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日${args[0]}连涨`; } } ], + ["DOWNNDAY", { Name:"DOWNNDAY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日${args[0]}连跌`; } } ], + ["NDAY", { Name:"NDAY", Param:{ Count:3 }, ToString:function(args) { return `最近${args[2]}日${args[0]}一直大于${args[1]}`; } } ], + ["EXIST", { Name:"EXIST", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日存在${args[0]}`; } } ], + ["EXISTR", { Name:"EXISTR", Param:{ Count:3 }, ToString:function(args) { return `从前${args[1]}日到前${args[2]}日存在${args[0]}`; } } ], + ["EVERY", { Name:"EVERY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日一直存在${args[0]}`; } } ], + ["LAST", { Name:"LAST", Param:{ Count:3 }, ToString:function(args) { return `从前${args[1]}日到前${args[2]}日持续${args[0]}`; } } ], + ["NOT", { Name:"NOT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}取反`; } } ], + ["IF", { Name:"IF", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + ["IFF", { Name:"IFF", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + ["IFN", { Name:"IFN", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + + ["MAX", { Name:"MAX", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}的较大值`; } } ], + ["MIN", { Name:"MIN", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}的较小值`; } } ], + ["ACOS", { Name:"ACOS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反余弦`; } } ], + ["ASIN", { Name:"ASIN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反正弦`; } } ], + ["ATAN", { Name:"ATAN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反正切`; } } ], + ["COS", { Name:"COS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的余弦`; } } ], + ["SIN", { Name:"SIN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的正弦`; } } ], + ["TAN", { Name:"TAN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的正切`; } } ], + ["EXP", { Name:"EXP", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的指数`; } } ], + ["LN", { Name:"LN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的自然对数`; } } ], + ["LOG", { Name:"LOG", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的对数`; } } ], + ["SQRT", { Name:"SQRT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的开方`; } } ], + ["ABS", { Name:"ABS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的绝对值`; } } ], + ["POW", { Name:"POW", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}乘幂`; } } ], + ["CEILING", { Name:"CEILING", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的向上舍入`; } } ], + ["FLOOR", { Name:"FLOOR", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的向上舍入`; } } ], + ["INTPART", { Name:"INTPART", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的整数部分`; } } ], + ["BETWEEN", { Name:"BETWEEN", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}位于${args[1]}和${args[2]}之间`; } } ], + ["FRACPART", { Name:"FRACPART", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的小数部分`; } } ], + ["ROUND", { Name:"ROUND", Param:{ Count:1 }, ToString:function(args) { return `对${args[0]}(进行)四舍五入`; } } ], + ["ROUND2", { Name:"ROUND2", Param:{ Count:2 }, ToString:function(args) { return `对${args[0]}(进行)四舍五入`; } } ], + ["SIGN", { Name:"SIGN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的符号`; } } ], + ["MOD", { Name:"MOD", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}关于${args[1]}的模`; } } ], + ["RAND", { Name:"RAND", Param:{ Count:1 }, ToString:function(args) { return `随机正整数`; } } ], + + ["AVEDEV", { Name:"AVEDEV", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日平均绝对偏差`; } } ], + ["DEVSQ", { Name:"DEVSQ", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日数据偏差平方和`; } } ], + ["FORCAST", { Name:"FORCAST", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日线性回归预测值`; } } ], + ["TSMA", { Name:"TSMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}在${args[1]}个周期内的时间序列三角移动平均`; } } ], + ["SLOPE", { Name:"SLOPE", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日线性回归斜率`; } } ], + ["STD", { Name:"STD", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日估算标准差`; } } ], + ["STDP", { Name:"STDP", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日总体标准差`; } } ], + ["STDDEV", { Name:"STDDEV", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日标准偏差`; } } ], + ["VAR", { Name:"VAR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日估算样本方差`; } } ], + ["VARP", { Name:"VARP", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日总体样本方差`; } } ], + ["COVAR", { Name:"COVAR", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[2]}周期的协方差`; } } ], + ["RELATE", { Name:"RELATE", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[0]}周期的相关系数`; } } ], + ["BETA", { Name:"BETA", Param:{ Count:1 }, ToString:function(args) { return `β(Beta)系数`; } } ], + ["BETAEX", { Name:"BETAEX", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[2]}周期的相关放大系数`; } } ], + + ["COST", { Name:"COST", Param:{ Count:1 }, ToString:function(args) { return `获利盘为${args[0]}%的成本分布`; } } ], + ["WINNER", { Name:"WINNER", Param:{ Count:1 }, ToString:function(args) { return `以${args[0]}计算的获利盘比例`; } } ], + ["LWINNER", { Name:"LWINNER", Param:{ Count:2 }, ToString:function(args) { return `最近${args[0]}日那部分成本以${args[1]}价格卖出的获利盘比例`; } } ], + ["PWINNER", { Name:"PWINNER", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}日前那部分成本以${args[1]}价格卖出的获利盘比例`; } } ], + ["COSTEX", { Name:"COSTEX", Param:{ Count:2 }, ToString:function(args) { return `位于价格${args[0]}和${args[1]}间的成本`; } } ], + ["PPART", { Name:"PPART", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}日前那部分成本占总成本的比例`; } } ], + + ["SAR", { Name:"SAR", Param:{ Count:3 }, ToString:function(args) { return `步长为${args[1]}极限值为${args[0]}的${args[2]}日抛物转向`; } } ], + ["SARTURN", { Name:"SARTURN", Param:{ Count:3 }, ToString:function(args) { return `步长为${args[1]}极限值为${args[0]}的${args[2]}日抛物转向点`; } } ], + + //字符串函数 + ["CON2STR", { Name:"CON2STR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}转为字符串`; } } ], + ["VAR2STR", { Name:"VAR2STR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}转为字符串`; } } ], + ["STR2CON", { Name:"STR2CON", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}转为数字`; } } ], + ["STRLEN", { Name:"STRLEN", Param:{ Count:1 }, ToString:function(args) { return `得到${args[0]}字符串长度`; } } ], + ["STRCAT", { Name:"STRCAT", Param:{ Count:2 }, ToString:function(args) { return `字符串相加`; } } ], + ["VARCAT", { Name:"VARCAT", Param:{ Count:2 }, ToString:function(args) { return `字符串相加`; } } ], + ["STRSPACE", { Name:"STRSPACE", Param:{ Count:1 }, ToString:function(args) { return `字符串${args[0]}加一空格`; } } ], + ["SUBSTR", { Name:"SUBSTR", Param:{ Count:3 }, ToString:function(args) { return `字符串${args[0]}中取一部分`; } } ], + ["STRCMP", { Name:"STRCMP", Param:{ Count:2 }, ToString:function(args) { return `字符串${args[0]}和字符串${args[1]}比较`; } } ], + ["FINDSTR", { Name:"FINDSTR", Param:{ Count:2 }, ToString:function(args) { return `字符串${args[0]}中查找字符串${args[1]}`; } } ], + ["NAMEINCLUD", { Name:"NAMEINCLUD", Param:{ Count:1 }, ToString:function(args) { return `查找品种名称中包含${args[0]}`; } } ], + ["CODELIKE", { Name:"CODELIKE", Param:{ Count:1 }, ToString:function(args) { return `查找品种名称中包含${args[0]}`; } } ], + ["INBLOCK", { Name:"AVEDEV", Param:{ Count:1 }, ToString:function(args) { return `属于${args[0]}板块`; } } ], + + + [ + "HHVBARS", + { + Name:"HHVBARS", Param:{ Count:2 }, + ToString:function(args) + { + if (args[1]==0) return `历史${args[0]}新高距今天数`; + return `${args[1]}日内${args[0]}新高距今天数`; + } + } + ], + + [ + "LLVBARS", + { + Name:"LLVBARS", Param:{ Count:2 }, + ToString:function(args) + { + if (args[1]==0) return `历史${args[0]}新低距今天数`; + return `${args[1]}日内${args[0]}新低距今天数`; + } + } + ] + ] + ); + + this.CallFunctionExplain=function(funcName, args, node) + { + if (this.FUNCTION_INFO_LIST.has(funcName)) + { + var item=this.FUNCTION_INFO_LIST.get(funcName); + if (item.Param.Count!=args.length) + this.ThrowUnexpectedNode(node,`函数${funcName}参数个数不正确. 需要${item.Param.Count}个参数`); + return item.ToString(args); + } + + switch(funcName) + { + case "CALCSTOCKINDEX": + return `引用${args[0]}的${args[1]}指标第${args[2]}个输出值`; + + case "PEAK": + case "PEAKBARS": + case "ZIG": + case "ZIGA": + case "TROUGH": + case "TROUGHBARS": + return this.GetZIGExplain(funcName,args); + + case "FINANCE": + return this.GetFinanceExplain(args); + case "DYNAINFO": + return this.GetDynainfoExplain(args); + + case 'CLOSE': + case 'C': + case 'VOL': + case 'V': + case 'OPEN': + case 'O': + case 'HIGH': + case 'H': + case 'LOW': + case 'L': + case 'AMOUNT': + case 'AMO': + return this.GetOtherSymbolExplain( {FunctionName:funcName, Args:args} ,node); + + //绘图函数 + case "PLOYLINE": + return `当满足条件${args[0]}时以${args[1]}位置为顶点画折线连接`; + case "DRAWLINE": + return `当满足条件${args[0]}时,在${args[1]}位置画直线起点,当满足条件${args[2]}时,在${args[3]}位置画直线终点,${args[4]}表示是否延长`; + case "DRAWSL": + return `当满足条件${args[0]}时,在${args[1]}位置画斜线线性回归,${args[2]}斜率,${args[3]}长度,${args[4]}方向`; + case "DRAWKLINE": + return 'K线'; + case "DRAWICON": + return `当满足条件${args[0]}时,在${args[1]}位置画${args[2]}号图标`; + case "DRAWTEXT": + return `当满足条件${args[0]}时,在${args[1]}位置书写文字`; + case "DRAWTEXT_FIX": + return `当满足条件${args[0]}时,在横轴${args[1]}纵轴${args[2]}位置书写文字`; + case "DRAWNUMBER": + return `当满足条件${args[0]}时,在${args[1]}位置书写数字`; + case "DRAWNUMBER_FIX": + return `当满足条件${args[0]}时,在横轴${args[1]}纵轴${args[2]}位置书写数字`; + case "RGB": + return `自定色[${args[0]},${args[1]},${args[2]}]`; + case "DRAWBAND": + return '画带状线'; + case "DRAWRECTREL": + return "相对位置上画矩形."; + case "DRAWGBK": + return "填充背景"; + case "STICKLINE": + var barType=""; + if (args[4]==-1) barType="虚线空心柱"; + else if (args[4]==0) barType="实心柱"; + else barType="实线空心柱"; + return `当满足条件${args[0]}时, 在${args[1]}和${args[2]}位置之间画柱状线,宽度为${args[3]},${barType}`; + + default: + this.ThrowUnexpectedNode(node,`函数${funcName}不存在`); + } + } + + this.GetDynainfoExplain=function(args) + { + const DATA_NAME_MAP=new Map( + [ + [3,"前收盘价"], [4,"开盘价"], [5,"最高价"], [6,"最低价"], [7,"现价"], [8,'总量'], [9,"现量"], + [10,"总金额"], [11,"均价"], [12,"日涨跌"], [13,"振幅"], [14,"涨幅"], [15,"开盘时的成交金额"], + [16,"前5日每分钟均量"], [17,"量比"], [18,"上涨家数"], [19,"下跌家数"] + ]); + + var id=args[0]; + if (DATA_NAME_MAP.has(id)) return DATA_NAME_MAP.get(id); + + return `即时行情[${id}]`; + } + + this.GetFinanceExplain=function(args) + { + const DATA_NAME_MAP=new Map( + [ + [1,"总股本"], [2,"市场类型"], [3,"沪深品种类型"], [4,"沪深行业代码"], [5,"B股"], [6,"H股"], [7,"流通股本[股]"], [8,"股东人数[户]"], [9,"资产负债率%"], + [10,"总资产"], [11,"流动资产"], [12,"固定资产"], [13,"无形资产"], [15,"流动负债"], [16,"少数股东权益"] + + ]); + var id=args[0]; + + if (DATA_NAME_MAP.has(id)) return DATA_NAME_MAP.get(id); + + return `财务数据[${id}]`; + } + + this.GetZIGExplain=function(funcName,args) + { + var value=args[0]; + if (value==0) value="开盘价"; + else if (value==1) value="最高价"; + else if (value==2) value="最低价"; + else if (value==3) value="收盘价"; + + switch(funcName) + { + case "PEAK": + return `${value}的${args[1]}%之字转向的前${args[2]}个波峰值`; + case "PEAKBARS": + return `${value}的${args[1]}5%之字转向的前${args[2]}个波峰位置`; + case "ZIG": + return `${value}的${args[1]}的之字转向`; + case "ZIGA": + return `${value}变化${args[1]}的之字转向`; + case "TROUGH": + return `${value}的${args[1]}%之字转向的前${args[2]}个波谷值`; + case "TROUGHBARS": + return `${value}的${args[1]}%之字转向的前${args[2]}个波谷位置`; + } + } + + this.GetColorExplain=function(colorName) + { + const COLOR_MAP=new Map( + [ + ['COLORBLACK','黑色'],['COLORBLUE','蓝色'],['COLORGREEN','绿色'],['COLORCYAN','青色'],['COLORRED','红色'], + ['COLORMAGENTA','洋红色'],['COLORBROWN','棕色'],['COLORLIGRAY','淡灰色'],['COLORGRAY','深灰色'],['COLORLIBLUE','淡蓝色'], + ['COLORLIGREEN','淡绿色'],['COLORLICYAN','淡青色'],['COLORLIRED','淡红色'],['COLORLIMAGENTA','淡洋红色'],['COLORWHITE','白色'],['COLORYELLOW','黄色'] + ]); + + if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName); + + //COLOR 自定义色 + //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。 + //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。 + if (colorName.indexOf('COLOR')==0) return '#'+colorName.substr(5); + + return 'rgb(30,144,255)'; + } + + this.GetLineWidthExplain=function(lineWidth) + { + var width=parseInt(lineWidth.replace("LINETHICK","")); + if (IFrameSplitOperator.IsPlusNumber(width)) return width; + return 1; + } + + this.SymbolPeriodExplain=function(valueName,period) + { + const mapStockDataName=new Map( + [ + ['CLOSE',"收盘价"],["C","收盘价"],['VOL',"成交量"],['V',"成交量"], ['OPEN',"开盘价"], ['O',"开盘价"], + ['HIGH',"最高价"],['H',"最高价"], ['LOW',"最低价"],['L',"最低价"],['AMOUNT',"成交金额"],['AMO',"成交金额"], + ['VOLINSTK',"持仓量"] + ]); + //MIN1,MIN5,MIN15,MIN30,MIN60,DAY,WEEK,MONTH,SEASON,YEAR + const mapPeriodName=new Map( + [ + ["MIN1","1分钟"], ["MIN5", "5分钟"], ["MIN15", "15分钟"], ["MIN30","30分钟"],["MIN60","60分钟"], + ["DAY","日"],["WEEK","周"], ["MONTH", "月"], ['SEASON',"季"], ["YEAR", "年"],["WEEK2","双周"], ["HALFYEAR", "半年"] + ]); + + var dataName=valueName; + if (mapStockDataName.has(valueName)) dataName=mapStockDataName.get(valueName); + + var periodName=period; + if (mapPeriodName.has(period)) periodName=mapPeriodName.get(period); + + return `${dataName}[取${periodName}数据]`; + } + + this.GetOtherSymbolExplain=function(obj, node) + { + const mapStockDataName=new Map( + [ + ['CLOSE',"收盘价"],["C","收盘价"],['VOL',"成交量"],['V',"成交量"], ['OPEN',"开盘价"], ['O',"开盘价"], + ['HIGH',"最高价"],['H',"最高价"], ['LOW',"最低价"],['L',"最低价"],['AMOUNT',"成交金额"],['AMO',"成交金额"], + ['VOLINSTK',"持仓量"] + ]); + + if (obj.FunctionName) + { + var args=obj.Args; + var dataName=mapStockDataName.get(obj.FunctionName); + return `[${args[0]}]${dataName}`; + } + else if (obj.Literal) + { + var value=obj.Literal.toUpperCase(); + var args=value.split("$"); + if (!mapStockDataName.has(args[1])) return ""; + var symbol=args[0]; + var dataName=mapStockDataName.get(args[1]); + return `[${symbol}]${dataName}`; + } + } + + this.IsDrawFunction=function(name) + { + let setFunctionName=new Set( + [ + "STICKLINE","DRAWTEXT",'SUPERDRAWTEXT','DRAWLINE','DRAWBAND','DRAWKLINE','DRAWKLINE_IF','PLOYLINE', + 'POLYLINE','DRAWNUMBER',"DRAWNUMBER_FIX",'DRAWICON','DRAWCHANNEL','PARTLINE','DRAWTEXT_FIX','DRAWGBK','DRAWTEXT_LINE','DRAWRECTREL',"DRAWTEXTABS", + 'DRAWOVERLAYLINE',"FILLRGN", "FILLRGN2","FILLTOPRGN", "FILLBOTTOMRGN", "FILLVERTICALRGN","FLOATRGN","DRAWSL", "DRAWGBK2" + ]); + if (setFunctionName.has(name)) return true; + + return false; + } + + //赋值 + this.VisitAssignmentExpression=function(node) + { + let left=node.Left; + if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node); + + let varName=left.Name; + + let right=node.Right; + let value=null; + if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(right); + else if (right.Type==Syntax.CallExpression) + value=this.VisitCallExpression(right); + else if (right.Type==Syntax.Literal) + { + value=right.Value; + if (IFrameSplitOperator.IsString(value) && right.Value.indexOf("$")>0) + value=this.GetOtherSymbolExplain({ Literal:value }, node); + } + else if (right.Type==Syntax.Identifier) //右值是变量 + value=this.ReadVariable(right.Name,right); + else if (right.Type==Syntax.MemberExpression) + value=this.ReadMemberVariable(right); + else if (right.Type==Syntax.UnaryExpression) + { + if (right.Operator=='-') + { + var tempValue=this.GetNodeValue(right.Argument); + value='-'+tempValue; + } + else + { + value=right.Argument.Value; + } + } + + JSConsole.Complier.Log('[JSExplainer::VisitAssignmentExpression]' , varName, ' = ',value); + this.VarTable.set(varName,value); + } + + //逻辑运算 + this.VisitBinaryExpression=function(node) + { + let stack=[]; + stack.push(node); + let temp=null; + + while(stack.length!=0) + { + temp=stack[stack.length-1]; + if (temp.Left && node!=temp.Left && node!=temp.Right) + { + stack.push(temp.Left); + } + else if (temp.Right && node!=temp.Right) + { + stack.push(temp.Right); + } + else + { + let value=stack.pop(); + if (value.Type==Syntax.BinaryExpression) //只遍历操作符就可以 + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + value.Out=`(${leftValue} ${value.Operator} ${rightValue})`; + if (leftValue=="收盘价" && rightValue=="开盘价") + { + if (value.Operator==">") value.Out='(收阳线)'; + else if (value.Operator=="<") value.Out='(收阴线)'; + else if (value.Operator=="=") value.Out='(平盘)'; + } + else if (leftValue=="开盘价" && rightValue=="收盘价") + { + if (value.Operator=="<") value.Out='(收阳线)'; + else if (value.Operator==">") value.Out='(收阴线)'; + else if (value.Operator=="=") value.Out='(平盘)'; + } + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] BinaryExpression',value); + } + else if (value.Type==Syntax.LogicalExpression) + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '&&': + case 'AND': + value.Out=`(${leftValue} 并且 ${rightValue})`; + break; + case '||': + case 'OR': + value.Out=`(${leftValue} 或者 ${rightValue})`; + break; + } + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] LogicalExpression',value); + } + + node=temp; + } + } + + return node.Out; + + } + + this.GetNodeValue=function(node) + { + switch(node.Type) + { + case Syntax.Literal: //数字 + return node.Value; + case Syntax.UnaryExpression: + if (node.Operator=='-') + { + let value=this.GetNodeValue(node.Argument); + return '-'+value; + } + return node.Argument.Value; + case Syntax.Identifier: + let value=this.ReadVariable(node.Name,node); + return value; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + return node.Out; + case Syntax.CallExpression: + return this.VisitCallExpression(node); + default: + this.ThrowUnexpectedNode(node); + } + } + + //读取变量 + this.ReadVariable=function(name,node) + { + if (this.ConstVarTable.has(name)) + { + let data=this.ConstVarTable.get(name); + return data; + } + + if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量 + + if (this.VarTable.has(name)) return this.VarTable.get(name); + + if (name.indexOf('#')>0) + { + var aryPeriod=name.split('#'); + return this.SymbolPeriodExplain(aryPeriod[0],aryPeriod[1]); + } + + this.ThrowUnexpectedNode(node, '变量'+name+'不存在'); + return name; + } + + this.ThrowUnexpectedNode=function(node,message) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg); + + } + + this.ThrowError=function() + { + + } +} + + +JSComplier.Explain=function(code,option, errorCallback) +{ + //异步调用 + //var asyncExecute= async function() es5不能执行 去掉异步 + var asyncExplain= function() + { + try + { + JSConsole.Complier.Log('[JSComplier.Explain]',code,option); + + JSConsole.Complier.Log('[JSComplier.Explain] parser .....'); + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + + let ast=program; + JSConsole.Complier.Log('[JSComplier.Explain] parser finish.', ast); + + JSConsole.Complier.Log('[JSComplier.Explain] explain .....'); + let execute=new JSExplainer(ast,option); + execute.ErrorCallback=errorCallback; //执行错误回调 + execute.JobList=parser.Node.GetDataJobList(); + execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT}); + let result=execute.Run(); + + }catch(error) + { + JSConsole.Complier.Log(error); + + if (errorCallback) errorCallback(error, option.CallbackParam); + } + } + + asyncExplain(); + + JSConsole.Complier.Log('[JSComplier.Explain] async explain.'); +} \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.extendchart.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.extendchart.wechat.js new file mode 100644 index 0000000..09df6bf --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.extendchart.wechat.js @@ -0,0 +1,1003 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 图形扩展画法 +*/ +//日志 +import { JSConsole } from "./umychart.console.wechat.js" + +//行情数据结构体 及涉及到的行情算法(复权,周期等) +import +{ + JSCommon_ChartData as ChartData, JSCommon_HistoryData as HistoryData, + JSCommon_SingleData as SingleData, JSCommon_MinuteData as MinuteData, + JSCommon_Guid as Guid, + JSCommon_ToFixedPoint as ToFixedPoint, + JSCommon_ToFixedRect as ToFixedRect, +} from "./umychart.data.wechat.js"; + +import +{ + JSCommonCoordinateData as JSCommonCoordinateData, + JSCommonCoordinateData_MARKET_SUFFIX_NAME as MARKET_SUFFIX_NAME +} from "./umychart.coordinatedata.wechat.js"; + +import +{ + JSCommonResource_Global_JSChartResource as g_JSChartResource, + JSCommonResource_JSCHART_LANGUAGE_ID as JSCHART_LANGUAGE_ID, + JSCommonResource_Global_JSChartLocalization as g_JSChartLocalization, +} from './umychart.resource.wechat.js' + +import +{ + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, +} from './umychart.framesplit.wechat.js' + +function IExtendChartPainting() +{ + this.Canvas; //画布 + this.ChartBorder; //边框信息 + this.ChartFrame; //框架画法 + this.Name; //名称 + this.Data; // = new ChartData(); //数据区 + this.ClassName = 'IExtendChartPainting'; + this.IsDynamic = false; + this.IsEraseBG = false; //是否每次画的时候需要擦除K线图背景 + this.IsAnimation=false; + this.DrawAfterTitle = false; //是否在动态标题画完以后再画,防止动态标题覆盖 + + //上下左右间距 + this.Left = 5; + this.Right = 5; + this.Top = 5; + this.Bottom = 5; + + this.Draw = function () { } //画图接口 + this.SetOption = function (option) { } //设置参数接口 +} + +//K线Tooltip, 显示在左边或右边 +function KLineTooltipPaint() +{ + this.newMethod = IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.IsDynamic = true; + this.IsEraseBG = true; + this.DrawAfterTitle = true; + this.ClassName = 'KLineTooltipPaint'; + this.LatestPoint; //手势位置 + this.ShowPosition=0; //显示位置 0=左 1=右 + + this.BorderColor = g_JSChartResource.TooltipPaint.BorderColor; //边框颜色 + this.BGColor = g_JSChartResource.TooltipPaint.BGColor; //背景色 + this.TitleColor = g_JSChartResource.TooltipPaint.TitleColor; //标题颜色 + this.Font = [g_JSChartResource.TooltipPaint.TitleFont]; + + this.Width = 50; + this.Height = 100; + this.LineHeight = 15; //行高 + + this.Left = 1; + this.Top = 0; + + this.HQChart; + this.KLineTitlePaint; + this.IsHScreen = false; //是否横屏 + this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + + this.GetLeft = function () + { + if (this.IsHScreen) + { + return this.ChartBorder.GetRightEx()-this.Height-this.Top; + } + else + { + if (this.ShowPosition==0) + return this.ChartBorder.GetLeft()+this.Left; + else + return this.ChartBorder.GetRight()-this.Width-this.Left; + } + } + + this.GetTop = function () + { + if (this.IsHScreen) + { + if (this.ShowPosition==0) + return this.ChartBorder.GetTop()+this.Left; + else + return this.ChartBorder.GetBottom()-this.Width-this.Left; + } + else + { + return this.ChartBorder.GetTopEx()+this.Top; + } + } + + this.Draw = function () + { + if (!this.HQChart || !this.HQChart.TitlePaint || !this.HQChart.TitlePaint[0]) return; + if (!this.HQChart.IsOnTouch) return; + + this.KLineTitlePaint = this.HQChart.TitlePaint[0]; + var klineData = this.KLineTitlePaint.GetCurrentKLineData(); + if (!klineData) return; + + var upperSymbol; + if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase(); + var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)?true:false; + + var lineCount = 8; //显示函数 + if (this.ClassName === 'MinuteTooltipPaint') + { + lineCount=7; + if (isFutures && IFrameSplitOperator.IsNumber(klineData.Position)) ++lineCount; //期货多一个持仓量 + } + else + { + if (IFrameSplitOperator.IsNumber(klineData.Time)) ++lineCount; //分钟K线多一列时间 + if (isFutures && IFrameSplitOperator.IsNumber(klineData.Position)) ++lineCount; //持仓量 + } + + this.IsHScreen = this.ChartFrame.IsHScreen === true; + this.Canvas.font = this.Font[0]; + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数 + var maxText = ' 擎: 9999.99亿 '; + if (defaultfloatPrecision >= 5) maxText = ` 擎: ${99.99.toFixed(defaultfloatPrecision)} `; //小数位数太多了 + this.Width = this.Canvas.measureText(maxText).width; + this.Height = this.LineHeight * lineCount + 2 * 2; + if (klineData && klineData.High>0) + { + maxText=` 擎: ${klineData.High.toFixed(defaultfloatPrecision)} `; + var textWidth=this.Canvas.measureText(maxText).width; + if (textWidth>this.Width) this.Width=textWidth; + } + + this.CalculateShowPosition(); + this.DrawBG(); + this.DrawTooltipData(klineData); + this.DrawBorder(); + } + + //判断显示位置 + this.CalculateShowPosition=function() + { + this.ShowPosition=0; + if (!this.LatestPoint) return; + + if(this.IsHScreen) + { + var top=this.ChartBorder.GetTop(); + var height=this.ChartBorder.GetHeight(); + var yCenter=top+height/2; + if (this.LatestPoint.Y0) + { + var value = (item.Close - item.YClose) / item.YClose * 100; + var color = this.KLineTitlePaint.GetColor(value, 0); + var text = value.toFixed(2) + '%'; + } + else + { + var text='--.--'; + var color = this.KLineTitlePaint.GetColor(0, 0); + } + this.Canvas.fillStyle = color; + this.Canvas.fillText(text, left + labelWidth, top); + + this.Canvas.fillStyle = this.TitleColor; + + if (IFrameSplitOperator.IsNumber(item.Vol)) + { + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + //持仓量 + var upperSymbol; + if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) + { + this.Canvas.fillStyle = this.TitleColor; + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + if (this.IsHScreen) this.Canvas.restore(); + } + + //设置参数接口 + this.SetOption = function (option) + { + if (option.LineHeight > 0) this.LineHeight = option.LineHeight; + if (option.BGColor) this.BGColor = option.BGColor; + if (option.LanguageID > 0) this.LanguageID = option.LanguageID; + } +} + +function MinuteTooltipPaint() +{ + this.newMethod = KLineTooltipPaint; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'MinuteTooltipPaint'; + this.IsShowAveragePrice=true; + + this.GetTop=function() + { + if (this.IsHScreen) + { + if (this.ShowPosition==0) + return this.ChartBorder.GetTop()+this.Left; + else + return this.ChartBorder.GetBottom()-this.Width-this.Left; + } + else + { + return this.ChartBorder.GetTop()+this.Top; + } + } + + this.GetLeft=function() + { + if (this.IsHScreen) + { + return this.ChartBorder.GetRight()-this.Height-this.Top; + } + else + { + if (this.ShowPosition==0) + return this.ChartBorder.GetLeft()+this.Left; + else + return this.ChartBorder.GetRight()-this.Width-this.Left; + } + } + + this.DrawTooltipData = function (item) + { + //console.log('[KLineTooltipPaint::DrawKLineData] ', item); + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数 + var left = this.GetLeft() + 2; + var top = this.GetTop() + 3; + this.YClose = this.KLineTitlePaint.YClose; + + if (this.IsHScreen) + { + this.Canvas.save(); + var x = this.GetLeft() + this.Height, y = this.GetTop(); + + this.Canvas.translate(x, y); + this.Canvas.rotate(90 * Math.PI / 180); + + //x, y 作为原点 + left = 2; + top = 3; + } + + this.Canvas.textBaseline = "top"; + this.Canvas.textAlign = "left"; + this.Canvas.font = this.Font[0]; + var labelWidth = this.Canvas.measureText('擎: ').width; + + var aryDateTime = item.DateTime.split(' '); + if (aryDateTime && aryDateTime.length == 2) + { + var text = this.HQChart.FormatDateString(aryDateTime[0]); + this.Canvas.fillStyle = this.TitleColor; + this.Canvas.fillText(text, left, top); + + top += this.LineHeight; + text = this.HQChart.FormatTimeString(aryDateTime[1]); + this.Canvas.fillText(text, left, top); + } + + top += this.LineHeight; + this.Canvas.fillStyle = this.TitleColor; + text = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID); + this.Canvas.fillText(text, left, top); + var color = this.KLineTitlePaint.GetColor(item.Close, this.YClose); + text = item.Close.toFixed(defaultfloatPrecision); + this.Canvas.fillStyle = color; + this.Canvas.fillText(text, left + labelWidth, top); + + if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true) + { + top += this.LineHeight; + this.Canvas.fillStyle = this.TitleColor; + text = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID); + this.Canvas.fillText(text, left, top); + var color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose); + var text = item.AvPrice.toFixed(defaultfloatPrecision); + this.Canvas.fillStyle = color; + this.Canvas.fillText(text, left + labelWidth, top); + } + + top += this.LineHeight; + this.Canvas.fillStyle = this.TitleColor; + text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID); + this.Canvas.fillText(text, left, top); + var value = (item.Close - this.YClose) / this.YClose * 100; + var color = this.KLineTitlePaint.GetColor(value, 0); + var text = value.toFixed(2) + '%'; + this.Canvas.fillStyle = color; + this.Canvas.fillText(text, left + labelWidth, top); + + if (IFrameSplitOperator.IsNumber(item.Vol)) + { + this.Canvas.fillStyle = this.TitleColor; + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + //持仓量 + var upperSymbol; + if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) + { + this.Canvas.fillStyle = this.TitleColor; + top += this.LineHeight; + text = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID); + this.Canvas.fillText(text, left, top); + var text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID); + this.Canvas.fillText(text, left + labelWidth, top); + } + + + if (this.IsHScreen) this.Canvas.restore(); + } +} + + +////////////////////////////////////////////////////////////////////////////// +// 弹幕 +//弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 } +function BarrageList() +{ + this.PlayList = []; //正在播放队列 + this.Cache = []; //没有播放的弹幕数据 + this.MinLineHeight = 40; + this.Height; //高度 + this.Step = 1; + + //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 } + this.GetPlayList = function (obj) + { + var canves = obj.Canves; + var right = obj.Right; + var left = obj.Left; + var width = right - left; + var isMoveStep = obj.IsMoveStep; + + var list = []; + var yOffset = 0; + for (var i = 0; i < this.PlayList.length; ++i) + { + var ary = this.PlayList[i]; + var lineHeight = this.MinLineHeight; + if (ary.Height > this.MinLineHeight) lineHeight = ary.Height; + + var bAddNewItem = true; //是否需要加入新弹幕 + var bRemoveFirst = false; //是否删除第1个数据 + for (var j = 0; j < ary.Data.length; ++j) + { + var item = ary.Data[j]; + var playItem = { X: item.X, Y: yOffset, Text: item.Text, Color: item.Color, Height: lineHeight, Font: item.Font, Info: item.Info }; + list.push(playItem); + + if (!isMoveStep) continue; + + if (j == ary.Data.length - 1 && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕 + { + bAddNewItem = false; + if (!item.TextWidth) + { + if (item.Font && item.Font.Name) canves.font = item.Font.Name; + else canves.font = obj.Font; + item.TextWidth = canves.measureText(playItem.Text + '擎擎').width; + } + + if (item.X >= item.TextWidth) + bAddNewItem = true; + } + else if (j == 0) + { + bRemoveFirst = false; + if (!item.TextWidth) + { + if (item.Font && item.Font.Name) canves.font = item.Font.Name; + else canves.font = obj.Font; + item.TextWidth = canves.measureText(playItem.Text + '擎擎').width; + } + + if (item.X > width + item.TextWidth) bRemoveFirst = true; + } + + item.X += this.Step; + } + + if (isMoveStep && bAddNewItem && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕 + { + var cacheItem = this.Cache.shift(); + var newItem = { X: 0, Text: cacheItem.Text, Color: cacheItem.Color, Font: cacheItem.Font, Info: cacheItem.Info }; + ary.Data.push(newItem); + } + + if (isMoveStep && bRemoveFirst && ary.Data.length > 0) + { + var removeItem = ary.Data.shift(); + this.OnItemPlayEnd(obj.HQChart, removeItem); + } + + yOffset += lineHeight; + } + + return list; + } + + //根据高度计算播放队列个数 + this.CacluatePlayLine = function (height) + { + this.Height = height; + var lineCount = parseInt(height / this.MinLineHeight); + if (this.PlayList.length < lineCount) + { + var addCount = lineCount - this.PlayList.length; + for (var i = 0; i < addCount; ++i) + { + this.PlayList.push({ Data: [] }); + } + } + else if (this.PlayList.length > lineCount) + { + var removeCount = this.PlayList.length - lineCount; + for (var i = 0; i < removeCount; ++i) + { + var ary = this.PlayList.pop(); + for (var j = 0; j < ary.Data.length; ++j) + { + var item = ary.Data[j]; + var cacheItem = { Text: item.Text, Color: item.Color, Font: item.Font, Info: item.Info }; + this.Cache.unshift(cacheItem); + } + } + } + + JSConsole.Chart.Log(`[BarrageList::CacluatePlayLine] LineCount=${this.PlayList.length} Height=${this.Height}`) + } + + //添加弹幕 + this.AddBarrage = function (barrageData) + { + for (var i in barrageData) { + var item = barrageData[i]; + this.Cache.push(item); + } + } + + this.OnItemPlayEnd = function (hqChart, item) //单挑弹幕播放完毕 + { + //监听事件 + var event = hqChart.GetBarrageEvent(); + if (!event || !event.Callback) return; + + event.Callback(event, item, this); + } + + this.Count = function () { return this.Cache.length; } //未播放的弹幕个数 +} + +//背景图 支持横屏 +function BackgroundPaint() +{ + this.newMethod = IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'BackgroundPaint'; + + this.IsDynamic = false; + this.IsCallbackDraw = true; //在回调函数里绘制, 不在Draw()中绘制 + + this.FrameID = 0; + this.Data; //背景数据 { Start:, End:, Color:[] } + this.ID = Guid(); //唯一的ID + + /* + this.Data= + [ + { Start:{ Date:20181201 }, End:{ Date:20181230 }, Color:'rgb(44,55,44)' } , + { Start:{ Date:20190308 }, End:{ Date:20190404 }, Color:['rgb(44,55,255)','rgb(200,55,255)'] } + ] + */ + + this.ChartSubFrame; + this.ChartBorder; + this.KData; + this.Period; + this.XPointCount = 0; + + this.SetOption = function (option) //设置 + { + if (option.FrameID > 0) this.FrameID = option.FrameID; + if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID = option.ID; + } + + this.Draw = function () + { + if (!this.Data || !this.HQChart) return; + if (!this.ChartFrame || !this.ChartFrame.SubFrame || this.ChartFrame.SubFrame.length <= this.FrameID) return; + var klineChart = this.HQChart.ChartPaint[0]; + if (!klineChart || !klineChart.Data) return; + + this.ChartSubFrame = this.ChartFrame.SubFrame[this.FrameID].Frame; + this.ChartBorder = this.ChartSubFrame.ChartBorder; + this.KData = klineChart.Data; + this.Period = this.HQChart.Period; + if (!this.KData || this.KData.Data.length <= 0) return; + + var isHScreen = (this.ChartSubFrame.IsHScreen === true); + this.XPointCount = this.ChartSubFrame.XPointCount; + var xPointCount = this.ChartSubFrame.XPointCount; + + var firstKItem = this.KData.Data[this.KData.DataOffset]; + var endIndex = this.KData.DataOffset + xPointCount - 1; + if (endIndex >= this.KData.Data.length) endIndex = this.KData.Data.length - 1; + var endKItem = this.KData.Data[endIndex]; + var showData = this.GetShowData(firstKItem, endKItem); + if (!showData || showData.length <= 0) return; + + var kLineMap = this.BuildKLineMap(); + var bottom = this.ChartBorder.GetBottomEx(); + var top = this.ChartBorder.GetTopEx(); + var height = this.ChartBorder.GetHeightEx(); + if (isHScreen) + { + top = this.ChartBorder.GetRightEx(); + bottom = this.ChartBorder.GetLeftEx(); + height = this.ChartBorder.GetWidthEx(); + } + + for (var i in showData) + { + var item = showData[i]; + var rt = this.GetBGCoordinate(item, kLineMap); + if (!rt) continue; + + if (Array.isArray(item.Color)) + { + var gradient; + if (isHScreen) gradient = this.Canvas.createLinearGradient(bottom, rt.Left, top, rt.Left); + else gradient = this.Canvas.createLinearGradient(rt.Left, top, rt.Left, bottom); + var offset = 1 / item.Color.length; + for (var i in item.Color) + { + gradient.addColorStop(i * offset, item.Color[i]); + } + this.Canvas.fillStyle = gradient; + } + else + { + this.Canvas.fillStyle = item.Color; + } + + if (isHScreen) this.Canvas.fillRect(ToFixedRect(bottom), ToFixedRect(rt.Left), ToFixedRect(height), ToFixedRect(rt.Width)); + else this.Canvas.fillRect(ToFixedRect(rt.Left), ToFixedRect(top), ToFixedRect(rt.Width), ToFixedRect(height)); + } + } + + this.GetShowData = function (first, end) + { + var aryData = []; + for (var i in this.Data) { + var item = this.Data[i]; + var showItem = {}; + if (item.Start.Date >= first.Date && item.Start.Date <= end.Date) showItem.Start = item.Start; + if (item.End.Date >= first.Date && item.End.Date <= end.Date) showItem.End = item.End; + if (showItem.Start || showItem.End) + { + showItem.Color = item.Color; + aryData.push(showItem); + } + } + + return aryData; + } + + this.BuildKLineMap = function () + { + var isHScreen = (this.ChartSubFrame.IsHScreen === true); + var dataWidth = this.ChartSubFrame.DataWidth; + var distanceWidth = this.ChartSubFrame.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + + var mapKLine = { Data: new Map() }; //Key: date / date time, Value:索引 + for (var i = this.KData.DataOffset, j = 0; i < this.KData.Data.length && j < this.XPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var kItem = this.KData.Data[i]; + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + var x = left + (right - left) / 2; + + if (j == 0) mapKLine.XLeft = left; + mapKLine.XRight = right; + + var value = { Index: i, ShowIndex: j, X: x, Right: right, Left: left, Date: kItem.Date }; + if (ChartData.IsMinutePeriod(this.Period, true)) + { + var key = `Date:${kItem.Date} Time:${kItem.Time}`; + value.Time = kItem.Time; + } + else + { + var key = `Date:${kItem.Date}`; + } + + mapKLine.Data.set(key, value); + } + + return mapKLine; + } + + this.GetBGCoordinate = function (item, kLineMap) + { + var xLeft = null, xRight = null; + if (item.Start) + { + if (ChartData.IsMinutePeriod(this.Period, true)) + var key = `Date:${item.Start.Date} Time:${item.Start.Time}`; + else + var key = `Date:${item.Start.Date}`; + + if (kLineMap.Data.has(key)) + { + var findItem = kLineMap.Data.get(key); + xLeft = findItem.Left; + } + else + { + for (var kItem of kLineMap.Data) + { + var value = kItem[1]; + if (value.Date > item.Start.Date) + { + xLeft = value.Left; + break; + } + } + } + } + else + { + xLeft = kLineMap.XLeft; + } + + if (item.End) + { + if (ChartData.IsMinutePeriod(this.Period, true)) + var key = `Date:${item.End.Date} Time:${item.End.Time}`; + else + var key = `Date:${item.End.Date}`; + + if (kLineMap.Data.has(key)) + { + var findItem = kLineMap.Data.get(key); + xRight = findItem.Right; + } + else + { + var previousX = null; + for (var kItem of kLineMap.Data) + { + var value = kItem[1]; + if (value.Date > item.End.Date) + { + xRight = previousX; + break; + } + previousX = value.Right; + } + } + } + else + { + xRight = kLineMap.XRight; + } + + if (xLeft == null || xRight == null) return null; + + return { Left: xLeft, Right: xRight, Width: xRight - xLeft }; + } +} + + +//弹幕 +function BarragePaint() +{ + this.newMethod = IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'BarragePaint'; + this.IsAnimation = true; + this.IsEraseBG = true; + this.HQChart; + + this.Font = g_JSChartResource.Barrage.Font; + this.TextColor = g_JSChartResource.Barrage.Color; + this.FontHeight = g_JSChartResource.Barrage.Height; + + this.BarrageList = new BarrageList(); //字幕列表 + this.IsMoveStep = false; + + + this.SetOption = function (option) //设置参数接口 + { + if (option) + { + if (option.Step > 0) this.BarrageList.Step = option.Step; + if (option.MinLineHeight) this.Barrage.MinLineHeight = option.MinLineHeight; + } + } + + this.DrawHScreen = function () + { + var height = this.ChartBorder.GetWidth(); + var left = this.ChartBorder.GetTop(); + var right = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetRightEx(); + var wdith = this.ChartBorder.GetChartWidth(); + + if (height != this.BarrageList.Height) + this.BarrageList.CacluatePlayLine(height); + + this.Canvas.textBaseline = "middle"; + this.Canvas.textAlign = "left"; + + var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart }); + this.IsMoveStep = false; + if (!play) return; + + this.Canvas.save(); + this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0); + this.Canvas.rotate(90 * Math.PI / 180); + + for (var i = 0; i < play.length; ++i) + { + var item = play[i]; + if (item.Color) this.Canvas.fillStyle = item.Color; + else this.Canvas.fillStyle = this.TextColor; + if (item.Font) this.Canvas.font = item.Font.Name; + else this.Canvas.font = this.Font; + + var fontHeight = this.FontHeight; + if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height; + var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2); + + this.Canvas.fillText(item.Text, right - item.X, top + yOffset); + } + + this.Canvas.restore(); + } + + this.Draw = function () + { + if (this.ChartFrame.IsHScreen) + { + this.DrawHScreen(); + return; + } + + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRight(); + var top = this.ChartBorder.GetTopEx(); + var height = this.ChartBorder.GetHeight(); + + if (height != this.BarrageList.Height) + this.BarrageList.CacluatePlayLine(height); + + this.Canvas.textBaseline = "middle"; + this.Canvas.textAlign = "left"; + + var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart }); + this.IsMoveStep = false; + if (!play) return; + + for (var i = 0; i < play.length; ++i) + { + var item = play[i]; + if (item.Color) this.Canvas.fillStyle = item.Color; + else this.Canvas.fillStyle = this.TextColor; + if (item.Font) this.Canvas.font = item.Font.Name; + else this.Canvas.font = this.Font; + + var fontHeight = this.FontHeight; + if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height; + var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2); + + this.Canvas.fillText(item.Text, right - item.X, top + yOffset); + } + } +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonExtendChartPaint: + { + IExtendChartPainting: IExtendChartPainting, + KLineTooltipPaint: KLineTooltipPaint, + BarragePaint: BarragePaint, + MinuteTooltipPaint: MinuteTooltipPaint, + BackgroundPaint: BackgroundPaint, + }, + + //单个类导出 + JSCommonExtendChartPaint_IExtendChartPainting: IExtendChartPainting, + JSCommonExtendChartPaint_KLineTooltipPaint: KLineTooltipPaint, + JSCommonExtendChartPaint_BarragePaint: BarragePaint, + JSCommonExtendChartPaint_MinuteTooltipPaint: MinuteTooltipPaint, + JSCommonExtendChartPaint_BackgroundPaint: BackgroundPaint, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.framesplit.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.framesplit.wechat.js new file mode 100644 index 0000000..ed4be6b --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.framesplit.wechat.js @@ -0,0 +1,2024 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 坐标轴相关算法 +*/ + +import +{ + JSCommonResource_Global_JSChartResource as g_JSChartResource, + JSCommonResource_JSCHART_LANGUAGE_ID as JSCHART_LANGUAGE_ID, + JSCommonResource_Global_JSChartLocalization as g_JSChartLocalization, +} from './umychart.resource.wechat.js' + +import { + JSCommon_ChartData as ChartData, JSCommon_HistoryData as HistoryData, + JSCommon_SingleData as SingleData, JSCommon_MinuteData as MinuteData, + JSCommon_JSCHART_EVENT_ID as JSCHART_EVENT_ID, +} from "./umychart.data.wechat.js"; + +import { JSCommonCoordinateData as JSCommonCoordinateData } from "./umychart.coordinatedata.wechat.js"; +var MARKET_SUFFIX_NAME = JSCommonCoordinateData.MARKET_SUFFIX_NAME; + +var WEEK_NAME=["日","一","二","三","四","五","六"]; + +//坐标信息 +function CoordinateInfo() +{ + this.Value; //坐标数据 + this.Message = new Array(); //坐标输出文字信息 + this.TextColor = g_JSChartResource.FrameSplitTextColor //文字颜色 + this.Font = g_JSChartResource.FrameSplitTextFont; //字体 + this.LineColor = g_JSChartResource.FrameSplitPen; //线段颜色 + this.LineType = 1; //线段类型 -1 不画线段 +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +//坐标分割 +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +function IFrameSplitOperator() +{ + this.ChartBorder; //边框信息 + this.Frame; //框架信息 + this.FrameSplitData; //坐标轴分割方法 + this.SplitCount = 5; //刻度个数 + this.StringFormat = 0; //刻度字符串格式 -1 刻度文字全部不显示 -2 刻度文字右边不显示 + this.IsShowLeftText = true; //显示左边刻度 + this.IsShowRightText = true; //显示右边刻度 + this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.GetEventCallback; //事件回调 + + ////////////////////// + // data.Min data.Max data.Interval data.Count + // + this.IntegerCoordinateSplit = function (data) + { + var splitItem = this.FrameSplitData.Find(data.Interval); + if (!splitItem) return false; + if (data.Interval == splitItem.Interval) return true; + + var fixMax=data.Max, fixMin=data.Min; + var maxValue=data.Max/splitItem.FixInterval; + var minValue=data.Min/splitItem.FixInterval; + //调整到整数倍数,不能整除的 +1 + if (IFrameSplitOperator.IsFloat(maxValue)) fixMax=parseInt((maxValue+0.5).toFixed(0))*splitItem.FixInterval; + if (IFrameSplitOperator.IsFloat(minValue)) fixMin=parseInt((minValue-0.5).toFixed(0))*splitItem.FixInterval; + if (data.Min == 0) fixMin = 0; //最小值是0 不用调整了. + if (fixMin < 0 && data.Min > 0) fixMin = 0; //都是正数的, 最小值最小调整为0 + + var count = 0; + for (var i = fixMin; (i - fixMax) < 0.00000001; i += splitItem.FixInterval) + { + ++count; + } + + data.Interval = splitItem.FixInterval; + data.Max = fixMax; + data.Min = fixMin; + data.Count = count; + + return true; + } + + this.Filter = function (aryInfo, keepZero) //keepZero 保留0轴 + { + if (this.SplitCount <= 0 || aryInfo.length <= 0 || aryInfo.length <= this.SplitCount) return aryInfo; + + //分割线比预设的多, 过掉一些 + var filter = parseInt(aryInfo.length / this.SplitCount); + if (filter <= 1) filter = 2; + var data = []; + + for (var i = 0; i < aryInfo.length; i += filter) + { + if (i + filter >= aryInfo.length && i != aryInfo.length - 1) //最后一个数据放进去 + { + data.push(aryInfo[aryInfo.length - 1]); + } + else { + data.push(aryInfo[i]); + } + } + + if (this.SplitCount == 2 && data.length > 2) //之显示第1个和最后一个刻度 + { + for (var i = 1; i < data.length - 1; ++i) + { + var item = data[i]; + item.Message[0] = null; + item.Message[1] = null; + } + } + + if (keepZero) //如果不存在0轴,增加一个0轴,刻度信息部显示 + { + var bExsitZero = false; + for (var i = 0; i < data; ++i) + { + var item = data[i]; + if (Math.abs(item.Value) < 0.00000001) + { + bExsitZero = true; + break; + } + } + + if (bExsitZero == false) + { + var zeroCoordinate = new CoordinateInfo(); + zeroCoordinate.Value = 0; + zeroCoordinate.Message[0] = null + zeroCoordinate.Message[1] = null; + data.push(zeroCoordinate); + } + } + + return data; + } + + this.RemoveZero = function (aryInfo) //移除小数后面多余的0 + { + //所有的数字小数点后面都0,才会去掉 + var isAllZero = [true, true]; + for (var i in aryInfo) { + var item = aryInfo[i]; + var message = item.Message[0]; + if (!this.IsDecimalZeroEnd(message)) isAllZero[0] = false; + + var message = item.Message[1]; + if (!this.IsDecimalZeroEnd(message)) isAllZero[1] = false; + } + + if (isAllZero[0] == false && isAllZero[1] == false) return; + for (var i in aryInfo) + { + var item = aryInfo[i]; + if (isAllZero[0]) + { + var message = item.Message[0]; + if (message != null) + { + if (typeof (message) == 'number') message = message.toString(); + item.Message[0] = message.replace(/[.][0]+/g, ''); + } + } + + if (isAllZero[1]) + { + var message = item.Message[1]; + if (message != null) + { + if (typeof (message) == 'number') message = message.toString(); + item.Message[1] = message.replace(/[.][0]+/g, ''); + } + } + } + } + + this.IsDecimalZeroEnd = function (text) //是否是0结尾的小数 + { + if (text == null) return true; + if (text == '0') return true; + if (typeof (text) == 'number') text = text.toString(); + + var pos = text.search(/[.]/); + if (pos < 0) return false; + + for (var i = pos + 1; i < text.length; ++i) + { + var char = text.charAt(i); + if (char >= '1' && char <= '9') return false; + } + + return true; + } +} + +//字符串格式化 千分位分割 +IFrameSplitOperator.FormatValueThousandsString=function(value,floatPrecision) +{ + if (value==null || isNaN(value)) + { + if (floatPrecision>0) + { + var nullText='-.'; + for(var i=0;i0){ + var numFloat = num.split('.')[1]; + var numM = num.split('.')[0]; + while (numM.length > 3) + { + result = ',' + numM.slice(-3) + result; + numM = numM.slice(0, numM.length - 3); + } + if (numM) { result = numM + result + '.' + numFloat; } + }else{ + while (num.length > 3) + { + result = ',' + num.slice(-3) + result; + num = num.slice(0, num.length - 3); + } + if (num) { result = num + result; } + } + + return result; +} + +//数据输出格式化 floatPrecision=小数位数 +IFrameSplitOperator.FormatValueString=function(value, floatPrecision,languageID) +{ + if (value==null || isNaN(value)) + { + if (floatPrecision>0) + { + var nullText='-.'; + for(var i=0;i-0.00000000001) + { + return "0"; + } + + var absValue = Math.abs(value); + if (languageID===JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID) + { + if (absValue < 10000) + return value.toFixed(floatPrecision); + else if (absValue < 1000000) + return (value/1000).toFixed(floatPrecision)+"K"; + else if (absValue < 1000000000) + return (value/1000000).toFixed(floatPrecision)+"M"; + else if (absValue < 1000000000000) + return (value/1000000000).toFixed(floatPrecision)+"B"; + else + return (value/1000000000000).toFixed(floatPrecision)+"T"; + } + else + { + if (absValue < 10000) + return value.toFixed(floatPrecision); + else if (absValue < 100000000) + return (value/10000).toFixed(floatPrecision)+"万"; + else if (absValue < 1000000000000) + return (value/100000000).toFixed(floatPrecision)+"亿"; + else + return (value/1000000000000).toFixed(floatPrecision)+"万亿"; + } + + return ''; +} + +//整形输出格式化 floatPrecision=小数位数 +IFrameSplitOperator.FromatIntegerString=function(value, floatPrecision,languageID) +{ + if (value<10000 && IFrameSplitOperator.IsInteger(value)) floatPrecision=0; //<10000的整形 去掉小数位数 + return IFrameSplitOperator.FormatValueString(value, floatPrecision,languageID); +} + +IFrameSplitOperator.NumberToString=function(value) +{ + if (value<10) return '0'+value.toString(); + return value.toString(); +} + +IFrameSplitOperator.FormatDateString=function(value,format,languageID) +{ + var year=parseInt(value/10000); + var month=parseInt(value/100)%100; + var day=value%100; + + switch(format) + { + case 'MM-DD': + return IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day); + case "YYYY/MM/DD": + return year.toString() + '/' + IFrameSplitOperator.NumberToString(month) + '/' + IFrameSplitOperator.NumberToString(day); + case "YYYY/MM/DD/W": + { + var date=new Date(year,month-1,day); + var week=g_JSChartLocalization.GetText(WEEK_NAME[date.getDay()],languageID); + return year.toString() + '/' + IFrameSplitOperator.NumberToString(month) + '/' + IFrameSplitOperator.NumberToString(day)+"/"+ week.toString(); + } + case "DD/MM/YYYY": + return IFrameSplitOperator.NumberToString(day) + '/' + IFrameSplitOperator.NumberToString(month) + '/' + year.toString(); + default: + return year.toString() + '-' + IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day); + } +} + +IFrameSplitOperator.FormatTimeString=function(value, format) //format hh:mm:ss +{ + if (format=='HH:MM:SS') + { + var hour=parseInt(value/10000); + var minute=parseInt((value%10000)/100); + var second=value%100; + return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second); + } + else if (format == 'HH:MM') + { + var hour = parseInt(value / 100); + var minute = value % 100; + return IFrameSplitOperator.NumberToString(hour) + ':' + IFrameSplitOperator.NumberToString(minute); + } + else + { + if (value < 10000) + { + var hour = parseInt(value / 100); + var minute = value % 100; + return IFrameSplitOperator.NumberToString(hour) + ':' + IFrameSplitOperator.NumberToString(minute); + } + else + { + var hour = parseInt(value / 10000); + var minute = parseInt((value % 10000) / 100); + var second = value % 100; + return IFrameSplitOperator.NumberToString(hour) + ':' + IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second); + } + + } +} + +//报告格式化 +IFrameSplitOperator.FormatReportDateString=function(value) +{ + var year=parseInt(value/10000); + var month=parseInt(value/100)%100; + var monthText; + switch(month) + { + case 3: + monthText="一季度报"; + break; + case 6: + monthText="半年报"; + break; + case 9: + monthText="三季度报"; + break; + case 12: + monthText="年报"; + break; + } + + return year.toString()+ monthText; +} + +IFrameSplitOperator.FormatDateTimeString=function(value,isShowDate) +{ + var aryValue=value.split(' '); + if (aryValue.length<2) return ""; + var time=parseInt(aryValue[1]); + var minute=time%100; + var hour=parseInt(time/100); + var text=(hour<10? ('0'+hour.toString()):hour.toString()) + ':' + (minute<10?('0'+minute.toString()):minute.toString()); + + if (isShowDate==true) + { + var date=parseInt(aryValue[0]); + var year=parseInt(date/10000); + var month=parseInt(date%10000/100); + var day=date%100; + text=year.toString() +'-'+ (month<10? ('0'+month.toString()) :month.toString()) +'-'+ (day<10? ('0'+day.toString()):day.toString()) +" " +text; + } + + return text; +} + +//字段颜色格式化 +IFrameSplitOperator.FormatValueColor = function (value, value2) +{ + if (value != null && value2 == null) //只传一个值的 就判断value正负 + { + if (value == 0) return 'PriceNull'; + else if (value > 0) return 'PriceUp'; + else return 'PriceDown'; + } + + //2个数值对比 返回颜色 + if (value == null || value2 == null) return 'PriceNull'; + if (value == value2) return 'PriceNull'; + else if (value > value2) return 'PriceUp'; + else return 'PriceDown'; +} + +IFrameSplitOperator.IsNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return true; +} + +//判断是否是正数 +IFrameSplitOperator.IsPlusNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return value>0; +} + +//是否是整形 +IFrameSplitOperator.IsInteger=function(x) +{ + return (typeof x === 'number') && (x % 1 === 0); +} + +//判断字段是否存在 +IFrameSplitOperator.IsObjectExist=function(obj) +{ + if (obj===undefined) return false; + if (obj==null) return false; + + return true; +} + +//是否时bool +IFrameSplitOperator.IsBool=function(value) +{ + if (value===true || value===false) return true; + return false; +} + +IFrameSplitOperator.IsString=function(value) +{ + var type=typeof(value); + if (type=='string') return true; + return false; +} + +IFrameSplitOperator.IsFloat=function(value) +{ + if (value===undefined) return false; + if (value==null) return false; + if (isNaN(value)) return false; + + return value!=parseInt(value); +} + +//是否是非空的数组 +IFrameSplitOperator.IsNonEmptyArray=function(ary) +{ + if (!ary) return; + if (!Array.isArray(ary)) return; + + return ary.length>0; +} + +//K线Y轴分割 +function FrameSplitKLinePriceY() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.CoordinateType = 0; //坐标类型 0=普通坐标 1=百分比坐标 (右边坐标刻度) + this.Symbol; + this.Data; //K线数据 (计算百分比坐标) + this.FrameSplitData2; //坐标轴分割方法(计算百分比刻度) + this.FloatPrecision = null; //小数位数 (设置了就使用这个位数,否则使用品种对应的小数位数) + + this.Custom = []; //[{Type:0}]; 定制刻度 0=显示最后的价格刻度 + this.SplitType = 0; //0=自动分割 1=固定分割 + + this.Operator = function () + { + var splitData = {}; + splitData.Max = this.Frame.HorizontalMax; + splitData.Min = this.Frame.HorizontalMin; + splitData.Count = this.SplitCount; + if (splitData.Max==splitData.Min) //如果一样上下扩大下 + { + splitData.Max+=splitData.Max*0.01; + splitData.Min-=splitData.Min*0.01 + } + splitData.Interval = (splitData.Max - splitData.Min) / (splitData.Count - 1); + + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + if (JSCommonCoordinateData.MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol)) defaultfloatPrecision = 0; //手机端指数不显示小数位数 + if (this.FloatPrecision != null) defaultfloatPrecision = this.FloatPrecision; + + var bFilter=true; + if (FrameSplitKLinePriceY.SplitCustom) + { + FrameSplitKLinePriceY.SplitCustom(this,splitData,defaultfloatPrecision); //自定义分割 + bFilter=false; + } + else + { + switch (this.CoordinateType) + { + case 1: + this.SplitPercentage(splitData, defaultfloatPrecision); + break; + default: + if (this.SplitType == 1) this.SplitFixed(splitData, defaultfloatPrecision); + else this.SplitDefault(splitData, defaultfloatPrecision); + this.CustomCoordinate(defaultfloatPrecision); + break; + } + } + + if (bFilter) this.Frame.HorizontalInfo = this.Filter(this.Frame.HorizontalInfo, false); + this.Frame.HorizontalMax = splitData.Max; + this.Frame.HorizontalMin = splitData.Min; + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SPLIT_YCOORDINATE); + if (event && event.Callback) + { + var data={ID:this.Frame.Identify, Frame:this.Frame }; + event.Callback(event,data,this); + } + } + } + + this.SplitDefault = function (splitData, floatPrecision) //默认坐标 + { + this.IntegerCoordinateSplit(splitData); + + this.Frame.HorizontalInfo = []; + for (var i = 0, value = splitData.Min; i < splitData.Count; ++i, value += splitData.Interval) + { + this.Frame.HorizontalInfo[i] = new CoordinateInfo(); + this.Frame.HorizontalInfo[i].Value = value; + if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0] = value.toFixed(floatPrecision); + if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1] = value.toFixed(floatPrecision); + } + } + + this.SplitPercentage = function (splitData, floatPrecision) //百分比坐标 + { + var firstOpenPrice = this.GetFirstOpenPrice(); + splitData.Max = (splitData.Max - firstOpenPrice) / firstOpenPrice; + splitData.Min = (splitData.Min - firstOpenPrice) / firstOpenPrice; + splitData.Interval = (splitData.Max - splitData.Min) / (splitData.Count - 1); + this.IntegerCoordinateSplit2(splitData); + + var maxValue=(1 + splitData.Max) * firstOpenPrice; + var minValue=(1 + splitData.Min) * firstOpenPrice; + + this.Frame.HorizontalInfo = []; + for (var i = 0, value = splitData.Min; i < splitData.Count; ++i, value += splitData.Interval) + { + var price = (value + 1) * firstOpenPrice; + if (pricemaxValue) continue; + + this.Frame.HorizontalInfo[i] = new CoordinateInfo(); + this.Frame.HorizontalInfo[i].Value = price; + if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0] = price.toFixed(floatPrecision); //左边价格坐标 + if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1] = (value * 100).toFixed(2) + '%'; //右边百分比 + } + + splitData.Min = (1 + splitData.Min) * firstOpenPrice; //最大最小值调整 + splitData.Max = (1 + splitData.Max) * firstOpenPrice; + } + + this.SplitFixed = function (splitData, floatPrecision) //固定分割坐标 + { + this.Frame.HorizontalInfo = []; + for (var i = 0, value = splitData.Min; i < splitData.Count; ++i, value += splitData.Interval) { + this.Frame.HorizontalInfo[i] = new CoordinateInfo(); + this.Frame.HorizontalInfo[i].Value = value; + if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0] = value.toFixed(floatPrecision); + if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1] = value.toFixed(floatPrecision); + } + } + + this.CustomCoordinate = function (floatPrecision) + { + this.Frame.CustomHorizontalInfo = []; + for (var i in this.Custom) + { + var item = this.Custom[i]; + if (item.Type == 0) //最新价格刻度 + { + var dec=floatPrecision; + //外部设置小数位数 + if (IFrameSplitOperator.IsNumber(item.FloatPrecision) && item.FloatPrecision>=0) dec=item.FloatPrecision; + var latestItem = this.GetLatestPrice(dec, item); + if (latestItem) this.Frame.CustomHorizontalInfo.push(latestItem); + } + else if (item.Type == 1) //固定价格刻度 + { + this.CustomFixedCoordinate(item); + } + } + } + + this.GetLatestPrice = function (floatPrecision, option) + { + if (!this.Data || !this.Data.Data) return null; + if (this.Data.Data.length <= 0) return null; + var latestItem = this.Data.Data[this.Data.Data.length - 1]; + var info = new CoordinateInfo(); + info.Type = 0; + info.Value = latestItem.Close; + info.TextColor = g_JSChartResource.FrameLatestPrice.TextColor; + info.LineType = 2; //虚线 + if (option.Position == 'left') info.Message[0] = latestItem.Close.toFixed(floatPrecision); + else info.Message[1] = latestItem.Close.toFixed(floatPrecision); + if (latestItem.Close > latestItem.Open) info.LineColor = g_JSChartResource.FrameLatestPrice.UpBarColor; + else if (latestItem.Close < latestItem.Open) info.LineColor = g_JSChartResource.FrameLatestPrice.DownBarColor; + else info.LineColor = g_JSChartResource.FrameLatestPrice.UnchagneBarColor; + + if (IFrameSplitOperator.IsNumber(option.LineType)) info.LineType=option.LineType; + if (option.IsShowLine == false) info.LineType = -1; + + return info; + } + + this.GetFirstOpenPrice = function () //获取显示第1个数据的开盘价 + { + if (!this.Data) return null; + var xPointCount = this.Frame.XPointCount; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) + { + var data = this.Data.Data[i]; + if (data.Open == null || data.High == null || data.Low == null || data.Close == null) continue; + + return data.Open; + } + return null; + } + + this.CustomFixedCoordinate = function (option) //固定坐标刻度 + { + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + for (var i in option.Data) + { + var item = option.Data[i]; + var info = new CoordinateInfo(); + info.Type = 1; + info.TextColor = item.TextColor; + info.LineColor = item.Color; + info.LineType = 2; //虚线 + if (IFrameSplitOperator.IsNumber(option.LineType)) info.LineType=option.LineType; + if (option.IsShowLine == false) info.LineType = -1; + + info.Value = item.Value; + var text; + if (item.Text) text = item.Text; + else text = info.Value.toFixed(defaultfloatPrecision); + if (option.Position == 'left') info.Message[0] = text; + else info.Message[1] = text; + + this.Frame.CustomHorizontalInfo.push(info); + } + } + + + ////////////////////// + // data.Min data.Max data.Interval data.Count + // + this.IntegerCoordinateSplit2 = function (data) + { + var splitItem = this.FrameSplitData2.Find(data.Interval); + if (!splitItem) return false; + if (data.Interval == splitItem.FixInterval) return true; + + var fixMax=data.Max, fixMin=data.Min; + var maxValue=data.Max/splitItem.FixInterval; + var minValue=data.Min/splitItem.FixInterval; + //调整到整数倍数,不能整除的 +1 + if (IFrameSplitOperator.IsFloat(maxValue)) fixMax=parseInt((maxValue+0.5).toFixed(0))*splitItem.FixInterval; + if (IFrameSplitOperator.IsFloat(minValue)) fixMin=parseInt((minValue-0.5).toFixed(0))*splitItem.FixInterval; + if (data.Min == 0) fixMin = 0; //最小值是0 不用调整了. + if (fixMin < 0 && data.Min > 0) fixMin = 0; //都是正数的, 最小值最小调整为0 + + var count = 0; + for (var i = fixMin; (i - fixMax) < 0.00000001; i += splitItem.FixInterval) + { + ++count; + } + + data.Interval = splitItem.FixInterval; + data.Max = fixMax; + data.Min = fixMin; + data.Count = count; + + return true; + } + +} + +//一般的Y轴分割 +function FrameSplitY() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + this.SplitType=0; //0=自动分割 1=固定分割 + this.FloatPrecision = 2; //坐标小数位数(默认2) + this.FLOATPRECISION_RANGE = [1, 0.1, 0.01, 0.001, 0.0001]; + this.IgnoreYValue = null; //在这个数组里的数字不显示在刻度上 + this.LineType=null; //线段样式 + + this.IsShowYZero = true; + this.IntegerSplitData = null; + + this.Operator = function () + { + var splitData = {}; + splitData.Max = this.Frame.HorizontalMax; + splitData.Min = this.Frame.HorizontalMin; + + if (splitData.Max==splitData.Min) //如果一样上下扩大下 + { + if (splitData.Max==0) + { + splitData.Max=1; + splitData.Min=-1; + } + else + { + splitData.Max+=splitData.Max*0.01; + splitData.Min-=splitData.Min*0.01; + } + } + + if (this.Frame.YSpecificMaxMin) + { + splitData.Count = this.Frame.YSpecificMaxMin.Count; + splitData.Interval = (splitData.Max - splitData.Min) / (splitData.Count - 1); + } + else if (this.SplitType==1) + { + splitData.Count=this.SplitCount; + splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1); + } + else + { + splitData.Count = this.SplitCount * 2; //放大两倍 + if (this.FloatPrecision == 0) //页面配置了纵坐标小数位数FloatPrecision=0时执行 + { + splitData.Interval = (splitData.Max - splitData.Min) / (splitData.Count - 1); + this.IntegerCoordinateSplit2(splitData); + } + else { + splitData.Interval = (splitData.Max - splitData.Min) / (splitData.Count - 1); + this.IntegerCoordinateSplit(splitData); + } + } + + this.Frame.HorizontalInfo = []; + if (this.Frame.YSplitScale) + { + for (var i in this.Frame.YSplitScale) + { + var value = this.Frame.YSplitScale[i]; + var coordinate = new CoordinateInfo(); + coordinate.Value = value; + if (IFrameSplitOperator.IsNumber(this.LineType)) coordinate.LineType=this.LineType; + + var absValue = Math.abs(value); + var floatPrecision = this.FloatPrecision; //数据比小数位数还小, 调整小数位数 + if (absValue < 0.0000000001) + coordinate.Message[1] = 0; + else if (absValue < this.FLOATPRECISION_RANGE[this.FLOATPRECISION_RANGE.length - 1]) + coordinate.Message[1] = value.toExponential(2).toString(); + else + { + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + coordinate.Message[1] = IFrameSplitOperator.FormatValueString(value, floatPrecision, this.LanguageID); + } + coordinate.Message[0] = coordinate.Message[1]; + + if (this.StringFormat == -2) coordinate.Message[1] = null; //刻度右边不显示 + else if (this.StringFormat == -3) coordinate.Message[0] = null; //刻度左边不显示 + else if (this.StringFormat == -1) coordinate.Message[0] = coordinate.Message[1] = null; //刻度左右都不显示 + + this.Frame.HorizontalInfo.push(coordinate); + } + } + else + { + for (var i = 0, value = splitData.Min; i < splitData.Count; ++i, value += splitData.Interval) + { + var coordinate=new CoordinateInfo(); + this.Frame.HorizontalInfo[i] = coordinate; + coordinate.Value=value; + if (IFrameSplitOperator.IsNumber(this.LineType)) coordinate.LineType=this.LineType; + + if (this.StringFormat == 1) //手机端格式 如果有万,亿单位了 去掉小数 + { + var floatPrecision = this.FloatPrecision; + if (!isNaN(value) && Math.abs(value) > 1000) floatPrecision = 0; + this.Frame.HorizontalInfo[i].Message[1] = IFrameSplitOperator.FormatValueString(value, floatPrecision, this.LanguageID); + } + else if (this.StringFormat == -1) //刻度不显示 + { + + } + else + { + var absValue = Math.abs(value); + var floatPrecision = this.FloatPrecision; //数据比小数位数还小, 调整小数位数 + if (absValue < 0.0000000001) + this.Frame.HorizontalInfo[i].Message[1] = 0; + else if (absValue < this.FLOATPRECISION_RANGE[this.FLOATPRECISION_RANGE.length - 1]) + this.Frame.HorizontalInfo[i].Message[1] = value.toExponential(2).toString(); + else { + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + if (floatPrecision < this.FLOATPRECISION_RANGE.length && absValue < this.FLOATPRECISION_RANGE[floatPrecision])++floatPrecision; + this.Frame.HorizontalInfo[i].Message[1] = IFrameSplitOperator.FormatValueString(value, floatPrecision, this.LanguageID); + } + } + + this.Frame.HorizontalInfo[i].Message[0] = this.Frame.HorizontalInfo[i].Message[1]; + + if (this.StringFormat == -2) this.Frame.HorizontalInfo[i].Message[1] = null; //刻度右边不显示 + else if (this.StringFormat == -3) this.Frame.HorizontalInfo[i].Message[0] = null; //刻度左边不显示 + } + } + + this.FilterIgnoreYValue(); + this.Frame.HorizontalInfo = this.Filter(this.Frame.HorizontalInfo, (splitData.Max > 0 && splitData.Min < 0 && this.IsShowYZero)); + this.RemoveZero(this.Frame.HorizontalInfo); + this.Frame.HorizontalMax = splitData.Max; + this.Frame.HorizontalMin = splitData.Min; + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SPLIT_YCOORDINATE); + if (event && event.Callback) + { + var data={ID:this.Frame.Identify, Frame:this.Frame }; + event.Callback(event,data,this); + } + } + } + + this.FilterIgnoreYValue = function () + { + if (!this.IgnoreYValue || this.IgnoreYValue.length <= 0) return; + + var setValue = new Set(this.IgnoreYValue); + this.Frame.HorizontalInfo = this.Frame.HorizontalInfo.filter(item => !setValue.has(item.Value)); + this.IsShowYZero = !setValue.has(0); //是否显示0刻度 + } + + this.IntegerCoordinateSplit2 = function (data) //整数分割 + { + if (this.IntegerSplitData == null) this.IntegerSplitData = new IntegerSplitData(); + var splitItem = this.IntegerSplitData.Find(data.Interval); + if (!splitItem) return false; + if (data.Interval == splitItem.FixInterval) return true; + + var fixMax=data.Max, fixMin=data.Min; + var maxValue=data.Max/splitItem.FixInterval; + var minValue=data.Min/splitItem.FixInterval; + //调整到整数倍数,不能整除的 +1 + if (IFrameSplitOperator.IsFloat(maxValue)) fixMax=parseInt((maxValue+0.5).toFixed(0))*splitItem.FixInterval; + if (IFrameSplitOperator.IsFloat(minValue)) fixMin=parseInt((minValue-0.5).toFixed(0))*splitItem.FixInterval; + if (data.Min == 0) fixMin = 0; //最小值是0 不用调整了. + if (fixMin < 0 && data.Min > 0) fixMin = 0; //都是正数的, 最小值最小调整为0 + + var count = 0; + for (var i = fixMin; (i - fixMax) < 0.00000001; i += splitItem.FixInterval) + { + ++count; + } + + data.Interval = splitItem.FixInterval; + data.Max = fixMax; + data.Min = fixMin; + data.Count = count; + + return true; + } +} + +function FrameSplitKLineX() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.ShowText = true; //是否显示坐标信息 + this.MinDistance = 12; //刻度间隔 + this.Period; //周期 + this.Symbol; //股票代码 + this.MinTextDistance = 50; + + this.SplitDateTime = function () //根据时间分割 + { + this.Frame.VerticalInfo = []; + var itemWidth = this.Frame.DistanceWidth + this.Frame.DataWidth; + var xOffset = this.Frame.Data.DataOffset; + var xPointCount = this.Frame.XPointCount; + var lastYear = null, lastMonth = null; + var textDistance = 0; + + for (var i = 0, index = xOffset; i < xPointCount && index < this.Frame.Data.Data.length; ++i, ++index) + { + textDistance += itemWidth; + var infoData = null; + if (i == 0) + { + var date = IFrameSplitOperator.FormatDateString(this.Frame.Data.Data[index].Date, 'MM-DD'); + infoData = { Value: index - xOffset, Text: date }; + } + else if (textDistance > this.MinTextDistance) + { + var time = IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time); + infoData = { Value: index - xOffset, Text: time }; + } + + if (infoData) + { + var info = new CoordinateInfo(); + info.Value = infoData.Value; + if (this.ShowText) info.Message[0] = infoData.Text; + if (info.Value==0) info.LineType=-1; //第1个分割线不画 + this.Frame.VerticalInfo.push(info); + textDistance = 0; + if (i == 0) textDistance = -(this.MinTextDistance / 2); + } + } + } + + this.SplitSecond = function () //根据时间分割 + { + this.Frame.VerticalInfo = []; + var itemWidth = this.Frame.DistanceWidth + this.Frame.DataWidth; + var xOffset = this.Frame.Data.DataOffset; + var xPointCount = this.Frame.XPointCount; + var lastYear = null, lastMonth = null; + var textDistance = 0; + + for (var i = 0, index = xOffset; i < xPointCount && index < this.Frame.Data.Data.length; ++i, ++index) + { + textDistance += itemWidth; + var infoData = null; + if (i == 0) + { + var date = IFrameSplitOperator.FormatDateString(this.Frame.Data.Data[index].Date, 'MM-DD'); + infoData = { Value: index - xOffset, Text: date }; + } + else if (textDistance > this.MinTextDistance) + { + var time = IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time,"HH:MM:SS"); + infoData = { Value: index - xOffset, Text: time }; + } + + if (infoData) + { + var info = new CoordinateInfo(); + info.Value = infoData.Value; + if (this.ShowText) info.Message[0] = infoData.Text; + this.Frame.VerticalInfo.push(info); + textDistance = 0; + if (i == 0) textDistance = -(this.MinTextDistance / 2); + } + } + } + + this.SplitDate = function () //根据日期分割 + { + this.Frame.VerticalInfo = []; + var xOffset = this.Frame.Data.DataOffset; + var xPointCount = this.Frame.XPointCount; + var lastYear = null, lastMonth = null; + var monthCount=0; + + for (var i = 0, index = xOffset, distance = this.MinDistance; i < xPointCount && index < this.Frame.Data.Data.length; ++i, ++index) + { + var year = parseInt(this.Frame.Data.Data[index].Date / 10000); + var month = parseInt(this.Frame.Data.Data[index].Date / 100) % 100; + if (lastMonth != month) ++monthCount; + + if ((distance < this.MinDistance && lastYear == year) || + (lastYear != null && lastYear == year && lastMonth != null && lastMonth == month)) + { + lastMonth = month; + ++distance; + continue; + } + + var info = new CoordinateInfo(); + info.Value = index - xOffset; + //info.TextColor = "rgb(51,51,51)"; + var text; + if (lastYear == null || lastYear != year) + { + text = year.toString(); + } + else if (lastMonth == null || lastMonth != month) + { + text = month.toString() + "月"; + } + + lastYear = year; + lastMonth = month; + + if (this.ShowText) info.Message[0] = text; + if (info.Value==0) info.LineType=-1; //第1个分割线不画 + + this.Frame.VerticalInfo.push(info); + distance = 0; + } + + if (this.Period == 0 && monthCount <= 2) + this.SplitShortDate(); + } + + //分隔在2个月一下的格式 + this.SplitShortDate = function () + { + this.Frame.VerticalInfo = []; + var xOffset = this.Frame.Data.DataOffset; + var xPointCount = this.Frame.XPointCount; + var minDistance = 12; + var isFirstYear = true; + for (var i = 0, index = xOffset, distance = minDistance; i < xPointCount && index < this.Frame.Data.Data.length; ++i, ++index) { + var year = parseInt(this.Frame.Data.Data[index].Date / 10000); + //var month=parseInt(this.Frame.Data.Data[index].Date/100)%100; + //var day=parseInt(this.Frame.Data.Data[index].Date%100); + + if (distance < minDistance) + { + ++distance; + continue; + } + + var info = new CoordinateInfo(); + info.Value = index - xOffset; + var text; + if (isFirstYear) + { + text = year.toString(); + isFirstYear = false; + } + else + { + text = IFrameSplitOperator.FormatDateString(this.Frame.Data.Data[index].Date, 'MM-DD'); + } + + if (this.ShowText) info.Message[0] = text; + if (info.Value==0) info.LineType=-1; //第1个分割线不画 + + this.Frame.VerticalInfo.push(info); + distance = 0; + } + } + + this.Operator = function () + { + if (this.Frame.Data == null) return; + if (FrameSplitKLineX.SplitCustom) FrameSplitKLineX.SplitCustom(this); //自定义分割 + else if (ChartData.IsMinutePeriod(this.Period, true)) this.SplitDateTime(); + else if (ChartData.IsSecondPeriod(this.Period)) this.SplitSecond(); + else this.SplitDate(); + } + + this.CreateCoordinateInfo=function() + { + return new CoordinateInfo(); //创建一个节点坐标 + } +} + +//FrameSplitKLineX.SplitCustom=function(split) { } + +function FrameSplitMinutePriceY() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.High = null; //最高最低价 + this.Low = null; + + this.YClose; //昨收 + this.Data; //分钟数据 + this.AverageData; //分钟均线数据 + this.OverlayChartPaint; + this.SplitCount = 7; + this.Symbol; + this.SplitType=0; //0=默认根据最大最小值分割 1=涨跌停分割 2=数据最大最大值分割 + this.LimitPrice; //{Max: Min:} 涨跌停价 + this.Custom; + + this.Operator = function () + { + this.Frame.HorizontalInfo = []; + this.Frame.CustomHorizontalInfo = []; + if (!this.Data) return; + + var range=this.GetMaxMin(); + + if (this.Symbol && MARKET_SUFFIX_NAME.IsUSA(this.Symbol.toUpperCase())) + { + this.USASplit(range); + } + else if (this.SplitType==2) + { + this.USASplit(range); + } + else + { + this.DefaultSplit(range); + } + + this.CustomCoordinate(); + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SPLIT_YCOORDINATE); + if (event && event.Callback) + { + var data={ID:this.Frame.Identify, Frame:this.Frame }; + event.Callback(event,data,this); + } + } + } + + this.GetMaxMin = function () //计算图中所有的数据的最大最小值 + { + var max = this.YClose; + var min = this.YClose; + + for (var i in this.Data.Data) + { + if (!this.Data.Data[i]) continue; //价格必须大于0 + if (max < this.Data.Data[i]) max = this.Data.Data[i]; + if (min > this.Data.Data[i]) min = this.Data.Data[i]; + } + + if (this.AverageData) + { + for (var i in this.AverageData.Data) + { + if (!this.AverageData.Data[i]) continue; //价格必须大于0 + if (max < this.AverageData.Data[i]) max = this.AverageData.Data[i]; + if (min > this.AverageData.Data[i]) min = this.AverageData.Data[i]; + } + } + + if (this.OverlayChartPaint && this.OverlayChartPaint.length > 0 && this.OverlayChartPaint[0] && this.OverlayChartPaint[0].Symbol) + { + var range = this.OverlayChartPaint[0].GetMaxMin(); + if (range.Max && range.Max > max) max = range.Max; + if (range.Min && range.Min < min) min = range.Min; + } + + if (this.SplitType==1 && this.LimitPrice) + { + if (maxthis.LimitPrice.Min) min=this.LimitPrice.Min; + } + + if (IFrameSplitOperator.IsNumber(this.High) && IFrameSplitOperator.IsNumber(this.Low)) + { + if (max < this.High) max = this.High; + if (min > this.Low) min = this.Low; + } + + return { Max: max, Min: min }; + } + + this.USASplit=function(range) + { + var max=range.Max; + var min=range.Min; + + if (max==min) + { + max=max+max*0.1; + min=min-min*0.1; + } + else + { + var height=this.Frame.ChartBorder.GetHeight(); //画布的高度 + var spacePrice=5*(max-min)/height; + max+=spacePrice; + min-=spacePrice; + if (min<0) min=range.Min; + } + + var showCount=this.SplitCount; + var distance=(max-min)/(showCount-1); + const minDistance=[1, 0.1, 0.01, 0.001, 0.0001]; + var defaultfloatPrecision=JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + if (distance0) coordinate.TextColor=g_JSChartResource.UpTextColor; + else if (per<0) coordinate.TextColor=g_JSChartResource.DownTextColor; + if (this.IsShowRightText) + { + if (this.RightTextFormat==1) coordinate.Message[1]=strPrice; + else coordinate.Message[1]=IFrameSplitOperator.FormatValueString(per,2)+'%'; //百分比 + } + } + + this.Frame.HorizontalInfo.push(coordinate); + } + + if (this.YClose>min && this.YClose 0) this.Frame.HorizontalInfo[i].TextColor = g_JSChartResource.UpTextColor; + else if (per < 0) this.Frame.HorizontalInfo[i].TextColor = g_JSChartResource.DownTextColor; + this.Frame.HorizontalInfo[i].Message[1] = IFrameSplitOperator.FormatValueString(per, 2) + '%'; //百分比 + } + } + + this.Frame.HorizontalMax = max; + this.Frame.HorizontalMin = min; + } + + this.CustomCoordinate = function () //自定义刻度 + { + if (!this.Custom) return; + + for (var i in this.Custom) + { + var item = this.Custom[i]; + if (item.Type == 1) + this.CustomFixedCoordinate(item); + else if (item.Type==0) + { + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + var latestItem = this.GetLatestPrice(defaultfloatPrecision, item); + if (latestItem) this.Frame.CustomHorizontalInfo.push(latestItem); + } + } + } + + this.GetLatestPrice = function (floatPrecision, option) + { + if (!this.Data || !this.Data.Data) return null; + if (this.Data.Data.length <= 0) return null; + var price = this.Data.Data[this.Data.Data.length - 1]; + if (!IFrameSplitOperator.IsNumber(price) || !IFrameSplitOperator.IsNumber(this.YClose)) return null; + + var info = new CoordinateInfo(); + info.Type = 0; + info.Value = price; + info.TextColor = g_JSChartResource.FrameLatestPrice.TextColor; + info.LineType = 2; //虚线 + if (option.Position == 'left') info.Message[0] = price.toFixed(floatPrecision); + else info.Message[1] = price.toFixed(floatPrecision); + if (price > this.YClose) info.LineColor = g_JSChartResource.FrameLatestPrice.UpBarColor; + else if (price < this.YClose) info.LineColor = g_JSChartResource.FrameLatestPrice.DownBarColor; + else info.LineColor = g_JSChartResource.FrameLatestPrice.UnchagneBarColor; + + if (IFrameSplitOperator.IsNumber(option.LineType)) info.LineType=option.LineType; + if (option.IsShowLine == false) info.LineType = -1; + + return info; + } + + this.CustomFixedCoordinate = function (option) //固定坐标刻度 + { + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + for (var i in option.Data) + { + var item = option.Data[i]; + var info = new CoordinateInfo(); + info.Type = 1; + info.TextColor = item.TextColor; + info.LineColor = item.Color; + info.LineType = 2; //虚线 + if (IFrameSplitOperator.IsNumber(option.LineType)) info.LineType=option.LineType; + if (option.IsShowLine == false) info.LineType = -1; + + if (IFrameSplitOperator.IsNumber(item.Increase)) //涨幅计算价格 + { + if (!IFrameSplitOperator.IsNumber(this.YClose)) conintue; + info.Value = this.YClose * (1 + item.Increase); + } + else + { + info.Value = item.Value; + } + + var text; + if (item.Text) text = item.Text; + else text = info.Value.toFixed(defaultfloatPrecision); + if (option.Position == 'left') info.Message[0] = text; + else info.Message[1] = text; + + this.Frame.CustomHorizontalInfo.push(info); + } + } + +} + +function FrameSplitMinuteX() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.ShowText = true; //是否显示坐标信息 + this.Symbol = null; //股票代码 x轴刻度根据股票类型来调整 + this.DayCount = 1; + this.DayData; + + this.Operator = function () + { + this.Frame.VerticalInfo = []; + var xPointCount = this.Frame.XPointCount; + var width = this.Frame.ChartBorder.GetWidth(); + var isHScreen = (this.Frame.IsHScreen === true); + if (isHScreen) width = this.Frame.ChartBorder.GetHeight(); + + const minuteCoordinate = JSCommonCoordinateData.MinuteCoordinateData; + var xcoordinateData = minuteCoordinate.GetCoordinateData(this.Symbol, width); + var minuteCount = xcoordinateData.Count; + var minuteMiddleCount = xcoordinateData.MiddleCount > 0 ? xcoordinateData.MiddleCount : parseInt(minuteCount / 2);; + + var xcoordinate = xcoordinateData.Data; + this.Frame.XPointCount = 243; + + this.Frame.XPointCount = minuteCount * this.DayCount; + this.Frame.MinuteCount = minuteCount; + this.Frame.VerticalInfo = []; + + if (this.DayCount <= 1) + { + for (var i in xcoordinate) + { + var info = new CoordinateInfo(); + //info.TextColor = "rgb(51,51,51)"; + info.Value = xcoordinate[i][0]; + if (this.ShowText) + info.Message[0] = xcoordinate[i][3]; + this.Frame.VerticalInfo[i] = info; + } + } + else + { + for (var i = this.DayData.length - 1, j = 0; i >= 0; --i, ++j) + { + var info = new CoordinateInfo(); + info.Value = j * minuteCount + minuteMiddleCount; + info.LineType = -1; + if (this.ShowText) info.Message[0] = IFrameSplitOperator.FormatDateString(this.DayData[i].Date, 'MM-DD'); + this.Frame.VerticalInfo.push(info); + + var info = new CoordinateInfo(); + info.Value = (j + 1) * minuteCount; + this.Frame.VerticalInfo.push(info); + } + } + } +} + +function FrameSplitXData() +{ + this.newMethod = IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.ShowText = true; //是否显示坐标信息 + + this.Operator = function () + { + if (this.Frame.Data == null || this.Frame.XData == null) return; + this.Frame.VerticalInfo = []; + var xOffset = this.Frame.Data.DataOffset; + var xPointCount = this.Frame.XPointCount; + + for (var i = 0, index = xOffset; i < xPointCount && index < this.Frame.Data.Data.length; ++i, ++index) + { + var info = new CoordinateInfo(); + info.Value = index - xOffset; + + if (this.ShowText) + info.Message[0] = this.Frame.XData[i]; + + this.Frame.VerticalInfo.push(info); + } + } +} + +//深度图X轴价格信息 +function FrameSplitXDepth() +{ + this.newMethod=IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.ShowText=true; //是否显示坐标信息 + this.SplitCount=3; + this.Symbol; + this.LineType=3; + + this.Operator=function() + { + var xRange=this.Frame.VerticalRange; + if (!xRange) return; + this.Frame.VerticalInfo=[]; + + var floatPrecision=2; + if (this.Symbol) floatPrecision=JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + var xMax=xRange.Max; + var xMin=xRange.Min; + if (xRange.Bid) + { + var interval=(xRange.Bid.Max-xMin)/this.SplitCount; + for(var i=0;i item[0] && interval <= item[1]) { + var result = {}; + result.FixInterval = item[2]; + result.Increase = item[3]; + return result; + } + } + + return null; + } +} + +function PriceSplitData() { + this.newMethod = SplitData; //派生 + this.newMethod(); + delete this.newMethod; + + this.Data = [ + [0.000001, 0.000002, 0.000001, 0.0000001], + [0.000002, 0.000004, 0.000002, 0.0000002], + [0.000004, 0.000005, 0.000004, 0.0000001], + [0.000005, 0.00001, 0.000005, 0.0000005], + + [0.00001, 0.00002, 0.00001, 0.000001], + [0.00002, 0.00004, 0.00002, 0.000002], + [0.00004, 0.00005, 0.00004, 0.000001], + [0.00005, 0.0001, 0.00005, 0.000005], + + [0.0001, 0.0002, 0.0001, 0.00001], + [0.0002, 0.0004, 0.0002, 0.00002], + [0.0004, 0.0005, 0.0004, 0.00001], + [0.0005, 0.001, 0.0005, 0.00005], + + [0.001, 0.002, 0.001, 0.0001], + [0.002, 0.004, 0.002, 0.0002], + [0.004, 0.005, 0.004, 0.0001], + [0.005, 0.01, 0.005, 0.0005], + + [0.01, 0.02, 0.01, 0.001], + [0.02, 0.04, 0.02, 0.002], + [0.04, 0.05, 0.04, 0.001], + [0.05, 0.1, 0.05, 0.005], + + [0.1, 0.2, 0.1, 0.01], + [0.2, 0.4, 0.2, 0.02], + [0.4, 0.5, 0.2, 0.01], + [0.5, 0.8, 0.2, 0.05], + [0.8, 1, 0.5, 0.05], + + [1, 2, 0.5, 0.05], + [2, 4, 0.5, 0.05], + [4, 5, 0.5, 0.05], + [5, 10, 0.5, 0.05], + + [10, 12, 10, 2], + [20, 40, 20, 5], + [40, 50, 40, 2], + [50, 100, 50, 10], + + [100, 200, 100, 10], + [200, 400, 200, 20], + [400, 500, 400, 10], + [500, 1000, 500, 50], + + [1000, 2000, 1000, 50], + [2000, 4000, 2000, 50], + [4000, 5000, 4000, 50], + [5000, 10000, 5000, 100], + + [10000, 20000, 10000, 1000], + [20000, 40000, 20000, 2000], + [40000, 50000, 40000, 1000], + [50000, 100000, 50000, 5000], + + [100000, 200000, 100000, 10000], + [200000, 400000, 200000, 20000], + [400000, 500000, 400000, 10000], + [500000, 1000000, 500000, 50000], + + [1000000, 2000000, 1000000, 100000], + [2000000, 4000000, 2000000, 200000], + [4000000, 5000000, 4000000, 100000], + [5000000, 10000000, 5000000, 500000], + + [10000000, 20000000, 10000000, 1000000], + [20000000, 40000000, 20000000, 2000000], + [40000000, 50000000, 40000000, 1000000], + [50000000, 100000000, 50000000, 5000000], + + [100000000, 200000000, 100000000, 10000000], + [200000000, 400000000, 200000000, 20000000], + [400000000, 500000000, 400000000, 10000000], + [500000000, 1000000000, 500000000, 50000000], + + [1000000000, 2000000000, 1000000000, 100000000], + [2000000000, 4000000000, 2000000000, 200000000], + [4000000000, 5000000000, 4000000000, 100000000], + [5000000000, 10000000000, 5000000000, 500000000], + ]; +} + +//整数分割 +function IntegerSplitData() { + this.newMethod = SplitData; //派生 + this.newMethod(); + delete this.newMethod; + + this.Data = + [ + [0.000001, 0.000002, 0.000001, 0.0000001], + [0.000002, 0.000004, 0.000002, 0.0000002], + [0.000004, 0.000005, 0.000004, 0.0000001], + [0.000005, 0.00001, 0.000005, 0.0000005], + + [0.00001, 0.00002, 0.00001, 0.000001], + [0.00002, 0.00004, 0.00002, 0.000002], + [0.00004, 0.00005, 0.00004, 0.000001], + [0.00005, 0.0001, 0.00005, 0.000005], + + [0.0001, 0.0002, 0.0001, 0.00001], + [0.0002, 0.0004, 0.0002, 0.00002], + [0.0004, 0.0005, 0.0004, 0.00001], + [0.0005, 0.001, 0.0005, 0.00005], + + [0.001, 0.002, 0.001, 0.0001], + [0.002, 0.004, 0.002, 0.0002], + [0.004, 0.005, 0.004, 0.0001], + [0.005, 0.01, 0.005, 0.0005], + + [0.01, 0.02, 0.01, 0.001], + [0.02, 0.04, 0.02, 0.002], + [0.04, 0.05, 0.04, 0.001], + [0.05, 0.1, 0.05, 0.005], + + [0.1, 0.2, 1, 1], + [0.2, 0.4, 1, 1], + [0.4, 0.5, 1, 1], + [0.5, 0.8, 1, 1], + [0.8, 1, 1, 1], + + [1, 2, 1, 1], + [2, 4, 2, 1], + [4, 5, 4, 1], + [5, 10, 5, 1], + + [10, 12, 10, 2], + [20, 40, 20, 5], + [40, 50, 40, 2], + [50, 100, 50, 10], + + [100, 200, 100, 10], + [200, 400, 200, 20], + [400, 500, 400, 10], + [500, 1000, 500, 50], + + [1000, 2000, 1000, 50], + [2000, 4000, 2000, 50], + [4000, 5000, 4000, 50], + [5000, 10000, 5000, 100], + + [10000, 20000, 10000, 1000], + [20000, 40000, 20000, 2000], + [40000, 50000, 40000, 1000], + [50000, 100000, 50000, 5000], + + [100000, 200000, 100000, 10000], + [200000, 400000, 200000, 20000], + [400000, 500000, 400000, 10000], + [500000, 1000000, 500000, 50000], + + [1000000, 2000000, 1000000, 100000], + [2000000, 4000000, 2000000, 200000], + [4000000, 5000000, 4000000, 100000], + [5000000, 10000000, 5000000, 500000], + + [10000000, 20000000, 10000000, 1000000], + [20000000, 40000000, 20000000, 2000000], + [40000000, 50000000, 40000000, 1000000], + [50000000, 100000000, 50000000, 5000000], + + [100000000, 200000000, 100000000, 10000000], + [200000000, 400000000, 200000000, 20000000], + [400000000, 500000000, 400000000, 10000000], + [500000000, 1000000000, 500000000, 50000000], + + [1000000000, 2000000000, 1000000000, 100000000], + [2000000000, 4000000000, 2000000000, 200000000], + [4000000000, 5000000000, 4000000000, 100000000], + [5000000000, 10000000000, 5000000000, 500000000], + ]; +} + + +///////////////////////////////////////////////////////////////////////////////// +// +function IChangeStringFormat() { + this.Data; + this.Value; //数据 + this.Text; //输出字符串 + + this.Operator = function () { + return false; + } + } + + + function HQPriceStringFormat() + { + this.newMethod = IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Symbol; + this.FrameID; + this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.PercentageText; //百分比 + this.RValue; //右边值 + this.RText; + + this.Operator = function () + { + this.RText = null; + if (IFrameSplitOperator.IsString(this.RValue)) this.RText = this.RValue; + if (!this.Value) return false; + + var defaultfloatPrecision = 2; //价格小数位数 + if (this.FrameID == 0) //第1个窗口显示原始价格 + { + var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.Symbol); + this.Text = this.Value.toFixed(defaultfloatPrecision); + } + else + { + this.Text = IFrameSplitOperator.FormatValueString(this.Value, defaultfloatPrecision, this.LanguageID); + } + + return true; + } + } + + function HQDateStringFormat() + { + this.newMethod = IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.DateFormatType=0; //0=YYYY-MM-DD 1=YYYY/MM/DD 2=YYYY/MM/DD/W 3=DD/MM/YYYY + this.LanguageID=0; + + this.Operator = function () + { + if (!IFrameSplitOperator.IsNumber(this.Value) || this.Value<0) return false; + if (!this.Data) return false; + + var index = this.Value; + index = parseInt(index.toFixed(0)); + if (this.Data.DataOffset + index >= this.Data.Data.length) return false; + + var currentData = this.Data.Data[this.Data.DataOffset + index]; + var date = currentData.Date; + var dateFormatString="YYYY-MM-DD"; + if (this.DateFormatType==1) dateFormatString="YYYY/MM/DD"; + else if (this.DateFormatType==2) dateFormatString="YYYY/MM/DD/W"; + else if (this.DateFormatType==3) dateFormatString="DD/MM/YYYY"; + this.Text = IFrameSplitOperator.FormatDateString(date,dateFormatString,this.LanguageID); + if (ChartData.IsMinutePeriod(this.Data.Period, true)) // 分钟周期 + { + var time = IFrameSplitOperator.FormatTimeString(currentData.Time); + this.Text = this.Text + " " + time; + } + else if (ChartData.IsSecondPeriod(this.Data.Period)) + { + var time = IFrameSplitOperator.FormatTimeString(currentData.Time,"HH:MM:SS"); + this.Text = this.Text + " " + time; + } + + return true; + } + } + + function HQMinuteTimeStringFormat() + { + this.newMethod = IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Frame; + this.Symbol; + + this.Operator = function () + { + if (this.Value == null || isNaN(this.Value)) return false; + + var index = Math.abs(this.Value); + index = parseInt(index.toFixed(0)); + var showIndex = index; + if (this.Frame && this.Frame.MinuteCount) showIndex = index % this.Frame.MinuteCount; + + var timeStringData = JSCommonCoordinateData.MinuteTimeStringData; + var timeData = timeStringData.GetTimeData(this.Symbol); + if (!timeData) return false; + + if (showIndex < 0) showIndex = 0; + else if (showIndex > timeData.length) showIndex = timeData.length - 1; + if (this.Frame && index >= this.Frame.XPointCount) + showIndex = timeData.length - 1; + + var time = timeData[showIndex]; + this.Text = IFrameSplitOperator.FormatTimeString(time); + return true; + } + } + + function DivTooltipDataForamt() + { + this.DataMap=new Map( + [ + ["CorssCursor_XStringFormat", { Create:function() { return new HQDateStringFormat(); } }], + ["CorssCursor_YStringFormat", { Create:function() { return new HQPriceStringFormat(); } }] + ] + ); + + this.Create=function(name) + { + if (!this.DataMap.has(name)) return null; + var item=this.DataMap.get(name); + return item.Create(); + } + } + + var g_DivTooltipDataForamt=new DivTooltipDataForamt(); + + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonSplit: + { + CoordinateInfo: CoordinateInfo, + IFrameSplitOperator: IFrameSplitOperator, + FrameSplitKLinePriceY: FrameSplitKLinePriceY, + FrameSplitY: FrameSplitY, + FrameSplitKLineX: FrameSplitKLineX, + FrameSplitMinutePriceY: FrameSplitMinutePriceY, + FrameSplitMinuteX: FrameSplitMinuteX, + FrameSplitXData: FrameSplitXData, + SplitData: SplitData, + PriceSplitData: PriceSplitData, + FrameSplitXDepth:FrameSplitXDepth, + IChangeStringFormat:IChangeStringFormat, + HQPriceStringFormat:HQPriceStringFormat, + HQDateStringFormat:HQDateStringFormat, + HQMinuteTimeStringFormat:HQMinuteTimeStringFormat, + }, + + JSCommonSplit_CoordinateInfo: CoordinateInfo, + JSCommonSplit_IFrameSplitOperator: IFrameSplitOperator, + JSCommonSplit_FrameSplitKLinePriceY: FrameSplitKLinePriceY, + JSCommonSplit_FrameSplitY: FrameSplitY, + JSCommonSplit_FrameSplitKLineX: FrameSplitKLineX, + JSCommonSplit_FrameSplitMinutePriceY: FrameSplitMinutePriceY, + JSCommonSplit_FrameSplitMinuteX: FrameSplitMinuteX, + JSCommonSplit_FrameSplitXData: FrameSplitXData, + JSCommonSplit_SplitData: SplitData, + JSCommonSplit_PriceSplitData: PriceSplitData, + JSCommonSplit_FrameSplitXDepth:FrameSplitXDepth, + + JSCommonFormat_IChangeStringFormat:IChangeStringFormat, + JSCommonFormat_HQPriceStringFormat:HQPriceStringFormat, + JSCommonFormat_HQDateStringFormat:HQDateStringFormat, + JSCommonFormat_HQMinuteTimeStringFormat:HQMinuteTimeStringFormat, + JSCommonFormat_Global_DataFormat :g_DivTooltipDataForamt, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.hqIndexformula.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.hqIndexformula.wechat.js new file mode 100644 index 0000000..ad123ab --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.hqIndexformula.wechat.js @@ -0,0 +1,934 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 指标计算方法 2.0 版本使用的算法 +*/ + +function HQIndexFormula() +{ + +} + +//指数平均数指标 EMA(close,10) +HQIndexFormula.EMA=function(data,dayCount) +{ + var result = []; + + var offset=0; + if (offset>=data.length) return result; + + //取首个有效数据 + for(;offsetdata.length) return result; + + var max=-10000; + for(var i=n,j=0;idata[max]) + max = j; + } + } + + result[i] = data[max]; + } + + return result; +} + +HQIndexFormula.LLV=function(data,n) +{ + var result = []; + if (n>data.length) return result; + + var min=-10000; + + for(var i=n;idata[min]?min:i; + } + else + { + for(var j=(min=i-n+1)+1;j<=i;++j) + { + if(data[j]=data.length) return result; + + result=data.slice(0,data.length-n); + + for(var i=0;idata2比较 返回 0/1 数组 +HQIndexFormula.ARRAY_GT=function(data,data2) +{ + var result=[]; + var IsNumber=typeof(data2)=="number"; + if (IsNumber) + { + for(var i in data) + { + result[i]=(data[i]>data2 ? 1:0); + } + } + else + { + var count=Math.max(data.length,data2.length) + + for(var i=0;idata2[i] ? 1:0; + else + result[i]=null; + } + } + + return result; +} + +//数组 data>=data2比较 返回 0/1 数组 +HQIndexFormula.ARRAY_GTE=function(data,data2) +{ + var result=[]; + var IsNumber=typeof(data2)=="number"; + if (IsNumber) + { + for(var i in data) + { + result[i]=(data[i]>=data2 ? 1:0); + } + } + else + { + var count=Math.max(data.length,data2.length) + + for(var i=0;i=data2[i] ? 1:0; + else + result[i]=null; + } + } + + return result; +} + +//数组 data=data.length) return result; + + var i=dayCount; + for(;i=data.length) return result; + + var index=0; + for(;indexdata2[index]&&data[index-1]REF(CLOSE,1),VOL,-VOL);\n\ +OBV:SUM(IF(CLOSE==REF(CLOSE,1),0,VA),0);\n\ +MAOBV:MA(OBV,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DMI=function() +{ + let data= + { + Name:'DMI', Description:'趋向指标', IsMainIndex:false, + Args:[ { Name:'N', Value:14}, { Name:'MM', Value:6} ], + Script: //脚本 +'MTR:=EXPMEMA(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(REF(CLOSE,1)-LOW)),N);\n\ +HD :=HIGH-REF(HIGH,1);\n\ +LD :=REF(LOW,1)-LOW;\n\ +DMP:=EXPMEMA(IF(HD>0&&HD>LD,HD,0),N);\n\ +DMM:=EXPMEMA(IF(LD>0&&LD>HD,LD,0),N);\n\ +PDI: DMP*100/MTR;\n\ +MDI: DMM*100/MTR;\n\ +ADX: EXPMEMA(ABS(MDI-PDI)/(MDI+PDI)*100,MM);\n\ +ADXR:EXPMEMA(ADX,MM);' + + }; + + return data; +} + +JSIndexScript.prototype.CR=function() +{ + let data= + { + Name:'CR', Description:'带状能量线', IsMainIndex:false, + Args:[ { Name:'N', Value:26}, { Name:'M1', Value:10},{ Name:'M2', Value:20},{ Name:'M3', Value:40},{ Name:'M4', Value:62} ], + Script: //脚本 +'MID:=REF(HIGH+LOW,1)/2;\n\ +CR:SUM(MAX(0,HIGH-MID),N)/SUM(MAX(0,MID-LOW),N)*100;\n\ +MA1:REF(MA(CR,M1),M1/2.5+1);\n\ +MA2:REF(MA(CR,M2),M2/2.5+1);\n\ +MA3:REF(MA(CR,M3),M3/2.5+1);\n\ +MA4:REF(MA(CR,M4),M4/2.5+1);' + + }; + + return data; +} + +JSIndexScript.prototype.PSY=function() +{ + let data= + { + Name:'PSY', Description:'心理线', IsMainIndex:false, + Args:[ { Name:'N', Value:12}, { Name:'M', Value:6} ], + Script: //脚本 +'PSY:COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\r\ +PSYMA:MA(PSY,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CCI=function() +{ + let data= + { + Name:'CCI', Description:'商品路径指标', IsMainIndex:false, + Args:[ { Name:'N', Value:14} ], + Script: //脚本 +'TYP:=(HIGH+LOW+CLOSE)/3;\n\ +CCI:(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));' + + }; + + return data; +} + +JSIndexScript.prototype.DMA=function() +{ + let data= + { + Name:'DMA', Description:'平均差', IsMainIndex:false, + Args:[ { Name:'N1', Value:10},{ Name:'N2', Value:50},{ Name:'M', Value:10} ], + Script: //脚本 +'DIF:MA(CLOSE,N1)-MA(CLOSE,N2);\n\ +DIFMA:MA(DIF,M);' + + }; + + return data; +} + +JSIndexScript.prototype.TRIX=function() +{ + let data= + { + Name:'TRIX', Description:'三重指数平均线', IsMainIndex:false, + Args:[ { Name:'N', Value:12},{ Name:'M', Value:9} ], + Script: //脚本 +'MTR:=EMA(EMA(EMA(CLOSE,N),N),N);\n\ +TRIX:(MTR-REF(MTR,1))/REF(MTR,1)*100;\n\ +MATRIX:MA(TRIX,M) ;' + + }; + + return data; +} + +JSIndexScript.prototype.VR=function() +{ + let data= + { + Name:'VR', Description:'成交量变异率', IsMainIndex:false, + Args:[ { Name:'N', Value:26},{ Name:'M', Value:6} ], + Script: //脚本 +'TH:=SUM(IF(CLOSE>REF(CLOSE,1),VOL,0),N);\n\ +TL:=SUM(IF(CLOSEREF(CLOSE,1),MIDA,MIDB),0);\n\ +MAWAD:MA(WAD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CHO=function() +{ + let data= + { + Name:'CHO', Description:'佳庆指标', IsMainIndex:false, + Args:[ { Name:'N1', Value:10}, { Name:'N2', Value:20}, { Name:'M', Value:6}], + Script: //脚本 +'MID:=SUM(VOL*(2*CLOSE-HIGH-LOW)/(HIGH+LOW),0);\n\ +CHO:MA(MID,N1)-MA(MID,N2);\n\ +MACHO:MA(CHO,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ADTM=function() +{ + let data= + { + Name:'ADTM', Description:'动态买卖气指标', IsMainIndex:false, + Args:[ { Name:'N', Value:23}, { Name:'M', Value:8}], + Script: //脚本 +'DTM:=IF(OPEN<=REF(OPEN,1),0,MAX((HIGH-OPEN),(OPEN-REF(OPEN,1))));\n\ +DBM:=IF(OPEN>=REF(OPEN,1),0,MAX((OPEN-LOW),(OPEN-REF(OPEN,1))));\n\ +STM:=SUM(DTM,N);\n\ +SBM:=SUM(DBM,N);\n\ +ADTM:IF(STM>SBM,(STM-SBM)/STM,IF(STM==SBM,0,(STM-SBM)/SBM));\n\ +MAADTM:MA(ADTM,M);' + + }; + + return data; +} + +JSIndexScript.prototype.HSL=function() +{ + let data= + { + Name:'HSL', Description:'换手线', IsMainIndex:false, + Args:[ { Name:'N', Value:5} ], + Script: //脚本 +'HSL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100);\n\ +MAHSL:MA(HSL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.BIAS36=function() +{ + let data= + { + Name:'BIAS36', Description:'三六乖离', IsMainIndex:false, + Args:[ { Name:'M', Value:6} ], + Script: //脚本 +'BIAS36:MA(CLOSE,3)-MA(CLOSE,6);\n\ +BIAS612:MA(CLOSE,6)-MA(CLOSE,12);\n\ +MABIAS:MA(BIAS36,M);' + + }; + + return data; +} + +JSIndexScript.prototype.BIAS_QL=function() +{ + let data= + { + Name:'BIAS_QL', Description:'乖离率-传统版', IsMainIndex:false, + Args:[ { Name:'N', Value:6}, { Name:'M', Value:6} ], + Script: //脚本 +'BIAS :(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\ +BIASMA :MA(BIAS,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DPO=function() +{ + let data= + { + Name:'DPO', Description:'区间震荡线', IsMainIndex:false, + Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ], + Script: //脚本 +'DPO:CLOSE-REF(MA(CLOSE,N),N/2+1);\n\ +MADPO:MA(DPO,M);' + + }; + + return data; +} + +JSIndexScript.prototype.OSC=function() +{ + let data= + { + Name:'OSC', Description:'变动速率线', IsMainIndex:false, + Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ], + Script: //脚本 +'OSC:100*(CLOSE-MA(CLOSE,N));\n\ +MAOSC:EXPMEMA(OSC,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ATR=function() +{ + let data= + { + Name:'ATR', Description:'真实波幅', IsMainIndex:false, + Args:[ { Name:'N', Value:14}], + Script: //脚本 +'MTR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));\n\ +ATR:MA(MTR,N);' + + }; + + return data; +} + +JSIndexScript.prototype.NVI=function() +{ + let data= + { + Name:'ATR', Description:'负成交量', IsMainIndex:false, + Args:[ { Name:'N', Value:72} ], + Script: //脚本 +'NVI:100*MULAR(IF(VREF(V,1),C/REF(C,1),1),0);\n\ +MANVI:MA(NVI,N);' + + }; + + return data; +} + +JSIndexScript.prototype.UOS=function() +{ + let data= + { + Name:'UOS', Description:'终极指标', IsMainIndex:false, + Args:[ { Name:'N1', Value:7} ,{ Name:'N2', Value:14},{ Name:'N3', Value:28},{ Name:'M', Value:6}], + Script: //脚本 +'TH:=MAX(HIGH,REF(CLOSE,1));\n\ +TL:=MIN(LOW,REF(CLOSE,1));\n\ +ACC1:=SUM(CLOSE-TL,N1)/SUM(TH-TL,N1);\n\ +ACC2:=SUM(CLOSE-TL,N2)/SUM(TH-TL,N2);\n\ +ACC3:=SUM(CLOSE-TL,N3)/SUM(TH-TL,N3);\n\ +UOS:(ACC1*N2*N3+ACC2*N1*N3+ACC3*N1*N2)*100/(N1*N2+N1*N3+N2*N3);\n\ +MAUOS:EXPMEMA(UOS,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CYW=function() +{ + let data= + { + Name:'CYW', Description:'主力控盘', IsMainIndex:false, + Args:[ ], + Script: //脚本 +'VAR1:=CLOSE-LOW;\n\ +VAR2:=HIGH-LOW;\n\ +VAR3:=CLOSE-HIGH;\n\ +VAR4:=IF(HIGH>LOW,(VAR1/VAR2+VAR3/VAR2)*VOL,0);\n\ +CYW: SUM(VAR4,10)/10000, COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.LON=function() +{ + let data= + { + Name:'LON', Description:'龙系长线', IsMainIndex:false, + Args:[ { Name:'N', Value:10} ], + Script: //脚本 +'LC := REF(CLOSE,1);\n\ +VID := SUM(VOL,2)/(((HHV(HIGH,2)-LLV(LOW,2)))*100);\n\ +RC := (CLOSE-LC)*VID;\n\ +LONG := SUM(RC,0);\n\ +DIFF := SMA(LONG,10,1);\n\ +DEA := SMA(LONG,20,1);\n\ +LON : DIFF-DEA;\n\ +LONMA : MA(LON,10);\n\ +LONT : LON, COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.NDB = function () +{ + let data = + { + Name: 'NDB', Description: '脑电波', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 5 }, { Name: 'P2', Value: 10 }], + Script: //脚本 +'HH:=IF(C/REF(C,1)>1.098 AND L>REF(H,1),2*C-REF(C,1)-H,2*C-H-L);\n\ +V1:= BARSCOUNT(C) - 1;\n\ +V2:= 2 * REF(C, V1) - REF(H, V1) - REF(L, V1);\n\ +DK: SUM(HH, 0) + V2;\n\ +MDK5: MA(DK, P1);\n\ +MDK10: MA(DK, P2);' + + }; + + return data; +} + +JSIndexScript.prototype.SKDJ = function () +{ + let data = + { + Name: 'SKDJ', Description: '慢速随机指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M', Value: 3 }], + Script: //脚本 + 'LOWV:=LLV(LOW,N);\n\ +HIGHV:=HHV(HIGH,N);\n\ +RSV:=EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M);\n\ +K:EMA(RSV,M);\n\ +D:MA(K,M);' + + }; + + return data; +} + +JSIndexScript.prototype.KD = function () +{ + let data = + { + Name: 'KD', Description: '随机指标KD', IsMainIndex: false, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M1', Value: 3 }, { Name: 'M2', Value: 3 }], + Script: //脚本 + 'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:SMA(RSV,M1,1);\n\ +D:SMA(K,M2,1);' + + }; + + return data; +} + +JSIndexScript.prototype.FKX = function () +{ + let data = + { + Name: 'FKX', Description: '反K线', IsMainIndex: true, + Args: [], + Script: //脚本 + 'DRAWKLINE(-LOW, -OPEN, -HIGH, -CLOSE);' + }; + + return data; +} + +JSIndexScript.prototype.DKCOL = function () +{ + let data = + { + Name: 'DKCOL', Description: '多空能量柱(适用于分时主图)', IsMainIndex: true, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'FF:=(C-REF(C,N))/REF(C,N);\n\ +STICKLINE(FF>0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORRED;\n\ +STICKLINE(FF<0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORGREEN;' + + }; + + return data; +} + +JSIndexScript.prototype.UDL = function () +{ + let data = + { + Name: 'UDL', Description: '引力线', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 3 }, { Name: 'N2', Value: 5 }, { Name: 'N3', Value: 10 }, { Name: 'N4', Value: 20 }, { Name: 'M', Value: 6 }], + Script: //脚本 + 'UDL:(MA(CLOSE,N1)+MA(CLOSE,N2)+MA(CLOSE,N3)+MA(CLOSE,N4))/4;\n\ +MAUDL:MA(UDL,M);' + + }; + + return data; +} + +JSIndexScript.prototype.MFI = function () +{ + let data = + { + Name: 'MFI', Description: '资金流量指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 14 }, { Name: 'N2', Value: 6 }], + Script: //脚本 + 'TYP := (HIGH + LOW + CLOSE)/3;\n\ +V1:=SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP=0,DIF,0);\n\ +VD:=IF(DIF<0,-DIF,0);\n\ +MAU1:=MEMA(VU,M1);\n\ +MAD1:=MEMA(VD,M1);\n\ +MAU2:=MEMA(VU,M2);\n\ +MAD2:=MEMA(VD,M2);\n\ +RSI10:MA(100*MAU1/(MAU1+MAD1),M1);\n\ +RSI6:MA(100*MAU2/(MAU2+MAD2),M2);' + + }; + + return data; +} + +JSIndexScript.prototype.CYD = function () +{ + let data = + { + Name: 'CYD', Description: '承接因子', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 }], + Script: //脚本 + 'CYDS:WINNER(CLOSE)/(VOL/CAPITAL);\n\ +CYDN:WINNER(CLOSE)/MA(VOL/CAPITAL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.CYF = function () +{ + let data = + { + Name: 'CYF', Description: '市场能量', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 }], + Script: //脚本 + 'CYF:100-100/(1+EMA(HSL,N));' + + }; + + return data; +} + +JSIndexScript.prototype.TAPI = function () +{ + let data = + { + Name: 'TAPI', Description: '加权指数成交值', IsMainIndex: false, + Args: [{ Name: 'M', Value: 6 }], + Script: //脚本 + 'TAPI:AMOUNT/INDEXC;\n\ +MATAIP:MA(TAPI,M);' + + }; + + return data; +} + +JSIndexScript.prototype.VMACD = function () +{ + let data = + { + Name: 'VMACD', Description: '量平滑异同平均', IsMainIndex: false, + Args: [{ Name: 'SHORT', Value: 12 }, { Name: 'LONG', Value: 26 }, { Name: 'MID', Value: 9 }], + Script: //脚本 + 'DIF:EMA(VOL,SHORT)-EMA(VOL,LONG);\n\ +DEA:EMA(DIF,MID);\n\ +MACD:DIF-DEA,COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.QACD = function () +{ + let data = + { + Name: 'QACD', Description: '快速异同平均', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 12 }, { Name: 'N2', Value: 26 }, { Name: 'M', Value: 9 }], + Script: //脚本 + 'DIF:EMA(CLOSE,N1)-EMA(CLOSE,N2);\n\ +MACD:EMA(DIF,M);\n\ +DDIF:DIF-MACD;' + + }; + + return data; +} + +JSIndexScript.prototype.VPT = function () +{ + let data = + { + Name: 'VPT', Description: '量价曲线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 51 }, { Name: 'M', Value: 6 }], + Script: //脚本 + 'VPT:SUM(VOL*(CLOSE-REF(CLOSE,1))/REF(CLOSE,1),N);\n\ +MAVPT:MA(VPT,M);' + + }; + + return data; +} + +JSIndexScript.prototype.WVAD = function () +{ + let data = + { + Name: 'WVAD', Description: '威廉变异离散量', IsMainIndex: false, + Args: [{ Name: 'N', Value: 24 }, { Name: 'M', Value: 6 }], + Script: //脚本 + 'WVAD:SUM((CLOSE-OPEN)/(HIGH-LOW)*VOL,N)/10000;\n\ +MAWVAD:MA(WVAD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DBQR = function () +{ + let data = + { + Name: 'WVAD', Description: '对比强弱', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'M1', Value: 10 }, { Name: 'M2', Value: 20 }, { Name: 'M3', Value: 60 }], + Script: //脚本 + 'ZS:(INDEXC-REF(INDEXC,N))/REF(INDEXC,N);\n\ +GG:(CLOSE-REF(CLOSE,N))/REF(CLOSE,N);\n\ +MADBQR1:MA(GG,M1);\n\ +MADBQR2:MA(GG,M2);\n\ +MADBQR3:MA(GG,M3);' + + }; + + return data; +} + +JSIndexScript.prototype.JS = function () +{ + let data = + { + Name: 'JS', Description: '加速线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'M1', Value: 5 }, { Name: 'M2', Value: 10 }, { Name: 'M3', Value: 20 }], + Script: //脚本 + 'JS:100*(CLOSE-REF(CLOSE,N))/(N*REF(CLOSE,N));\n\ +MAJS1:MA(JS,M1);\n\ +MAJS2:MA(JS,M2);\n\ +MAJS3:MA(JS,M3);' + + }; + + return data; +} + +JSIndexScript.prototype.CYE = function () +{ + let data = + { + Name: 'CYE', Description: '市场趋势', IsMainIndex: false, + Args: [], + Script: //脚本 + 'MAL:=MA(CLOSE,5);\n\ +MAS:=MA(MA(CLOSE,20),5);\n\ +CYEL:(MAL-REF(MAL,1))/REF(MAL,1)*100;\n\ +CYES:(MAS-REF(MAS,1))/REF(MAS,1)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.QR = function () +{ + let data = + { + Name: 'QR', Description: '强弱指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 }], + Script: //脚本 + '个股: (CLOSE-REF(CLOSE,N))/REF(CLOSE,N)*100; \n\ +大盘: (INDEXC-REF(INDEXC,N))/REF(INDEXC,N)*100; \n\ +强弱值:EMA(个股-大盘,2),COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.GDX = function () +{ + let data = + { + Name: 'GDX', Description: '轨道线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 30 }, { Name: 'M', Value: 9 }], + Script: //脚本 + 'AA:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,N))/MA(CLOSE,N); \n\ +轨道:DMA(CLOSE,AA);\n\ +压力线:(1+M/100)*轨道; \n\ +支撑线:(1-M/100)*轨道;' + + }; + + return data; +} + +JSIndexScript.prototype.JLHB = function () +{ + let data = + { + Name: 'JLHB', Description: '绝路航标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 7 }, { Name: 'M', Value: 5 }], + Script: //脚本 + 'VAR1:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*80; \n\ +B:SMA(VAR1,N,1); \n\ +VAR2:SMA(B,M,1); \n\ +绝路航标:IF(CROSS(B,VAR2) AND B<40,50,0);' + + }; + + return data; +} + +JSIndexScript.prototype.PCNT = function () +{ + let data = + { + Name: 'PCNT', Description: '幅度比', IsMainIndex: false, + Args: [{ Name: 'M', Value: 5 }], + Script: //脚本 + 'PCNT:(CLOSE-REF(CLOSE,1))/CLOSE*100;\n\ +MAPCNT:EXPMEMA(PCNT,M);' + + }; + + return data; +} + +JSIndexScript.prototype.AMO = function () +{ + let data = + { + Name: 'AMO', Description: '成交金额', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 5 }, { Name: 'M2', Value: 10 }], + Script: //脚本 + 'AMOW:AMOUNT/10000.0,VOLSTICK;\n\ +AMO1:MA(AMOW,M1);\n\ +AMO2:MA(AMOW,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.VRSI = function () +{ + let data = + { + Name: 'VRSI', Description: '相对强弱量', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 6 }, { Name: 'N2', Value: 12 }, { Name: 'N3', Value: 24 }], + Script: //脚本 + 'LC:=REF(VOL,1);\n\ +RSI1:SMA(MAX(VOL-LC,0),N1,1)/SMA(ABS(VOL-LC),N1,1)*100;\n\ +RSI2:SMA(MAX(VOL-LC,0),N2,1)/SMA(ABS(VOL-LC),N2,1)*100;\n\ +RSI3:SMA(MAX(VOL-LC,0),N3,1)/SMA(ABS(VOL-LC),N3,1)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.HSCOL = function () +{ + let data = + { + Name: 'HSCOL', Description: '换手柱', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'HSCOL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100),VOLSTICK;\n\ +MAHSL:MA(HSCOL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.DBQRV = function () +{ + let data = + { + Name: 'DBQRV', Description: '对比强弱量(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'ZS:(INDEXV-REF(INDEXV,N))/REF(INDEXV,N);\n\ +GG:(VOL-REF(VOL,N))/REF(VOL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.DBLB = function () +{ + let data = + { + Name: 'DBLB', Description: '对比量比(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'M', Value: 5 }], + Script: //脚本 + 'GG:=VOL/SUM(REF(VOL,1),N);\n\ +ZS:=INDEXV/SUM(REF(INDEXV,1),N);\n\ +DBLB:GG/ZS;\n\ +MADBLB:MA(DBLB,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ACD = function () +{ + let data = + { + Name: 'ACD', Description: '升降线', IsMainIndex: false, + Args: [{ Name: 'M', Value: 20 }], + Script: //脚本 + 'LC:=REF(CLOSE,1);\n\ +DIF:=CLOSE-IF(CLOSE>LC,MIN(LOW,LC),MAX(HIGH,LC));\n\ +ACD:SUM(IF(CLOSE==LC,0,DIF),0);\n\ +MAACD:EXPMEMA(ACD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.EXPMA = function () + { + let data = + { + Name: 'EXPMA', Description: '指数平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 12 }, { Name: 'M2', Value: 50 }], + Script: //脚本 + 'EXP1:EMA(CLOSE,M1);\n\ +EXP2:EMA(CLOSE,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.EXPMA_S = function () +{ + let data = + { + Name: 'EXPMA_S', Description: '指数平均线-副图', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 12 }, { Name: 'M2', Value: 50 }], + Script: //脚本 + 'EXP1:EMA(CLOSE,M1);\n\ +EXP2:EMA(CLOSE,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.HMA = function () +{ + let data = + { + Name: 'HMA', Description: '高价平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 }, { Name: 'M2', Value: 12 }, { Name: 'M3', Value: 30 }, { Name: 'M4', Value: 72 }, { Name: 'M5', Value: 144 }], + Script: //脚本 + 'HMA1:MA(HIGH,M1);\n\ +HMA2:MA(HIGH,M2);\n\ +HMA3:MA(HIGH,M3);\n\ +HMA4:MA(HIGH,M4);\n\ +HMA5:MA(HIGH,M5);' + + }; + + return data; +} + +JSIndexScript.prototype.LMA = function () +{ + let data = + { + Name: 'LMA', Description: '低价平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 }, { Name: 'M2', Value: 12 }, { Name: 'M3', Value: 30 }, { Name: 'M4', Value: 72 }, { Name: 'M5', Value: 144 }], + Script: //脚本 + 'LMA1:MA(LOW,M1);\n\ +LMA2:MA(LOW,M2);\n\ +LMA3:MA(LOW,M3);\n\ +LMA4:MA(LOW,M4);\n\ +LMA5:MA(LOW,M5);' + + }; + + return data; +} + +JSIndexScript.prototype.VMA = function () +{ + let data = + { + Name: 'VMA', Description: '变异平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 }, { Name: 'M2', Value: 12 }, { Name: 'M3', Value: 30 }, { Name: 'M4', Value: 72 }, { Name: 'M5', Value: 144 }], + Script: //脚本 + 'VV:=(HIGH+OPEN+LOW+CLOSE)/4;\n\ +VMA1:MA(VV,M1);\n\ +VMA2:MA(VV,M2);\n\ +VMA3:MA(VV,M3);\n\ +VMA4:MA(VV,M4);\n\ +VMA5:MA(VV,M5);' + + }; + + return data; +} + + +JSIndexScript.prototype.AMV = function () +{ + let data = + { + Name: 'AMV', Description: '成本价均线', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 6 }, { Name: 'M2', Value: 12 }, { Name: 'M3', Value: 30 }, { Name: 'M4', Value: 72 }, { Name: 'M5', Value: 144 }], + Script: //脚本 + 'AMOV:=VOL*(OPEN+CLOSE)/2;\n\ +AMV1:SUM(AMOV,M1)/SUM(VOL,M1);\n\ +AMV2:SUM(AMOV,M2)/SUM(VOL,M2);\n\ +AMV3:SUM(AMOV,M3)/SUM(VOL,M3);\n\ +AMV4:SUM(AMOV,M4)/SUM(VOL,M4);' + + }; + + return data; +} + +JSIndexScript.prototype.BBIBOLL = function () +{ + let data = + { + Name: 'BBIBOLL', Description: '多空布林线', IsMainIndex: true, + Args: [{ Name: 'N', Value: 11 }, { Name: 'M', Value: 6 }], + Script: //脚本 + 'CV:=CLOSE;\n\ +BBIBOLL:(MA(CV,3)+MA(CV,6)+MA(CV,12)+MA(CV,24))/4;\n\ +UPR:BBIBOLL+M*STD(BBIBOLL,N);\n\ +DWN:BBIBOLL-M*STD(BBIBOLL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.ALLIGAT = function () +{ + let data = + { + Name: 'ALLIGAT', Description: '鳄鱼线', IsMainIndex: true, + Args: [], + Script: //脚本 + 'NN:=(H+L)/2;\n\ +上唇:REF(MA(NN,5),3),COLOR40FF40;\n\ +牙齿:REF(MA(NN,8),5),COLOR0000C0;\n\ +下颚:REF(MA(NN,13),8),COLORFF4040;' + + }; + + return data; +} + +JSIndexScript.prototype.ZX = function () +{ + let data = + { + Name: 'ZX', Description: '重心线', IsMainIndex: false, + Args: [], + Script: //脚本 + 'AV:0.01*AMOUNT/VOL;' + + }; + + return data; +} + +JSIndexScript.prototype.XS = function () +{ + let data = + { + Name: 'XS', Description: '薛斯通道', IsMainIndex: true, + Args: [{ Name: 'N', Value: 13 }], + Script: //脚本 + 'VAR2:=CLOSE*VOL;\n\ +VAR3:=EMA((EMA(VAR2,3)/EMA(VOL,3)+EMA(VAR2,6)/EMA(VOL,6)+EMA(VAR2,12)/EMA(VOL,12)+EMA(VAR2,24)/EMA(VOL,24))/4,N);\n\ +SUP:1.06*VAR3;\n\ +SDN:VAR3*0.94;\n\ +VAR4:=EMA(CLOSE,9);\n\ +LUP:EMA(VAR4*1.14,5);\n\ +LDN:EMA(VAR4*0.86,5);' + + }; + + return data; +} + +JSIndexScript.prototype.XS2 = function () +{ + let data = + { + Name: 'XS2', Description: '薛斯通道II', IsMainIndex: true, + Args: [{ Name: 'N', Value: 102 }, { Name: 'M', Value: 7 }], + Script: //脚本 + 'AA:=MA((2*CLOSE+HIGH+LOW)/4,5); \n\ +通道1:AA*N/100; \n\ +通道2:AA*(200-N)/100; \n\ +CC:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,20))/MA(CLOSE,20); \n\ +DD:=DMA(CLOSE,CC); \n\ +通道3:(1+M/100)*DD; \n\ +通道4:(1-M/100)*DD;' + + }; + + return data; +} + +JSIndexScript.prototype.SG_XDT = function () +{ + let data = + { + Name: 'SG-XDT', Description: '心电图(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 5 }, { Name: 'P2', Value: 10 }], + Script: //脚本 + 'QR:CLOSE/INDEXC*1000;\n\ +MQR1:MA(QR,5);\n\ +MQR2:MA(QR,10);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_SMX = function () +{ + let data = + { + Name: 'SG-SMX', Description: '生命线(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 50 }], + Script: //脚本 + 'H1:=HHV(HIGH,N);\n\ +L1:=LLV(LOW,N);\n\ +H2:=HHV(INDEXH,N);\n\ +L2:=LLV(INDEXL,N);\n\ +ZY:=CLOSE/INDEXC*2000;\n\ +ZY1:EMA(ZY,3);\n\ +ZY2:EMA(ZY,17);\n\ +ZY3:EMA(ZY,34);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_LB = function () +{ + let data = + { + Name: 'SG-LB', Description: '量比(需下载日线)', IsMainIndex: false, + Args: [], + Script: //脚本 + 'ZY2:=VOL/INDEXV*1000;\n\ +量比:ZY2;\n\ +MA5:MA(ZY2,5);\n\ +MA10:MA(ZY2,10);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_PF = function () +{ + let data = + { + Name: 'SG-PF', Description: '强势股评分(需下载日线)', IsMainIndex: false, + Args: [], + Script: //脚本 + 'ZY1:=CLOSE/INDEXC*1000;\n\ +A1:=IF(ZY1>HHV(ZY1,3),10,0);\n\ +A2:=IF(ZY1>HHV(ZY1,5),15,0);\n\ +A3:=IF(ZY1>HHV(ZY1,10),20,0);\n\ +A4:=IF(ZY1>HHV(ZY1,2),10,0);\n\ +A5:=COUNT(ZY1>REF(ZY1,1) ,9)*5;\n\ +强势股评分:A1+A2+A3+A4+A5;' + + }; + + return data; +} + +JSIndexScript.prototype.RAD = function () +{ + let data = + { + Name: 'RAD', Description: '威力雷达(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'D', Value: 3 }, { Name: 'S', Value: 30 }, { Name: 'M', Value: 30 }], + Script: //脚本 + 'SM:=(OPEN+HIGH+CLOSE+LOW)/4;\n\ +SMID:=MA(SM,D);\n\ +IM:=(INDEXO+INDEXH+INDEXL+INDEXC)/4;\n\ +IMID:=MA(IM,D);\n\ +SI1:=(SMID-REF(SMID,1))/SMID;\n\ +II:=(IMID-REF(IMID,1))/IMID;\n\ +RADER1:SUM((SI1-II)*2,S)*1000;\n\ +RADERMA:SMA(RADER1,M,1);' + + }; + + return data; +} + +JSIndexScript.prototype.SHT = function () +{ + let data = + { + Name: 'SHT', Description: '龙系短线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'VAR1:=MA((VOL-REF(VOL,1))/REF(VOL,1),5);\n\ +VAR2:=(CLOSE-MA(CLOSE,24))/MA(CLOSE,24)*100;\n\ +MY: VAR2*(1+VAR1);\n\ +SHT: MY, COLORSTICK;\n\ +SHTMA: MA(SHT,N);' + + }; + + return data; +} + +JSIndexScript.prototype.ZLJC = function () +{ + let data = + { + Name: 'ZLJC', Description: '主力进出', IsMainIndex: false, + Args: [], + Script: //脚本 + 'VAR1:=(CLOSE+LOW+HIGH)/3; \n\ +VAR2:=SUM(((VAR1-REF(LOW,1))-(HIGH-VAR1))*VOL/100000/(HIGH-LOW),0); \n\ +VAR3:=EMA(VAR2,1); \n\ +JCS:VAR3; \n\ +JCM:MA(VAR3,12); \n\ +JCL:MA(VAR3,26);' + + }; + + return data; +} + +JSIndexScript.prototype.ZLMM = function () +{ + let data = + { + Name: 'ZLMM', Description: '主力买卖', IsMainIndex: false, + Args: [], + Script: //脚本 + 'LC :=REF(CLOSE,1);\n\ +RSI2:=SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100;\n\ +RSI3:=SMA(MAX(CLOSE-LC,0),18,1)/SMA(ABS(CLOSE-LC),18,1)*100;\n\ +MMS:MA(3*RSI2-2*SMA(MAX(CLOSE-LC,0),16,1)/SMA(ABS(CLOSE-LC),16,1)*100,3);\n\ +MMM:EMA(MMS,8);\n\ +MML:MA(3*RSI3-2*SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100,5);' + + }; + + return data; +} + +JSIndexScript.prototype.SLZT = function () +{ + let data = + { + Name: 'SLZT', Description: '神龙在天', IsMainIndex: false, + Args: [], + Script: //脚本 + '白龙: MA(CLOSE,125);\n\ +黄龙: 白龙+2*STD(CLOSE,170);\n\ +紫龙: 白龙-2*STD(CLOSE,145);\n\ +青龙: SAR(125,1,7), LINESTICK;\n\ +VAR2:=HHV(HIGH,70);\n\ +VAR3:=HHV(HIGH,20);\n\ +红龙: VAR2*0.83;\n\ +蓝龙: VAR3*0.91;' + + }; + + return data; +} + +JSIndexScript.prototype.ADVOL = function () +{ + let data = + { + Name: 'ADVOL', Description: '龙系离散量', IsMainIndex: false, + Args: [], + Script: //脚本 + 'A:=SUM(((CLOSE-LOW)-(HIGH-CLOSE))*VOL/10000/(HIGH-LOW),0);\n\ +ADVOL:A;\n\ +MA1:MA(A,30);\n\ +MA2:MA(MA1,100);' + + }; + + return data; +} + +JSIndexScript.prototype.CYC = function () +{ + let data = + { + Name: 'CYC', Description: '成本均线', IsMainIndex: true, + Args: [{ Name: 'P1', Value: 5 }, { Name: 'P2', Value: 13 }, { Name: 'P3', Value: 34 }], + Script: //脚本 + 'JJJ:=IF(DYNAINFO(8)>0.01,0.01*DYNAINFO(10)/DYNAINFO(8),DYNAINFO(3));\n\ +DDD:=(DYNAINFO(5)<0.01 || DYNAINFO(6)<0.01);\n\ +JJJT:=IF(DDD,1,(JJJ<(DYNAINFO(5)+0.01) && JJJ>(DYNAINFO(6)-0.01)));\n\ +CYC1:IF(JJJT,0.01*EMA(AMOUNT,P1)/EMA(VOL,P1),EMA((HIGH+LOW+CLOSE)/3,P1));\n\ +CYC2:IF(JJJT,0.01*EMA(AMOUNT,P2)/EMA(VOL,P2),EMA((HIGH+LOW+CLOSE)/3,P2));\n\ +CYC3:IF(JJJT,0.01*EMA(AMOUNT,P3)/EMA(VOL,P3),EMA((HIGH+LOW+CLOSE)/3,P3));\n\ +CYC4:IF(JJJT,DMA(AMOUNT/(100*VOL),100*VOL/FINANCE(7)),EMA((HIGH+LOW+CLOSE)/3,120));' + + }; + + return data; +} + +JSIndexScript.prototype.CYS = function () +{ + let data = + { + Name: 'CYS', Description: '市场盈亏', IsMainIndex: false, + Args: [], + Script: //脚本 + 'CYC13:EMA(AMOUNT,13)/EMA(VOL,13);\n\ +CYS:(CLOSE-CYC13)/CYC13*100;' + + }; + + return data; +} + +JSIndexScript.prototype.CYQKL = function () +{ + let data = + { + Name: 'CYQKL', Description: '博弈K线长度', IsMainIndex: false, + Args: [], + Script: //脚本 + 'KL:100*(WINNER(CLOSE)-WINNER(OPEN));' + + }; + + return data; +} + +JSIndexScript.prototype.SCR = function () +{ + let data = + { + Name: 'SCR', Description: '筹码集中度', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 90 }], + Script: //脚本 + 'A:=P1+(100-P1)/2;\n\ +B:=(100-P1)/2;\n\ +CC:=COST(A);\n\ +DD:=COST(B);\n\ +SCR:(CC-DD)/(CC+DD)*100/2;' + + }; + + return data; +} + + +JSIndexScript.prototype.ASR = function () +{ + let data = + { + Name: 'ASR', Description: '浮筹比例', IsMainIndex: false, + Args: [], + Script: //脚本 + 'ASR:(WINNER(C*1.1)-WINNER(C*0.9))/WINNER(HHV(H,0))*100;' + + }; + + return data; +} + +JSIndexScript.prototype.SAR = function () +{ + let data = + { + Name: 'SAR', Description: '抛物转向', IsMainIndex: true, + Args: [{ Name: 'P', Value: 10 },{ Name: 'STEP', Value: 2 },{ Name: 'MAXP', Value: 20 }], + Script: //脚本 +'S:SAR(P,STEP,MAXP),CIRCLEDOT;' + + }; + + return data; +} + +JSIndexScript.prototype.TJCJL = function () +{ + let data = + { + Name: '太极成交量', Description: '太极成交量', IsMainIndex: true, + Args: [], + Script: //脚本 +'总手:VOL,NODRAW;\n\ +ZZ:=IF(REF(C,1)>REF(O,1) AND O>REF(C,1)*1.014 AND CV5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,0,VOL,10,0),COLORRED;\n\ +STICKLINE(CROSS(C6,C) AND V>V5*1.2 AND V>V12*1.2,0,VOL,10,0),COLORGREEN;\n\ +STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND CMA(VOL,5)*2 AND V>V34*3 AND CV5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,VOL*0.5,0,10,0),COLORRED;\n\ +STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND CV5*1.2 AND V>V12*1.2,VOL*0.5,0,10,0),COLORRED;' + + }; + + return data; +} + +JSIndexScript.prototype.VOLRate = function () +{ + let data = + { + Name: '量比', Description: '量比', IsMainIndex: false, Condition: { Period: [CONDITION_PERIOD.MINUTE_ID, CONDITION_PERIOD.MULTIDAY_MINUTE_ID] }, + Args: [], + Script: //脚本 + "LIANGBI:VOLR;" + }; + + return data; +} + +/* + 飞龙四式-主图 +*/ +JSIndexScript.prototype.Dragon4_Main = function () +{ + let data = + { + Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: true, + Args: [{ Name: 'N1', Value: 5 }, { Name: 'N2', Value: 10 }, { Name: 'N3', Value: 50 }, { Name: 'N4', Value: 60 }], + Script: //脚本 + '蜻蜓点水:=EMA(CLOSE,N1),COLORGRAY;\n\ +魔界:=EMA(CLOSE,N2),COLORGREEN;\n\ +水:=EMA(CLOSE,N3),COLORRED;\n\ +DRAWKLINE(HIGH,OPEN,LOW,CLOSE);\n\ +生命线:MA(CLOSE,N4),COLORBLUE,LINETHICK2;\n\ +DRAWBAND(魔界,\'RGB(186,225,250)\',水,\'RGB(253,194,124)\');\n\ +DRAWBAND(蜻蜓点水,\'RGB(128,138,135)\',魔界,\'RGB(0,0,255)\');' + + }; + + return data; +} + +JSIndexScript.prototype.Dragon4_Fig = function () { + let data = + { + Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: false, + Args: [], + Script: //脚本 + '倍:VOL>=REF(V,1)*1.90 AND C>REF(C,1),COLORYELLOW;\n\ +低:VOLREF(V,1),3)==3 AND COUNT(C>O,3)==3,COLORBROWN;\n\ +缩量涨:COUNT(C>REF(C,1),2)==2 AND COUNT(V=REF(C,1),V,0,2,0),COLORRED;\n\ +STICKLINE(C=2038 AND MONTH>=1,0,1); +VAR2:=REF(LOW,1)*VAR1; +VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1; +VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1; +VAR5:=LLV(LOW,30)*VAR1; +VAR6:=HHV(VAR4,30)*VAR1; +VAR7:=IF(MA(CLOSE,58),1,0)*VAR1; +VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1; +吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORRED; +吸筹B:STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORRED; + +散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORFFFF00,LINETHICK2; +RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100; +K:=SMA(RSV,3,1); +D:=SMA(K,3,1); +J:=3*K-2*D; +主力线:EMA(J,5),COLORFF00FF,LINETHICK2; +DRAWICON(CROSS(主力线,散户线),主力线,1); +DRAWICON(CROSS(散户线,主力线),主力线,2); +*/ + +JSIndexScript.prototype.FundsAnalysis = function () +{ + let data = + { + Name: '资金分析', Description: '资金分析', IsMainIndex: false, + Args: [{ Name: 'M', Value: 55 }, { Name: 'N', Value: 34 }], + Script: //脚本 + 'LC:=REF(CLOSE,1);\n\ +RSI:=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);\n\ +FF:=EMA(CLOSE,3);\n\ +MA15:=EMA(CLOSE,21); DRAWTEXT(CROSS(85,RSI),75,\'▼\'),COLORGREEN;\n\ +VAR1:=IF(YEAR>=2038 AND MONTH>=1,0,1);\n\ +VAR2:=REF(LOW,1)*VAR1;\n\ +VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1;\n\ +VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1;\n\ +VAR5:=LLV(LOW,30)*VAR1;\n\ +VAR6:=HHV(VAR4,30)*VAR1;\n\ +VAR7:=IF(MA(CLOSE,58),1,0)*VAR1;\n\ +VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1;\n\ +吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORFB2F3B;\n\ +{吸筹B}STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORFB2F3B;\n\ +\n\ +散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORAA89BD,LINETHICK2;\n\ +RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:=SMA(RSV,3,1);\n\ +D:=SMA(K,3,1);\n\ +J:=3*K-2*D;\n\ +主力线:EMA(J,5),COLORF39800,LINETHICK2;\n\ +DRAWICON(CROSS(主力线,散户线),主力线,1);\n\ +DRAWICON(CROSS(散户线,主力线),主力线,2);' + }; + + return data; +} + +JSIndexScript.prototype.MarginProportion = function () +{ + let data = + { + Name: '融资占比(%)', Description: '融资占比', IsMainIndex: false, + Args: [], + Script: //脚本 + '占比:MARGIN(2);' + }; + + return data; +} + +JSIndexScript.prototype.BTX = function () +{ + let data = + { + Name: 'BTX', Description: '宝塔线', IsMainIndex: false, + Args: [], + Script: //脚本 + 'B1:=REF(C,1);\n\ +B2:= REF(C, 2);\n\ +SS:= IF(C > REF(C, 1) AND REF(C, 1) >= REF(C, 2), 1, IF(C < REF(C, 1) AND REF(C, 1) <= REF(C, 2), -1, IF(C > REF(C, 2) AND REF(C, 2) > REF(C, 1), 2, IF(C < REF(C, 2) AND REF(C, 2) < REF(C, 1), -2, 0))));\n\ +SM:= IF(REF(SS, 1) <> 0, REF(SS, 1), IF(REF(SS, 2) <> 0, REF(SS, 2), IF(REF(SS, 3) <> 0, REF(SS, 3), IF(REF(SS, 5) <> 0, REF(SS, 5), IF(REF(SS, 6) <> 0, REF(SS, 6), IF(REF(SS, 7) <> 0, REF(SS, 7), 0))))));\n\ +MC:= IF(REF(SS, 1) <> 0, B2, IF(SM > 0, MIN(B1, B2), MAX(B1, B2)));\n\ +TOW1:= IF(C > REF(C, 1), C, REF(C, 1));\n\ +TOW2:= IF((SS == -1 OR SS == -2) AND SM > 0, B2, TOW1);\n\ +TOWER:= IF(TOW1 > TOW2, TOW1, TOW2);\n\ +STICKLINE(SS == 1 OR SM >= 1 AND SS == 0, B1, C, 10, 1), COLORRED;\n\ +STICKLINE(SS == -1 OR SM <= -1 AND SS == 0, B1, C, 10, 0), COLORCYAN;\n\ +STICKLINE(SS == 2, B2, C, 10, 1), COLORRED;\n\ +STICKLINE(SS == -2, B2, C, 10, 0), COLORCYAN;\n\ +STICKLINE((SS == -1 OR SS == -2) AND SM > 0, B2, B1, 10, 1), COLORRED;\n\ +STICKLINE((SS == 1 OR SS == 2) AND SM < 0, B2, B1, 10, 0), COLORCYAN;' + }; + + return data; +} + + +JSIndexScript.prototype.EMPTY = function () +{ + let data = + { + Name: '', Description: '空指标', IsMainIndex: true, + Args: [], + Script: //脚本 + 'VAR2:=C;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_BSPoint = function () +{ + let data = + { + Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, + Args: [], + Script: //脚本 + 'MA5:MA(CLOSE,5);\n\ + MA13:MA(CLOSE,13);\n\ + MA21:MA(CLOSE,21);\n\ + MA34:MA(CLOSE,34);\n\ + {MA55:MA(CLOSE,55),COLOR0000FF;}\n\ + {MA120:=MA(CLOSE,120),COLORFFFF00;}\n\ + 天使:=EMA(C,2),COLOR000000;\n\ + 魔鬼:=EMA(SLOPE(C,21)*20+C,42),COLOR000000;\n\ + 买:=CROSS(天使,魔鬼);\n\ + 卖:=CROSS(魔鬼,天使);\n\ + DRAWICON(买,L*0.99,13),COLORYELLOW;\n\ + DRAWICON(卖,H*1.01,14),COLORGREEN;\n\ + DRAWKLINE_IF(天使>=魔鬼,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\ + DRAWKLINE_IF(天使<魔鬼,HIGH,CLOSE,LOW,OPEN),COLORBLUE;\n\ + DRAWKLINE_IF(CROSS(天使,魔鬼),HIGH,CLOSE,LOW,OPEN),COLORYELLOW;\n\ + DRAWKLINE_IF(CROSS(魔鬼,天使),HIGH,CLOSE,LOW,OPEN),COLORBLACK;' + }; + + return data; +} + +JSIndexScript.prototype.NewsNegative=function() +{ + let data= + { + Name: '负面新闻', Description: '负面新闻统计', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'N2', Value: 10 }], + Script: //脚本 + '负面:NEWS(1);\n\ + MA1:MA(负面,N);\n\ + MA2:MA(负面,N2);' + }; + + return data; +} + +JSIndexScript.prototype.NewsResearch = function () { + let data = + { + Name: '机构调研', Description: '机构调研统计', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'N2', Value: 10 }], + Script: //脚本 + '次数:NEWS(2);\n\ + MA1:MA(次数,N);\n\ + MA2:MA(次数,N2);' + }; + + return data; +} + +JSIndexScript.prototype.NewsInteract = function () { + let data = + { + Name: '董秘连线', Description: '互动易统计', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }, { Name: 'N2', Value: 10 }], + Script: //脚本 + '个数:NEWS(3);\n\ + MA1:MA(个数,N);\n\ + MA2:MA(个数,N2);' + }; + + return data; +} + +JSIndexScript.prototype.UpDownAnalyze = function () +{ + let data = + { + Name: '涨跌趋势', Description: '涨跌趋势', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + "上涨家数:UPCOUNT('CNA.CI'),COLORRED;\n\ +下跌家数:DOWNCOUNT('CNA.CI'),COLORGREEN;" + }; + + return data; +} + +//外包指标 +JSIndexScript.prototype.FXG_BSPoint = function () +{ + let data = + { + Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, + Args: [], + Script: //脚本 + 'MA5:MA(CLOSE,5);\n\ + MA13:MA(CLOSE,13);\n\ + MA21:MA(CLOSE,21);\n\ + MA34:MA(CLOSE,34);\n\ + {MA55:MA(CLOSE,55),COLOR0000FF;}\n\ + {MA120:=MA(CLOSE,120),COLORFFFF00;}\n\ + 天使:=EMA(C,2),COLOR000000;\n\ + 魔鬼:=EMA(SLOPE(C,21)*20+C,42),COLOR000000;\n\ + 买:=CROSS(天使,魔鬼);\n\ + 卖:=CROSS(魔鬼,天使);\n\ + SUPERDRAWTEXT(买,L,"B",2,5),COLORYELLOW;\n\ + SUPERDRAWTEXT(卖,L,"S",1,5),COLORGREEN;\n\ + DRAWKLINE_IF(天使>=魔鬼,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\ + DRAWKLINE_IF(天使<魔鬼,HIGH,CLOSE,LOW,OPEN),COLORBLUE;\n\ + DRAWKLINE_IF(CROSS(天使,魔鬼),HIGH,CLOSE,LOW,OPEN),COLORYELLOW;\n\ + DRAWKLINE_IF(CROSS(魔鬼,天使),HIGH,CLOSE,LOW,OPEN),COLORBLACK;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX = function () +{ + let data = + { + Name: '涨停多空线', Description: '涨停多空线', IsMainIndex: false, + Args: [], + Script: //脚本 + '做多能量线: SMA((CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100,5,1)-8,COLORRED,LINETHICK3;\n\ +做空能量线: SMA((HHV(HIGH,36)-CLOSE)/(HHV(HIGH,36)-LLV(LOW,36))*100,2,1),COLORGREEN,LINETHICK3;\n\ +20,POINTDOT,COLORF00FF0;\n\ +50,POINTDOT,COLORGREEN;\n\ +80,POINTDOT,COLORLIBLUE;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX2 = function () +{ + let data = + { + Name: '涨停吸筹区', Description: '涨停吸筹区', IsMainIndex: false, + Args: [], + Script: //脚本 + 'VAR0:=EMA(HHV(HIGH,500),21); \n\ +VAR1:=EMA(HHV(HIGH,250),21);\n\ +VAR2:=EMA(HHV(HIGH,90),21); \n\ +VAR3:=EMA(LLV(LOW,500),21); \n\ +VAR4:=EMA(LLV(LOW,250),21); \n\ +VAR5:=EMA(LLV(LOW,90),21);\n\ +\n\ +VAR6:=EMA((VAR3*0.96+VAR4*0.96+VAR5*0.96+VAR0*0.558+VAR1*0.558+VAR2*0.558)/6,21); \n\ +VAR7:=EMA((VAR3*1.25+VAR4*1.23+VAR5*1.2+VAR0*0.55+VAR1*0.55+VAR2*0.65)/6,21); \n\ +VAR8:=EMA((VAR3*1.3+VAR4*1.3+VAR5*1.3+VAR0*0.68+VAR1*0.68+VAR2*0.68)/6,21); \n\ +VAR9:=EMA((VAR6*3+VAR7*2+VAR8)/6*1.738,21); \n\ +VAR10:=REF(LOW,1); \n\ +VAR11:=SMA(ABS(LOW-VAR10),3,1)/SMA(MAX(LOW-VAR10,0),3,1)*100; \n\ +VAR12:=EMA(IFF(CLOSE*1.35<=VAR9,VAR11*10,VAR11/10),3); \n\ +VAR13:=LLV(LOW,30); \n\ +VAR14:=HHV(VAR12,30); \n\ +VAR15:=IFF(MA(CLOSE,58),1,0); \n\ +VAR16:=EMA(IFF(LOW<=VAR13,(VAR12+VAR14*2)/2,0),3)/618*VAR15;\n\ +\n\ +资金入场:IFF(VAR16>0,VAR16,0),LINETHICK,LINETHICK2, COLORFF0000; \n\ +\n\ +A1:IFF(资金入场>0,资金入场*1.2,0),STICK,LINETHICK5, COLORFF0000;\n\ +A2:IFF(资金入场>0,资金入场*0.8,0),STICK,LINETHICK5, COLORFF6600;\n\ +A3:IFF(资金入场>0,资金入场*0.6,0),STICK,LINETHICK5, COLORFF9900;\n\ +A4:IFF(资金入场>0,资金入场*0.4,0) ,STICK,LINETHICK5,COLORFFCC00;\n\ +A5:IFF(资金入场>0,资金入场*0.2,0) ,STICK,LINETHICK5,COLORFFFF00;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX3 = function () +{ + let data = + { + Name: '量能黄金点', Description: '量能黄金点', IsMainIndex: false, + Args: [], + Script: //脚本 + 'A:=IFF((CLOSE>126.32),VOL,VOL); \n\ +主力:=MA(A,4),COLORRED;\n\ +游资:=MA(A,8),COLORYELLOW;\n\ +大户:=MA(A,16),COLORF0F000;\n\ +散户:=MA(A,32),COLOR00FF00;\n\ +主比:=ABS(((主力)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORRED;\n\ +游比:=ABS(((游资)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORYELLOW;\n\ +大比:=ABS(((大户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORF0F000;\n\ +散比:=ABS(((散户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLOR00FF00;\n\ +警戒线:MA(A,180),COLORFF66FF;\n\ +STICKLINE((主力 > 0),0,主力,2.5,0),COLOR1020BB;\n\ +STICKLINE((主力 > 0),0,主力,0.7,0),COLORRED;\n\ +STICKLINE((游资 > 0),0,游资,2.5,0),COLOR009CFF;\n\ +STICKLINE((游资 > 0),0,游资,0.7,0),COLORYELLOW;\n\ +STICKLINE((大户 > 0),0,大户,2.5,0),COLORFF8800;\n\ +STICKLINE((大户 > 0),0,大户,0.7,0),COLORLIBLUE;\n\ +STICKLINE((散户 > 0),0,散户,2.5,0),COLOR00CA00;\n\ +STICKLINE((散户 > 0),0,散户,0.7,0),COLORGREEN;' + }; + + return data; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +//五彩K线 + +JSIndexScript.prototype.COLOR_KSTAR1 = function () +{ + let data = + { + Name: '十字星', Description: '十字星', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:CLOSE==OPEN&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_KSTAR2 = function () +{ + let data = + { + Name: '早晨之星', Description: '早晨之星', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:(REF(CLOSE,2)/REF(OPEN,2)<0.95) && (REF(OPEN,1) < REF(CLOSE,2)) && (ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03) && CLOSE/OPEN>1.05 && CLOSE>REF(CLOSE,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_KSTAR3 = function () +{ + let data = + { + Name: '黄昏之星', Description: '黄昏之星', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05 && REF(OPEN,1)>REF(CLOSE,2) && ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03 && CLOSE/OPEN<0.95 && CLOSE1.03;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K220 = function () +{ + let data = + { + Name: '身怀六甲', Description: '身怀六甲', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1)>0.04&&\n\ + ABS(CLOSE-OPEN)/CLOSE<0.005&&\n\ + MAX(CLOSE,OPEN)MIN(REF(CLOSE,1),REF(OPEN,1));' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K300 = function () +{ + let data = + { + Name: '三个白武士', Description: '三个白武士', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:UPNDAY(CLOSE,3)&&NDAY(CLOSE,OPEN,3);' + }; + + return data; +} + + +JSIndexScript.prototype.COLOR_K310 = function () +{ + let data = + { + Name: '三只乌鸦', Description: '三只乌鸦', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:DOWNNDAY(CLOSE,3)&&NDAY(OPEN,CLOSE,3);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K380 = function () +{ + let data = + { + Name: '光头阳线', Description: '光头阳线', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:HIGH==CLOSE&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K390 = function () +{ + let data = + { + Name: '光脚阴线', Description: '光脚阴线', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:LOW==CLOSE&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K134 = function () +{ + let data = + { + Name: '垂死十字', Description: '垂死十字', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:CLOSE==OPEN&&CLOSE==LOW&&CLOSE1.05&&CLOSE>REF(CLOSE,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K150 = function () +{ + let data = + { + Name: '黄昏十字星', Description: '黄昏十字星', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05&&\n\ +REF(OPEN,1)>REF(CLOSE,2)&&\n\ +REF(OPEN,1)=REF(CLOSE,1)&&\n\ +CLOSE/OPEN<0.95&&CLOSE3*(MAX(OPEN,CLOSE)-LOW)&&\n\ +CLOSE>MA(CLOSE,5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K165 = function () +{ + let data = + { + Name: '倒转锤头', Description: '倒转锤头', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'KSTAR:MIN(OPEN,CLOSE)==LOW&&\n\ +HIGH-LOW>3*(MAX(OPEN,CLOSE)-LOW)&&\n\ +CLOSE3*(HIGH-MIN(OPEN,CLOSE))&&\n\ +CLOSE3*(HIGH-MIN(OPEN,CLOSE))&&\n\ +CLOSE>MA(CLOSE,5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K190 = function () +{ + let data = + { + Name: '穿头破脚', Description: '穿头破脚', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'OUT:(REF(CLOSE,1)/REF(OPEN,1)>1.03&&\n\ +CLOSE/OPEN<0.96&&\n\ +CLOSEREF(CLOSE,1))||\n\ +(REF(CLOSE,1)/REF(OPEN,1)<0.97&&\n\ +CLOSE/OPEN>1.04&&\n\ +CLOSE>REF(OPEN,1)&&OPENREF(VOL,1)||VOL>(CAPITAL*0.1);\n\ +BB:=OPEN>=(REF(HIGH,1))&&REF(HIGH,1)>(REF(HIGH,2)*1.06);\n\ +CC:=CLOSE>(REF(CLOSE,1))-(REF(CLOSE,1)*0.01);\n\ +DD:=CLOSE<(HIGH*0.965) && HIGH>(OPEN*1.05);\n\ +EE:=LOW(REF(CLOSE,1)*1.06);\n\ +FF:=(HIGH-(MAX(OPEN,CLOSE)))/2>(MIN(OPEN,CLOSE))-LOW;\n\ +GG:=(ABS(OPEN-CLOSE))/2<(MIN(OPEN,CLOSE)-LOW);\n\ +SWORDO:AA&&BB&&CC&&DD&&EE&&FF&&GG;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_CSFR = function () +{ + let data = + { + Name: '出水芙蓉', Description: '出水芙蓉', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'A:=CLOSE>OPEN;\n\ +B:=A&&CLOSE>MA(CLOSE,S)&&CLOSE>MA(CLOSE,M)&&CLOSE>MA(CLOSE,LL);\n\ +CC:=B&&OPEN0.0618*CLOSE;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_WYGD = function () +{ + let data = + { + Name: '乌云盖顶', Description: '乌云盖顶', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( \n\ +REF(CLOSE,1)/REF(OPEN,1)>1.03 AND \n\ +CLOSE/OPEN<0.97 AND \n\ +OPEN>REF(CLOSE,1) AND CLOSE1.03 AND \n\ +OPENREF(CLOSE,1), 3);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_SZTAI = function () +{ + let data = + { + Name: '十字胎', Description: '十字胎', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1) > 0.04 AND \n\ +CLOSE==OPEN AND CLOSE < MAX(REF(CLOSE,1),REF(OPEN,1)) AND \n\ +CLOSE > MIN(REF(CLOSE,1),REF(OPEN,1)), 2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_PINGDING = function () +{ + let data = + { + Name: '平顶', Description: '平顶', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET(ABS(HIGH-REF(HIGH,1))/HIGH<0.001,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_PINGDI = function () +{ + let data = + { + Name: '平底', Description: '平底', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET((ABS(LOW-REF(LOW,1))/LOW<0.001 AND \n\ +ABS(REF(LOW,1)-REF(LOW,2))/REF(LOW,1)<=0.001),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_DAYANZHU = function () +{ + let data = + { + Name: '大阳烛', Description: '大阳烛', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:CLOSE/OPEN>1.05 AND HIGH/LOW < CLOSE/OPEN+0.018;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_DAYINGZHU = function () +{ + let data = + { + Name: '大阴烛', Description: '大阴烛', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:OPEN/CLOSE > 1.05 AND HIGH/LOW < OPEN/CLOSE+0.018;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_HYFG = function () +{ + let data = + { + Name: '好友反攻', Description: '好友反攻', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( (REF(CLOSE,1)OPEN AND ABS(CLOSE-REF(CLOSE,1))/CLOSE<0.002),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_TKQK = function () +{ + let data = + { + Name: '跳空缺口', Description: '跳空缺口', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( HIGHREF(HIGH,1),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_SFWY = function () +{ + let data = + { + Name: '双飞乌鸦', Description: '双飞乌鸦', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( REF(CLOSE,1)1.03 AND \n\ +REF(CLOSE,3)REF(HIGH,3) AND \n\ +REF(HIGH,4)>REF(HIGH,2) AND \n\ +REF(HIGH,4)>REF(HIGH,1) AND \n\ +CLOSE/OPEN>1.03 AND \n\ +CLOSE>REF(CLOSE,4), 5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_XDSBQ = function () +{ + let data = + { + Name: '下跌三部曲', Description: '下跌三部曲', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( \n\ +REF(CLOSE,4)/REF(OPEN,4)<0.97 AND \n\ +REF(CLOSE,3)>REF(OPEN,3) AND \n\ +REF(CLOSE,2)>REF(OPEN,2) AND \n\ +REF(CLOSE,1)>REF(OPEN,1) AND \n\ +REF(LOW,4)REF(HIGH,3) AND \n\ +REF(HIGH,4)>REF(HIGH,2) AND \n\ +REF(HIGH,4)>REF(HIGH,1) AND \n\ +CLOSE/OPEN<0.97 AND \n\ +CLOSE0.667;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_CHSY = function () +{ + let data = + { + Name: '长上影', Description: '长上影', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR2:(HIGH-MAX(CLOSE,OPEN))/(HIGH-LOW)>0.667,COLORBLUE;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_FENLI = function () +{ + let data = + { + Name: '分离', Description: '分离', IsMainIndex: true, InstructionType: 2, + Script: //脚本 + 'VAR1:BACKSET( OPEN==REF(OPEN,1) AND (CLOSE-OPEN)*(REF(CLOSE,1)-REF(OPEN,1))<0,2);' + }; + + return data; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//交易系统 + +JSIndexScript.prototype.TRADE_BIAS = function () +{ + let data = + { + Name: 'BIAS', Description: '乖离率专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 12 }, { Name: 'LL', Value: 6 }, { Name: 'LH', Value: 6 }], + Script: //脚本 + 'BIAS:=(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\ +ENTERLONG:CROSS(-LL,BIAS);\n\ +EXITLONG:CROSS(BIAS,LH);' + + }; + + return data; +} + +JSIndexScript.prototype.TRADE_CCI = function () +{ + let data = + { + Name: 'CCI', Description: 'CCI专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 14 }], + Script: //脚本 + 'TYP:=(HIGH+LOW+CLOSE)/3;\n\ +CCI:=(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));\n\ +INDEX:=CCI;\n\ +ENTERLONG:CROSS(INDEX,-100);\n\ +EXITLONG:CROSS(100,INDEX);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_DMI = function () +{ + let data = + { + Name: 'DMI', Description: '趋向专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 14 }], + Script: //脚本 + 'MTR:=SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N);\n\ +HD :=HIGH-REF(HIGH,1);\n\ +LD :=REF(LOW,1)-LOW;\n\ +PDM:=SUM(IF(HD>0&&HD>LD,HD,0),N);\n\ +MDM:=SUM(IF(LD>0&&LD>HD,LD,0),N);\n\ +PDI:=PDM*100/MTR;\n\ +MDI:=MDM*100/MTR;\n\ +ENTERLONG:CROSS(PDI,MDI);\n\ +EXITLONG:CROSS(MDI,PDI);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_KD = function () +{ + let data = + { + Name: 'KD', Description: 'KD指标专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M1', Value: 3 }, { Name: 'M2', Value: 3 }], + Script: //脚本 + 'WRSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +WK:=SMA(WRSV,M1,1);\n\ +D:=SMA(WK,M2,1);\n\ +ENTERLONG:CROSS(WK,D)&&WK<20;\n\ +EXITLONG:CROSS(D,WK)&&WK>80;' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_BOLL = function () +{ + let data = + { + Name: 'BOLL', Description: '布林带专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 20 }], + Script: //脚本 + 'MID :=MA(CLOSE,N);\n\ +UPPER:=MID+2*STD(CLOSE,N);\n\ +LOWER:=MID-2*STD(CLOSE,N);\n\ +ENTERLONG:CROSS(CLOSE,LOWER);\n\ +EXITLONG:CROSS(CLOSE,UPPER);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_KDJ = function () +{ + let data = + { + Name: 'KDJ', Description: 'KDJ专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M1', Value: 3 }], + Script: //脚本 + 'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:=SMA(RSV,M1,1);\n\ +D:=SMA(K,M1,1);\n\ +J:=3*K-2*D;\n\ +ENTERLONG:CROSS(J,0);\n\ +EXITLONG:CROSS(100,J);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MA = function () +{ + let data = + { + Name: 'MA', Description: '均线专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'SHORT', Value: 5 }, { Name: 'LONG', Value: 20 }], + Script: //脚本 + 'ENTERLONG:CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));\n\ +EXITLONG:CROSS(MA(CLOSE,LONG),MA(CLOSE,SHORT));' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MACD = function () +{ + let data = + { + Name: 'MACD', Description: 'MACD专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'LONG', Value: 26 }, { Name: 'SHORT', Value: 12 }, { Name: 'M', Value: 9 }], + Script: //脚本 + 'DIFF:=EMA(CLOSE,SHORT) - EMA(CLOSE,LONG);\n\ +DEA := EMA(DIFF,M);\n\ +MACD := 2*(DIFF-DEA);\n\ +ENTERLONG:CROSS(MACD,0);\n\ +EXITLONG:CROSS(0,MACD);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MTM = function () +{ + let data = + { + Name: 'MTM', Description: '动力指标专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 6 }], + Script: //脚本 + 'WMTM:=C-REF(C,N);\n\ +ENTERLONG:CROSS(WMTM,0);\n\ +EXITLONG:CROSS(0,WMTM);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_PSY = function () +{ + let data = + { + Name: 'PSY', Description: 'PSY心理线专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 12 }, { Name: 'LL', Value: 10 }, { Name: 'LH', Value: 85 }], + Script: //脚本 + 'MYPSY:=COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\n\ +ENTERLONG:CROSS(LL,MYPSY);\n\ +EXITLONG:CROSS(MYPSY,LH);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_ROC = function () +{ + let data = + { + Name: 'ROC', Description: '变动速率专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 12 }, { Name: 'M', Value: 6 }], + Script: //脚本 + 'WROC:=MA(100*(CLOSE-REF(CLOSE,N))/REF(CLOSE,N),M);\n\ +ENTERLONG:CROSS(WROC,0);\n\ +EXITLONG:CROSS(0,WROC);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_RSI = function () +{ + let data = + { + Name: 'RSI', Description: '相对强弱专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 6 }, { Name: 'LL', Value: 20 }, { Name: 'LH', Value: 80 }], + Script: //脚本 + 'LC:=REF(CLOSE,1);\n\ +WRSI:=SMA(MAX(CLOSE-LC,0),N,1)/SMA(ABS(CLOSE-LC),N,1)*100;\n\ +ENTERLONG:CROSS(WRSI,LL);\n\ +EXITLONG:CROSS(LH,WRSI);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_VR = function () +{ + let data = + { + Name: 'VR', Description: 'VR容量比率专家系统', IsMainIndex: true, InstructionType: 1, + Args: [{ Name: 'N', Value: 26 }, { Name: 'LL', Value: 70 }, { Name: 'LH', Value: 250 }], + Script: //脚本 + 'WVR := SUM((IF(CLOSE>OPEN,VOL,0)+IF(CLOSE=OPEN,VOL/2,0)),N)/SUM((IF(CLOSE REF(VAR5, 1), VAR5,0), COLORRED, NODRAW;\n\ +洗盘: IF(VAR5 < REF(VAR5, 1), VAR5,0), COLORYELLOW, NODRAW;\n\ +STICKLINE(VAR5> REF(VAR5, 1),0, VAR5, 50, 0), COLORRED;\n\ +STICKLINE(VAR5 < REF(VAR5, 1), 0, VAR5, 50, 0), COLORYELLOW;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index2 = function () +{ + let data = + { + Name: '牛熊区间', Description: '牛熊区间', IsMainIndex: false, YSpecificMaxMin: { Max: 100, Min: 1, Count: 4 }, YSplitScale: [1, 50, 100], + Args: [], + Script: //脚本 +'短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\ ++ 13 * REF(H, 7) + 12 * REF(H, 8) + 11 * REF(H, 9) + 10 * REF(H, 10) + 9 * REF(H, 11) + 8 * REF(H, 12)\n\ ++ 7 * REF(H, 13) + 6 * REF(H, 14) + 5 * REF(H, 15) + 4 * REF(H, 16) + 3 * REF(H, 17) + 2 * REF(H, 18) +\n\ +REF(H, 20))/ 210, COLORBLUE, LINETHICK1;\n\ +短低L:= (20 * L + 19 * REF(L, 1) + 18 * REF(L, 2) + 17 * REF(L, 3) + 16 * REF(L, 4) + 15 * REF(L, 5) + 14 * REF(L, 6)\n\ ++ 13 * REF(L, 7) + 12 * REF(L, 8) + 11 * REF(L, 9) + 10 * REF(L, 10) + 9 * REF(L, 11) + 8 * REF(L, 12)\n\ ++ 7 * REF(L, 13) + 6 * REF(L, 14) + 5 * REF(L, 15) + 4 * REF(L, 16) + 3 * REF(L, 17) + 2 * REF(L, 18) +\n\ +REF(L, 20)) / 210, COLORBLUE, LINETHICK1;\n\ +D90H:= EMA(短高H, 90), COLORRED, LINETHICK1;\n\ +D90L:= EMA(短低L, 90), COLORRED, LINETHICK1;\n\ +D90差:= D90H - D90L;\n\ +D90顶:= D90H + D90差 * 2, COLORRED, LINETHICK1;\n\ +D90底:= D90L - D90差 * 2, COLORRED, LINETHICK1;\n\ +高0:= (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1 + EMA(EMA(H, 25), 25), LINETHICK1, COLORWHITE;\n\ +低0:= EMA(EMA(L, 25), 25) - (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1, LINETHICK1, COLORWHITE;\n\ +多头定位:= 低0 >= D90底 AND 高0 >= D90顶;\n\ +空头定位:= 高0 <= D90顶 AND 低0 <= D90底;\n\ +震荡定位:= 低0 >= D90底 AND 高0 <= D90顶;\n\ +牛市: IF(多头定位 == 1, 100, 1), COLORRED, NODRAW;\n\ +熊市: IF(空头定位 == 1, 100, 1), COLORGREEN, NODRAW;\n\ +震荡: IF(震荡定位 == 1, 100, 1), COLORGRAY, NODRAW;\n\ +STICKLINE(多头定位 == 1, 100, 1, 100, 0), COLORRED;\n\ +STICKLINE(空头定位 == 1, 100, 1, 100, 0), COLORGREEN;\n\ +STICKLINE(震荡定位 == 1, 100, 1, 100, 0), COLORGRAY;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index3 = function () +{ + let data = + { + Name: '持仓信号', Description: '持仓信号', IsMainIndex: true, + Args: [], + Script: //脚本 +'买线:=EMA(C,2);\n\ +卖线:=EMA(SLOPE(C,30)*5+C,20); \n\ +BU:=CROSS(买线,卖线);\n\ +SEL:=CROSS(卖线,买线);\n\ +\n\ +STICKLINE(买线>=卖线 AND CLOSE>OPEN,LOW,HIGH,0,1),COLORRED;\n\ +STICKLINE(买线>=卖线 AND CLOSE=卖线 AND CLOSE>OPEN,CLOSE,OPEN,50,1),COLORRED;\n\ +STICKLINE(买线>=卖线 AND CLOSEOPEN,LOW,HIGH,0,1),COLORGREEN;\n\ +STICKLINE(买线<卖线 AND CLOSEOPEN,CLOSE,OPEN,50,1),COLORGREEN;\n\ +\n\ +HHJSJDA:=(3*CLOSE+OPEN+LOW+HIGH)/6;\n\ +HHJSJDB:=(19*HHJSJDA+19*REF(HHJSJDA,1)+18*REF(HHJSJDA,2)+17*REF(HHJSJDA,3)+16*REF(HHJSJDA,4)+15*REF(HHJSJDA,5)+14*REF(HHJSJDA,6)\n\ ++13*REF(HHJSJDA,7)+12*REF(HHJSJDA,8)+11*REF(HHJSJDA,9)+10*REF(HHJSJDA,10)+9*REF(HHJSJDA,11)+8*REF(HHJSJDA,12)+7*REF(HHJSJDA,13)+6*REF(HHJSJDA,14)+5*REF(HHJSJDA,15)+4*REF(HHJSJDA,16)+3*REF(HHJSJDA,17)+2*REF\n\ +(HHJSJDA,20)+REF(HHJSJDA,19))/210,COLORYELLOW;\n\ +HHJSJDC:=MA(HHJSJDB,5),COLORRED;\n\ +快线:HHJSJDB,COLORYELLOW;\n\ +慢线:HHJSJDC,COLORRED;\n\ +\n\ +SVAR11:=HHV(HIGH,34);\n\ +SVAR14:=CLOSE-REF(CLOSE,1);\n\ +SVAR15:=MAX(SVAR14,0);\n\ +SVAR16:=ABS(SVAR14);\n\ +SVAR17:=SMA(SVAR15,7,1)/SMA(SVAR16,7,1)*100;\n\ +SVAR18:=SMA(SVAR15,13,1)/SMA(SVAR16,13,1)*100;\n\ +SVAR19:=BARSCOUNT(CLOSE);\n\ +SVAR20:=SMA(MAX(SVAR14,0),6,1)/SMA(ABS(SVAR14),6,1)*100;\n\ +SVAR21:=(-200)*(HHV(HIGH,60)-CLOSE)/(HHV(HIGH,60)-LLV(LOW,60))+100;\n\ +SVAR1A:=(CLOSE-LLV(LOW,15))/(HHV(HIGH,15)-LLV(LOW,15))*100;\n\ +SVAR1B:=SMA((SMA(SVAR1A,4,1)-50)*2,3,1);\n\ +SVAR1C:=(INDEXC-LLV(INDEXL,14))/(HHV(INDEXH,14)-LLV(INDEXL,14))*100;\n\ +SVAR1D:=SMA(SVAR1C,4,1);\n\ +SVAR1E:=SMA(SVAR1D,3,1);\n\ +SVAR1F:=(HHV(HIGH,30)-CLOSE)/CLOSE*100;\n\ +SVAR22:=SVAR20<=25 AND SVAR21<-95 AND SVAR1F>20 AND SVAR1B<-30 AND SVAR1E<30 AND SVAR11-CLOSE>=-0.25 AND SVAR17<22 AND SVAR18<28 AND SVAR19>50;\n\ +BUY3:=CROSS(SVAR22,0.5) AND COUNT(SVAR22==1,10)==1;\n\ +\n\ +SVARF:=LOW*0.9;\n\ +SVAR10X:=100-3*SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1)+2*SMA(SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1),15,1);\n\ +SVAR11X:=SVARFREF(VOL,1) AND CLOSE>REF(CLOSE,1);\n\ +BUY2:=SVAR11X AND COUNT(SVAR11X,30)==1;\n\ +\n\ +VAR1:=(CLOSE+HIGH+LOW+OPEN)/4;\n\ +VAR2:=SUMBARS(VOL,CAPITAL);\n\ +VAR3:=HHV(VAR1,VAR2);\n\ +VAR4:=LLV(VAR1,VAR2);\n\ +VAR5:=(2*VAR1-VAR4-REF(VAR4,1))/(VAR3-VAR4);\n\ +VAR6:=(VAR1-VAR4)/(VAR3-VAR4);\n\ +VAR7:=IF(VAR1<=VAR4,VAR5*60,VAR6*60);\n\ +VAR8:=600*(EMA(CLOSE,3)-EMA(LOW,30))/EMA(LOW,30);\n\ +VAR9:=EMA(VAR8,7);\n\ +VARC:=HHV(HIGH,9)-LLV(LOW,9);\n\ +VARD:=HHV(HIGH,9)-CLOSE;\n\ +VARE:=CLOSE-LLV(LOW,9);\n\ +VARF:=VARD/VARC*100-70;\n\ +VAR10:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*100;\n\ +VAR11:=(2*CLOSE+HIGH+LOW)/4;\n\ +VAR12:=SMA(VARE/VARC*100,3,1);\n\ +VAR13:=LLV(LOW,34);\n\ +VAR14:=SMA(VAR12,3,1)-SMA(VARF,9,1);\n\ +VAR15:=IF(VAR14>100,VAR14-100,0);\n\ +VAR16:=HHV(HIGH,34);\n\ +VAR17:=EMA((VAR11-VAR13)/(VAR16-VAR13)*100,8);\n\ +VAR18:=EMA(VAR17,5);\n\ +BUY:=STICKLINE(VAR17-VAR18>0,VAR17,VAR18,8,1),COLORRED;\n\ +SELL:=STICKLINE(VAR17-VAR18<0,VAR17,VAR18,8,1),COLORGREEN;\n\ +BUY1:=VAR17>VAR18 AND REF(VAR17,1)REF(VAR18,1);\n\ +\n\ +短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\ ++13*REF(H,7)+12*REF(H,8)+11*REF(H,9)+10*REF(H,10)+9*REF(H,11)+8*REF(H,12)\n\ ++7*REF(H,13)+6*REF(H,14)+5*REF(H,15)+4*REF(H,16)+3*REF(H,17)+2*REF(H,18)+\n\ +REF(H,20))/210,COLORBLUE,LINETHICK1;\n\ +短低L:=(20*L+19*REF(L,1)+18*REF(L,2)+17*REF(L,3)+16*REF(L,4)+15*REF(L,5)+14*REF(L,6)\n\ ++13*REF(L,7)+12*REF(L,8)+11*REF(L,9)+10*REF(L,10)+9*REF(L,11)+8*REF(L,12)\n\ ++7*REF(L,13)+6*REF(L,14)+5*REF(L,15)+4*REF(L,16)+3*REF(L,17)+2*REF(L,18)+\n\ +REF(L,20))/210,COLORBLUE,LINETHICK1;\n\ +D90H:=EMA(短高H,90),COLORRED,LINETHICK1;\n\ +D90L:=EMA(短低L,90),COLORRED,LINETHICK1;\n\ +D90差:=D90H-D90L;\n\ +D90顶:=D90H+D90差*2,COLORRED,LINETHICK1;\n\ +D90底:=D90L-D90差*2,COLORRED,LINETHICK1;\n\ +高0:=(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1+EMA(EMA(H,25),25),LINETHICK1,COLORWHITE;\n\ +低0:=EMA(EMA(L,25),25)-(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1,LINETHICK1,COLORWHITE;\n\ +多头定位:=低0>=D90底 AND 高0>=D90顶;\n\ +空头定位:=高0<=D90顶 AND 低0<=D90底;\n\ +震荡定位:=低0>=D90底 AND 高0<=D90顶;\n\ +\n\ +牛市:=多头定位==1;\n\ +熊市:=空头定位==1;\n\ +震荡:=震荡定位==1;\n\ +\n\ +非牛市:=熊市 OR 震荡;\n\ +非熊市:=牛市 OR 震荡;\n\ +\n\ +BUY11:=BUY1 AND 非熊市;\n\ +SELL11:=SELL1 AND 震荡定位==0;\n\ +\n\ +BUY111:=BUY11 AND COUNT(BUY11,10)<2;\n\ +BUY0:=BUY111 AND COUNT(BUY111,21)==1;\n\ +SELL111:=SELL11 AND COUNT(SELL11,10)<2;\n\ +SELL0:=SELL111 AND COUNT(SELL111,10)==1;\n\ +\n\ +XK1:=EMA(100*(CLOSE-LLV(LOW,34))/(HHV(HIGH,34)-LLV(LOW,34)),3)/4;\n\ +上穿:=REF(XK1,1)<5 AND XK1>=5;\n\ +BUY4:=上穿 AND COUNT(XK1<2,12)<1;\n\ +\n\ +SELL2:=REF(XK1,1)<=22.5 AND XK1>22.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,5)>0;\n\ +SELL3:=REF(XK1,1)>=21.5 AND XK1<21.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,12)>1;\n\ +SELL4:=SELL2 OR SELL3 AND COUNT((SELL2 OR SELL3)==1,5)==1;\n\ +\n\ +SUPERDRAWTEXT(BUY0,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(SELL0,H,"风险",1,10),COLORGREEN;\n\ +SUPERDRAWTEXT(BUY2,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(BUY4,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(SELL4,H,"风险",1,10),COLORGREEN;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index4 = function () +{ + let data = + { + Name: '股东实际增减持', Description: '股东实际增减持', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 +'增持:NEWS(4),NODRAW,COLORRED;\n\ +减持:NEWS(5),NODRAW,COLORGREEN;\n\ +STICKLINE(增持>0,0,增持,1,0),COLORRED;\n\ +STICKLINE(减持<0,0,减持,1,0),COLORGREEN;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index5 = function () +{ + let data = + { + Name: '大宗交易', Description: '大宗交易', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '交易次数:NEWS(7);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index6 = function () +{ + let data = + { + Name: '信托持股', Description: '信托持股', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '家数:NEWS(6);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index7 = function () { + let data = + { + Name: '官网新闻', Description: '官网新闻', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '个数:NEWS(8);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index8 = function () { + let data = + { + Name: '高管要闻', Description: '高管要闻', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '个数:NEWS(9);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index9 = function () +{ + let data = + { + Name: '股权质押', Description: '股权质押', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '次数:NEWS(10);' + }; + + return data; +} + +JSIndexScript.prototype.CJL = function () +{ + let data = + { + Name: 'CJL', Description: '期货持仓量', IsMainIndex: false, + Args: [], + Script: //脚本 + "成交量:VOL,VOLSTICK;\n\ +持仓量:VOLINSTK,LINEOVERLAY;" + }; + + return data; +} + +JSIndexScript.prototype.SQJZ = function () +{ + let data = + { + Name: 'SQJZ', Description: '神奇九转', IsMainIndex: true, + Script: //脚本 +"B:=C=9 AND REFXV(COUNT(B,9),8)=9);\n\ +DRAWNUMBER(B1 AND REF(B,1)=0,L,1),COLORMAGENTA;\n\ +B2:=(N=5 AND REFXV(COUNT(B,6),4)=6) OR (N=6 AND REFXV(COUNT(B,7),5)=7) OR (N=7 AND REFXV(COUNT(B,8),6)=8) OR (N>=8 AND REFXV(COUNT(B,9),7)=9);\n\ +DRAWNUMBER(B2 AND REF(B,2)=0,L,2),COLORMAGENTA;\n\ +B8:=(N=1 AND COUNT(B,8)=8) OR (N>=2 AND REFXV(COUNT(B,9),1)=9);\n\ +DRAWNUMBER(B8 AND REF(B,8)=0,L,8),COLORMAGENTA;\n\ +B9:=(N>=1 AND COUNT(B,9)=9);\n\ +DRAWNUMBER(B9 AND REF(B,9)=0,L,9),COLORBROWN;\n\ +S:=C>REF(C,4);\n\ +S1:=(N=6 AND REFXV(COUNT(S,6),5)=6) OR (N=7 AND REFXV(COUNT(S,7),6)=7) OR (N=8 AND REFXV(COUNT(S,8),7)=8) OR (N>=9 AND REFXV(COUNT(S,9),8)=9);\n\ +DRAWNUMBER(S1 AND REF(S,1)=0,H,1),COLORMAGENTA,DRAWABOVE;\n\ +S2:=(N=5 AND REFXV(COUNT(S,6),4)=6) OR (N=6 AND REFXV(COUNT(S,7),5)=7) OR (N=7 AND REFXV(COUNT(S,8),6)=8) OR (N>=8 AND REFXV(COUNT(S,9),7)=9);\n\ +DRAWNUMBER(S2 AND REF(S,2)=0,H,2),COLORMAGENTA,DRAWABOVE;\n\ +S8:=(N=1 AND COUNT(S,8)=8) OR (N>=2 AND REFXV(COUNT(S,9),1)=9);\n\ +DRAWNUMBER(S8 AND REF(S,8)=0,H,8),COLORMAGENTA,DRAWABOVE;\n\ +S9:=(N>=1 AND COUNT(S,9)=9);\n\ +DRAWNUMBER(S9 AND REF(S,9)=0,H,9),COLORGREEN,DRAWABOVE;" + }; + + return data; +} + +JSIndexScript.prototype.XT = function () +{ + let data = + { + Name: 'XT', Description: '箱体', IsMainIndex: true, + Args: [{ Name: 'N', Value: 10 }], + Script: //脚本 +"【箱顶】:PEAK(CLOSE,N,1)*0.98;\n\ +【箱底】:TROUGH(CLOSE,N,1)*1.02;\n\ +【箱高】:100*(【箱顶】-【箱底】)/【箱底】,NODRAW;" + }; + + return data; +} + +JSIndexScript.prototype.CFJT = function () +{ + let data = + { + Name: 'CFJT', Description: '财富阶梯', IsMainIndex: true, + Script: //脚本 +"突破:=REF(EMA(C,14),1);\n\ +A1X:=(EMA(C,10)-突破)/突破*100;\n\ +多方:=IF(A1X>=0,REF(EMA(C,10),BARSLAST(CROSS(A1X,0))+1),DRAWNULL);\n\ +空方:=IF(A1X<0,REF(EMA(C,10),BARSLAST(CROSS(0,A1X))+1),DRAWNULL);\n\ +STICKLINE(A1X>=0,多方,突破,110,0),COLORRED;\n\ +STICKLINE(A1X<0,空方,突破,110,0),COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.CYX = function () +{ + let data = + { + Name: 'CYX', Description: '撑压线', IsMainIndex: true, + Args: [{ Name: 'N', Value: 7 }], + Script: //脚本 +"Z1:=STRCAT(HYBLOCK,' ');\n\ +Z2:=STRCAT(Z1,DYBLOCK);\n\ +Z3:=STRCAT(Z2,' ');\n\ +DRAWTEXT_FIX(ISLASTBAR,0,0,0,STRCAT(Z3,GNBLOCK)),COLOR00C0C0;\n\ +A1:=REF(H,N)=HHV(H,2*N+1);\n\ +B1:=FILTER(A1,N);\n\ +C1:=BACKSET(B1,N+1);\n\ +D1:=FILTER(C1,N);\n\ +A2:=REF(L,N)=LLV(L,2*N+1);\n\ +B2:=FILTER(A2,N);\n\ +C2:=BACKSET(B2,N+1);\n\ +D2:=FILTER(C2,N);\n\ +E1:=(REF(LLV(L,2*N),1)+REF(HHV(H,2*N),1))/2;\n\ +E2:=(H+L)/2;\n\ +H1:=(D1 AND NOT(D2 AND E1>=E2)) OR ISLASTBAR OR BARSCOUNT(C)=1;\n\ +L1:=(D2 AND NOT(D1 AND E1=E2);\n\ +X1:=REF(BARSLAST(H1),1)+1;\n\ +F1:=BACKSET(H1 AND COUNT(L1,X1)>0,LLVBARS(IF(L1,L,10000),X1));\n\ +G1:=F1>REF(F1,1);\n\ +I1:=BACKSET(G1,2);\n\ +LD:=I1>REF(I1,1);\n\ +L2:=LD OR ISLASTBAR OR BARSCOUNT(C)=1;\n\ +X2:=REF(BARSLAST(L2),1)+1;\n\ +F2:=BACKSET(L2 AND COUNT(H2,X2)>0,HHVBARS(IF(H2,H,0),X2));\n\ +G2:=F2>REF(F2,1);\n\ +I2:=BACKSET(G2,2);\n\ +HD:=I2>REF(I2,1);\n\ +R1:=BACKSET(ISLASTBAR,BARSLAST(HD)+1);\n\ +S1:=R1>REF(R1,1);\n\ +T1:=BACKSET(ISLASTBAR,BARSLAST(LD)+1);\n\ +U1:=T1>REF(T1,1);\n\ +R2:=BACKSET(S1,REF(BARSLAST(HD),1)+2);\n\ +S2:=R2>REF(R2,1);\n\ +T2:=BACKSET(U1,REF(BARSLAST(LD),1)+2);\n\ +U2:=T2>REF(T2,1);\n\ +DRAWLINE(S2,H,S1,H,1),LINETHICK2,COLORRED;\n\ +DRAWLINE(U2,L,U1,L,1),LINETHICK2,COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.WAVE = function () +{ + let data = + { + Name: 'WAVE', Description: '波浪分析', IsMainIndex: true, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +"ZIG(3,N);" + }; + + return data; +} + + +module.exports = +{ + JSCommonIndexScript: + { + JSIndexScript: JSIndexScript + } +}; + + + diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.index.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.index.wechat.js new file mode 100644 index 0000000..069159a --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.index.wechat.js @@ -0,0 +1,1119 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 指标基类及定制指标 +*/ + +import { + JSCommonResource_Global_JSChartResource as g_JSChartResource, +} from './umychart.resource.wechat.js' + +import { JSCommonComplier } from "./umychart.complier.wechat.js"; //通达信编译器 + +//图形库 +import { + JSCommonChartPaint_IChartPainting as IChartPainting, + JSCommonChartPaint_ChartSingleText as ChartSingleText, + JSCommonChartPaint_ChartKLine as ChartKLine, + JSCommonChartPaint_ChartLine as ChartLine, + JSCommonChartPaint_ChartSubLine as ChartSubLine, + JSCommonChartPaint_ChartPointDot as ChartPointDot, + JSCommonChartPaint_ChartStick as ChartStick, + JSCommonChartPaint_ChartLineStick as ChartLineStick, + JSCommonChartPaint_ChartStickLine as ChartStickLine, + JSCommonChartPaint_ChartOverlayKLine as ChartOverlayKLine, + JSCommonChartPaint_ChartMinuteInfo as ChartMinuteInfo, + JSCommonChartPaint_ChartRectangle as ChartRectangle, + JSCommonChartPaint_ChartMultiText as ChartMultiText, + JSCommonChartPaint_ChartMultiLine as ChartMultiLine, + JSCommonChartPaint_ChartMultiBar as ChartMultiBar, + JSCommonChartPaint_ChartPie as ChartPie, + JSCommonChartPaint_ChartCircle as ChartCircle, + JSCommonChartPaint_ChartChinaMap as ChartChinaMap, + JSCommonChartPaint_ChartRadar as ChartRadar, + JSCommonChartPaint_ChartCorssCursor as ChartCorssCursor, + JSCommonChartPaint_ChartBuySell as ChartBuySell, + JSCommonChartPaint_ChartMACD as ChartMACD, + JSCommonChartPaint_ChartSplashPaint as ChartSplashPaint, + JSCommonChartPaint_ChartBackground as ChartBackground, + JSCommonChartPaint_ChartMinuteVolumBar as ChartMinuteVolumBar, + JSCommonChartPaint_ChartMultiHtmlDom as ChartMultiHtmlDom, + JSCommonChartPaint_ChartLock as ChartLock, + JSCommonChartPaint_ChartVolStick as ChartVolStick, + JSCommonChartPaint_ChartBand as ChartBand, +} from "./umychart.chartpaint.wechat.js"; + +import +{ + JSCommonSplit_CoordinateInfo as CoordinateInfo, + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, + JSCommonSplit_FrameSplitKLinePriceY as FrameSplitKLinePriceY, + JSCommonSplit_FrameSplitY as FrameSplitY, + JSCommonSplit_FrameSplitKLineX as FrameSplitKLineX, + JSCommonSplit_FrameSplitMinutePriceY as FrameSplitMinutePriceY, + JSCommonSplit_FrameSplitMinuteX as FrameSplitMinuteX, + JSCommonSplit_FrameSplitXData as FrameSplitXData, + JSCommonSplit_SplitData as SplitData, + JSCommonSplit_PriceSplitData as PriceSplitData, +} from './umychart.framesplit.wechat.js' + +import +{ + JSCommonChartTitle_IChartTitlePainting as IChartTitlePainting, + JSCommonChartTitle_DynamicKLineTitlePainting as DynamicKLineTitlePainting, + JSCommonChartTitle_DynamicMinuteTitlePainting as DynamicMinuteTitlePainting, + JSCommonChartTitle_DynamicChartTitlePainting as DynamicChartTitlePainting, + JSCommonChartTitle_DynamicTitleData as DynamicTitleData, + JSCommonChartTitle_STRING_FORMAT_TYPE as STRING_FORMAT_TYPE, +} from './umychart.charttitle.wechat.js' +////////////////////////////////////////////////////////// +// +// 指标信息 +// +function IndexInfo(name, param) +{ + this.Name = name; //名字 + this.Param = param; //参数 + this.LineColor; //线段颜色 + this.ReqeustData = null; //数据请求 +} + +function BaseIndex(name) + { + this.Index; //指标阐述 + this.Name = name; //指标名字 + this.UpdateUICallback; //数据到达回调 + + //默认创建都是线段 + this.Create = function (hqChart, windowIndex) + { + for (var i in this.Index) + { + if (!this.Index[i].Name) continue; + + var maLine = new ChartLine(); + maLine.Canvas = hqChart.Canvas; + maLine.Name = this.Name + '-' + i.toString(); + maLine.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + maLine.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + maLine.Color = this.Index[i].LineColor; + + hqChart.ChartPaint.push(maLine); + } + } + + //指标不支持 周期/复权/股票等 + this.NotSupport = function (hqChart, windowIndex, message) + { + var paint = hqChart.GetChartPaint(windowIndex); + for (var i in paint) + { + paint[i].Data.Data = []; //清空数据 + if (i == 0) paint[i].NotSupportMessage = message; + } + } + + //格式化指标名字+参数 + //格式:指标名(参数1,参数2,参数3,...) + this.FormatIndexTitle = function () + { + var title = this.Name; + var param = null; + + for (var i in this.Index) + { + var item = this.Index[i]; + if (item.Param == null) continue; + + if (param) param += ',' + item.Param.toString(); + else param = item.Param.toString(); + } + + if (param) title += '(' + param + ')'; + + return title; + } + + this.InvokeUpdateUICallback = function (paint) + { + if (typeof (this.UpdateUICallback) != 'function') return; + + let indexData = new Array(); + for (let i in paint) + { + indexData.push({ Name: this.Index[i].Name, Data: paint[i].Data }); + } + + this.UpdateUICallback(indexData); + } +} + +//脚本指标 +//name=指标名字 args=参数名字 参数值 +function ScriptIndex(name, script, args, option) +{ + this.newMethod = BaseIndex; //派生 + this.newMethod(name); + delete this.newMethod; + + this.Script = script; + this.Arguments = []; + this.OutVar = []; + this.ID; //指标ID + this.FloatPrecision = 2; //小数位数 + this.StringFormat; + this.KLineType = null; //K线显示类型 + this.InstructionType; //五彩K线, 交易指标 + this.YSpecificMaxMin = null; //最大最小值 + this.YSplitScale = null; //固定刻度 + this.OutName=null; //动态输出指标名字 + + //指标上锁配置信息 + this.IsLocked = false; //是否锁住指标 + this.LockCallback = null; + this.LockID = null; + this.LockBG = null; //锁背景色 + this.LockTextColor = null; + this.LockText = null; + this.LockFont = null; + this.LockCount = 10; + + if (option) + { + if (option.FloatPrecision >= 0) this.FloatPrecision = option.FloatPrecision; + if (option.StringFormat > 0) this.StringFormat = option.StringFormat; + if (option.ID) this.ID = option.ID; + if (option.KLineType) this.KLineType = option.KLineType; + if (option.InstructionType) this.InstructionType = option.InstructionType; + if (option.YSpecificMaxMin) this.YSpecificMaxMin = option.YSpecificMaxMin; + if (option.YSplitScale) this.YSplitScale = option.YSplitScale; + if (option.OutName) this.OutName=option.OutName; + } + + if (option && option.Lock) + { + if (option.Lock.IsLocked == true) this.IsLocked = true; //指标上锁 + if (option.Lock.Callback) this.LockCallback = option.Lock.Callback; //锁回调 + if (option.Lock.ID) this.LockID = option.Lock.ID; //锁ID + if (option.Lock.BG) this.LockBG = option.Lock.BG; + if (option.Lock.TextColor) this.LockTextColor = option.Lock.TextColor; + if (option.Lock.Text) this.LockText = option.Lock.Text; + if (option.Lock.Font) this.LockFont = option.Lock.Font; + if (option.Lock.Count) this.LockCount = option.Lock.Count; + } + + if (args) this.Arguments = args; + + this.SetLock = function (lockData) { + if (lockData.IsLocked == true) { + this.IsLocked = true; //指标上锁 + if (lockData.Callback) this.LockCallback = lockData.Callback; //锁回调 + if (lockData.ID) this.LockID = lockData.ID; //锁ID + if (lockData.BG) this.LockBG = lockData.BG; + if (lockData.TextColor) this.LockTextColor = lockData.TextColor; + if (lockData.Text) this.LockText = lockData.Text; + if (lockData.Font) this.LockFont = lockData.Font; + if (lockData.Count) this.LockCount = lockData.Count; + } + else { //清空锁配置信息 + this.IsLocked = false; //是否锁住指标 + this.LockCallback = null; + this.LockID = null; + this.LockBG = null; //锁背景色 + this.LockTextColor = null; + this.LockText = null; + this.LockFont = null; + this.LockCount = 10; + } + } + + this.ExecuteScript = function (hqChart, windowIndex, hisData) + { + this.OutVar = []; + let self = this; + let param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData, + Self: this + }; + + let hqDataType = 0; //默认K线 + if (hqChart.ClassName === 'MinuteChartContainer') hqDataType = 2; //分钟数据 + let option = + { + HQDataType: hqDataType, + Symbol: hqChart.Symbol, + Data: hisData, + SourceData: hqChart.SourceData, //原始数据 + Callback: this.RecvResultData, CallbackParam: param, + Async: true, + MaxReqeustDataCount: hqChart.MaxReqeustDataCount, + MaxRequestMinuteDayCount: hqChart.MaxRequestMinuteDayCount, + Arguments: this.Arguments + }; + + if (hqChart.NetworkFilter) option.NetworkFilter = hqChart.NetworkFilter; + + let code = this.Script; + let run = JSCommonComplier.JSComplier.Execute(code, option, hqChart.ScriptErrorCallback); + } + + this.RecvResultData = function (outVar, param) + { + let hqChart = param.HQChart; + let windowIndex = param.WindowIndex; + let hisData = param.HistoryData; + param.Self.OutVar = outVar; + param.Self.BindData(hqChart, windowIndex, hisData); + + if (param.Self.IsLocked == false) //不上锁 + { + param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(null); + } + else //上锁 + { + let lockData = + { + IsLocked: true, Callback: param.Self.LockCallback, IndexName: param.Self.Name, ID: param.Self.LockID, + BG: param.Self.LockBG, Text: param.Self.LockText, TextColor: param.Self.LockTextColor, Font: param.Self.LockFont, + Count: param.Self.LockCount + }; + param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData); + } + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + var event = hqChart.GetIndexEvent(); //指标计算完成回调 + if (event) + { + var self = param.Self; + var data = { + OutVar: self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData, + Stock: { Symbol: hqChart.Symbol, Name: hqChart.Name } + }; + event.Callback(event, data, self); + } + } + + this.CreateLine = function (hqChart, windowIndex, varItem, id) + { + let line = new ChartLine(); + line.Canvas = hqChart.Canvas; + line.DrawType = 1; //无效数不画 + line.Name = varItem.Name; + line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color = this.GetColor(varItem.Color); + else line.Color = this.GetDefaultColor(id); + if (varItem.IsShow==false) line.IsShow=false; + if (varItem.LineWidth) + { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) line.LineWidth = width; + } + + if (varItem.IsDotLine) line.IsDotLine = true; //虚线 + if (varItem.IsShow == false) line.IsShow = false; + + let titleIndex = windowIndex + 1; + line.Data.Data = varItem.Data; + if (varItem.IsShowTitle===false) //NOTEXT 不绘制标题 + { + } + else if (IFrameSplitOperator.IsString(varItem.Name) && varItem.Name.indexOf("NOTEXT")==0) //标题中包含NOTEXT不绘制标题 + { + } + else + { + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(line.Data, (varItem.NoneName==true? null: varItem.Name) , line.Color); + } + + hqChart.ChartPaint.push(line); + } + + this.CreateOverlayLine = function (hqChart, windowIndex, varItem, id) + { + let line = new ChartSubLine(); + line.Canvas = hqChart.Canvas; + line.DrawType = 1; //无效数不画 + line.Name = varItem.Name; + line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color = this.GetColor(varItem.Color); + else line.Color = this.GetDefaultColor(id); + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) line.LineWidth = width; + } + + if (varItem.IsDotLine) line.IsDotLine = true; //虚线 + if (varItem.IsShow == false) line.IsShow = false; + + let titleIndex = windowIndex + 1; + line.Data.Data = varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(line.Data, varItem.Name, line.Color); + + hqChart.ChartPaint.push(line); + } + + //创建柱子 + this.CreateBar = function (hqChart, windowIndex, varItem, id) + { + let bar = new ChartStickLine(); + bar.Canvas = hqChart.Canvas; + if (varItem.Draw.Width > 0) bar.LineWidth = varItem.Draw.Width; + else bar.LineWidth=1; + + bar.Name = varItem.Name; + bar.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + bar.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) bar.Color = this.GetColor(varItem.Color); + else bar.Color = this.GetDefaultColor(id); + + let titleIndex = windowIndex + 1; + bar.Data.Data = varItem.Draw.DrawData; + bar.BarType = varItem.Draw.Type; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(bar); + } + + //创建文本 + this.CreateText = function (hqChart, windowIndex, varItem, id) + { + let chartText = new ChartSingleText(); + chartText.Canvas = hqChart.Canvas; + chartText.TextAlign='left'; + + chartText.Name = varItem.Name; + chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.ReloadResource(); + + if (varItem.Color) chartText.Color = this.GetColor(varItem.Color); + else chartText.Color = this.GetDefaultColor(id); + + let titleIndex = windowIndex + 1; + if (varItem.Draw.Position) chartText.Position=varItem.Draw.Position; //赋值坐标 + if (varItem.Draw.DrawData) chartText.Data.Data = varItem.Draw.DrawData; + chartText.Text = varItem.Draw.Text; + if (varItem.Draw.Direction > 0) chartText.Direction = varItem.Draw.Direction; + if (varItem.Draw.YOffset > 0) chartText.YOffset = varItem.Draw.YOffset; + if (varItem.Draw.TextAlign) chartText.TextAlign = varItem.Draw.TextAlign; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + hqChart.ChartPaint.push(chartText); + } + + //COLORSTICK + this.CreateMACD = function (hqChart, windowIndex, varItem, id) + { + let chartMACD = new ChartMACD(); + chartMACD.Canvas = hqChart.Canvas; + + chartMACD.Name = varItem.Name; + chartMACD.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartMACD.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.LineWidth) + { + var width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chartMACD.LineWidth=width; + } + + let titleIndex = windowIndex + 1; + chartMACD.Data.Data = varItem.Data; + var clrTitle=this.GetDefaultColor(id); + if (varItem.Color) clrTitle= this.GetColor(varItem.Color); + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chartMACD.Data, varItem.Name, clrTitle); + + hqChart.ChartPaint.push(chartMACD); + } + + this.CreatePointDot = function (hqChart, windowIndex, varItem, id) { + let pointDot = new ChartPointDot(); + pointDot.Canvas = hqChart.Canvas; + pointDot.Name = varItem.Name; + pointDot.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + pointDot.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) pointDot.Color = this.GetColor(varItem.Color); + else pointDot.Color = this.GetDefaultColor(id); + + if (varItem.Radius) pointDot.Radius = varItem.Radius; + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) pointDot.Radius = width; + } + + let titleIndex = windowIndex + 1; + pointDot.Data.Data = varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(pointDot.Data, varItem.Name, pointDot.Color); + + hqChart.ChartPaint.push(pointDot); + } + + this.CreateStick = function (hqChart, windowIndex, varItem, id) { + let chart = new ChartStick(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color = this.GetColor(varItem.Color); + else chart.Color = this.GetDefaultColor(id); + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) chart.LineWidth = width; + } + + let titleIndex = windowIndex + 1; + chart.Data.Data = varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateLineStick = function (hqChart, windowIndex, varItem, id) { + let chart = new ChartLineStick(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color = this.GetColor(varItem.Color); + else chart.Color = this.GetDefaultColor(id); + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) chart.LineWidth = width; + } + + let titleIndex = windowIndex + 1; + chart.Data.Data = varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateStraightLine = function (hqChart, windowIndex, varItem, id) { + let line = new ChartLine(); + line.DrawType = 1; + line.Canvas = hqChart.Canvas; + line.Name = varItem.Name; + line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color = this.GetColor(varItem.Color); + else line.Color = this.GetDefaultColor(id); + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) line.LineWidth = width; + } + + let titleIndex = windowIndex + 1; + line.Data.Data = varItem.Draw.DrawData; + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + + hqChart.ChartPaint.push(line); + } + + this.CreateVolStick = function (hqChart, windowIndex, varItem, id, hisData) { + let chart = new ChartVolStick(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + chart.KLineDrawType = hqChart.KLineDrawType; //设置K线显示类型 + if (varItem.Color) chart.Color = this.GetColor(varItem.Color); + else chart.Color = this.GetDefaultColor(id); + + let titleIndex = windowIndex + 1; + chart.Data.Data = varItem.Data; + chart.HistoryData = hisData; + hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateBand = function (hqChart, windowIndex, varItem, id) { + let chart = new ChartBand(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.FirstColor = varItem.Draw.Color[0]; + chart.SecondColor = varItem.Draw.Color[1]; + chart.Data.Data = varItem.Draw.DrawData; + + hqChart.ChartPaint.push(chart); + } + + this.CreatePolyLine = function (hqChart, windowIndex, varItem, id) { + let line = new ChartLine(); + line.Canvas = hqChart.Canvas; + line.Name = varItem.Name; + line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color = this.GetColor(varItem.Color); + else line.Color = this.GetDefaultColor(id); + + if (varItem.LineWidth) { + let width = parseInt(varItem.LineWidth.replace("LINETHICK", "")); + if (!isNaN(width) && width > 0) line.LineWidth = width; + } + + let titleIndex = windowIndex + 1; + line.Data.Data = varItem.Draw.DrawData; + //hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(line.Data, ' ', line.Color); //给一个空的标题 + + hqChart.ChartPaint.push(line); + } + + //创建K线图 + this.CreateKLine = function (hqChart, windowIndex, varItem, id) { + let chart = new ChartKLine(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data.Data = varItem.Draw.DrawData; + chart.IsShowMaxMinPrice = false; + + if (varItem.Color) //如果设置了颜色,使用外面设置的颜色 + chart.UnchagneColor = chart.DownColor = chart.UpColor = this.GetColor(varItem.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateNumberText = function (hqChart, windowIndex, varItem, id) { + let chartText = new ChartSingleText(); + chartText.Canvas = hqChart.Canvas; + + chartText.Name = varItem.Name; + chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.ReloadResource(); + + chartText.TextAlign="center"; + if (varItem.Color) chartText.Color = this.GetColor(varItem.Color); + else chartText.Color = this.GetDefaultColor(id); + if (varItem.IsDrawAbove) chartText.Direction=1; + else chartText.Direction=2; + + let titleIndex = windowIndex + 1; + chartText.Data.Data = varItem.Draw.DrawData.Value; + chartText.Text = varItem.Draw.DrawData.Text; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + hqChart.ChartPaint.push(chartText); + } + + //创建图标 + this.CreateIcon = function (hqChart, windowIndex, varItem, id) { + let chartText = new ChartSingleText(); + chartText.Canvas = hqChart.Canvas; + chartText.TextAlign = 'center'; + + chartText.Name = varItem.Name; + chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + let titleIndex = windowIndex + 1; + chartText.Data.Data = varItem.Draw.DrawData; + chartText.Text = varItem.Draw.Icon.Symbol; + if (varItem.Color) chartText.Color = this.GetColor(varItem.Color); + else if (varItem.Draw.Icon.Color) chartText.Color = varItem.Draw.Icon.Color; + else chartText.Color = 'rgb(0,0,0)'; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(chartText); + } + + this.CreateRectangle = function (hqChart, windowIndex, varItem, i) + { + let chart = new ChartRectangle(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Color = [varItem.Draw.DrawData.Color]; + chart.Rect = varItem.Draw.DrawData.Rect; + if (varItem.Color) chart.BorderColor = this.GetColor(varItem.Color); + hqChart.ChartPaint.push(chart); + } + + this.CreateBackgroud=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartBackground(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Draw && varItem.Draw.DrawData) + { + var drawData=varItem.Draw.DrawData; + chart.Color=drawData.Color; + chart.ColorAngle=drawData.Angle; + + if (drawData.Data) chart.Data.Data=drawData.Data; + } + + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiText = function (hqChart, windowIndex, varItem, i) + { + let chart = new ChartMultiText(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data = hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts = varItem.Draw.DrawData; + hqChart.ChartPaint.push(chart); + } + + this.CreateMulitHtmlDom=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartMultiHtmlDom(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts=varItem.Draw.DrawData; + chart.DrawCallback= varItem.Draw.Callback; + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiLine = function (hqChart, windowIndex, varItem, i) + { + let chart = new ChartMultiLine(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data = hqChart.ChartPaint[0].Data;//绑定K线 + chart.Lines = varItem.Draw.DrawData; + + if (varItem.Draw.LineDash) chart.LineDash=varItem.Draw.LineDash; + if (IFrameSplitOperator.IsNumber(varItem.Draw.LineWidth)) chart.LineWidth=varItem.Draw.LineWidth; + + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiBar = function (hqChart, windowIndex, varItem, i) + { + let chart = new ChartMultiBar(); + chart.Canvas = hqChart.Canvas; + chart.Name = varItem.Name; + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data = hqChart.ChartPaint[0].Data;//绑定K线 + chart.Bars = varItem.Draw.DrawData; + hqChart.ChartPaint.push(chart); + } + + //创建K线背景 + this.CreateSelfKLine = function (hqChart, windowIndex, hisData) + { + let chart = new ChartKLine(); + chart.Canvas = hqChart.Canvas; + chart.Name = "Self Kline" + chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data = hisData + chart.IsShowMaxMinPrice = false; + chart.IsShowKTooltip = false; + chart.DrawType = this.KLineType; + + hqChart.ChartPaint.push(chart); + } + + this.BindInstructionData = function (hqChart, windowIndex, hisData) //绑定指示指标 + { + if (this.OutVar == null || this.OutVar.length < 0) return; + if (this.InstructionType == 2) + { + let varItem = this.OutVar[this.OutVar.length - 1]; //取最后一组数据作为指示数据 + hqChart.SetInstructionData(this.InstructionType, { Data: varItem.Data }); //设置指示数据 + return true; + } + else if (this.InstructionType == 1) //交易系统 + { + var buyData, sellData; + for (var i in this.OutVar) + { + let item = this.OutVar[i]; + if (item.Name == 'ENTERLONG') buyData = item.Data; + else if (item.Name == 'EXITLONG') sellData = item.Data; + } + + hqChart.SetInstructionData(this.InstructionType, { Buy: buyData, Sell: sellData }); //设置指示数据 + return true; + } + } + + + this.BindData = function (hqChart, windowIndex, hisData) + { + if (windowIndex == 0 && this.InstructionType) + { + this.BindInstructionData(hqChart, windowIndex, hisData); + return; + } + + //清空指标图形 + hqChart.DeleteIndexPaint(windowIndex); + if (windowIndex == 0) hqChart.ShowKLine(true); + + if (this.OutVar == null || this.OutVar.length < 0) return; + + //叠加一个K线背景 + if (this.KLineType != null) + { + if (this.KLineType === 0 || this.KLineType === 1 || this.KLineType === 2) this.CreateSelfKLine(hqChart, windowIndex, hisData); + else if (this.KLineType === -1 && windowIndex == 0) hqChart.ShowKLine(false); + } + + if (windowIndex >= 1 && hqChart.Frame) + { + hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision = this.FloatPrecision; + if (this.YSpecificMaxMin) hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = this.YSpecificMaxMin; //最大最小值 + if (this.YSplitScale) hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale = this.YSplitScale; //固定刻度 + } + + for (let i in this.OutVar) + { + let item = this.OutVar[i]; + if (item.IsExData === true) continue; //扩展数据不显示图形 + if (item.Type==1000 || item.Type==1001) continue; //数据集合, 字符串 + + if (item.Type == 0) + { + if (item.IsOverlayLine) this.CreateOverlayLine(hqChart, windowIndex, item, i); + else this.CreateLine(hqChart, windowIndex, item, i); + } + else if (item.Type == 1) + { + switch (item.Draw.DrawType) + { + case 'STICKLINE': + this.CreateBar(hqChart, windowIndex, item, i); + break; + case 'DRAWTEXT': + case 'SUPERDRAWTEXT': + case 'DRAWTEXT_FIX': + this.CreateText(hqChart, windowIndex, item, i); + break; + case 'DRAWLINE': + this.CreateStraightLine(hqChart, windowIndex, item, i); + break; + case 'DRAWBAND': + this.CreateBand(hqChart, windowIndex, item, i); + break; + case 'DRAWKLINE': + this.CreateKLine(hqChart, windowIndex, item, i); + break; + case 'DRAWKLINE_IF': + this.CreateKLine(hqChart, windowIndex, item, i); + break; + case 'POLYLINE': + this.CreatePolyLine(hqChart, windowIndex, item, i); + break; + case 'DRAWNUMBER': + this.CreateNumberText(hqChart, windowIndex, item, i); + break; + case 'DRAWICON': + this.CreateIcon(hqChart, windowIndex, item, i); + break; + case 'DRAWRECTREL': + this.CreateRectangle(hqChart, windowIndex, item, i); + break; + case 'DRAWGBK': + case "DRAWGBK2": + this.CreateBackgroud(hqChart,windowIndex,item,i); + break; + + //第3方指标定制 + case 'MULTI_TEXT': + this.CreateMultiText(hqChart, windowIndex, item, i); + break; + case "MULTI_HTMLDOM": + this.CreateMulitHtmlDom(hqChart,windowIndex,item,i); + break; + case 'MULTI_LINE': + this.CreateMultiLine(hqChart, windowIndex, item, i); + break; + case 'MULTI_BAR': + this.CreateMultiBar(hqChart, windowIndex, item, i); + break; + case "KLINE_BG": + this.CreateBackgroud(hqChart,windowIndex,item,i); + break; + } + } + else if (item.Type == 2) + { + this.CreateMACD(hqChart, windowIndex, item, i); + } + else if (item.Type == 3) + { + this.CreatePointDot(hqChart, windowIndex, item, i); + } + else if (item.Type == 4) + { + this.CreateLineStick(hqChart, windowIndex, item, i); + } + else if (item.Type == 5) + { + this.CreateStick(hqChart, windowIndex, item, i); + } + else if (item.Type == 6) + { + this.CreateVolStick(hqChart, windowIndex, item, i, hisData); + } + + var titlePaint = hqChart.TitlePaint[windowIndex + 1]; + if (titlePaint && titlePaint.Data && i < titlePaint.Data.length) //设置标题数值 小数位数和格式 + { + if (this.StringFormat > 0) titlePaint.Data[i].StringFormat = this.StringFormat; + if (this.FloatPrecision >= 0) titlePaint.Data[i].FloatPrecision = this.FloatPrecision; + if (this.OutName && this.OutName.length>0 && this.Arguments && this.Arguments.length>0) + { + titlePaint.SetDynamicOutName(this.OutName,this.Arguments); + } + } + } + + let titleIndex = windowIndex + 1; + hqChart.TitlePaint[titleIndex].Title = this.Name; + + let indexParam = ''; + for (let i in this.Arguments) + { + let item = this.Arguments[i]; + if (indexParam.length > 0) indexParam += ','; + indexParam += item.Value.toString(); + } + + if (indexParam.length > 0) hqChart.TitlePaint[titleIndex].Title = this.Name + '(' + indexParam + ')'; + + if (hqChart.UpdateUICallback) hqChart.UpdateUICallback('ScriptIndex', this.OutVar, + { WindowIndex: windowIndex, Name: this.Name, Arguments: this.Arguments, HistoryData: hisData }); //通知上层回调 + + return true; + } + + + this.GetDefaultColor = function (id) //给一个默认的颜色 + { + let COLOR_ARRAY = g_JSChartResource.ColorArray; + let number = parseInt(id); + return COLOR_ARRAY[number % (COLOR_ARRAY.length - 1)]; + } + + + this.GetColor = function (colorName) //获取颜色 + { + let COLOR_MAP = new Map([ + ['COLORBLACK', 'rgb(0,0,0)'], + ['COLORBLUE', 'rgb(18,95,216)'], + ['COLORGREEN', 'rgb(25,158,0)'], + ['COLORCYAN', 'rgb(0,255,198)'], + ['COLORRED', 'rgb(238,21,21)'], + ['COLORMAGENTA', 'rgb(255,0,222)'], + ['COLORBROWN', 'rgb(149,94,15)'], + ['COLORLIGRAY', 'rgb(218,218,218)'], //画淡灰色 + ['COLORGRAY', 'rgb(133,133,133)'], //画深灰色 + ['COLORLIBLUE', 'rgb(94,204,255)'], //淡蓝色 + ['COLORLIGREEN', 'rgb(183,255,190)'], //淡绿色 + ['COLORLICYAN', 'rgb(154,255,242)'], //淡青色 + ['COLORLIRED', 'rgb(255,172,172)'], //淡红色 + ['COLORLIMAGENTA', 'rgb(255,145,241)'], //淡洋红色 + ['COLORWHITE', 'rgb(255,255,255)'], //白色 + ['COLORYELLOW', 'rgb(255,198,0)'] + ]); + + if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName); + + //COLOR 自定义色 + //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。 + //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。 + if (colorName.indexOf('COLOR') == 0) return '#' + colorName.substr(5); + return 'rgb(30,144,255)'; + } +} + + + + + +//市场多空 +function MarketLongShortIndex() +{ + this.newMethod = BaseIndex; //派生 + this.newMethod('市场多空'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo("多空指标", null), + new IndexInfo("多头区域", null), + new IndexInfo("空头区域", null) + ); + + this.Index[0].LineColor = g_JSChartResource.Index.LineColor[0]; + this.Index[1].LineColor = g_JSChartResource.UpBarColor; + this.Index[2].LineColor = g_JSChartResource.DownBarColor; + + this.LongShortData; //多空数据 + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = null; + if (i == 0) + paint = new ChartLine(); + else + paint = new ChartStraightLine(); + + paint.Color = this.Index[i].LineColor; + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.LongShortData = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: g_JSChartResource.Index.MarketLongShortApiUrl, + data: + { + + }, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) { + self.RecvData(recvData, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + if (recvData.data.data.length <= 0) return; + + var aryData = new Array(); + for (var i in recvData.data.data) { + var item = recvData.data.data[i]; + var indexData = new SingleData(); + indexData.Date = item[0]; + indexData.Value = item[1]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.LongShortData = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data = this.LongShortData; + paint[0].NotSupportMessage = null; + paint[1].Data.Data[0] = 8; + paint[2].Data.Data[0] = 1; + + //指定[0,9] + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 9, Min: 0, Count: 3 }; + + var titleIndex = windowIndex + 1; + + for (var i in paint) { + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, this.Index[i].Name, this.Index[i].LineColor); + if (i > 0) hqChart.TitlePaint[titleIndex].Data[i].DataType = "StraightLine"; + } + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + + if (hqChart.UpdateUICallback) hqChart.UpdateUICallback('MarketLongShortIndex', paint, { WindowIndex: windowIndex, HistoryData: hisData }); //通知上层回调 + return true; + } + +} + + + +module.exports = +{ + JSCommonIndex: + { + IndexInfo: IndexInfo, + BaseIndex: BaseIndex, + ScriptIndex:ScriptIndex, + }, + + //单个类导出 + JSCommonIndex_IndexInfo: IndexInfo, + JSCommonIndex_BaseIndex: BaseIndex, + JSCommonIndex_ScriptIndex:ScriptIndex, +}; diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.klineinfo.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.klineinfo.wechat.js new file mode 100644 index 0000000..2c62a8c --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.klineinfo.wechat.js @@ -0,0 +1,702 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 小程序信息地雷数据 +*/ + +import { + JSCommonResource_Global_JSChartResource as g_JSChartResource, +} from './umychart.resource.wechat.js' + +var KLINE_INFO_TYPE= +{ + INVESTOR:1, //互动易 + ANNOUNCEMENT:2, //公告 + PFORECAST:3, //业绩预告 + + ANNOUNCEMENT_QUARTER_1:4, //一季度报 + ANNOUNCEMENT_QUARTER_2:5, //半年报 + ANNOUNCEMENT_QUARTER_3:6, //2季度报 + ANNOUNCEMENT_QUARTER_4:7, //年报 + + RESEARCH:8, //调研 + BLOCKTRADING:9, //大宗交易 + TRADEDETAIL:10, //龙虎榜 + + POLICY:11 //策略信息 +} + +function KLineInfoData() +{ + this.ID; + this.Date; + this.Title; + this.InfoType; + this.ExtendData; //扩展数据 +} + +/* + 信息地雷 + 信息地雷列表 +*/ +function JSKLineInfoMap() +{ +} + +JSKLineInfoMap.Get=function(id) +{ + var infoMap=new Map( + [ + ["互动易", {Create:function(){ return new InvestorInfo()} }], + ["公告", {Create:function(){ return new AnnouncementInfo()} }], + ["业绩预告", {Create:function(){ return new PforecastInfo()} }], + ["调研", {Create:function(){ return new ResearchInfo()} }], + ["大宗交易", {Create:function(){ return new BlockTrading()} }], + ["龙虎榜", {Create:function(){ return new TradeDetail()} }], + ["策略选股", {Create: function () { return new PolicyInfo() } }] + ] + ); + + return infoMap.get(id); +} + +function IKLineInfo() +{ + this.MaxReqeustDataCount=1000; + this.StartDate=20160101; + this.Data; + + this.GetToday=function() + { + var date=new Date(); + var today=date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate(); + return today; + } +} + +/* + 互动易 +*/ +function InvestorInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + var param={ HQChart:hqChart }; + this.Data=[]; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Investor.ApiUrl, + data: + { + "filed": ["question","answerdate","symbol","id"], + "symbol": [param.HQChart.Symbol], + "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data || !data.list || data.list.length<=0) return; + + for (var i in data.list) + { + var item = data.list[i]; + var infoData=new KLineInfoData(); + infoData.Date=item.answerdate; + infoData.Title=item.question; + infoData.InfoType=KLINE_INFO_TYPE.INVESTOR; + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +/* + 公告 +*/ +function AnnouncementInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + var param={ HQChart:hqChart }; + this.Data=[]; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Announcement.ApiUrl, + data: + { + "filed": ["title","releasedate","symbol","id"], + "symbol": [param.HQChart.Symbol], + "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data) return; + if (!data.report || data.report.length<=0) return; + + for (var i in data.report) + { + var item = data.report[i]; + var infoData=new KLineInfoData(); + infoData.Date=item.releasedate; + infoData.Title=item.title; + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT; + for(var j in item.type) + { + var typeItem=item.type[j]; + switch(typeItem) + { + case "一季度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1; + break; + case "半年度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2; + break; + case "三季度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3; + break; + case "年度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4; + break; + } + } + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +/* + 业绩预告 +*/ +function PforecastInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + this.Data = []; + var param={ HQChart:hqChart }; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Pforecast.ApiUrl, + data: + { + "field": ["pforecast.type","pforecast.reportdate","fweek"], + "condition": + [ + {"item":["pforecast.reportdate","int32","gte",this.StartDate]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data.stock || data.stock.length!=1) return; + if (!data.stock[0].stockday || data.stock[0].stockday.length<=0) return; + + for (var i in data.stock[0].stockday) + { + var item = data.stock[0].stockday[i]; + if (item.pforecast.length>0) + { + var dataItem=item.pforecast[0]; + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.Title=dataItem.type; + infoData.InfoType=KLINE_INFO_TYPE.PFORECAST; + infoData.ExtendData={ Type:dataItem.type, ReportDate:dataItem.reportdate} + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + this.Data.push(infoData); + } + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +/* + 投资者关系 (调研) +*/ +function ResearchInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + var param= { HQChart:hqChart }; + + this.Data=[]; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Research.ApiUrl, + data: + { + "filed": ["releasedate","researchdate","level","symbol","id"], + "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data) return; + if (!data.list || data.list.length<=0) return; + + for (var i in data.list) + { + var item = data.list[i]; + var infoData=new KLineInfoData(); + infoData.ID=item.id; + infoData.Date= item.researchdate; + infoData.InfoType=KLINE_INFO_TYPE.RESEARCH; + infoData.ExtendData={ Level:item.level }; + this.Data.push(infoData); + + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +/* + 大宗交易 +*/ +function BlockTrading() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + var param={ HQChart:hqChart,}; + this.Data=[]; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.BlockTrading.ApiUrl, + data: + { + "field": ["blocktrading.price","blocktrading.vol","blocktrading.premium","fweek","price"], + "condition": + [ + {"item":["date","int32","gte",this.StartDate]}, + {"item":["blocktrading.vol","int32","gte","0"]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data || !data.stock || data.stock.length!=1) return; + if (!data.stock[0].stockday || data.stock[0].stockday.length<=0) return; + + for (var i in data.stock[0].stockday) + { + var item = data.stock[0].stockday[i]; + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.InfoType=KLINE_INFO_TYPE.BLOCKTRADING; + infoData.ExtendData= + { + Price:item.blocktrading.price, //交易价格 + Premium:item.blocktrading.premium, //溢价 (百分比%) + Vol:item.blocktrading.vol, //交易金额单位(万元) + ClosePrice:item.price, //收盘价 + }; + + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +/* + 龙虎榜 +*/ +function TradeDetail() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.RequestData=function(hqChart) + { + var self = this; + var param={ HQChart:hqChart }; + + this.Data=[]; + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain+g_JSChartResource.KLine.Info.TradeDetail.ApiUrl, + data: + { + "field": ["tradedetail.typeexplain","tradedetail.type","fweek"], + "condition": + [ + {"item":["date","int32","gte",this.StartDate]}, + {"item":["tradedetail.type","int32","gte","0"]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + method:"post", + dataType: "json", + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + var data=recvData.data; + if (!data || !data.stock || data.stock.length!=1) return; + if (!data.stock[0].stockday || data.stock[0].stockday.length<=0) return; + + for (var i in data.stock[0].stockday) + { + var item = data.stock[0].stockday[i]; + + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.InfoType=KLINE_INFO_TYPE.TRADEDETAIL; + infoData.ExtendData={Detail:new Array()}; + + for(var j in item.tradedetail) + { + var tradeItem=item.tradedetail[j]; + infoData.ExtendData.Detail.push({"Type":tradeItem.type,"TypeExplain":tradeItem.typeexplain}); + } + + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +//策略信息 +function PolicyInfo() +{ + this.newMethod = IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.PolicyList = []; //筛选的策略名字 {Name:策略名, Guid:策略的GUID} + + this.SetPolicyList=function(aryPolicy) + { + for(let i in aryPolicy) + { + this.PolicyList.push({Name:aryPolicy[i]}); + } + } + + this.RequestData = function (hqChart) + { + var self = this; + this.Data = []; + var param = { HQChart: hqChart }; + + // setTimeout(function () { self.RecvData(null, param); }, 2000); //模拟数据到达 + + //请求数据 + wx.request({ + url: g_JSChartResource.Domain + g_JSChartResource.KLine.Info.Policy.ApiUrl, + data: { + "symbol": [param.HQChart.Symbol], + field: ["policy"], + "condition": [ + { "item": ["date", "int32", "gte", this.StartDate, "lte", this.GetToday()] }], + "start": 0, + "end": this.MaxReqeustDataCount + }, + method: "post", + dataType: "json", + success: function (recvData) { + self.RecvData(recvData, param); + } + + }) + + return true; + } + + this.RecvData = function (recvData, param) + { + var data = recvData.data; + if (!data.stock || data.stock.length != 1) return; + if (!data.stock[0].stockday || data.stock[0].stockday.length <= 0) return; + + var setName=new Set(); + for(var i in this.PolicyList) //把需要过滤的策略名字放set里, 方便后面过滤 + { + setName.add(this.PolicyList[i].Name); + } + + for (var i in data.stock[0].stockday) + { + var item = data.stock[0].stockday[i]; + var infoData = new KLineInfoData(); + infoData.Date = item.date; + infoData.InfoType = KLINE_INFO_TYPE.POLICY; + infoData.ExtendData = []; + for (var j in item.policy) + { + var name = item.policy[j].name; + if (setName.has(name)) infoData.ExtendData.push({ Name: name }); + } + + if (infoData.ExtendData.length>0) this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + + if (param.HQChart.UpdateUICallback) param.HQChart.UpdateUICallback('RecvPolicyInfoData', this); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// 走势图信息地雷 +// +// +//////////////////////////////////////////////////////////////////////////////////////////// +function JSMinuteInfoMap() { } + +JSMinuteInfoMap.InfoMap = new Map( +[ + ["大盘异动", { Create: function () { return new MarketEventInfo() } }], +]); + +JSMinuteInfoMap.Get = function (id) +{ + return JSMinuteInfoMap.InfoMap.get(id); +} + +function IMinuteInfo() +{ + this.Data; + this.ClassName = 'IMinuteInfo'; +} + +////////////////////////////////////////////////////////////////////// +// 大盘异动 +// 结构 {Date:日期 Time:时间, Title:标题, Type:0 } +//////////////////////////////////////////////////////////////////// +function MarketEventInfo() +{ + this.newMethod = IMinuteInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName = 'MarketEventInfo'; + + this.RequestData = function (hqChart) + { + var self = this; + this.Data = []; + var param = + { + HQChart: hqChart + }; + + var url = g_JSChartResource.CacheDomain + '/cache/analyze/shszevent/marketevent/concept/' + hqChart.TradeDate + '.json'; + + if (hqChart.NetworkFilter) { + var obj = + { + Name: 'MarketEventInfo::RequestData', //类名:: + Explain: '大盘异动', + Request: { Url: url, Type: 'Get', Data: { Date: hqChart.TradeDate, Symbol: hqChart.Symbol } }, + Self: this, + PreventDefault: false + }; + hqChart.NetworkFilter(obj, function (data) + { + self.RecvData(data, param); + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + wx.request({ + url: url, + method: "get", + dataType: "json", + success: function (recvData) { + self.RecvData(recvData, param); + }, + error: function (http, e) { + console.warn("[MarketEventInfo::RequestData] error, http ", e, http); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) + { + var data=recvData.data; + for (var i in data.event) + { + var event = data.event[i]; + for (var j in event.data) + { + var item = event.data[j]; + if (item.length < 2) continue; + var info = { Date: event.date, Time: item[0], Title: item[1], Type: 0 }; + this.Data.push(info); + } + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonKLineInfo: + { + JSKLineInfoMap: JSKLineInfoMap, + KLINE_INFO_TYPE: KLINE_INFO_TYPE, + JSMinuteInfoMap: JSMinuteInfoMap, + }, + + //单个类导出 + JSCommon_JSKLineInfoMap: JSKLineInfoMap, + JSCommon_KLINE_INFO_TYPE: KLINE_INFO_TYPE, + JSCommon_JSMinuteInfoMap: JSMinuteInfoMap, +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.regressiontest.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.regressiontest.wechat.js new file mode 100644 index 0000000..0c45f0e --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.regressiontest.wechat.js @@ -0,0 +1,410 @@ +/* + Copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 个股指标回测 +*/ + + +/* + 指标回测 + 计算: Trade: {Count 交易次数 Days:交易天数 Success:成功交易次数 Fail:失败交易次数} + Day: {Count:总运行 Max:最长运行 Min:最短运行 Average:平均运行} + Profit: 总收益 StockProfit:个股收益 Excess:超额收益 MaxDropdown:最大回撤 Beta:β(Beta)系数(指标里面需要又大盘数据) + NetValue: [ {Date:日期, Net:净值, Close:股票收盘价, IndexClose:大盘的收盘价}, ] +*/ + + +function RegressionTest() +{ + //只读数据不能修改 + this.HistoryData; //K线数据 + this.BuyData; //策略买数据 + this.SellData; //策略卖数据 + this.IndexClose; //大盘收盘价 + this.NetCalculateModel=0; //净值及收益计算模型 0=使用B点开盘价计算 1=使用B点下一天的开盘价计算 + + this.InitialCapital=10000; //初始资金1W + + //计算结果数据 + this.Data=new Map(); //key:DATA_NAME value:数据 + + this.SetPolicyData=function(obj) //设置策略结果的数据 {KLineData:个股K线数据, BuyData:策略买数据, SellData:策略卖数据, IndexClose:大盘收盘价} + { + this.HistoryData=obj.KLineData; //K线数据 + this.BuyData=obj.BuyData; //策略买数据 + this.SellData=obj.SellData; //策略卖数据 + if (obj.IndexClose) this.IndexClose=obj.IndexClose; //大盘收盘价 如果没有大盘数据 就不计算β(Beta)系数 和指数涨幅数据 + } + + this.ClearData=function() //清空所有的结果数据 + { + this.Data=new Map() + } + + this.GetBSData=function(startDate) //BS点配对 { B:[{Data:K线数据, Count:天数, NextOpen:下一天的开盘价 }], S:{Data:K线数据}} + { + var index=null; + for(var i=0;i=startDate) + { + index=i; + break; + } + } + if (index===null) return null; + + console.log(`[RegressionTest::GetBSData] startDate=${startDate} index=${index}`); + var aryBS=[]; + var bsItem=null; + for(var i=index;i0) + { + var bItem={Data:kLineItem, Count:0 }; + if (i+10) + { + var bItem={Data:kLineItem, Count:0}; + if (i+10) + { + bsItem.S={Data:kLineItem}; + aryBS.push(bsItem); + bsItem=null; + } + } + } + + var data={StartDate:this.HistoryData[index].Date, StartIndex:index, Count:this.HistoryData.length-index, BSData:aryBS }; + + console.log('[RegressionTest::GetBSData] data',data); + return data; + } + + this.Calculate=function(data) + { + var day={ Count:data.Count, Max:null, Min:null, Average:null }; //Count:总运行 Max:最长运行 Min:最短运行 Average:平均运行 + var trade={Count:0, Days:0, Success:0 , Fail:0, SuccessRate:0}; //Count 交易次数 Days:交易天数 Success:成功交易次数 Fail:失败交易次数 + + for(var i in data.BSData) + { + var item=data.BSData[i]; + for(var j in item.B) + { + var bItem=item.B[j]; + if (day.Max===null) day.Max=bItem.Count; + else if (day.MaxbItem.Count) day.Min=bItem.Count; + + ++trade.Count; + trade.Days+=bItem.Count; + + if (item.S.Data.Close>bItem.Data.Open) ++trade.Success; + else ++trade.Fail; + } + } + + if (trade.Count>0) + { + day.Average=trade.Days/trade.Count; + trade.SuccessRate=trade.Success/trade.Count; + } + + //计算收益(总收益) + var profit=1,buyPrice; + for(var i in data.BSData) + { + var item=data.BSData[i]; + if (this.NetCalculateModel===1 && item.B[0].NextOpen>0 ) buyPrice=item.B[0].NextOpen; + else buyPrice=item.B[0].Data.Open; + var sellPrice=item.S.Data.Close; + var value=(sellPrice-buyPrice)/buyPrice+1; + profit*=value; + } + profit-=1; //公式:[(1+收益1)*(1+收益2)*(1+收益3)……(1+收益n)-1] x 100% + + //标的证券收益 + var yClose=this.HistoryData[data.StartIndex].Close; //使用前收盘 + var close=this.HistoryData[this.HistoryData.length-1].Close; //最后一个大盘收盘价 + var stockProfit=(close-yClose)/yClose; + + console.log(`[RegressionTest::Calculate] stock profit first[${this.HistoryData[data.StartIndex].Date}, YClose=${this.HistoryData[data.StartIndex].YClose}] end[${this.HistoryData[this.HistoryData.length-1].Date}, Close=${this.HistoryData[this.HistoryData.length-1].Close}]`); + + var netValue=this.CaclulateNetValue(data); + var maxDropdown=null, beta=null; + if (netValue && netValue.length>0) + { + maxDropdown=this.CaclulateMaxDropdown(netValue); + if (this.IndexClose) beta=this.CaclulateBeta(netValue); + } + + //Profit:收益 StockProfit:标的证券收益 Excess:超额收益(加上BS配对的数据) + var result={ Day:day, Trade:trade, Profit:profit, StockProfit:stockProfit, Excess:profit-stockProfit, NetValue:netValue, MaxDropdown:maxDropdown, Beta:beta,BSDataPair:data.BSData}; + + console.log('[RegressionTest::Calculate] NetCalculateModel, result ',this.NetCalculateModel, result); + return result; + } + + this.CaclulateNetValue=function(data) //计算净值 + { + var index=data.StartIndex; + + var aryDay=[]; //{Close:收盘 , Open:开盘, Position:持仓数量, Cache:现金 , MarketValue:总市值} + var lastDayItem={Position:0, Cache:this.InitialCapital }; + var bsItem=null, buyPrice; + for(var i=index;i0) //买 + { + bsItem={ B:{Data:kLineItem}, S:null}; + if (this.NetCalculateModel===1 && i+10) + buyPrice=this.HistoryData[i+1].Open; //使用B点下一天的开盘价买 + else + buyPrice=dayItem.Open; + + let position=parseInt(dayItem.Cache/buyPrice); //开盘价买 + let cache=dayItem.Cache-buyPrice*position; //剩余的现金 + + dayItem.Position=position; + dayItem.Cache=cache; + dayItem.MarketValue=dayItem.Position*dayItem.Close+dayItem.Cache; //市值 股票+现金 + } + } + else + { + if (sellItem>0) //卖 + { + bsItem.S={Data:kLineItem}; + bsItem=null; + + let stockValue=dayItem.Position*dayItem.Close; //卖掉的股票钱 + dayItem.Position=0; + dayItem.Cache+=stockValue; //卖掉的钱放到现金里面 + dayItem.MarketValue=dayItem.Position*dayItem.Close+dayItem.Cache; //市值 股票+现金 + } + } + + //缓存上一天的数据 + lastDayItem.Position=dayItem.Position; + lastDayItem.Cache=dayItem.Cache; + + dayItem.Net=dayItem.MarketValue/this.InitialCapital; //净值 + if (this.IndexClose) dayItem.IndexClose=this.IndexClose[i]; //指数收盘价 + aryDay.push(dayItem); + } + + //console.log('[RegressionTest::CaclulateNetValue] aryDay',aryDay); + if (aryDay.length<=0) return []; + + var netValue=[]; //净值 {Date:日期, Net:净值, Close:股票收盘价, IndexClose:大盘的收盘价} + for(var i=0;i0 && lastItem.IndexClose>0) indexProfit[i-1]=(item.IndexClose-lastItem.IndexClose)/lastItem.IndexClose; + if (item.Net>0 && lastItem.Net>0) bsProfit[i-1]=(item.Net-lastItem.Net)/lastItem.Net; + //if (item.Close>0 && lastItem.Close>0) bsProfit[i-1]=(item.Close-lastItem.Close)/lastItem.Close; + + indexProfitTotal+=indexProfit[i-1]; + bsProfitTotal+=bsProfit[i-1]; + + lastItem=item; + } + + var averageIndexProfit=indexProfitTotal/indexProfit.length; + var averageBSProfit=bsProfitTotal/bsProfit.length; + + var betaCOV=0; //协方差 + for(var i=0;i DRAWICON + this.DRAWICON= + { + Text: + { + MaxSize:50, //字体最大 + MinSize:20, //字体最小 + + Zoom: + { + Type:2, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'Arial' //字体 + } + } + + this.DRAWTEXT= + { + MaxSize:18, //字体最大 + MinSize:18, //字体最小 + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'微软雅黑' //字体 + } + + this.DRAWNUMBER= + { + MaxSize:18, //字体最大 + MinSize:18, //字体最小 + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'微软雅黑' //字体 + } + + this.DRAWABOVE= + { + YOffset:0 //y坐标向上偏移 + } + + this.CIRCLEDOT= + { + Radius:1.3 + } + + this.POINTDOT= + { + Radius:2 + } + + //深度图 + this.DepthChart= + { + BidColor: { Line:"rgb(82,176,123)", Area:"rgba(82,176,123,0.8)"}, //卖 + AskColor: { Line:"rgb(207,76,89)", Area:"rgba(207,76,89, 0.8)"}, //买 + LineWidth:4 + } + + this.DepthCorss= + { + BidColor: { Line:"rgb(82,176,123)" }, //卖 + AskColor: { Line:"rgb(207,76,89)" }, //买 + LineWidth:2, //线段宽度 + LineDash:[3,3], + Tooltip: + { + BGColor:'rgba(236,240,245, 0.8)', TextColor:"rgb(130,140,151)", + Border:{ Top:5, Left:20, Bottom:5, Center: 5}, + Font:"14px 微软雅黑", + LineHeight:16 //单行高度 + } + } + + // //自定义风格 + this.SetStyle = function (style) + { + if (style.TooltipBGColor) this.TooltipBGColor = style.TooltipBGColor; + if (style.TooltipAlpha) this.TooltipAlpha = style.TooltipAlpha; + if (style.BGColor) this.BGColor = style.BGColor; + if (style.SelectRectBGColor) this.SelectRectBGColor = style.SelectRectBGColor; + if (style.UpBarColor) this.UpBarColor = style.UpBarColor; + if (style.DownBarColor) this.DownBarColor = style.DownBarColor; + if (style.UnchagneBarColor) this.UnchagneBarColor = style.UnchagneBarColor; + if (style.Minute) + { + if (style.Minute.VolBarColor) this.Minute.VolBarColor = style.Minute.VolBarColor; + if (style.Minute.PriceColor) this.Minute.PriceColor = style.Minute.PriceColor; + if (style.Minute.AvPriceColor) this.Minute.AvPriceColor = style.Minute.AvPriceColor; + if (style.Minute.AreaPriceColor) this.Minute.AreaPriceColor = style.Minute.AreaPriceColor; + } + if (style.DefaultTextColor) this.DefaultTextColor = style.DefaultTextColor; + if (style.DefaultTextFont) this.DefaultTextFont = style.DefaultTextFont; + if (style.DynamicTitleFont) this.DynamicTitleFont = style.DynamicTitleFont; + if (style.IndexTitleBGColor) this.IndexTitleBGColor=style.IndexTitleBGColor; + if (style.UpTextColor) this.UpTextColor = style.UpTextColor; + if (style.DownTextColor) this.DownTextColor = style.DownTextColor; + if (style.UnchagneTextColor) this.UnchagneTextColor = style.UnchagneTextColor; + if (style.CloseLineColor) this.CloseLineColor = style.CloseLineColor; + if (style.CloseLineAreaColor) this.CloseLineAreaColor = style.CloseLineAreaColor; + if (style.FrameBorderPen) this.FrameBorderPen = style.FrameBorderPen; + if (style.FrameSplitPen) this.FrameSplitPen = style.FrameSplitPen; + if (style.FrameSplitTextColor) this.FrameSplitTextColor = style.FrameSplitTextColor; + if (style.FrameSplitTextFont) this.FrameSplitTextFont = style.FrameSplitTextFont; + if (style.FrameTitleBGColor) this.FrameTitleBGColor = style.FrameTitleBGColor; + + if (style.Frame) + { + if (style.Frame.XBottomOffset) this.Frame.XBottomOffset = style.Frame.XBottomOffset; + if (style.Frame.YTopOffset) this.Frame.YTopOffset = style.Frame.YTopOffset; + } + + if (style.FrameLatestPrice) + { + if (style.FrameLatestPrice.TextColor) this.FrameLatestPrice.TextColor = style.FrameLatestPrice.TextColor; + if (style.FrameLatestPrice.UpBarColor) this.FrameLatestPrice.UpBarColor = style.FrameLatestPrice.UpBarColor; + if (style.FrameLatestPrice.DownBarColor) this.FrameLatestPrice.DownBarColor = style.FrameLatestPrice.DownBarColor; + if (style.FrameLatestPrice.UnchagneBarColor) this.FrameLatestPrice.UnchagneBarColor = style.FrameLatestPrice.UnchagneBarColor; + if (style.FrameLatestPrice.BGAlpha) this.FrameLatestPrice.BGAlpha = style.FrameLatestPrice.BGAlpha; + } + + if (style.CorssCursorBGColor) this.CorssCursorBGColor = style.CorssCursorBGColor; + if (style.CorssCursorTextColor) this.CorssCursorTextColor = style.CorssCursorTextColor; + if (style.CorssCursorTextFont) this.CorssCursorTextFont = style.CorssCursorTextFont; + if (style.CorssCursorHPenColor) this.CorssCursorHPenColor = style.CorssCursorHPenColor; + if (style.CorssCursorVPenColor) this.CorssCursorVPenColor = style.CorssCursorVPenColor; + if (style.KLine) this.KLine = style.KLine; + if (style.Index) + { + if (style.Index.LineColor) this.Index.LineColor = style.Index.LineColor; + if (style.Index.NotSupport) this.Index.NotSupport = style.Index.NotSupport; + } + + if (style.ColorArray) this.ColorArray = style.ColorArray; + + if (style.DrawPicture) + { + this.DrawPicture.LineColor = style.DrawPicture.LineColor; + this.DrawPicture.PointColor = style.DrawPicture.PointColor; + } + + if (style.TooltipPaint) + { + if (style.TooltipPaint.BGColor) this.TooltipPaint.BGColor = style.TooltipPaint.BGColor; + if (style.TooltipPaint.BorderColor) this.TooltipPaint.BorderColor = style.TooltipPaint.BorderColor; + if (style.TooltipPaint.TitleColor) this.TooltipPaint.TitleColor = style.TooltipPaint.TitleColor; + if (style.TooltipPaint.TitleFont) this.TooltipPaint.TitleFont = style.TooltipPaint.TitleFont; + } + + if (style.Title) + { + if (style.Title.TradeIndexColor) this.Title.TradeIndexColor=style.Title.TradeIndexColor; + if (style.Title.ColorIndexColor) this.Title.ColorIndexColor=style.Title.ColorIndexColor; + + if (style.Title.VolColor) this.Title.VolColor=style.Title.VolColor; + if (style.Title.AmountColor) this.Title.AmountColor=style.Title.AmountColor; + if (style.Title.DateTimeColor) this.Title.DateTimeColor=style.Title.DateTimeColor; + if (style.Title.NameColor) this.Title.NameColor=style.Title.NameColor; + if (style.Title.SettingColor) this.Title.SettingColor=style.Title.SettingColor; + if (style.Title.TurnoverRateColor) this.Title.TurnoverRateColor=style.Title.TurnoverRateColor; + if (style.Title.PositionColor) this.Title.PositionColor=style.Title.PositionColor; + } + + if (style.DRAWICON) + { + if (style.DRAWICON.Text) + { + var item=style.DRAWICON.Text; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWICON.Text.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWICON.Text.MinSize=item.MinSize; + if (item.Zoom) this.DRAWICON.Text.Zoom=item.Zoom; + if (item.FontName) this.DRAWICON.Text.FontName=item.FontName; + } + } + + if (style.DRAWTEXT) + { + var item=style.DRAWTEXT; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWICON.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWICON.MinSize=item.MinSize; + if (item.Zoom) this.DRAWICON.Zoom=item.Zoom; + if (item.FontName) this.DRAWICON.FontName=item.FontName; + } + + if (style.DRAWNUMBER) + { + var item=style.DRAWNUMBER; + if (this.IsPlusNumber(item.MaxSize)) this.DRAWNUMBER.Text.MaxSize=item.MaxSize; + if (this.IsPlusNumber(item.MinSize)) this.DRAWNUMBER.Text.MinSize=item.MinSize; + if (item.Zoom) this.DRAWNUMBER.Text.Zoom=item.Zoom; + if (item.FontName) this.DRAWNUMBER.Text.FontName=item.FontName; + } + + if (style.DRAWABOVE) + { + var item=style.DRAWABOVE; + if (this.IsNumber(item.YOffset)) this.DRAWABOVE.YOffset=item.YOffset; + } + + if (style.DepthChart) + { + var item=style.DepthChart; + if (item.BidColor) + { + if (item.BidColor.Line) this.DepthChart.BidColor.Line=item.BidColor.Line; + if (item.BidColor.Area) this.DepthChart.BidColor.Area=item.BidColor.Area; + } + if (item.AskColor) + { + if (item.AskColor.Line) this.DepthChart.AskColor.Line=item.AskColor.Line; + if (item.AskColor.Area) this.DepthChart.AskColor.Area=item.AskColor.Area; + } + + if (item.LineWidth) this.DepthChart.LineWidth=item.LineWidth; + } + + if (style.DepthCorss) + { + var item=style.DepthCorss; + if (item.BidColor) + { + if (item.BidColor.Line) this.DepthCorss.BidColor.Line=item.BidColor.Line; + } + + if (item.AskColor) + { + if (item.AskColor.Line) this.DepthCorss.AskColor.Line=item.AskColor.Line; + } + + if (item.LineWidth) this.DepthCorss.LineWidth=item.LineWidth; + if (item.LineDash) this.DepthCorss.LineDash=item.LineDash; + + if (item.Tooltip) + { + var tooltip=item.Tooltip; + if (tooltip.BGColor) this.DepthCorss.Tooltip.BGColor=tooltip.BGColor; + if (tooltip.TextColor) this.DepthCorss.Tooltip.TextColor=tooltip.TextColor; + if (tooltip.Font) this.DepthCorss.Tooltip.Font=tooltip.Font; + if (tooltip.LineHeight) this.DepthCorss.Tooltip.LineHeight=tooltip.LineHeight; + + var border=tooltip.Border; + if (this.IsNumber(border.Top)) this.DepthCorss.Tooltip.Border.Top=border.Top; + if (this.IsNumber(border.Left)) this.DepthCorss.Tooltip.Border.Left=border.Left; + if (this.IsNumber(border.Bottom)) this.DepthCorss.Tooltip.Border.Bottom=border.Bottom; + if (this.IsNumber(border.Center)) this.DepthCorss.Tooltip.Border.Center=border.Center; + } + } + + if (style.CIRCLEDOT) + { + var item=style.CIRCLEDOT; + if (this.IsNumber(item.Radius)) this.CIRCLEDOT.Radius=item.Radius; + } + + if (style.POINTDOT) + { + var item=style.POINTDOT; + if (this.IsNumber(item.Radius)) this.POINTDOT.Radius=item.Radius; + } + } + + + this.IsNumber=function(value) + { + if (value==null) return false; + if (isNaN(value)) return false; + + return true; + } + + //判断是否是正数 + this.IsPlusNumber=function(value) + { + if (value==null) return false; + if (isNaN(value)) return false; + + return value>0; + } +} + +var g_JSChartResource = new JSChartResource(); + +var JSCHART_LANGUAGE_ID = +{ + LANGUAGE_CHINESE_ID: 0, + LANGUAGE_ENGLISH_ID: 1 +}; + +function JSChartLocalization() +{ + this.TextResource = new Map([ + //内部tooltip + ['Tooltip-Open', { CN: '开:', EN: 'O:' }], + ['Tooltip-High', { CN: '高:', EN: 'H:' }], + ['Tooltip-Low', { CN: '低:', EN: 'L:' }], + ['Tooltip-Close', { CN: '收:', EN: 'C:' }], + ['Tooltip-Increase', { CN: '幅:', EN: 'I:' }], + ['Tooltip-Vol', { CN: '量:', EN: 'V:' }], + ['Tooltip-Amount', { CN: '额:', EN: 'A:' }], + ['Tooltip-AvPrice', { CN: '均:', EN: 'AP:' }], + ['Tooltip-Price', { CN: '价:', EN: 'P:' }], + ['Tooltip-Exchange', { CN: '换:', EN: 'E:' }], + ['Tooltip-Position', { CN: '持:', EN: 'P:' }], + + //K线动态标题 + ['KTitle-Open', { CN: '开:', EN: 'O:' }], + ['KTitle-High', { CN: '高:', EN: 'H:' }], + ['KTitle-Low', { CN: '低:', EN: 'L:' }], + ['KTitle-Close', { CN: '收:', EN: 'C:' }], + ['KTitle-Increase', { CN: '幅:', EN: 'I:' }], + ['KTitle-Vol', { CN: '量:', EN: 'V:' }], + ['KTitle-Amount', { CN: '额:', EN: 'A:' }], + ['KTitle-Exchange', { CN: '换:', EN: 'E:' }], + ['KTitle-Position', { CN: '持:', EN: 'P:' }], + + //走势图动态标题 + ['MTitle-Close', { CN: '价:', EN: 'C:' }], + ['MTitle-AvPrice', { CN: '均:', EN: 'AC:' }], + ['MTitle-Increase', { CN: '幅:', EN: 'I:' }], + ['MTitle-Vol', { CN: '量:', EN: 'V:' }], + ['MTitle-Amount', { CN: '额:', EN: 'A:' }], + ['MTitle-Position', { CN: '持:', EN: 'P:' }], + + //周期 + ['日线', { CN: '日线', EN: '1D' }], + ['周线', { CN: '周线', EN: '1W' }], + ['双周', { CN: '双周', EN: "2W" }], + ['月线', { CN: '月线', EN: '1M' }], + ['年线', { CN: '年线', EN: '1Y' }], + ['1分', { CN: '1分', EN: '1Min' }], + ['5分', { CN: '5分', EN: '5Min' }], + ['15分', { CN: '15分', EN: '15Min' }], + ['30分', { CN: '30分', EN: '30Min' }], + ['60分', { CN: '60分', EN: '60Min' }], + ['季线', { CN: '季线', EN: '1Q' }], + ['分笔', { CN: '分笔', EN: 'Tick' }], + ['2小时', { CN: '2小时', EN: '2H' }], + ['4小时', { CN: '4小时', EN: '4H' }], + + //复权 + ['不复权', { CN: '不复权', EN: 'No Right' }], + ['前复权', { CN: '前复权', EN: 'Pro Right' }], + ['后复权', { CN: '后复权', EN: 'Post Right' }], + + //week + ['日', { CN: '日', EN: 'Sun.' }], + ['一', { CN: '一', EN: 'Mon.' }], + ['二', { CN: '二', EN: 'Tues.' }], + ['三', { CN: '三', EN: 'Wed.' }], + ['四', { CN: '四', EN: 'Thur.' }], + ['五', { CN: '五', EN: 'Fri.' }], + ['六', { CN: '六', EN: 'Sat.' }], + + ['1月', { CN: '1月', EN: 'Jan' }], + ['2月', { CN: '2月', EN: 'Feb' }], + ['3月', { CN: '3月', EN: 'Mar' }], + ['4月', { CN: '4月', EN: 'Apr' }], + ['5月', { CN: '5月', EN: 'May' }], + ['6月', { CN: '6月', EN: 'Jun' }], + ['7月', { CN: '7月', EN: 'Jul' }], + ['8月', { CN: '8月', EN: 'Aug' }], + ['9月', { CN: '9月', EN: 'Sept' }], + ['10月', { CN: '10月', EN: 'Oct' }], + ['11月', { CN: '11月', EN: 'Nov' }], + ['12月', { CN: '12月', EN: 'Dec' }], + + ['自定义分钟', { CN: '分', EN: 'Min' }], + ['自定义日线', { CN: '日', EN: 'D' }], + ['自定义秒', { CN: '秒', EN: 'S' }] + + ]); + + this.GetText = function (key, language) + { + var item = this.TextResource.get(key); + if (!item) return ''; + + switch (language) + { + case JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID: + return item.CN; + case JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID: + return item.EN; + default: + return item.CN; + } + } + + this.SetTextResource = function (key, value) + { + this.TextResource.set(key, value) + } +}; + +var g_JSChartLocalization = new JSChartLocalization(); + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonResource: + { + JSChartResource: JSChartResource, + Global_JSChartResource: g_JSChartResource, + Global_JSChartLocalization: g_JSChartLocalization, + JSCHART_LANGUAGE_ID: JSCHART_LANGUAGE_ID, + }, + + //单个类导出 + JSCommonResource_JSChartResource: JSChartResource, + JSCommonResource_Global_JSChartResource: g_JSChartResource, + JSCommonResource_Global_JSChartLocalization: g_JSChartLocalization, + JSCommonResource_JSCHART_LANGUAGE_ID: JSCHART_LANGUAGE_ID +}; \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.style.wechat.js b/uni_modules/jones-hqchart2/js_sdk/umychart.style.wechat.js new file mode 100644 index 0000000..002c04c --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.style.wechat.js @@ -0,0 +1,342 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 不同风格行情配置文件 (微信小程序版本) +*/ + +var BLACK_STYLE = //黑色风格 +{ + BGColor:'rgb(0,0,0)', + TooltipBGColor: "rgb(255, 255, 255)", //背景色 + TooltipAlpha: 0.92, //透明度 + + SelectRectBGColor: "rgba(1,130,212,0.06)", //背景色 + // SelectRectAlpha: 0.06; //透明度 + + UpBarColor: "rgb(238,21,21)", + DownBarColor: "rgb(25,158,0)", + UnchagneBarColor: "rgb(199,199,199)", + + Minute: { + VolBarColor: null, + PriceColor: "rgb(25,180,231)", + AvPriceColor: "rgb(255,236,0)", + }, + + + DefaultTextColor: "rgb(101,104,112)", + DefaultTextFont: '14px 微软雅黑', + IndexTitleBGColor:'rgb(211,211,211)', + + DynamicTitleFont: '12px 微软雅黑', //指标动态标题字体 + + + UpTextColor: "rgb(238,21,21)", + DownTextColor: "rgb(25,158,0)", + UnchagneTextColor: "rgb(101,104,112)", + CloseLineColor: 'rgb(178,34,34)', + + Title: + { + TradeIndexColor:'rgb(105,105,105)', //交易指标颜色 + ColorIndexColor:'rgb(112,128,144)', //五彩K线颜色 + + VolColor:"rgb(101,104,112)", //标题成交量 + AmountColor:"rgb(101,104,112)", //成交金额 + DateTimeColor:"rgb(101,104,112)", //时间,日期 + SettingColor:"rgb(101,104,112)", //周期,复权 + NameColor:"rgb(101,104,112)" , //股票名称 + TurnoverRateColor:'rgb(101,104,112)', //换手率 + PositionColor:"rgb(101,104,112)" //持仓 + }, + + FrameBorderPen: "rgba(236,236,236,0.13)", //边框 + FrameSplitPen: "rgba(236,236,236,0.13)", //分割线 + FrameSplitTextColor: "rgb(101,104,112)", //刻度文字颜色 + FrameSplitTextFont: "12px 微软雅黑", //坐标刻度文字字体 + FrameTitleBGColor: "rgb(246,251,253)", //标题栏背景色 + Frame: { XBottomOffset: 0 }, //X轴文字向下偏移 + + FrameLatestPrice: { + TextColor: 'rgb(255,255,255)', //最新价格文字颜色 + UpBarColor: "rgb(238,21,21)", //上涨 + DownBarColor: "rgb(25,158,0)", //下跌 + UnchagneBarColor: "rgb(190,190,190)", //平盘 + BGAlpha: 0.6 + }, + + CorssCursorBGColor: "rgb(43,54,69)", //十字光标背景 + CorssCursorTextColor: "rgb(255,255,255)", + CorssCursorTextFont: "12px 微软雅黑", + CorssCursorHPenColor: "rgb(130,130,130)", //十字光标线段颜色 + CorssCursorVPenColor: "rgb(130,130,130)", //十字光标线段颜色 + + KLine: + { + MaxMin: { Font: '12px 微软雅黑', Color: 'rgb(111,111,111)', RightArrow:"→", LeftArrow:"←", HighYOffset:0, LowYOffset:0 }, //K线最大最小值显示 + Info: //信息地雷 + { + Color: 'rgb(205,149,12)', + TextColor: '#afc0da', + TextBGColor: '#1a283e', + Investor: + { + ApiUrl: '/API/NewsInteract', //互动易 + }, + Announcement: //公告 + { + ApiUrl: '/API/ReportList', + }, + Pforecast: //业绩预告 + { + ApiUrl: '/API/StockHistoryDay', + }, + Research: //调研 + { + ApiUrl: '/API/InvestorRelationsList', + }, + BlockTrading: //大宗交易 + { + ApiUrl: '/API/StockHistoryDay', + }, + TradeDetail: //龙虎榜 + { + ApiUrl: '/API/StockHistoryDay', + }, + Policy: //策略 + { + ApiUrl: '/API/StockHistoryDay', + } + } + }, + + Index: { //指标线段颜色 + LineColor: [ + "rgb(255,189,09)", + "rgb(22,198,255)", + "rgb(174,35,161)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + + ], + NotSupport: { Font: "14px 微软雅黑", TextColor: "rgb(52,52,52)" } + }, + + ColorArray: //自定义指标默认颜色 + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ], + //画图工具 + DrawPicture: + { + LineColor: "rgb(30,144,255)", + PointColor: "rgb(105,105,105)", + }, + + TooltipPaint: //Tooltip + { + BGColor: 'rgba(20,20,20,0.8)', //背景色 + BorderColor: 'rgb(210,210,210)', //边框颜色 + TitleColor: 'rgb(210,210,210)', //标题颜色 + TitleFont:'13px 微软雅黑' //字体 + }, + + //深度图 + DepthChart: + { + BidColor: { Line:"rgb(82,176,123)", Area:"rgba(82,176,123,0.5)"}, //卖 + AskColor: { Line:"rgb(207,76,89)", Area:"rgba(207,76,89, 0.5)"}, //买 + LineWidth:4 + }, + + DepthCorss: + { + BidColor: { Line:"rgb(82,176,123)" }, //卖 + AskColor: { Line:"rgb(207,76,89)" }, //买 + LineWidth:2, //线段宽度 + LineDash:[3,3], + Tooltip: + { + BGColor:'rgba(54,54,54, 0.8)', TextColor:"rgb(203,215,224)", + Border:{ Top:5, Left:20, Bottom:5, Center: 5}, + Font:"14px 微软雅黑", + LineHeight:16 //单行高度 + } + } +}; + + +var WHITE_STYLE= //白色风格 +{ + BGColor:'rgb(255,255,255)', + TooltipBGColor: "rgb(255, 255, 255)", //背景色 + TooltipAlpha: 0.92, //透明度 + + SelectRectBGColor:"rgba(1,130,212,0.06)", //背景色 + // this.SelectRectAlpha=0.06; //透明度 + + UpBarColor: "rgb(238,21,21)", + DownBarColor: "rgb(25,158,0)", + UnchagneBarColor: "rgb(0,0,0)", + + Minute: + { + VolBarColor : "rgb(238,127,9)", + PriceColor : "rgb(50,171,205)", + AvPriceColor : "rgb(238,127,9)", + }, + + DefaultTextColor: "rgb(43,54,69)", + DefaultTextFont: '14px 微软雅黑', + + DynamicTitleFont: '12px 微软雅黑', //指标动态标题字体 + + + UpTextColor: "rgb(238,21,21)", + DownTextColor: "rgb(25,158,0)", + UnchagneTextColor: "rgb(0,0,0)", + CloseLineColor: 'rgb(178,34,34)', + + FrameBorderPen: "rgb(225,236,242)", //边框 + FrameSplitPen: "rgb(225,236,242)", //分割线 + FrameSplitTextColor: "rgb(51,51,51)", //刻度文字颜色 + FrameSplitTextFont: "12px 微软雅黑", //坐标刻度文字字体 + FrameTitleBGColor: "rgb(246,251,253)", //标题栏背景色 + + CorssCursorBGColor: "rgb(43,54,69)", //十字光标背景 + CorssCursorTextColor: "rgb(255,255,255)", + CorssCursorTextFont: "12px 微软雅黑", + CorssCursorPenColor: "rgb(130,130,130)", //十字光标线段颜色 + + KLine: + { + MaxMin: { Font: '12px 微软雅黑', Color: 'rgb(111,111,111)' }, //K线最大最小值显示 + Info: //信息地雷 + { + Color: 'rgb(205,149,12)', + TextColor: '#197de9', + TextBGColor: '#e1e4ef', + Investor: + { + ApiUrl: '/API/NewsInteract', //互动易 + }, + Announcement: //公告 + { + ApiUrl: '/API/ReportList', + }, + Pforecast: //业绩预告 + { + ApiUrl: '/API/StockHistoryDay', + }, + Research: //调研 + { + ApiUrl: '/API/InvestorRelationsList', + }, + BlockTrading: //大宗交易 + { + ApiUrl: '/API/StockHistoryDay', + }, + TradeDetail: //龙虎榜 + { + ApiUrl: '/API/StockHistoryDay', + }, + Policy: //策略 + { + ApiUrl: '/API/StockHistoryDay', + } + } + }, + + Index: + { //指标线段颜色 + LineColor: + [ + "rgb(255,189,09)", + "rgb(22,198,255)", + "rgb(174,35,161)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ], + NotSupport: { Font: "14px 微软雅黑", TextColor: "rgb(52,52,52)" } + }, + + ColorArray: //自定义指标默认颜色 + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ], + + //画图工具 + DrawPicture: + { + LineColor: "rgb(30,144,255)", + PointColor: "rgb(105,105,105)", + }, +} + +var STYLE_TYPE_ID= +{ + BLACK_ID:1, //黑色风格 + WHITE_ID:2 //白色风格 +} + + +function GetStyleConfig(styleid) //获取一个风格的配置变量 +{ + switch (styleid) + { + case STYLE_TYPE_ID.BLACK_ID: + return BLACK_STYLE; + break; + case STYLE_TYPE_ID.WHITE_ID: + return WHITE_STYLE; + break; + default: + return null; + } +} + + +module.exports = + { + JSCommonHQStyle: + { + GetStyleConfig:GetStyleConfig, + STYLE_TYPE_ID:STYLE_TYPE_ID + } + }; + + diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.canvas.helper.js b/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.canvas.helper.js new file mode 100644 index 0000000..5e26374 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.canvas.helper.js @@ -0,0 +1,71 @@ +/* +copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + uniapp canvas 兼容方法 +*/ + +function JSUniAppCanvasHelper() { } + +JSUniAppCanvasHelper.MeasureText=function(text, canvas) +{ + var font = canvas.font; + var fontSize = 12; + var pos=font.search('px'); + if (pos>0) + { + var strSize = font.substring(0,pos); + fontSize = parseInt(strSize); + } + text = String(text); + var text = text.split(''); + var width = 0; + for (let i = 0; i < text.length; i++) + { + let item = text[i]; + if (/[a-zA-Z]/.test(item)) + { + width += 7; + } else if (/[0-9]/.test(item)) + { + width += 5.5; + } else if (/\./.test(item)) + { + width += 2.7; + } else if (/-/.test(item)) + { + width += 3.25; + } else if (/[\u4e00-\u9fa5]/.test(item)) + { + width += 10; + } else if (/\(|\)/.test(item)) + { + width += 3.73; + } else if (/\s/.test(item)) + { + width += 2.5; + } else if (/%/.test(item)) + { + width += 8; + } else + { + width += 10; + } + } + return width * fontSize / 10; +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommonUniApp: + { + JSUniAppCanvasHelper: JSUniAppCanvasHelper, + } +}; + diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.h5.js b/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.h5.js new file mode 100644 index 0000000..5b09e71 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.uniapp.h5.js @@ -0,0 +1,84908 @@ + +//日志输出类 +var JSConsole= +{ + Chart:{ Log:console.log }, //图形日志 + Complier:{ Log:console.log }, //编译器日志 + JSTable:{ Log:console.log } //表格日志 +}; + + + + +/* + Copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 系统指标 (H5版本) +*/ + + +/* + 指标数据脚本 系统内置指标都写在这里 + Name:指标名字 + Description:指标描述信息 + Args:参数 { Name:名字, Value=值 } + IsMainIndex:是否是主图指标 true=主图指标 false=副图指标 + KLineType:K线设置 -1=主图不显示K线(只在主图有效) 0=在副图显示K线 1=在副图显示K线(收盘价线) 2=在副图显示K线(美国线) + InstructionType: 1=专家指示 2=五彩K线 + FloatPrecision: 小数位数 缺省=2 + YSplitScale: Y固定刻度 [1,8,10] + YSpecificMaxMin: 固定Y轴最大最小值 { Max: 9, Min: 0, Count: 3 }; + StringFormat: 1=带单位万/亿 2=原始格式 + Condition: 限制条件 { Symbol:'Index'/'Stock'(只支持指数/股票),Period:[](支持的周期), Include:[](指定支持的股票,代码全部大写包括后缀)} + OutName:动态输出变量名字 [{Name:原始变量名, DynamicName:动态名字格式}] 如 {Name:"MA1", DynamicName:"MA{M1}"}; +*/ + +//周期条件枚举 +var CONDITION_PERIOD= +{ + MINUTE_ID:101, //分钟 走势图 + MULTIDAY_MINUTE_ID:102, //多日分钟 走势图 + HISTORY_MINUTE_ID:103, //历史分钟 走势图 + + //K线周期 + KLINE_DAY_ID:0, + KLINE_WEEK_ID:1, + KLINE_TWOWEEK_ID:21, + KLINE_MONTH_ID:2, + KLINE_QUARTER_ID:9, + + KLINE_YEAR_ID:3, + KLINE_MINUTE_ID:4, + KLINE_5_MINUTE_ID:5, + KLINE_15_MINUTE_ID:6, + KLINE_30_MINUTE_ID:7, + KLINE_60_MINUTE_ID:8, +}; + +//自定义的指标脚本 +function CustomIndexScript() +{ + this.DataMap=new Map(); //key=指标id, value=data {ID:, Name:指标名字, Description:指标描述信息 Args:参数 ......} + + this.Get=function(id) + { + if (!this.DataMap.has(id)) return null; + return this.DataMap.get(id); + } + + this.Add=function(data) + { + this.DataMap.set(data.ID, data); + } +} + +var g_CustomIndex=new CustomIndexScript(); + +function JSIndexScript() +{ + this.DataMap=new Map( + [ + ['MA', this.MA],['均线', this.MA], + ["MA4", this.MA4],["MA5", this.MA5],["MA6", this.MA6],["MA7", this.MA7],["MA8", this.MA8], + ['BOLL', this.BOLL],['BOLL副图', this.BOLL2],['BBI', this.BBI], + ['DKX', this.DKX],['MIKE', this.MIKE],['PBX', this.PBX], + ['ENE', this.ENE],['MACD', this.MACD],['KDJ', this.KDJ],["MACD2", this.MACD2], + ['VOL', this.VOL],['RSI', this.RSI],['BRAR', this.BRAR], + ['WR', this.WR],['BIAS', this.BIAS],['OBV', this.OBV], + ['DMI', this.DMI],['CR', this.CR],['PSY', this.PSY], + ['CCI', this.CCI],['DMA', this.DMA],['TRIX', this.TRIX], + ['VR', this.VR],['EMV', this.EMV],['ROC', this.ROC], + ['MTM', this.MTM],['FSL', this.FSL],['CYR', this.CYR], + ['MASS', this.MASS],['WAD', this.WAD],['CHO', this.CHO], + ['ADTM', this.ADTM],['HSL', this.HSL],['BIAS36', this.BIAS36], + ['BIAS_QL', this.BIAS_QL],['DPO', this.DPO],['OSC', this.OSC], + ['ATR', this.ATR],['NVI', this.NVI],['PVI', this.PVI], + ['UOS', this.UOS],['CYW', this.CYW],['LON', this.LON], + ['NDB', this.NDB],['SKDJ',this.SKDJ],['KD',this.KD],['FKX',this.FKX], + ['DKCOL',this.DKCOL],['UDL',this.UDL],['MFI',this.MFI],['LWR',this.LWR], + ['MARSI',this.MARSI],['CYD',this.CYD],['CYF',this.CYF],['TAPI',this.TAPI], + ['VMACD',this.VMACD],['QACD',this.QACD],['VPT',this.VPT],['WVAD',this.WVAD], + ['DBQR',this.DBQR],['JS',this.JS],['CYE',this.CYE],['QR',this.QR],['GDX',this.GDX], + ['JLHB',this.JLHB],['PCNT',this.PCNT],['BTX', this.BTX],['AMO',this.AMO], + ['VRSI',this.VRSI],['HSCOL',this.HSCOL],['DBQRV',this.DBQRV],['DBLB',this.DBLB], + ['ACD',this.ACD],['EXPMA',this.EXPMA],['EXPMA_S',this.EXPMA_S],['HMA',this.HMA], + ['LMA',this.LMA],['VMA',this.VMA],['AMV',this.AMV],['BBIBOLL',this.BBIBOLL], + ['ALLIGAT',this.ALLIGAT],['ZX',this.ZX],['XS',this.XS],['XS2',this.XS2], + ['SG-XDT',this.SG_XDT],['SG-SMX',this.SG_SMX],['SG-LB',this.SG_LB],['SG-PF',this.SG_PF], + ['RAD',this.RAD],['SHT',this.SHT],['ZLJC',this.ZLJC],['ZLMM',this.ZLMM],['SLZT',this.SLZT], + ['ADVOL',this.ADVOL],['CYC',this.CYC],['CYS',this.CYS],['CYQKL',this.CYQKL], + ['SCR',this.SCR],['ASR',this.ASR],['SAR',this.SAR],['TJCJL',this.TJCJL],['量比',this.VOLRate], + ['平均K线',this.HeikinAshi], ["ADL", this.ADL],["SQJZ", this.SQJZ],["XT", this.XT],["CFJT", this.CFJT],["CYX",this.CYX], + ["WAVE",this.WAVE], + ['VOL-TDX',this.VOL_TDX], + ['EMPTY', this.EMPTY], //什么都不显示的指标 + ['神奇九转', this.NineTurns], + + //通达信特色指标 + ["散户线", this.ShareholderCount],["NXTS", this.NXTS],["FKX", this.FKX],["两融资金", this.Margin4], + ["ZSDB",this.ZSDB], + + ['CJL2', this.CJL], //期货持仓量 + ['ASI', this.ASI],['DC', this.DC],['DEMA', this.DEMA],["VWAP", this.VWAP], + + ['飞龙四式', this.Dragon4_Main],['飞龙四式-附图', this.Dragon4_Fig], + ['资金分析', this.FundsAnalysis],['融资占比',this.MarginProportion],['负面新闻', this.NewsNegative], + ['涨跌趋势', this.UpDownAnalyze],['北上资金', this.HK2SHSZ],['股东人数', this.ShareHolder], + + ["两融余额", this.Margin2],["两融余额2", this.Margin3], + + ['Zealink-资金吸筹', this.Zealink_Index1], ['Zealink-牛熊区间', this.Zealink_Index2],['Zealink-持仓信号', this.Zealink_Index3], + ['Zealink-增减持',this.Zealink_Index4],['Zealink-大宗交易', this.Zealink_Index5], ['Zealink-信托持股', this.Zealink_Index6], + ['Zealink-官网新闻', this.Zealink_Index7], ['Zealink-高管要闻', this.Zealink_Index8],['Zealink-股权质押', this.Zealink_Index9], + ['Zealink-操盘BS点', this.Zealink_Index10],['Zealink-操盘BS点2', this.Zealink_Index11], + + //外包指标 + ['放心股-操盘BS点',this.FXG_BSPoint], + ['放心股-涨停多空线',this.FXG_INDEX], + ['放心股-涨停吸筹区',this.FXG_INDEX2], + ['放心股-量能黄金点',this.FXG_INDEX3], + + //五彩K线(函数COLOR_开头) + ['五彩K线-十字星',this.COLOR_KSTAR1],['五彩K线-早晨之星',this.COLOR_KSTAR2],['五彩K线-黄昏之星',this.COLOR_KSTAR3],['五彩K线-长十字',this.COLOR_SHI1], + ['五彩K线-身怀六甲',this.COLOR_K220],['五彩K线-三个白武士',this.COLOR_K300],['五彩K线-三只乌鸦',this.COLOR_K310],['五彩K线-光头阳线',this.COLOR_K380], + ['五彩K线-光脚阴线',this.COLOR_K390],['五彩K线-垂死十字',this.COLOR_K134],['五彩K线-早晨十字星',this.COLOR_K140],['五彩K线-黄昏十字星',this.COLOR_K150], + ['五彩K线-射击之星',this.COLOR_K160],['五彩K线-倒转锤头',this.COLOR_K165],['五彩K线-锤头',this.COLOR_K170],['五彩K线-吊颈',this.COLOR_K180], + ['五彩K线-穿头破脚',this.COLOR_K190],['五彩K线-出水芙蓉',this.COLOR_CSFR],['五彩K线-乌云盖顶',this.COLOR_WYGD],['五彩K线-曙光初现',this.COLOR_SGCJ], + ['五彩K线-十字胎',this.COLOR_SZTAI],['五彩K线-剑',this.COLOR_SWORD],['五彩K线-平顶',this.COLOR_PINGDING],['五彩K线-平底',this.COLOR_PINGDI], + ['五彩K线-大阳烛',this.COLOR_DAYANZHU],['五彩K线-大阴烛',this.COLOR_DAYINGZHU], + + ['五彩K线-好友反攻',this.COLOR_HYFG],['五彩K线-跳空缺口',this.COLOR_TKQK], + ['五彩K线-双飞乌鸦',this.COLOR_SFWY],['五彩K线-上升三部曲',this.COLOR_SSSBQ],['五彩K线-下跌三部曲',this.COLOR_XDSBQ],['五彩K线-长下影',this.COLOR_CHXY], + ['五彩K线-长上影',this.COLOR_CHSY],['五彩K线-分离',this.COLOR_FENLI], + + //交易系统 + ['交易系统-BIAS',this.TRADE_BIAS],['交易系统-CCI',this.TRADE_CCI],['交易系统-DMI',this.TRADE_DMI],['交易系统-KD',this.TRADE_KD], + ['交易系统-BOLL',this.TRADE_BOLL],['交易系统-KDJ',this.TRADE_KDJ],['交易系统-MA',this.TRADE_MA],['交易系统-MACD',this.TRADE_MACD], + ['交易系统-MTM',this.TRADE_MTM],['交易系统-PSY',this.TRADE_PSY],['交易系统-ROC',this.TRADE_ROC],['交易系统-RSI',this.TRADE_RSI], + ['交易系统-VR',this.TRADE_VR],['交易系统-DPSJ',this.TRADE_DPSJ], + + ['TEST', this.TEST] //测试用 + ]); +} + +JSIndexScript.AddIndex=function(aryIndex) //添加自定义指标 +{ + for(var i in aryIndex) + { + g_CustomIndex.Add(aryIndex[i]); + } +} + +JSIndexScript.prototype.Get=function(id) +{ + var data=g_CustomIndex.Get(id); + if (data) return data; + + var func=this.DataMap.get(id); + if (func) return func(); + + return null; +} + +JSIndexScript.prototype.Search=function(name) +{ + var result=[]; + var reg = new RegExp(name,'i'); + this.DataMap.forEach(function(value,key) + { + if (key.indexOf('交易系统-')!=0 && key.indexOf('五彩K线-')!=0 && key.search(reg)>=0) + result.push(key); + }); + + return result; +} + +JSIndexScript.prototype.MA=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20} ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" }], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);' + + }; + + return data; +} + +JSIndexScript.prototype.MA4=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20},{ Name:'M4', Value:60} ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" },{Name:'MA4',DynamicName:"MA{M4}" } ], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);\n\ +MA4:MA(CLOSE,M4);' + + }; + + return data; +} + +JSIndexScript.prototype.MA5=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20} ,{ Name:'M4', Value:60} ,{ Name:'M5', Value:0}], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" },{Name:'MA4',DynamicName:"MA{M4}" },{Name:'MA5',DynamicName:"MA{M5}" } ], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);\n\ +MA4:MA(CLOSE,M4);\n\ +MA5:MA(CLOSE,M5);' + + }; + + return data; +} + +JSIndexScript.prototype.MA6=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args: + [ + { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20} , { Name:'M4', Value:60} , + { Name:'M5', Value:0},{ Name:'M6', Value:0} + ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" },{Name:'MA4',DynamicName:"MA{M4}" }, + {Name:'MA5',DynamicName:"MA{M5}" } ,{ Name:'MA6',DynamicName:"MA{M6}" } ], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);\n\ +MA4:MA(CLOSE,M4);\n\ +MA5:MA(CLOSE,M5);\n\ +MA6:MA(CLOSE,M6);' + + }; + + return data; +} + +JSIndexScript.prototype.MA7=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args: + [ + { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20}, { Name:'M4', Value:60}, + { Name:'M5', Value:0},{ Name:'M6', Value:0} ,{ Name:'M7', Value:0 } + ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" },{Name:'MA4',DynamicName:"MA{M4}" }, + {Name:'MA5',DynamicName:"MA{M5}" } ,{ Name:'MA6',DynamicName:"MA{M6}" } ,{ Name:'MA7',DynamicName:"MA{M7}" }], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);\n\ +MA4:MA(CLOSE,M4);\n\ +MA5:MA(CLOSE,M5);\n\ +MA6:MA(CLOSE,M6);\n\ +MA7:MA(CLOSE,M7);' + + }; + + return data; +} + +JSIndexScript.prototype.MA8=function() +{ + let data= + { + Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2, + Args: + [ + { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20}, { Name:'M4', Value:60}, + { Name:'M5', Value:0},{ Name:'M6', Value:0} ,{ Name:'M7', Value:0 } ,{ Name:'M8', Value:0 } + ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" },{Name:'MA3',DynamicName:"MA{M3}" },{Name:'MA4',DynamicName:"MA{M4}" }, + {Name:'MA5',DynamicName:"MA{M5}" } ,{ Name:'MA6',DynamicName:"MA{M6}" } ,{ Name:'MA7',DynamicName:"MA{M7}" },{ Name:'MA8',DynamicName:"MA{M8}" }], + Script: //脚本 +'MA1:MA(CLOSE,M1);\n\ +MA2:MA(CLOSE,M2);\n\ +MA3:MA(CLOSE,M3);\n\ +MA4:MA(CLOSE,M4);\n\ +MA5:MA(CLOSE,M5);\n\ +MA6:MA(CLOSE,M6);\n\ +MA7:MA(CLOSE,M7);\n\ +MA8:MA(CLOSE,M8);' + + }; + + return data; +} + +JSIndexScript.prototype.BOLL=function() +{ + let data= + { + Name:'BOLL', Description:'布林线', IsMainIndex:true, + Args:[ { Name:'M', Value:20} ], + Script: //脚本 +'BOLL:MA(CLOSE,M);\n\ +UB:BOLL+2*STD(CLOSE,M);\n\ +LB:BOLL-2*STD(CLOSE,M);' + + }; + + return data; +} + +JSIndexScript.prototype.BOLL2=function() +{ + let data= + { + Name:'BOLL2', Description:'布林线', IsMainIndex:false, KLineType:0, + Args:[ { Name:'M', Value:20} ], + Script: //脚本 +'BOLL:MA(CLOSE,M);\n\ +UB:BOLL+2*STD(CLOSE,M);\n\ +LB:BOLL-2*STD(CLOSE,M);' + + }; + + return data; +} + + +JSIndexScript.prototype.BBI=function() +{ + let data= + { + Name:'BBI', Description:'多空均线', IsMainIndex:true, + Args:[ { Name:'M1', Value:3}, { Name:'M2', Value:6}, { Name:'M3', Value:12}, { Name:'M4', Value:24} ], + Script: //脚本 + 'BBI:(MA(CLOSE,M1)+MA(CLOSE,M2)+MA(CLOSE,M3)+MA(CLOSE,M4))/4;' + + }; + + return data; +} + + +JSIndexScript.prototype.DKX=function() +{ + let data= + { + Name:'DKX', Description:'多空线', IsMainIndex:false, + Args:[ { Name:'M', Value:10} ], + Script: //脚本 +'MID:=(3*CLOSE+LOW+OPEN+HIGH)/6;\n\ +DKX:(20*MID+19*REF(MID,1)+18*REF(MID,2)+17*REF(MID,3)+\n\ +16*REF(MID,4)+15*REF(MID,5)+14*REF(MID,6)+\n\ +13*REF(MID,7)+12*REF(MID,8)+11*REF(MID,9)+\n\ +10*REF(MID,10)+9*REF(MID,11)+8*REF(MID,12)+\n\ +7*REF(MID,13)+6*REF(MID,14)+5*REF(MID,15)+\n\ +4*REF(MID,16)+3*REF(MID,17)+2*REF(MID,18)+REF(MID,20))/210;\n\ +MADKX:MA(DKX,M);' + + }; + + return data; +} + +JSIndexScript.prototype.MIKE=function() +{ + let data= + { + Name:'MIKE', Description:'麦克支撑压力', IsMainIndex:true, + Args:[ { Name:'N', Value:10} ], + Script: //脚本 +'HLC:=REF(MA((HIGH+LOW+CLOSE)/3,N),1);\n\ +HV:=EMA(HHV(HIGH,N),3);\n\ +LV:=EMA(LLV(LOW,N),3);\n\ +STOR:EMA(2*HV-LV,3);\n\ +MIDR:EMA(HLC+HV-LV,3);\n\ +WEKR:EMA(HLC*2-LV,3);\n\ +WEKS:EMA(HLC*2-HV,3);\n\ +MIDS:EMA(HLC-HV+LV,3);\n\ +STOS:EMA(2*LV-HV,3);' + + }; + + return data; +} + +JSIndexScript.prototype.PBX=function() +{ + let data= + { + Name:'PBX', Description:'瀑布线', IsMainIndex:true, + Args:[ { Name:'M1', Value:4}, { Name:'M2', Value:6}, { Name:'M3', Value:9}, { Name:'M4', Value:13},{ Name:'M5', Value:18},{ Name:'M6', Value:24} ], + Script: //脚本 +'PBX1:(EMA(CLOSE,M1)+MA(CLOSE,M1*2)+MA(CLOSE,M1*4))/3;\n\ +PBX2:(EMA(CLOSE,M2)+MA(CLOSE,M2*2)+MA(CLOSE,M2*4))/3;\n\ +PBX3:(EMA(CLOSE,M3)+MA(CLOSE,M3*2)+MA(CLOSE,M3*4))/3;\n\ +PBX4:(EMA(CLOSE,M4)+MA(CLOSE,M4*2)+MA(CLOSE,M4*4))/3;\n\ +PBX5:(EMA(CLOSE,M5)+MA(CLOSE,M5*2)+MA(CLOSE,M5*4))/3;\n\ +PBX6:(EMA(CLOSE,M6)+MA(CLOSE,M6*2)+MA(CLOSE,M6*4))/3;' + + }; + + return data; +} + +JSIndexScript.prototype.ENE=function() +{ + let data= + { + Name:'ENE', Description:'轨道线', IsMainIndex:true, + Args:[ { Name:'N', Value:25}, { Name:'M1', Value:6}, { Name:'M2', Value:6} ], + Script: //脚本 +'UPPER:(1+M1/100)*MA(CLOSE,N);\n\ +LOWER:(1-M2/100)*MA(CLOSE,N);\n\ +ENE:(UPPER+LOWER)/2;' + + }; + + return data; +} + +JSIndexScript.prototype.MACD=function() +{ + let data= + { + Name:'MACD', Description:'平滑异同平均', IsMainIndex:false, + Args:[ { Name:'SHORT', Value:12}, { Name:'LONG', Value:26}, { Name:'MID', Value:9} ], + Script: //脚本 +'DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG);\n\ +DEA:EMA(DIF,MID);\n\ +MACD:(DIF-DEA)*2,COLORSTICK;' + + }; + + return data; +} + +//上下柱子 +JSIndexScript.prototype.MACD2=function() +{ + let data= + { + Name:'MACD', Description:'平滑异同平均', IsMainIndex:false, + Args:[ { Name:'SHORT', Value:12}, { Name:'LONG', Value:26}, { Name:'MID', Value:9} ], + Script: //脚本 +'DIF2:=EMA(CLOSE,SHORT)-EMA(CLOSE,LONG);\n\ +DEA2:=EMA(DIF2,MID);\n\ +MACD:(DIF2-DEA2)*2,COLORSTICK,LINETHICK50;\n\ +DIF:DIF2;\n\ +DEA:DEA2;' + + }; + + return data; +} + +JSIndexScript.prototype.KDJ=function() +{ + let data= + { + Name:'KDJ', Description:'随机指标', IsMainIndex:false, + Args:[ { Name:'N', Value:9}, { Name:'M1', Value:3}, { Name:'M2', Value:3} ], + Script: //脚本 +'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:SMA(RSV,M1,1);\n\ +D:SMA(K,M2,1);\n\ +J:3*K-2*D;' + + }; + + return data; +} + +JSIndexScript.prototype.VOL=function() +{ + let data= + { + Name:'VOL', Description:'成交量', IsMainIndex:false,FloatPrecision:0, + Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10} ], + OutName:[ {Name:'MA1',DynamicName:"MA{M1}" }, {Name:'MA2',DynamicName:"MA{M2}" }], + Script: //脚本 +'VOL:VOL,VOLSTICK;\n\ +MA1:MA(VOL,M1);\n\ +MA2:MA(VOL,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.VOL_TDX=function() +{ + let data= + { + Name:'VOL-TDX', Description:'成交量(虚拟)', IsMainIndex:false,FloatPrecision:0, + Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10} ], + Script: //脚本 +'TOTAL:=IF(PERIOD=1,5,IF(PERIOD=2,15,IF(PERIOD=3,30,IF(PERIOD=4,60,IF(PERIOD=5,TOTALFZNUM,1)))));\n\ +MTIME:=MOD(FROMOPEN,TOTAL);\n\ +CTIME:=IF(MTIME<0.5,TOTAL,MTIME);\n\ +VVOL:IF((CURRBARSCOUNT=1 AND DYNAINFO(8)>1),VOL*(TOTAL+3)/(CTIME+3),DRAWNULL),NODRAW;\n\ +STICKLINE((CURRBARSCOUNT=1 AND DYNAINFO(8)>1),VVOL,0,-1,-1),COLORYELLOW;\n\ +VOLUME:VOL,VOLSTICK;\n\ +MAVOL1:MA(VOLUME,M1);\n\ +MAVOL2:MA(VOLUME,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.RSI=function() +{ + let data= + { + Name:'RSI', Description:'相对强弱指标', IsMainIndex:false, + Args:[ { Name:'N1', Value:6}, { Name:'N2', Value:12}, { Name:'N3', Value:24} ], + Script: //脚本 +'LC:=REF(CLOSE,1);\n\ +RSI1:SMA(MAX(CLOSE-LC,0),N1,1)/SMA(ABS(CLOSE-LC),N1,1)*100;\n\ +RSI2:SMA(MAX(CLOSE-LC,0),N2,1)/SMA(ABS(CLOSE-LC),N2,1)*100;\n\ +RSI3:SMA(MAX(CLOSE-LC,0),N3,1)/SMA(ABS(CLOSE-LC),N3,1)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.BRAR=function() +{ + let data= + { + Name:'BRAR', Description:'情绪指标', IsMainIndex:false, + Args:[ { Name:'N', Value:26} ], + Script: //脚本 +'BR:SUM(MAX(0,HIGH-REF(CLOSE,1)),N)/SUM(MAX(0,REF(CLOSE,1)-LOW),N)*100;\n\ +AR:SUM(HIGH-OPEN,N)/SUM(OPEN-LOW,N)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.WR=function() +{ + let data= + { + Name:'WR', Description:'威廉指标', IsMainIndex:false, + Args:[ { Name:'N', Value:10}, { Name:'N1', Value:6} ], + Script: //脚本 +'WR1:100*(HHV(HIGH,N)-CLOSE)/(HHV(HIGH,N)-LLV(LOW,N));\n\ +WR2:100*(HHV(HIGH,N1)-CLOSE)/(HHV(HIGH,N1)-LLV(LOW,N1));' + + }; + + return data; +} + +JSIndexScript.prototype.BIAS=function() +{ + let data= + { + Name:'BIAS', Description:'乖离率', IsMainIndex:false, + Args:[ { Name:'N1', Value:6}, { Name:'N2', Value:12}, { Name:'N3', Value:24} ], + Script: //脚本 +'BIAS1 :(CLOSE-MA(CLOSE,N1))/MA(CLOSE,N1)*100;\n\ +BIAS2 :(CLOSE-MA(CLOSE,N2))/MA(CLOSE,N2)*100;\n\ +BIAS3 :(CLOSE-MA(CLOSE,N3))/MA(CLOSE,N3)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.OBV=function() +{ + let data= + { + Name:'OBV', Description:'累积能量线', IsMainIndex:false, + Args:[ { Name:'M', Value:30} ], + Script: //脚本 +'VA:=IF(CLOSE>REF(CLOSE,1),VOL,-VOL);\n\ +OBV:SUM(IF(CLOSE==REF(CLOSE,1),0,VA),0);\n\ +MAOBV:MA(OBV,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DMI=function() +{ + let data= + { + Name:'DMI', Description:'趋向指标', IsMainIndex:false, + Args:[ { Name:'N', Value:14}, { Name:'MM', Value:6} ], + Script: //脚本 +'MTR:=EXPMEMA(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(REF(CLOSE,1)-LOW)),N);\n\ +HD :=HIGH-REF(HIGH,1);\n\ +LD :=REF(LOW,1)-LOW;\n\ +DMP:=EXPMEMA(IF(HD>0&&HD>LD,HD,0),N);\n\ +DMM:=EXPMEMA(IF(LD>0&&LD>HD,LD,0),N);\n\ +PDI: DMP*100/MTR;\n\ +MDI: DMM*100/MTR;\n\ +ADX: EXPMEMA(ABS(MDI-PDI)/(MDI+PDI)*100,MM);\n\ +ADXR:EXPMEMA(ADX,MM);' + + }; + + return data; +} + +JSIndexScript.prototype.CR=function() +{ + let data= + { + Name:'CR', Description:'带状能量线', IsMainIndex:false, + Args:[ { Name:'N', Value:26}, { Name:'M1', Value:10},{ Name:'M2', Value:20},{ Name:'M3', Value:40},{ Name:'M4', Value:62} ], + Script: //脚本 +'MID:=REF(HIGH+LOW,1)/2;\n\ +CR:SUM(MAX(0,HIGH-MID),N)/SUM(MAX(0,MID-LOW),N)*100;\n\ +MA1:REF(MA(CR,M1),M1/2.5+1);\n\ +MA2:REF(MA(CR,M2),M2/2.5+1);\n\ +MA3:REF(MA(CR,M3),M3/2.5+1);\n\ +MA4:REF(MA(CR,M4),M4/2.5+1);' + + }; + + return data; +} + +JSIndexScript.prototype.PSY=function() +{ + let data= + { + Name:'PSY', Description:'心理线', IsMainIndex:false, + Args:[ { Name:'N', Value:12}, { Name:'M', Value:6} ], + Script: //脚本 +'PSY:COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\r\ +PSYMA:MA(PSY,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CCI=function() +{ + let data= + { + Name:'CCI', Description:'商品路径指标', IsMainIndex:false, + Args:[ { Name:'N', Value:14} ], + Script: //脚本 +'TYP:=(HIGH+LOW+CLOSE)/3;\n\ +CCI:(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));' + + }; + + return data; +} + +JSIndexScript.prototype.DMA=function() +{ + let data= + { + Name:'DMA', Description:'平均差', IsMainIndex:false, + Args:[ { Name:'N1', Value:10},{ Name:'N2', Value:50},{ Name:'M', Value:10} ], + Script: //脚本 +'DIF:MA(CLOSE,N1)-MA(CLOSE,N2);\n\ +DIFMA:MA(DIF,M);' + + }; + + return data; +} + +JSIndexScript.prototype.TRIX=function() +{ + let data= + { + Name:'TRIX', Description:'三重指数平均线', IsMainIndex:false, + Args:[ { Name:'N', Value:12},{ Name:'M', Value:9} ], + Script: //脚本 +'MTR:=EMA(EMA(EMA(CLOSE,N),N),N);\n\ +TRIX:(MTR-REF(MTR,1))/REF(MTR,1)*100;\n\ +MATRIX:MA(TRIX,M) ;' + + }; + + return data; +} + +JSIndexScript.prototype.VR=function() +{ + let data= + { + Name:'VR', Description:'成交量变异率', IsMainIndex:false, + Args:[ { Name:'N', Value:26},{ Name:'M', Value:6} ], + Script: //脚本 +'TH:=SUM(IF(CLOSE>REF(CLOSE,1),VOL,0),N);\n\ +TL:=SUM(IF(CLOSEREF(CLOSE,1),MIDA,MIDB),0);\n\ +MAWAD:MA(WAD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CHO=function() +{ + let data= + { + Name:'CHO', Description:'佳庆指标', IsMainIndex:false, + Args:[ { Name:'N1', Value:10}, { Name:'N2', Value:20}, { Name:'M', Value:6}], + Script: //脚本 +'MID:=SUM(VOL*(2*CLOSE-HIGH-LOW)/(HIGH+LOW),0);\n\ +CHO:MA(MID,N1)-MA(MID,N2);\n\ +MACHO:MA(CHO,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ADTM=function() +{ + let data= + { + Name:'ADTM', Description:'动态买卖气指标', IsMainIndex:false, + Args:[ { Name:'N', Value:23}, { Name:'M', Value:8}], + Script: //脚本 +'DTM:=IF(OPEN<=REF(OPEN,1),0,MAX((HIGH-OPEN),(OPEN-REF(OPEN,1))));\n\ +DBM:=IF(OPEN>=REF(OPEN,1),0,MAX((OPEN-LOW),(OPEN-REF(OPEN,1))));\n\ +STM:=SUM(DTM,N);\n\ +SBM:=SUM(DBM,N);\n\ +ADTM:IF(STM>SBM,(STM-SBM)/STM,IF(STM==SBM,0,(STM-SBM)/SBM));\n\ +MAADTM:MA(ADTM,M);' + + }; + + return data; +} + +JSIndexScript.prototype.HSL=function() +{ + let data= + { + Name:'HSL', Description:'换手线', IsMainIndex:false, + Args:[ { Name:'N', Value:5} ], + Script: //脚本 +'HSL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100);\n\ +MAHSL:MA(HSL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.BIAS36=function() +{ + let data= + { + Name:'BIAS36', Description:'三六乖离', IsMainIndex:false, + Args:[ { Name:'M', Value:6} ], + Script: //脚本 +'BIAS36:MA(CLOSE,3)-MA(CLOSE,6);\n\ +BIAS612:MA(CLOSE,6)-MA(CLOSE,12);\n\ +MABIAS:MA(BIAS36,M);' + + }; + + return data; +} + +JSIndexScript.prototype.BIAS_QL=function() +{ + let data= + { + Name:'BIAS_QL', Description:'乖离率-传统版', IsMainIndex:false, + Args:[ { Name:'N', Value:6}, { Name:'M', Value:6} ], + Script: //脚本 +'BIAS :(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\ +BIASMA :MA(BIAS,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DPO=function() +{ + let data= + { + Name:'DPO', Description:'区间震荡线', IsMainIndex:false, + Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ], + Script: //脚本 +'DPO:CLOSE-REF(MA(CLOSE,N),N/2+1);\n\ +MADPO:MA(DPO,M);' + + }; + + return data; +} + +JSIndexScript.prototype.OSC=function() +{ + let data= + { + Name:'OSC', Description:'变动速率线', IsMainIndex:false, + Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ], + Script: //脚本 +'OSC:100*(CLOSE-MA(CLOSE,N));\n\ +MAOSC:EXPMEMA(OSC,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ATR=function() +{ + let data= + { + Name:'ATR', Description:'真实波幅', IsMainIndex:false, + Args:[ { Name:'N', Value:14}], + Script: //脚本 +'MTR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));\n\ +ATR:MA(MTR,N);' + + }; + + return data; +} + +JSIndexScript.prototype.NVI=function() +{ + let data= + { + Name:'ATR', Description:'负成交量', IsMainIndex:false, + Args:[ { Name:'N', Value:72} ], + Script: //脚本 +'NVI:100*MULAR(IF(VREF(V,1),C/REF(C,1),1),0);\n\ +MANVI:MA(NVI,N);' + + }; + + return data; +} + +JSIndexScript.prototype.UOS=function() +{ + let data= + { + Name:'UOS', Description:'终极指标', IsMainIndex:false, + Args:[ { Name:'N1', Value:7} ,{ Name:'N2', Value:14},{ Name:'N3', Value:28},{ Name:'M', Value:6}], + Script: //脚本 +'TH:=MAX(HIGH,REF(CLOSE,1));\n\ +TL:=MIN(LOW,REF(CLOSE,1));\n\ +ACC1:=SUM(CLOSE-TL,N1)/SUM(TH-TL,N1);\n\ +ACC2:=SUM(CLOSE-TL,N2)/SUM(TH-TL,N2);\n\ +ACC3:=SUM(CLOSE-TL,N3)/SUM(TH-TL,N3);\n\ +UOS:(ACC1*N2*N3+ACC2*N1*N3+ACC3*N1*N2)*100/(N1*N2+N1*N3+N2*N3);\n\ +MAUOS:EXPMEMA(UOS,M);' + + }; + + return data; +} + +JSIndexScript.prototype.CYW=function() +{ + let data= + { + Name:'CYW', Description:'主力控盘', IsMainIndex:false, + Args:[ ], + Script: //脚本 +'VAR1:=CLOSE-LOW;\n\ +VAR2:=HIGH-LOW;\n\ +VAR3:=CLOSE-HIGH;\n\ +VAR4:=IF(HIGH>LOW,(VAR1/VAR2+VAR3/VAR2)*VOL,0);\n\ +CYW: SUM(VAR4,10)/10000, COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.LON=function() +{ + let data= + { + Name:'LON', Description:'龙系长线', IsMainIndex:false, + Args:[ { Name:'N', Value:10} ], + Script: //脚本 +'LC := REF(CLOSE,1);\n\ +VID := SUM(VOL,2)/(((HHV(HIGH,2)-LLV(LOW,2)))*100);\n\ +RC := (CLOSE-LC)*VID;\n\ +LONG := SUM(RC,0);\n\ +DIFF := SMA(LONG,10,1);\n\ +DEA := SMA(LONG,20,1);\n\ +LON : DIFF-DEA;\n\ +LONMA : MA(LON,10);\n\ +LONT : LON, COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.NDB = function () +{ + let data = + { + Name: 'NDB', Description: '脑电波', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 5 }, { Name: 'P2', Value: 10 }], + Script: //脚本 +'HH:=IF(C/REF(C,1)>1.098 AND L>REF(H,1),2*C-REF(C,1)-H,2*C-H-L);\n\ +V1:= BARSCOUNT(C) - 1;\n\ +V2:= 2 * REF(C, V1) - REF(H, V1) - REF(L, V1);\n\ +DK: SUM(HH, 0) + V2;\n\ +MDK5: MA(DK, P1);\n\ +MDK10: MA(DK, P2);' + + }; + + return data; +} + +JSIndexScript.prototype.SKDJ = function () +{ + let data = + { + Name: 'SKDJ', Description: '慢速随机指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M', Value: 3 }], + Script: //脚本 +'LOWV:=LLV(LOW,N);\n\ +HIGHV:=HHV(HIGH,N);\n\ +RSV:=EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M);\n\ +K:EMA(RSV,M);\n\ +D:MA(K,M);' + + }; + + return data; +} + +JSIndexScript.prototype.KD = function () +{ + let data = + { + Name: 'KD', Description: '随机指标KD', IsMainIndex: false, + Args: [{ Name: 'N', Value: 9 }, { Name: 'M1', Value: 3 },{ Name: 'M2', Value: 3 }], + Script: //脚本 +'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:SMA(RSV,M1,1);\n\ +D:SMA(K,M2,1);' + + }; + + return data; +} + +JSIndexScript.prototype.FKX=function() +{ + let data= + { + Name:'FKX', Description:'反K线', IsMainIndex:true, + Args:[ ], + Script: //脚本 + 'DRAWKLINE(-LOW, -OPEN, -HIGH, -CLOSE);' + }; + + return data; +} + +JSIndexScript.prototype.DKCOL = function () +{ + let data = + { + Name: 'DKCOL', Description: '多空能量柱(适用于分时主图)', IsMainIndex: true, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +'FF:=(C-REF(C,N))/REF(C,N);\n\ +STICKLINE(FF>0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORRED;\n\ +STICKLINE(FF<0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORGREEN;' + + }; + + return data; +} + +JSIndexScript.prototype.UDL = function () +{ + let data = + { + Name: 'UDL', Description: '引力线', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 3 },{ Name: 'N2', Value: 5 },{ Name: 'N3', Value: 10 },{ Name: 'N4', Value: 20 },{ Name: 'M', Value: 6 }], + Script: //脚本 +'UDL:(MA(CLOSE,N1)+MA(CLOSE,N2)+MA(CLOSE,N3)+MA(CLOSE,N4))/4;\n\ +MAUDL:MA(UDL,M);' + + }; + + return data; +} + +JSIndexScript.prototype.MFI = function () +{ + let data = + { + Name: 'MFI', Description: '资金流量指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 14 },{ Name: 'N2', Value: 6 }], + Script: //脚本 +'TYP := (HIGH + LOW + CLOSE)/3;\n\ +V1:=SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP=0,DIF,0);\n\ +VD:=IF(DIF<0,-DIF,0);\n\ +MAU1:=MEMA(VU,M1);\n\ +MAD1:=MEMA(VD,M1);\n\ +MAU2:=MEMA(VU,M2);\n\ +MAD2:=MEMA(VD,M2);\n\ +RSI10:MA(100*MAU1/(MAU1+MAD1),M1);\n\ +RSI6:MA(100*MAU2/(MAU2+MAD2),M2);' + + }; + + return data; +} + +JSIndexScript.prototype.CYD = function () +{ + let data = + { + Name: 'CYD', Description: '承接因子', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 }], + Script: //脚本 +'CYDS:WINNER(CLOSE)/(VOL/CAPITAL);\n\ +CYDN:WINNER(CLOSE)/MA(VOL/CAPITAL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.CYF = function () +{ + let data = + { + Name: 'CYF', Description: '市场能量', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 }], + Script: //脚本 +'CYF:100-100/(1+EMA(HSL,N));' + + }; + + return data; +} + +JSIndexScript.prototype.TAPI = function () +{ + let data = + { + Name: 'TAPI', Description: '加权指数成交值', IsMainIndex: false, + Args: [{ Name: 'M', Value: 6 }], + Script: //脚本 +'TAPI:AMOUNT/INDEXC;\n\ +MATAIP:MA(TAPI,M);' + + }; + + return data; +} + +JSIndexScript.prototype.VMACD = function () +{ + let data = + { + Name: 'VMACD', Description: '量平滑异同平均', IsMainIndex: false, + Args: [{ Name: 'SHORT', Value: 12 },{ Name: 'LONG', Value: 26 },{ Name: 'MID', Value: 9 }], + Script: //脚本 +'DIF:EMA(VOL,SHORT)-EMA(VOL,LONG);\n\ +DEA:EMA(DIF,MID);\n\ +MACD:DIF-DEA,COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.QACD = function () +{ + let data = + { + Name: 'QACD', Description: '快速异同平均', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 12 },{ Name: 'N2', Value: 26 },{ Name: 'M', Value: 9 }], + Script: //脚本 +'DIF:EMA(CLOSE,N1)-EMA(CLOSE,N2);\n\ +MACD:EMA(DIF,M);\n\ +DDIF:DIF-MACD;' + + }; + + return data; +} + +JSIndexScript.prototype.VPT = function () +{ + let data = + { + Name: 'VPT', Description: '量价曲线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 51 },{ Name: 'M', Value: 6 }], + Script: //脚本 +'VPT:SUM(VOL*(CLOSE-REF(CLOSE,1))/REF(CLOSE,1),N);\n\ +MAVPT:MA(VPT,M);' + + }; + + return data; +} + +JSIndexScript.prototype.WVAD = function () +{ + let data = + { + Name: 'WVAD', Description: '威廉变异离散量', IsMainIndex: false, + Args: [{ Name: 'N', Value: 24 },{ Name: 'M', Value: 6 }], + Script: //脚本 +'WVAD:SUM((CLOSE-OPEN)/(HIGH-LOW)*VOL,N)/10000;\n\ +MAWVAD:MA(WVAD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.DBQR = function () +{ + let data = + { + Name: 'WVAD', Description: '对比强弱', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 },{ Name: 'M1', Value: 10 },{ Name: 'M2', Value: 20 },{ Name: 'M3', Value: 60 }], + Script: //脚本 +'ZS:(INDEXC-REF(INDEXC,N))/REF(INDEXC,N);\n\ +GG:(CLOSE-REF(CLOSE,N))/REF(CLOSE,N);\n\ +MADBQR1:MA(GG,M1);\n\ +MADBQR2:MA(GG,M2);\n\ +MADBQR3:MA(GG,M3);' + + }; + + return data; +} + +JSIndexScript.prototype.JS = function () +{ + let data = + { + Name: 'JS', Description: '加速线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 },{ Name: 'M1', Value: 5 },{ Name: 'M2', Value: 10 },{ Name: 'M3', Value: 20 }], + Script: //脚本 +'JS:100*(CLOSE-REF(CLOSE,N))/(N*REF(CLOSE,N));\n\ +MAJS1:MA(JS,M1);\n\ +MAJS2:MA(JS,M2);\n\ +MAJS3:MA(JS,M3);' + + }; + + return data; +} + +JSIndexScript.prototype.CYE = function () +{ + let data = + { + Name: 'CYE', Description: '市场趋势', IsMainIndex: false, + Args: [ ], + Script: //脚本 +'MAL:=MA(CLOSE,5);\n\ +MAS:=MA(MA(CLOSE,20),5);\n\ +CYEL:(MAL-REF(MAL,1))/REF(MAL,1)*100;\n\ +CYES:(MAS-REF(MAS,1))/REF(MAS,1)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.QR = function () +{ + let data = + { + Name: 'QR', Description: '强弱指标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 21 } ], + Script: //脚本 +'个股: (CLOSE-REF(CLOSE,N))/REF(CLOSE,N)*100; \n\ +大盘: (INDEXC-REF(INDEXC,N))/REF(INDEXC,N)*100; \n\ +强弱值:EMA(个股-大盘,2),COLORSTICK;' + + }; + + return data; +} + +JSIndexScript.prototype.GDX = function () +{ + let data = + { + Name: 'GDX', Description: '轨道线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 30 } ,{ Name: 'M', Value: 9 }], + Script: //脚本 +'AA:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,N))/MA(CLOSE,N); \n\ +轨道:DMA(CLOSE,AA);\n\ +压力线:(1+M/100)*轨道; \n\ +支撑线:(1-M/100)*轨道;' + + }; + + return data; +} + +JSIndexScript.prototype.JLHB = function () +{ + let data = + { + Name: 'JLHB', Description: '绝路航标', IsMainIndex: false, + Args: [{ Name: 'N', Value: 7 } ,{ Name: 'M', Value: 5 }], + Script: //脚本 +'VAR1:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*80; \n\ +B:SMA(VAR1,N,1); \n\ +VAR2:SMA(B,M,1); \n\ +绝路航标:IF(CROSS(B,VAR2) AND B<40,50,0);' + + }; + + return data; +} + +JSIndexScript.prototype.PCNT = function () +{ + let data = + { + Name: 'PCNT', Description: '幅度比', IsMainIndex: false, + Args: [{ Name: 'M', Value: 5 }], + Script: //脚本 +'PCNT:(CLOSE-REF(CLOSE,1))/CLOSE*100;\n\ +MAPCNT:EXPMEMA(PCNT,M);' + + }; + + return data; +} + +JSIndexScript.prototype.BTX = function () +{ + let data = + { + Name: 'BTX', Description: '宝塔线', IsMainIndex: false, + Args: [], + Script: //脚本 + 'B1:=REF(C,1);\n\ +B2:= REF(C, 2);\n\ +SS:= IF(C > REF(C, 1) AND REF(C, 1) >= REF(C, 2), 1, IF(C < REF(C, 1) AND REF(C, 1) <= REF(C, 2), -1, IF(C > REF(C, 2) AND REF(C, 2) > REF(C, 1), 2, IF(C < REF(C, 2) AND REF(C, 2) < REF(C, 1), -2, 0))));\n\ +SM:= IF(REF(SS, 1) <> 0, REF(SS, 1), IF(REF(SS, 2) <> 0, REF(SS, 2), IF(REF(SS, 3) <> 0, REF(SS, 3), IF(REF(SS, 5) <> 0, REF(SS, 5), IF(REF(SS, 6) <> 0, REF(SS, 6), IF(REF(SS, 7) <> 0, REF(SS, 7), 0))))));\n\ +MC:= IF(REF(SS, 1) <> 0, B2, IF(SM > 0, MIN(B1, B2), MAX(B1, B2)));\n\ +TOW1:= IF(C > REF(C, 1), C, REF(C, 1));\n\ +TOW2:= IF((SS == -1 OR SS == -2) AND SM > 0, B2, TOW1);\n\ +TOWER:= IF(TOW1 > TOW2, TOW1, TOW2);\n\ +STICKLINE(SS == 1 OR SM >= 1 AND SS == 0, B1, C, 10, 1), COLORRED;\n\ +STICKLINE(SS == -1 OR SM <= -1 AND SS == 0, B1, C, 10, 0), COLORCYAN;\n\ +STICKLINE(SS == 2, B2, C, 10, 1), COLORRED;\n\ +STICKLINE(SS == -2, B2, C, 10, 0), COLORCYAN;\n\ +STICKLINE((SS == -1 OR SS == -2) AND SM > 0, B2, B1, 10, 1), COLORRED;\n\ +STICKLINE((SS == 1 OR SS == 2) AND SM < 0, B2, B1, 10, 0), COLORCYAN;' + }; + + return data; +} + +JSIndexScript.prototype.AMO = function () +{ + let data = + { + Name: 'AMO', Description: '成交金额', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 5 },{ Name: 'M2', Value: 10 }], + Script: //脚本 +'AMOW:AMOUNT/10000.0,VOLSTICK;\n\ +AMO1:MA(AMOW,M1);\n\ +AMO2:MA(AMOW,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.VRSI = function () +{ + let data = + { + Name: 'VRSI', Description: '相对强弱量', IsMainIndex: false, + Args: [{ Name: 'N1', Value: 6 },{ Name: 'N2', Value: 12 },{ Name: 'N3', Value: 24 }], + Script: //脚本 +'LC:=REF(VOL,1);\n\ +RSI1:SMA(MAX(VOL-LC,0),N1,1)/SMA(ABS(VOL-LC),N1,1)*100;\n\ +RSI2:SMA(MAX(VOL-LC,0),N2,1)/SMA(ABS(VOL-LC),N2,1)*100;\n\ +RSI3:SMA(MAX(VOL-LC,0),N3,1)/SMA(ABS(VOL-LC),N3,1)*100;' + + }; + + return data; +} + +JSIndexScript.prototype.HSCOL = function () +{ + let data = + { + Name: 'HSCOL', Description: '换手柱', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +'HSCOL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100),VOLSTICK;\n\ +MAHSL:MA(HSCOL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.DBQRV = function () +{ + let data = + { + Name: 'DBQRV', Description: '对比强弱量(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +'ZS:(INDEXV-REF(INDEXV,N))/REF(INDEXV,N);\n\ +GG:(VOL-REF(VOL,N))/REF(VOL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.DBLB = function () +{ + let data = + { + Name: 'DBLB', Description: '对比量比(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 },{ Name: 'M', Value: 5 }], + Script: //脚本 +'GG:=VOL/SUM(REF(VOL,1),N);\n\ +ZS:=INDEXV/SUM(REF(INDEXV,1),N);\n\ +DBLB:GG/ZS;\n\ +MADBLB:MA(DBLB,M);' + + }; + + return data; +} + +JSIndexScript.prototype.ACD = function () +{ + let data = + { + Name: 'ACD', Description: '升降线', IsMainIndex: false, + Args: [{ Name: 'M', Value: 20 }], + Script: //脚本 +'LC:=REF(CLOSE,1);\n\ +DIF:=CLOSE-IF(CLOSE>LC,MIN(LOW,LC),MAX(HIGH,LC));\n\ +ACD:SUM(IF(CLOSE==LC,0,DIF),0);\n\ +MAACD:EXPMEMA(ACD,M);' + + }; + + return data; +} + +JSIndexScript.prototype.EXPMA = function () +{ + let data = + { + Name: 'EXPMA', Description: '指数平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 12 },{ Name: 'M2', Value: 50 }], + Script: //脚本 +'EXP1:EMA(CLOSE,M1);\n\ +EXP2:EMA(CLOSE,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.EXPMA_S = function () +{ + let data = + { + Name: 'EXPMA_S', Description: '指数平均线-副图', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 12 },{ Name: 'M2', Value: 50 }], + Script: //脚本 +'EXP1:EMA(CLOSE,M1);\n\ +EXP2:EMA(CLOSE,M2);' + + }; + + return data; +} + +JSIndexScript.prototype.HMA = function () +{ + let data = + { + Name: 'HMA', Description: '高价平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }], + Script: //脚本 +'HMA1:MA(HIGH,M1);\n\ +HMA2:MA(HIGH,M2);\n\ +HMA3:MA(HIGH,M3);\n\ +HMA4:MA(HIGH,M4);\n\ +HMA5:MA(HIGH,M5);' + + }; + + return data; +} + +JSIndexScript.prototype.LMA = function () +{ + let data = + { + Name: 'LMA', Description: '低价平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }], + Script: //脚本 +'LMA1:MA(LOW,M1);\n\ +LMA2:MA(LOW,M2);\n\ +LMA3:MA(LOW,M3);\n\ +LMA4:MA(LOW,M4);\n\ +LMA5:MA(LOW,M5);' + + }; + + return data; +} + +JSIndexScript.prototype.VMA = function () +{ + let data = + { + Name: 'VMA', Description: '变异平均线', IsMainIndex: true, + Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }], + Script: //脚本 +'VV:=(HIGH+OPEN+LOW+CLOSE)/4;\n\ +VMA1:MA(VV,M1);\n\ +VMA2:MA(VV,M2);\n\ +VMA3:MA(VV,M3);\n\ +VMA4:MA(VV,M4);\n\ +VMA5:MA(VV,M5);' + + }; + + return data; +} + + +JSIndexScript.prototype.AMV = function () +{ + let data = + { + Name: 'AMV', Description: '成本价均线', IsMainIndex: false, + Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }], + Script: //脚本 +'AMOV:=VOL*(OPEN+CLOSE)/2;\n\ +AMV1:SUM(AMOV,M1)/SUM(VOL,M1);\n\ +AMV2:SUM(AMOV,M2)/SUM(VOL,M2);\n\ +AMV3:SUM(AMOV,M3)/SUM(VOL,M3);\n\ +AMV4:SUM(AMOV,M4)/SUM(VOL,M4);' + + }; + + return data; +} + +JSIndexScript.prototype.BBIBOLL = function () +{ + let data = + { + Name: 'BBIBOLL', Description: '多空布林线', IsMainIndex: true, + Args: [{ Name: 'N', Value: 11 },{ Name: 'M', Value: 6 }], + Script: //脚本 +'CV:=CLOSE;\n\ +BBIBOLL:(MA(CV,3)+MA(CV,6)+MA(CV,12)+MA(CV,24))/4;\n\ +UPR:BBIBOLL+M*STD(BBIBOLL,N);\n\ +DWN:BBIBOLL-M*STD(BBIBOLL,N);' + + }; + + return data; +} + +JSIndexScript.prototype.ALLIGAT = function () +{ + let data = + { + Name: 'ALLIGAT', Description: '鳄鱼线', IsMainIndex: true, + Args: [], + Script: //脚本 +'NN:=(H+L)/2;\n\ +上唇:REF(MA(NN,5),3),COLOR40FF40;\n\ +牙齿:REF(MA(NN,8),5),COLOR0000C0;\n\ +下颚:REF(MA(NN,13),8),COLORFF4040;' + + }; + + return data; +} + +JSIndexScript.prototype.ZX = function () +{ + let data = + { + Name: 'ZX', Description: '重心线', IsMainIndex: false, + Args: [], + Script: //脚本 +'AV:0.01*AMOUNT/VOL;' + + }; + + return data; +} + +JSIndexScript.prototype.XS = function () +{ + let data = + { + Name: 'XS', Description: '薛斯通道', IsMainIndex: true, + Args: [{ Name: 'N', Value: 13 }], + Script: //脚本 +'VAR2:=CLOSE*VOL;\n\ +VAR3:=EMA((EMA(VAR2,3)/EMA(VOL,3)+EMA(VAR2,6)/EMA(VOL,6)+EMA(VAR2,12)/EMA(VOL,12)+EMA(VAR2,24)/EMA(VOL,24))/4,N);\n\ +SUP:1.06*VAR3;\n\ +SDN:VAR3*0.94;\n\ +VAR4:=EMA(CLOSE,9);\n\ +LUP:EMA(VAR4*1.14,5);\n\ +LDN:EMA(VAR4*0.86,5);' + + }; + + return data; +} + +JSIndexScript.prototype.XS2 = function () +{ + let data = + { + Name: 'XS2', Description: '薛斯通道II', IsMainIndex: true, + Args: [{ Name: 'N', Value: 102 },{ Name: 'M', Value: 7 }], + Script: //脚本 +'AA:=MA((2*CLOSE+HIGH+LOW)/4,5); \n\ +通道1:AA*N/100; \n\ +通道2:AA*(200-N)/100; \n\ +CC:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,20))/MA(CLOSE,20); \n\ +DD:=DMA(CLOSE,CC); \n\ +通道3:(1+M/100)*DD; \n\ +通道4:(1-M/100)*DD;' + + }; + + return data; +} + +JSIndexScript.prototype.SG_XDT = function () +{ + let data = + { + Name: 'SG-XDT', Description: '心电图(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 5 },{ Name: 'P2', Value: 10 }], + Script: //脚本 +'QR:CLOSE/INDEXC*1000;\n\ +MQR1:MA(QR,5);\n\ +MQR2:MA(QR,10);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_SMX = function () +{ + let data = + { + Name: 'SG-SMX', Description: '生命线(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'N', Value: 50 }], + Script: //脚本 +'H1:=HHV(HIGH,N);\n\ +L1:=LLV(LOW,N);\n\ +H2:=HHV(INDEXH,N);\n\ +L2:=LLV(INDEXL,N);\n\ +ZY:=CLOSE/INDEXC*2000;\n\ +ZY1:EMA(ZY,3);\n\ +ZY2:EMA(ZY,17);\n\ +ZY3:EMA(ZY,34);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_LB = function () +{ + let data = + { + Name: 'SG-LB', Description: '量比(需下载日线)', IsMainIndex: false, + Args: [], + Script: //脚本 +'ZY2:=VOL/INDEXV*1000;\n\ +量比:ZY2;\n\ +MA5:MA(ZY2,5);\n\ +MA10:MA(ZY2,10);' + + }; + + return data; +} + +JSIndexScript.prototype.SG_PF = function () +{ + let data = + { + Name: 'SG-PF', Description: '强势股评分(需下载日线)', IsMainIndex: false, + Args: [], + Script: //脚本 +'ZY1:=CLOSE/INDEXC*1000;\n\ +A1:=IF(ZY1>HHV(ZY1,3),10,0);\n\ +A2:=IF(ZY1>HHV(ZY1,5),15,0);\n\ +A3:=IF(ZY1>HHV(ZY1,10),20,0);\n\ +A4:=IF(ZY1>HHV(ZY1,2),10,0);\n\ +A5:=COUNT(ZY1>REF(ZY1,1) ,9)*5;\n\ +强势股评分:A1+A2+A3+A4+A5;' + + }; + + return data; +} + +JSIndexScript.prototype.RAD = function () +{ + let data = + { + Name: 'RAD', Description: '威力雷达(需下载日线)', IsMainIndex: false, + Args: [{ Name: 'D', Value: 3 },{ Name: 'S', Value: 30 },{ Name: 'M', Value: 30 }], + Script: //脚本 +'SM:=(OPEN+HIGH+CLOSE+LOW)/4;\n\ +SMID:=MA(SM,D);\n\ +IM:=(INDEXO+INDEXH+INDEXL+INDEXC)/4;\n\ +IMID:=MA(IM,D);\n\ +SI1:=(SMID-REF(SMID,1))/SMID;\n\ +II:=(IMID-REF(IMID,1))/IMID;\n\ +RADER1:SUM((SI1-II)*2,S)*1000;\n\ +RADERMA:SMA(RADER1,M,1);' + + }; + + return data; +} + +JSIndexScript.prototype.SHT = function () +{ + let data = + { + Name: 'SHT', Description: '龙系短线', IsMainIndex: false, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +'VAR1:=MA((VOL-REF(VOL,1))/REF(VOL,1),5);\n\ +VAR2:=(CLOSE-MA(CLOSE,24))/MA(CLOSE,24)*100;\n\ +MY: VAR2*(1+VAR1);\n\ +SHT: MY, COLORSTICK;\n\ +SHTMA: MA(SHT,N);' + + }; + + return data; +} + +JSIndexScript.prototype.ZLJC = function () +{ + let data = + { + Name: 'ZLJC', Description: '主力进出', IsMainIndex: false, + Args: [], + Script: //脚本 +'VAR1:=(CLOSE+LOW+HIGH)/3; \n\ +VAR2:=SUM(((VAR1-REF(LOW,1))-(HIGH-VAR1))*VOL/100000/(HIGH-LOW),0); \n\ +VAR3:=EMA(VAR2,1); \n\ +JCS:VAR3; \n\ +JCM:MA(VAR3,12); \n\ +JCL:MA(VAR3,26);' + + }; + + return data; +} + +JSIndexScript.prototype.ZLMM = function () +{ + let data = + { + Name: 'ZLMM', Description: '主力买卖', IsMainIndex: false, + Args: [], + Script: //脚本 +'LC :=REF(CLOSE,1);\n\ +RSI2:=SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100;\n\ +RSI3:=SMA(MAX(CLOSE-LC,0),18,1)/SMA(ABS(CLOSE-LC),18,1)*100;\n\ +MMS:MA(3*RSI2-2*SMA(MAX(CLOSE-LC,0),16,1)/SMA(ABS(CLOSE-LC),16,1)*100,3);\n\ +MMM:EMA(MMS,8);\n\ +MML:MA(3*RSI3-2*SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100,5);' + + }; + + return data; +} + +JSIndexScript.prototype.SLZT = function () +{ + let data = + { + Name: 'SLZT', Description: '神龙在天', IsMainIndex: false, + Args: [], + Script: //脚本 +'白龙: MA(CLOSE,125);\n\ +黄龙: 白龙+2*STD(CLOSE,170);\n\ +紫龙: 白龙-2*STD(CLOSE,145);\n\ +青龙: SAR(125,1,7), LINESTICK;\n\ +VAR2:=HHV(HIGH,70);\n\ +VAR3:=HHV(HIGH,20);\n\ +红龙: VAR2*0.83;\n\ +蓝龙: VAR3*0.91;' + + }; + + return data; +} + +JSIndexScript.prototype.ADVOL = function () +{ + let data = + { + Name: 'ADVOL', Description: '龙系离散量', IsMainIndex: false, + Args: [], + Script: //脚本 +'A:=SUM(((CLOSE-LOW)-(HIGH-CLOSE))*VOL/10000/(HIGH-LOW),0);\n\ +ADVOL:A;\n\ +MA1:MA(A,30);\n\ +MA2:MA(MA1,100);' + + }; + + return data; +} + +JSIndexScript.prototype.CYC = function () +{ + let data = + { + Name: 'CYC', Description: '成本均线', IsMainIndex: true, + Args: [{ Name: 'P1', Value: 5 },{ Name: 'P2', Value: 13 },{ Name: 'P3', Value: 34 }], + Script: //脚本 +'JJJ:=IF(DYNAINFO(8)>0.01,0.01*DYNAINFO(10)/DYNAINFO(8),DYNAINFO(3));\n\ +DDD:=(DYNAINFO(5)<0.01 || DYNAINFO(6)<0.01);\n\ +JJJT:=IF(DDD,1,(JJJ<(DYNAINFO(5)+0.01) && JJJ>(DYNAINFO(6)-0.01)));\n\ +CYC1:IF(JJJT,0.01*EMA(AMOUNT,P1)/EMA(VOL,P1),EMA((HIGH+LOW+CLOSE)/3,P1));\n\ +CYC2:IF(JJJT,0.01*EMA(AMOUNT,P2)/EMA(VOL,P2),EMA((HIGH+LOW+CLOSE)/3,P2));\n\ +CYC3:IF(JJJT,0.01*EMA(AMOUNT,P3)/EMA(VOL,P3),EMA((HIGH+LOW+CLOSE)/3,P3));\n\ +CYC4:IF(JJJT,DMA(AMOUNT/(100*VOL),100*VOL/FINANCE(7)),EMA((HIGH+LOW+CLOSE)/3,120));' + + }; + + return data; +} + +JSIndexScript.prototype.CYS = function () +{ + let data = + { + Name: 'CYS', Description: '市场盈亏', IsMainIndex: false, + Args: [], + Script: //脚本 +'CYC13:EMA(AMOUNT,13)/EMA(VOL,13);\n\ +CYS:(CLOSE-CYC13)/CYC13*100;' + + }; + + return data; +} + +JSIndexScript.prototype.CYQKL = function () +{ + let data = + { + Name: 'CYQKL', Description: '博弈K线长度', IsMainIndex: false, + Args: [], + Script: //脚本 +'KL:100*(WINNER(CLOSE)-WINNER(OPEN));' + + }; + + return data; +} + +JSIndexScript.prototype.SCR = function () +{ + let data = + { + Name: 'SCR', Description: '筹码集中度', IsMainIndex: false, + Args: [{ Name: 'P1', Value: 90 }], + Script: //脚本 +'A:=P1+(100-P1)/2;\n\ +B:=(100-P1)/2;\n\ +CC:=COST(A);\n\ +DD:=COST(B);\n\ +SCR:(CC-DD)/(CC+DD)*100/2;' + + }; + + return data; +} + + +JSIndexScript.prototype.ASR = function () +{ + let data = + { + Name: 'ASR', Description: '浮筹比例', IsMainIndex: false, + Args: [], + Script: //脚本 +'ASR:(WINNER(C*1.1)-WINNER(C*0.9))/WINNER(HHV(H,0))*100;' + + }; + + return data; +} + +JSIndexScript.prototype.SAR = function () +{ + let data = + { + Name: 'SAR', Description: '抛物转向', IsMainIndex: true, + Args: [{ Name: 'P', Value: 10 },{ Name: 'STEP', Value: 2 },{ Name: 'MAXP', Value: 20 }], + Script: //脚本 +'S:SAR(P,STEP,MAXP),CIRCLEDOT;' + + }; + + return data; +} + +JSIndexScript.prototype.TJCJL = function () +{ + let data = + { + Name: '太极成交量', Description: '太极成交量', IsMainIndex: true, + Args: [], + Script: //脚本 +'总手:VOL,NODRAW;\n\ +DRAWTEXT_FIX(ISLASTBAR,0,0,0,"说明: 红色柱为吸货量,绿色为出货量,黄色为天量,蓝色为地量"),COLORGRAY;\n\ +ZZ:=IF(REF(C,1)>REF(O,1) AND O>REF(C,1)*1.014 AND CV5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,0,VOL,3,0),COLORRED;\n\ +STICKLINE(CROSS(C6,C) AND V>V5*1.2 AND V>V12*1.2,0,VOL,3,0),COLORGREEN;\n\ +STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND CMA(VOL,5)*2 AND V>V34*3 AND CV5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,VOL*0.5,0,3,0),COLORRED;\n\ +STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND CV5*1.2 AND V>V12*1.2,VOL*0.5,0,3,0),COLORRED;' + + }; + + return data; +} + +/* + 飞龙四式-主图 +*/ +JSIndexScript.prototype.Dragon4_Main = function () +{ + let data = + { + Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: true, + Args: [{ Name: 'N1', Value: 5 }, { Name: 'N2', Value: 10 }, { Name: 'N3', Value: 50 }, { Name: 'N4', Value: 60 }], + Script: //脚本 +'蜻蜓点水:=EMA(CLOSE,N1),COLORGRAY;\n\ +魔界:=EMA(CLOSE,N2),COLORGREEN;\n\ +水:=EMA(CLOSE,N3),COLORRED;\n\ +DRAWKLINE(HIGH,OPEN,LOW,CLOSE);\n\ +生命线:MA(CLOSE,N4),COLORBLUE,LINETHICK2;\n\ +DRAWBAND(魔界,\'RGB(186,225,250)\',水,\'RGB(253,194,124)\');\n\ +DRAWBAND(蜻蜓点水,\'RGB(128,138,135)\',魔界,\'RGB(0,0,255)\');' + + }; + + return data; +} + +JSIndexScript.prototype.Dragon4_Fig=function() +{ + let data = + { + Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: false, + Args: [], + Script: //脚本 +'倍:VOL>=REF(V,1)*1.90 AND C>REF(C,1),COLORYELLOW;\n\ +低:VOLREF(V,1),3)==3 AND COUNT(C>O,3)==3,COLORBROWN;\n\ +缩量涨:COUNT(C>REF(C,1),2)==2 AND COUNT(V=REF(C,1),V,0,2,0),COLORRED;\n\ +STICKLINE(C=2038 AND MONTH>=1,0,1); +VAR2:=REF(LOW,1)*VAR1; +VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1; +VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1; +VAR5:=LLV(LOW,30)*VAR1; +VAR6:=HHV(VAR4,30)*VAR1; +VAR7:=IF(MA(CLOSE,58),1,0)*VAR1; +VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1; +吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORRED; +吸筹B:STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORRED; + +散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORFFFF00,LINETHICK2; +RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100; +K:=SMA(RSV,3,1); +D:=SMA(K,3,1); +J:=3*K-2*D; +主力线:EMA(J,5),COLORFF00FF,LINETHICK2; +DRAWICON(CROSS(主力线,散户线),主力线,1); +DRAWICON(CROSS(散户线,主力线),主力线,2); +*/ + +JSIndexScript.prototype.FundsAnalysis=function() +{ + let data = + { + Name: '资金分析', Description: '资金分析', IsMainIndex: false, + Args: [{ Name: 'M', Value: 55 }, { Name: 'N', Value: 34 }], + Script: //脚本 +'LC:=REF(CLOSE,1);\n\ +RSI:=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);\n\ +FF:=EMA(CLOSE,3);\n\ +MA15:=EMA(CLOSE,21); DRAWTEXT(CROSS(85,RSI),75,\'▼\'),COLORGREEN;\n\ +VAR1:=IF(YEAR>=2038 AND MONTH>=1,0,1);\n\ +VAR2:=REF(LOW,1)*VAR1;\n\ +VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1;\n\ +VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1;\n\ +VAR5:=LLV(LOW,30)*VAR1;\n\ +VAR6:=HHV(VAR4,30)*VAR1;\n\ +VAR7:=IF(MA(CLOSE,58),1,0)*VAR1;\n\ +VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1;\n\ +吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORFB2F3B;\n\ +{吸筹B}STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORFB2F3B;\n\ +\n\ +散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORAA89BD,LINETHICK2;\n\ +RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:=SMA(RSV,3,1);\n\ +D:=SMA(K,3,1);\n\ +J:=3*K-2*D;\n\ +主力线:EMA(J,5),COLORF39800,LINETHICK2;\n\ +DRAWICON(CROSS(主力线,散户线),主力线,1);\n\ +DRAWICON(CROSS(散户线,主力线),主力线,2);' + }; + + return data; +} + +JSIndexScript.prototype.MarginProportion=function() +{ + let data = + { + Name: '融资占比(%)', Description: '融资占比', IsMainIndex: false, + Condition: { Period:[CONDITION_PERIOD.KLINE_DAY_ID] }, + Args: [], + Script: //脚本 + '占比:MARGIN(2);' + }; + + return data; +} + +JSIndexScript.prototype.Margin2=function() +{ + let data = + { + Name: '两融余额', Description: '融资融券余额', IsMainIndex: false, + Condition: { Period:[CONDITION_PERIOD.KLINE_DAY_ID] }, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'T1:MARGIN(1);\n\ + T2:MA(MARGIN(1),N);' + }; + + return data; +} + +JSIndexScript.prototype.Margin3=function() +{ + let data = + { + Name: '两融余额', Description: '融资融券余额', IsMainIndex: false, + Condition: { Period:[CONDITION_PERIOD.KLINE_DAY_ID] }, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 + 'T1:=MARGIN(1);\n\ + T2:=MA(MARGIN(1),N);\n\ + STICKLINE(T1-T2>=0,0,T1-T2,50,T1>T2),COLORRED;\n\ + STICKLINE(T1-T2<0,T1-T2,0,50,T1>T2),COLORGREEN;' + }; + + return data; +} + + + +JSIndexScript.prototype.FXG_BSPoint=function() +{ + let data = + { + Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, + Args: [], + Script: //脚本 + 'MA5:MA(CLOSE,5);\n\ + MA13:MA(CLOSE,13);\n\ + MA21:MA(CLOSE,21);\n\ + MA34:MA(CLOSE,34);\n\ + {MA55:MA(CLOSE,55),COLOR0000FF;}\n\ + {MA120:=MA(CLOSE,120),COLORFFFF00;}\n\ + 天使:=EMA(C,2),COLOR000000;\n\ + 魔鬼:=EMA(SLOPE(C,21)*20+C,42),COLOR000000;\n\ + 买:=CROSS(天使,魔鬼);\n\ + 卖:=CROSS(魔鬼,天使);\n\ + DRAWICON(买,L*0.97,13);\n\ + DRAWICON(卖,H*1.03,14);\n\ + DRAWKLINE_IF(天使>=魔鬼,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\ + DRAWKLINE_IF(天使<魔鬼,HIGH,CLOSE,LOW,OPEN),COLORBLUE;\n\ + DRAWKLINE_IF(CROSS(天使,魔鬼),HIGH,CLOSE,LOW,OPEN),COLORYELLOW;\n\ + DRAWKLINE_IF(CROSS(魔鬼,天使),HIGH,CLOSE,LOW,OPEN),COLORBLACK;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX=function() +{ + let data = + { + Name: '涨停多空线', Description: '涨停多空线', IsMainIndex: false, + Args: [], + Script: //脚本 +'做多能量线: SMA((CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100,5,1)-8,COLORRED,LINETHICK3;\n\ +做空能量线: SMA((HHV(HIGH,36)-CLOSE)/(HHV(HIGH,36)-LLV(LOW,36))*100,2,1),COLORGREEN,LINETHICK3;\n\ +20,POINTDOT,COLORF00FF0;\n\ +50,POINTDOT,COLORGREEN;\n\ +80,POINTDOT,COLORLIBLUE;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX2=function() +{ + let data = + { + Name: '涨停吸筹区', Description: '涨停吸筹区', IsMainIndex: false, + Args: [], + Script: //脚本 +'VAR0:=EMA(HHV(HIGH,500),21); \n\ +VAR1:=EMA(HHV(HIGH,250),21);\n\ +VAR2:=EMA(HHV(HIGH,90),21); \n\ +VAR3:=EMA(LLV(LOW,500),21); \n\ +VAR4:=EMA(LLV(LOW,250),21); \n\ +VAR5:=EMA(LLV(LOW,90),21);\n\ +\n\ +VAR6:=EMA((VAR3*0.96+VAR4*0.96+VAR5*0.96+VAR0*0.558+VAR1*0.558+VAR2*0.558)/6,21); \n\ +VAR7:=EMA((VAR3*1.25+VAR4*1.23+VAR5*1.2+VAR0*0.55+VAR1*0.55+VAR2*0.65)/6,21); \n\ +VAR8:=EMA((VAR3*1.3+VAR4*1.3+VAR5*1.3+VAR0*0.68+VAR1*0.68+VAR2*0.68)/6,21); \n\ +VAR9:=EMA((VAR6*3+VAR7*2+VAR8)/6*1.738,21); \n\ +VAR10:=REF(LOW,1); \n\ +VAR11:=SMA(ABS(LOW-VAR10),3,1)/SMA(MAX(LOW-VAR10,0),3,1)*100; \n\ +VAR12:=EMA(IFF(CLOSE*1.35<=VAR9,VAR11*10,VAR11/10),3); \n\ +VAR13:=LLV(LOW,30); \n\ +VAR14:=HHV(VAR12,30); \n\ +VAR15:=IFF(MA(CLOSE,58),1,0); \n\ +VAR16:=EMA(IFF(LOW<=VAR13,(VAR12+VAR14*2)/2,0),3)/618*VAR15;\n\ +\n\ +资金入场:IFF(VAR16>0,VAR16,0),LINETHICK,LINETHICK2, COLORFF0000; \n\ +\n\ +A1:IFF(资金入场>0,资金入场*1.2,0),STICK,LINETHICK5, COLORFF0000;\n\ +A2:IFF(资金入场>0,资金入场*0.8,0),STICK,LINETHICK5, COLORFF6600;\n\ +A3:IFF(资金入场>0,资金入场*0.6,0),STICK,LINETHICK5, COLORFF9900;\n\ +A4:IFF(资金入场>0,资金入场*0.4,0) ,STICK,LINETHICK5,COLORFFCC00;\n\ +A5:IFF(资金入场>0,资金入场*0.2,0) ,STICK,LINETHICK5,COLORFFFF00;' + }; + + return data; +} + +JSIndexScript.prototype.FXG_INDEX3=function() +{ + let data = + { + Name: '量能黄金点', Description: '量能黄金点', IsMainIndex: false,FloatPrecision:0, + Args: [], + Script: //脚本 +'A:=IFF((CLOSE>126.32),VOL,VOL); \n\ +主力:=MA(A,4),COLORRED;\n\ +游资:=MA(A,8),COLORYELLOW;\n\ +大户:=MA(A,16),COLORF0F000;\n\ +散户:=MA(A,32),COLOR00FF00;\n\ +主比:=ABS(((主力)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORRED;\n\ +游比:=ABS(((游资)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORYELLOW;\n\ +大比:=ABS(((大户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORF0F000;\n\ +散比:=ABS(((散户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLOR00FF00;\n\ +警戒线:MA(A,180),COLORFF66FF;\n\ +STICKLINE((主力 > 0),0,主力,2.5,0),COLOR1020BB;\n\ +STICKLINE((主力 > 0),0,主力,0.7,0),COLORRED;\n\ +STICKLINE((游资 > 0),0,游资,2.5,0),COLOR009CFF;\n\ +STICKLINE((游资 > 0),0,游资,0.7,0),COLORYELLOW;\n\ +STICKLINE((大户 > 0),0,大户,2.5,0),COLORFF8800;\n\ +STICKLINE((大户 > 0),0,大户,0.7,0),COLORLIBLUE;\n\ +STICKLINE((散户 > 0),0,散户,2.5,0),COLOR00CA00;\n\ +STICKLINE((散户 > 0),0,散户,0.7,0),COLORGREEN;' + }; + + return data; +} + + +JSIndexScript.prototype.NewsNegative=function() +{ + let data= + { + Name: '负面新闻', Description: '负面新闻统计', IsMainIndex: false,FloatPrecision:0, + Args: [{ Name: 'N', Value: 5 }, { Name: 'N2', Value: 10 }], + Script: //脚本 +'负面:NEWS(1);\n\ +MA1:MA(负面,N);\n\ +MA2:MA(负面,N2);' + }; + + return data; +} + +JSIndexScript.prototype.UpDownAnalyze=function() +{ + let data= + { + Name: '涨跌趋势', Description: '涨跌趋势', IsMainIndex: false,FloatPrecision:0, + Condition: { Period:[CONDITION_PERIOD.MINUTE_ID, CONDITION_PERIOD.MULTIDAY_MINUTE_ID, CONDITION_PERIOD.KLINE_DAY_ID] }, + Args: [], + Script: //脚本 +"上涨家数:UPCOUNT('CNA.CI'),COLORRED;\n\ +下跌家数:DOWNCOUNT('CNA.CI'),COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.HK2SHSZ=function() +{ + let data= + { + Name: '北上资金', Description: '北上资金', IsMainIndex: false,FloatPrecision:0, + Condition: { Period:[CONDITION_PERIOD.MINUTE_ID,CONDITION_PERIOD.MULTIDAY_MINUTE_ID,CONDITION_PERIOD.KLINE_DAY_ID] }, + Args: [], + Script: //脚本 + "净流入:HK2SHSZ(1),COLORSTICK;" + }; + + return data; +} + +JSIndexScript.prototype.ShareHolder=function() +{ + let data= + { + Name: '股东人数', Description: '股东人数', IsMainIndex: false,FloatPrecision:0, + Condition: { Period:[ + CONDITION_PERIOD.KLINE_DAY_ID, + CONDITION_PERIOD.KLINE_MONTH_ID, + CONDITION_PERIOD.KLINE_WEEK_ID, + CONDITION_PERIOD.KLINE_YEAR_ID] }, + Args: [], + Script: //脚本 + "人数:FINANCE(100);" + }; + + return data; +} + +JSIndexScript.prototype.VOLRate=function() +{ + let data= + { + Name: '量比', Description: '量比', IsMainIndex: false, Condition: { Period:[CONDITION_PERIOD.MINUTE_ID, CONDITION_PERIOD.MULTIDAY_MINUTE_ID ] }, + Args: [], + Script: //脚本 + "LIANGBI:VOLR;" + }; + + return data; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +//五彩K线 + +JSIndexScript.prototype.COLOR_KSTAR1=function() +{ + let data= + { + Name: '十字星', Description: '十字星', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:CLOSE==OPEN&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_KSTAR2=function() +{ + let data= + { + Name: '早晨之星', Description: '早晨之星', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:(REF(CLOSE,2)/REF(OPEN,2)<0.95) && (REF(OPEN,1) < REF(CLOSE,2)) && (ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03) && CLOSE/OPEN>1.05 && CLOSE>REF(CLOSE,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_KSTAR3=function() +{ + let data= + { + Name: '黄昏之星', Description: '黄昏之星', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05 && REF(OPEN,1)>REF(CLOSE,2) && ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03 && CLOSE/OPEN<0.95 && CLOSE1.03;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K220=function() +{ + let data= + { + Name: '身怀六甲', Description: '身怀六甲', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1)>0.04&&\n\ + ABS(CLOSE-OPEN)/CLOSE<0.005&&\n\ + MAX(CLOSE,OPEN)MIN(REF(CLOSE,1),REF(OPEN,1));' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K300=function() +{ + let data= + { + Name: '三个白武士', Description: '三个白武士', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:UPNDAY(CLOSE,3)&&NDAY(CLOSE,OPEN,3);' + }; + + return data; +} + + +JSIndexScript.prototype.COLOR_K310=function() +{ + let data= + { + Name: '三只乌鸦', Description: '三只乌鸦', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:DOWNNDAY(CLOSE,3)&&NDAY(OPEN,CLOSE,3);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K380=function() +{ + let data= + { + Name: '光头阳线', Description: '光头阳线', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:HIGH==CLOSE&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K390=function() +{ + let data= + { + Name: '光脚阴线', Description: '光脚阴线', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:LOW==CLOSE&&HIGH>LOW;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K134=function() +{ + let data= + { + Name: '垂死十字', Description: '垂死十字', IsMainIndex: true, InstructionType:2, + Script: //脚本 + 'KSTAR:CLOSE==OPEN&&CLOSE==LOW&&CLOSE1.05&&CLOSE>REF(CLOSE,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K150=function() +{ + let data= + { + Name: '黄昏十字星', Description: '黄昏十字星', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05&&\n\ +REF(OPEN,1)>REF(CLOSE,2)&&\n\ +REF(OPEN,1)=REF(CLOSE,1)&&\n\ +CLOSE/OPEN<0.95&&CLOSE3*(MAX(OPEN,CLOSE)-LOW)&&\n\ +CLOSE>MA(CLOSE,5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K165=function() +{ + let data= + { + Name: '倒转锤头', Description: '倒转锤头', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'KSTAR:MIN(OPEN,CLOSE)==LOW&&\n\ +HIGH-LOW>3*(MAX(OPEN,CLOSE)-LOW)&&\n\ +CLOSE3*(HIGH-MIN(OPEN,CLOSE))&&\n\ +CLOSE3*(HIGH-MIN(OPEN,CLOSE))&&\n\ +CLOSE>MA(CLOSE,5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_K190=function() +{ + let data= + { + Name: '穿头破脚', Description: '穿头破脚', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'OUT:(REF(CLOSE,1)/REF(OPEN,1)>1.03&&\n\ +CLOSE/OPEN<0.96&&\n\ +CLOSEREF(CLOSE,1))||\n\ +(REF(CLOSE,1)/REF(OPEN,1)<0.97&&\n\ +CLOSE/OPEN>1.04&&\n\ +CLOSE>REF(OPEN,1)&&OPENREF(VOL,1)||VOL>(CAPITAL*0.1);\n\ +BB:=OPEN>=(REF(HIGH,1))&&REF(HIGH,1)>(REF(HIGH,2)*1.06);\n\ +CC:=CLOSE>(REF(CLOSE,1))-(REF(CLOSE,1)*0.01);\n\ +DD:=CLOSE<(HIGH*0.965) && HIGH>(OPEN*1.05);\n\ +EE:=LOW(REF(CLOSE,1)*1.06);\n\ +FF:=(HIGH-(MAX(OPEN,CLOSE)))/2>(MIN(OPEN,CLOSE))-LOW;\n\ +GG:=(ABS(OPEN-CLOSE))/2<(MIN(OPEN,CLOSE)-LOW);\n\ +SWORDO:AA&&BB&&CC&&DD&&EE&&FF&&GG;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_CSFR=function() +{ + let data= + { + Name: '出水芙蓉', Description: '出水芙蓉', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'A:=CLOSE>OPEN;\n\ +B:=A&&CLOSE>MA(CLOSE,S)&&CLOSE>MA(CLOSE,M)&&CLOSE>MA(CLOSE,LL);\n\ +CC:=B&&OPEN0.0618*CLOSE;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_WYGD=function() +{ + let data= + { + Name: '乌云盖顶', Description: '乌云盖顶', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( \n\ +REF(CLOSE,1)/REF(OPEN,1)>1.03 AND \n\ +CLOSE/OPEN<0.97 AND \n\ +OPEN>REF(CLOSE,1) AND CLOSE1.03 AND \n\ +OPENREF(CLOSE,1), 3);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_SZTAI=function() +{ + let data= + { + Name: '十字胎', Description: '十字胎', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1) > 0.04 AND \n\ +CLOSE==OPEN AND CLOSE < MAX(REF(CLOSE,1),REF(OPEN,1)) AND \n\ +CLOSE > MIN(REF(CLOSE,1),REF(OPEN,1)), 2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_PINGDING=function() +{ + let data= + { + Name: '平顶', Description: '平顶', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET(ABS(HIGH-REF(HIGH,1))/HIGH<0.001,2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_PINGDI=function() +{ + let data= + { + Name: '平底', Description: '平底', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET((ABS(LOW-REF(LOW,1))/LOW<0.001 AND \n\ +ABS(REF(LOW,1)-REF(LOW,2))/REF(LOW,1)<=0.001),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_DAYANZHU=function() +{ + let data= + { + Name: '大阳烛', Description: '大阳烛', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:CLOSE/OPEN>1.05 AND HIGH/LOW < CLOSE/OPEN+0.018;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_DAYINGZHU=function() +{ + let data= + { + Name: '大阴烛', Description: '大阴烛', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:OPEN/CLOSE > 1.05 AND HIGH/LOW < OPEN/CLOSE+0.018;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_HYFG=function() +{ + let data= + { + Name: '好友反攻', Description: '好友反攻', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( (REF(CLOSE,1)OPEN AND ABS(CLOSE-REF(CLOSE,1))/CLOSE<0.002),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_TKQK=function() +{ + let data= + { + Name: '跳空缺口', Description: '跳空缺口', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( HIGHREF(HIGH,1),2);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_SFWY=function() +{ + let data= + { + Name: '双飞乌鸦', Description: '双飞乌鸦', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( REF(CLOSE,1)1.03 AND \n\ +REF(CLOSE,3)REF(HIGH,3) AND \n\ +REF(HIGH,4)>REF(HIGH,2) AND \n\ +REF(HIGH,4)>REF(HIGH,1) AND \n\ +CLOSE/OPEN>1.03 AND \n\ +CLOSE>REF(CLOSE,4), 5);' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_XDSBQ=function() +{ + let data= + { + Name: '下跌三部曲', Description: '下跌三部曲', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( \n\ +REF(CLOSE,4)/REF(OPEN,4)<0.97 AND \n\ +REF(CLOSE,3)>REF(OPEN,3) AND \n\ +REF(CLOSE,2)>REF(OPEN,2) AND \n\ +REF(CLOSE,1)>REF(OPEN,1) AND \n\ +REF(LOW,4)REF(HIGH,3) AND \n\ +REF(HIGH,4)>REF(HIGH,2) AND \n\ +REF(HIGH,4)>REF(HIGH,1) AND \n\ +CLOSE/OPEN<0.97 AND \n\ +CLOSE0.667;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_CHSY=function() +{ + let data= + { + Name: '长上影', Description: '长上影', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR2:(HIGH-MAX(CLOSE,OPEN))/(HIGH-LOW)>0.667,COLORBLUE;' + }; + + return data; +} + +JSIndexScript.prototype.COLOR_FENLI=function() +{ + let data= + { + Name: '分离', Description: '分离', IsMainIndex: true, InstructionType:2, + Script: //脚本 +'VAR1:BACKSET( OPEN==REF(OPEN,1) AND (CLOSE-OPEN)*(REF(CLOSE,1)-REF(OPEN,1))<0,2);' + }; + + return data; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//交易系统 + +JSIndexScript.prototype.TRADE_BIAS = function () +{ + let data = + { + Name: 'BIAS', Description: '乖离率专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 12 }, { Name: 'LL', Value: 6 },{ Name: 'LH', Value: 6 }], + Script: //脚本 +'BIAS:=(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\ +ENTERLONG:CROSS(-LL,BIAS);\n\ +EXITLONG:CROSS(BIAS,LH);' + + }; + + return data; +} + +JSIndexScript.prototype.TRADE_CCI = function () +{ + let data = + { + Name: 'CCI', Description: 'CCI专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 14 }], + Script: //脚本 +'TYP:=(HIGH+LOW+CLOSE)/3;\n\ +CCI:=(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));\n\ +INDEX:=CCI;\n\ +ENTERLONG:CROSS(INDEX,-100);\n\ +EXITLONG:CROSS(100,INDEX);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_DMI = function () +{ + let data = + { + Name: 'DMI', Description: '趋向专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 14 }], + Script: //脚本 +'MTR:=SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N);\n\ +HD :=HIGH-REF(HIGH,1);\n\ +LD :=REF(LOW,1)-LOW;\n\ +PDM:=SUM(IF(HD>0&&HD>LD,HD,0),N);\n\ +MDM:=SUM(IF(LD>0&&LD>HD,LD,0),N);\n\ +PDI:=PDM*100/MTR;\n\ +MDI:=MDM*100/MTR;\n\ +ENTERLONG:CROSS(PDI,MDI);\n\ +EXITLONG:CROSS(MDI,PDI);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_KD = function () +{ + let data = + { + Name: 'KD', Description: 'KD指标专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 9 },{ Name: 'M1', Value: 3 },{ Name: 'M2', Value: 3 }], + Script: //脚本 +'WRSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +WK:=SMA(WRSV,M1,1);\n\ +D:=SMA(WK,M2,1);\n\ +ENTERLONG:CROSS(WK,D)&&WK<20;\n\ +EXITLONG:CROSS(D,WK)&&WK>80;' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_BOLL = function () +{ + let data = + { + Name: 'BOLL', Description: '布林带专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 20 }], + Script: //脚本 +'MID :=MA(CLOSE,N);\n\ +UPPER:=MID+2*STD(CLOSE,N);\n\ +LOWER:=MID-2*STD(CLOSE,N);\n\ +ENTERLONG:CROSS(CLOSE,LOWER);\n\ +EXITLONG:CROSS(CLOSE,UPPER);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_KDJ = function () +{ + let data = + { + Name: 'KDJ', Description: 'KDJ专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 9 },{ Name: 'M1', Value: 3 }], + Script: //脚本 +'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\ +K:=SMA(RSV,M1,1);\n\ +D:=SMA(K,M1,1);\n\ +J:=3*K-2*D;\n\ +ENTERLONG:CROSS(J,0);\n\ +EXITLONG:CROSS(100,J);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MA = function () +{ + let data = + { + Name: 'MA', Description: '均线专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'SHORT', Value: 5 },{ Name: 'LONG', Value: 20 }], + Script: //脚本 +'ENTERLONG:CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));\n\ +EXITLONG:CROSS(MA(CLOSE,LONG),MA(CLOSE,SHORT));' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MACD = function () +{ + let data = + { + Name: 'MACD', Description: 'MACD专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'LONG', Value: 26 }, { Name: 'SHORT', Value: 12 }, { Name: 'M', Value: 9 }], + Script: //脚本 +'DIFF:=EMA(CLOSE,SHORT) - EMA(CLOSE,LONG);\n\ +DEA := EMA(DIFF,M);\n\ +MACD := 2*(DIFF-DEA);\n\ +ENTERLONG:CROSS(MACD,0);\n\ +EXITLONG:CROSS(0,MACD);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_MTM = function () +{ + let data = + { + Name: 'MTM', Description: '动力指标专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 6 }], + Script: //脚本 +'WMTM:=C-REF(C,N);\n\ +ENTERLONG:CROSS(WMTM,0);\n\ +EXITLONG:CROSS(0,WMTM);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_PSY = function () +{ + let data = + { + Name: 'PSY', Description: 'PSY心理线专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 12 },{ Name: 'LL', Value: 10 },{ Name: 'LH', Value: 85 }], + Script: //脚本 +'MYPSY:=COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\n\ +ENTERLONG:CROSS(LL,MYPSY);\n\ +EXITLONG:CROSS(MYPSY,LH);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_ROC = function () +{ + let data = + { + Name: 'ROC', Description: '变动速率专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 12 },{ Name: 'M', Value: 6 }], + Script: //脚本 +'WROC:=MA(100*(CLOSE-REF(CLOSE,N))/REF(CLOSE,N),M);\n\ +ENTERLONG:CROSS(WROC,0);\n\ +EXITLONG:CROSS(0,WROC);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_RSI = function () +{ + let data = + { + Name: 'RSI', Description: '相对强弱专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 6 },{ Name: 'LL', Value: 20 },{ Name: 'LH', Value: 80 }], + Script: //脚本 +'LC:=REF(CLOSE,1);\n\ +WRSI:=SMA(MAX(CLOSE-LC,0),N,1)/SMA(ABS(CLOSE-LC),N,1)*100;\n\ +ENTERLONG:CROSS(WRSI,LL);\n\ +EXITLONG:CROSS(LH,WRSI);' + }; + + return data; +} + +JSIndexScript.prototype.TRADE_VR = function () +{ + let data = + { + Name: 'VR', Description: 'VR容量比率专家系统', IsMainIndex: true, InstructionType:1, + Args: [{ Name: 'N', Value: 26 },{ Name: 'LL', Value: 70 },{ Name: 'LH', Value: 250 }], + Script: //脚本 +'WVR := SUM((IF(CLOSE>OPEN,VOL,0)+IF(CLOSE=OPEN,VOL/2,0)),N)/SUM((IF(CLOSE REF(VAR5, 1), VAR5,0), COLORRED, NODRAW;\n\ +洗盘: IF(VAR5 < REF(VAR5, 1), VAR5,0), COLORYELLOW, NODRAW;\n\ +STICKLINE(VAR5> REF(VAR5, 1),0, VAR5, 20, 0), COLORRED;\n\ +STICKLINE(VAR5 < REF(VAR5, 1), 0, VAR5, 20, 0), COLORYELLOW;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index2 = function () +{ + let data = + { + Name: '牛熊区间', Description: '牛熊区间', IsMainIndex: false,YSpecificMaxMin:{Max:100,Min:1,Count:4},YSplitScale:[1,50,100], + Args: [], + Script: //脚本 +'短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\ ++ 13 * REF(H, 7) + 12 * REF(H, 8) + 11 * REF(H, 9) + 10 * REF(H, 10) + 9 * REF(H, 11) + 8 * REF(H, 12)\n\ ++ 7 * REF(H, 13) + 6 * REF(H, 14) + 5 * REF(H, 15) + 4 * REF(H, 16) + 3 * REF(H, 17) + 2 * REF(H, 18) +\n\ +REF(H, 20))/ 210, COLORBLUE, LINETHICK1;\n\ +短低L:= (20 * L + 19 * REF(L, 1) + 18 * REF(L, 2) + 17 * REF(L, 3) + 16 * REF(L, 4) + 15 * REF(L, 5) + 14 * REF(L, 6)\n\ ++ 13 * REF(L, 7) + 12 * REF(L, 8) + 11 * REF(L, 9) + 10 * REF(L, 10) + 9 * REF(L, 11) + 8 * REF(L, 12)\n\ ++ 7 * REF(L, 13) + 6 * REF(L, 14) + 5 * REF(L, 15) + 4 * REF(L, 16) + 3 * REF(L, 17) + 2 * REF(L, 18) +\n\ +REF(L, 20)) / 210, COLORBLUE, LINETHICK1;\n\ +D90H:= EMA(短高H, 90), COLORRED, LINETHICK1;\n\ +D90L:= EMA(短低L, 90), COLORRED, LINETHICK1;\n\ +D90差:= D90H - D90L;\n\ +D90顶:= D90H + D90差 * 2, COLORRED, LINETHICK1;\n\ +D90底:= D90L - D90差 * 2, COLORRED, LINETHICK1;\n\ +高0:= (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1 + EMA(EMA(H, 25), 25), LINETHICK1, COLORWHITE;\n\ +低0:= EMA(EMA(L, 25), 25) - (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1, LINETHICK1, COLORWHITE;\n\ +多头定位:= 低0 >= D90底 AND 高0 >= D90顶;\n\ +空头定位:= 高0 <= D90顶 AND 低0 <= D90底;\n\ +震荡定位:= 低0 >= D90底 AND 高0 <= D90顶;\n\ +牛市: IF(多头定位 == 1, 100, 1), COLORRED, NODRAW;\n\ +熊市: IF(空头定位 == 1, 100, 1), COLORGREEN, NODRAW;\n\ +震荡: IF(震荡定位 == 1, 100, 1), COLORGRAY, NODRAW;\n\ +STICKLINE(多头定位 == 1, 100, 1, 100, 0), COLORRED;\n\ +STICKLINE(空头定位 == 1, 100, 1, 100, 0), COLORGREEN;\n\ +STICKLINE(震荡定位 == 1, 100, 1, 100, 0), COLORGRAY;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index3 = function () +{ + let data = + { + Name: '持仓信号', Description: '持仓信号', IsMainIndex: true, + Args: [], + Script: //脚本 +'买线:=EMA(C,2);\n\ +卖线:=EMA(SLOPE(C,30)*5+C,20); \n\ +BU:=CROSS(买线,卖线);\n\ +SEL:=CROSS(卖线,买线);\n\ +\n\ +STICKLINE(买线>=卖线 AND CLOSE>OPEN,LOW,HIGH,0,1),COLORRED;\n\ +STICKLINE(买线>=卖线 AND CLOSE=卖线 AND CLOSE>OPEN,CLOSE,OPEN,50,1),COLORRED;\n\ +STICKLINE(买线>=卖线 AND CLOSEOPEN,LOW,HIGH,0,1),COLORGREEN;\n\ +STICKLINE(买线<卖线 AND CLOSEOPEN,CLOSE,OPEN,50,1),COLORGREEN;\n\ +\n\ +HHJSJDA:=(3*CLOSE+OPEN+LOW+HIGH)/6;\n\ +HHJSJDB:(19*HHJSJDA+19*REF(HHJSJDA,1)+18*REF(HHJSJDA,2)+17*REF(HHJSJDA,3)+16*REF(HHJSJDA,4)+15*REF(HHJSJDA,5)+14*REF(HHJSJDA,6)\n\ ++13*REF(HHJSJDA,7)+12*REF(HHJSJDA,8)+11*REF(HHJSJDA,9)+10*REF(HHJSJDA,10)+9*REF(HHJSJDA,11)+8*REF(HHJSJDA,12)+7*REF(HHJSJDA,13)+6*REF(HHJSJDA,14)+5*REF(HHJSJDA,15)+4*REF(HHJSJDA,16)+3*REF(HHJSJDA,17)+2*REF\n\ +(HHJSJDA,20)+REF(HHJSJDA,19))/210,COLORYELLOW;\n\ +HHJSJDC:MA(HHJSJDB,5),COLORRED;\n\ +\n\ +SVAR11:=HHV(HIGH,34);\n\ +SVAR14:=CLOSE-REF(CLOSE,1);\n\ +SVAR15:=MAX(SVAR14,0);\n\ +SVAR16:=ABS(SVAR14);\n\ +SVAR17:=SMA(SVAR15,7,1)/SMA(SVAR16,7,1)*100;\n\ +SVAR18:=SMA(SVAR15,13,1)/SMA(SVAR16,13,1)*100;\n\ +SVAR19:=BARSCOUNT(CLOSE);\n\ +SVAR20:=SMA(MAX(SVAR14,0),6,1)/SMA(ABS(SVAR14),6,1)*100;\n\ +SVAR21:=(-200)*(HHV(HIGH,60)-CLOSE)/(HHV(HIGH,60)-LLV(LOW,60))+100;\n\ +SVAR1A:=(CLOSE-LLV(LOW,15))/(HHV(HIGH,15)-LLV(LOW,15))*100;\n\ +SVAR1B:=SMA((SMA(SVAR1A,4,1)-50)*2,3,1);\n\ +SVAR1C:=(INDEXC-LLV(INDEXL,14))/(HHV(INDEXH,14)-LLV(INDEXL,14))*100;\n\ +SVAR1D:=SMA(SVAR1C,4,1);\n\ +SVAR1E:=SMA(SVAR1D,3,1);\n\ +SVAR1F:=(HHV(HIGH,30)-CLOSE)/CLOSE*100;\n\ +SVAR22:=SVAR20<=25 AND SVAR21<-95 AND SVAR1F>20 AND SVAR1B<-30 AND SVAR1E<30 AND SVAR11-CLOSE>=-0.25 AND SVAR17<22 AND SVAR18<28 AND SVAR19>50;\n\ +BUY3:=CROSS(SVAR22,0.5) AND COUNT(SVAR22==1,10)==1;\n\ +\n\ +SVARF:=LOW*0.9;\n\ +SVAR10X:=100-3*SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1)+2*SMA(SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1),15,1);\n\ +SVAR11X:=SVARFREF(VOL,1) AND CLOSE>REF(CLOSE,1);\n\ +BUY2:=SVAR11X AND COUNT(SVAR11X,30)==1;\n\ +\n\ +VAR1:=(CLOSE+HIGH+LOW+OPEN)/4;\n\ +VAR2:=SUMBARS(VOL,CAPITAL);\n\ +VAR3:=HHV(VAR1,VAR2);\n\ +VAR4:=LLV(VAR1,VAR2);\n\ +VAR5:=(2*VAR1-VAR4-REF(VAR4,1))/(VAR3-VAR4);\n\ +VAR6:=(VAR1-VAR4)/(VAR3-VAR4);\n\ +VAR7:=IF(VAR1<=VAR4,VAR5*60,VAR6*60);\n\ +VAR8:=600*(EMA(CLOSE,3)-EMA(LOW,30))/EMA(LOW,30);\n\ +VAR9:=EMA(VAR8,7);\n\ +VARC:=HHV(HIGH,9)-LLV(LOW,9);\n\ +VARD:=HHV(HIGH,9)-CLOSE;\n\ +VARE:=CLOSE-LLV(LOW,9);\n\ +VARF:=VARD/VARC*100-70;\n\ +VAR10:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*100;\n\ +VAR11:=(2*CLOSE+HIGH+LOW)/4;\n\ +VAR12:=SMA(VARE/VARC*100,3,1);\n\ +VAR13:=LLV(LOW,34);\n\ +VAR14:=SMA(VAR12,3,1)-SMA(VARF,9,1);\n\ +VAR15:=IF(VAR14>100,VAR14-100,0);\n\ +VAR16:=HHV(HIGH,34);\n\ +VAR17:=EMA((VAR11-VAR13)/(VAR16-VAR13)*100,8);\n\ +VAR18:=EMA(VAR17,5);\n\ +BUY:=STICKLINE(VAR17-VAR18>0,VAR17,VAR18,8,1),COLORRED;\n\ +SELL:=STICKLINE(VAR17-VAR18<0,VAR17,VAR18,8,1),COLORGREEN;\n\ +BUY1:=VAR17>VAR18 AND REF(VAR17,1)REF(VAR18,1);\n\ +\n\ +短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\ ++13*REF(H,7)+12*REF(H,8)+11*REF(H,9)+10*REF(H,10)+9*REF(H,11)+8*REF(H,12)\n\ ++7*REF(H,13)+6*REF(H,14)+5*REF(H,15)+4*REF(H,16)+3*REF(H,17)+2*REF(H,18)+\n\ +REF(H,20))/210,COLORBLUE,LINETHICK1;\n\ +短低L:=(20*L+19*REF(L,1)+18*REF(L,2)+17*REF(L,3)+16*REF(L,4)+15*REF(L,5)+14*REF(L,6)\n\ ++13*REF(L,7)+12*REF(L,8)+11*REF(L,9)+10*REF(L,10)+9*REF(L,11)+8*REF(L,12)\n\ ++7*REF(L,13)+6*REF(L,14)+5*REF(L,15)+4*REF(L,16)+3*REF(L,17)+2*REF(L,18)+\n\ +REF(L,20))/210,COLORBLUE,LINETHICK1;\n\ +D90H:=EMA(短高H,90),COLORRED,LINETHICK1;\n\ +D90L:=EMA(短低L,90),COLORRED,LINETHICK1;\n\ +D90差:=D90H-D90L;\n\ +D90顶:=D90H+D90差*2,COLORRED,LINETHICK1;\n\ +D90底:=D90L-D90差*2,COLORRED,LINETHICK1;\n\ +高0:=(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1+EMA(EMA(H,25),25),LINETHICK1,COLORWHITE;\n\ +低0:=EMA(EMA(L,25),25)-(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1,LINETHICK1,COLORWHITE;\n\ +多头定位:=低0>=D90底 AND 高0>=D90顶;\n\ +空头定位:=高0<=D90顶 AND 低0<=D90底;\n\ +震荡定位:=低0>=D90底 AND 高0<=D90顶;\n\ +\n\ +牛市:=多头定位==1;\n\ +熊市:=空头定位==1;\n\ +震荡:=震荡定位==1;\n\ +\n\ +非牛市:=熊市 OR 震荡;\n\ +非熊市:=牛市 OR 震荡;\n\ +\n\ +BUY11:=BUY1 AND 非熊市;\n\ +SELL11:=SELL1 AND 震荡定位==0;\n\ +\n\ +BUY111:=BUY11 AND COUNT(BUY11,10)<2;\n\ +BUY0:=BUY111 AND COUNT(BUY111,21)==1;\n\ +SELL111:=SELL11 AND COUNT(SELL11,10)<2;\n\ +SELL0:=SELL111 AND COUNT(SELL111,10)==1;\n\ +\n\ +XK1:=EMA(100*(CLOSE-LLV(LOW,34))/(HHV(HIGH,34)-LLV(LOW,34)),3)/4;\n\ +上穿:=REF(XK1,1)<5 AND XK1>=5;\n\ +BUY4:=上穿 AND COUNT(XK1<2,12)<1;\n\ +\n\ +SELL2:=REF(XK1,1)<=22.5 AND XK1>22.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,5)>0;\n\ +SELL3:=REF(XK1,1)>=21.5 AND XK1<21.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,12)>1;\n\ +SELL4:=SELL2 OR SELL3 AND COUNT((SELL2 OR SELL3)==1,5)==1;\n\ +\n\ +SUPERDRAWTEXT(BUY0,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(SELL0,H,"风险",1,10),COLORGREEN;\n\ +SUPERDRAWTEXT(BUY2,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(BUY4,L,"机会",2,10),COLORRED;\n\ +SUPERDRAWTEXT(SELL4,H,"风险",1,10),COLORGREEN;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index4 = function () +{ + let data = + { + Name: '股东实际增减持', Description: '股东实际增减持', IsMainIndex: false, FloatPrecision:0, + Args: [], + Script: //脚本 +'增持:NEWS(4),NODRAW,COLORRED;\n\ +减持:NEWS(5),NODRAW,COLORGREEN;\n\ +STICKLINE(增持>0,0,增持,1,0),COLORRED;\n\ +STICKLINE(减持<0,0,减持,1,0),COLORGREEN;' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index5 = function () +{ + let data = + { + Name: '大宗交易', Description: '大宗交易', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '交易次数:NEWS(7);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index6 = function () +{ + let data = + { + Name: '信托持股', Description: '信托持股', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '家数:NEWS(6);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index7 = function () +{ + let data = + { + Name: '官网新闻', Description: '官网新闻', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '个数:NEWS(8);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index8 = function () +{ + let data = + { + Name: '高管要闻', Description: '高管要闻', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '个数:NEWS(9);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index9 = function () +{ + let data = + { + Name: '股权质押', Description: '股权质押', IsMainIndex: false, FloatPrecision: 0, + Args: [], + Script: //脚本 + '次数:NEWS(10);' + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index10 = function () +{ + let data = + { + Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, FloatPrecision: 0, + Args: [], + Script: //脚本 +"JJ:=(CLOSE+HIGH+LOW)/3; \n\ +A:=EMA(JJ,10); \n\ +B:=REF(A,1);\n\ +M1:=EMA(CLOSE,3);\n\ +M2:=EMA(CLOSE,8); \n\ +M3:=EMA(M2,13); \n\ +M4:=EMA(M2,55); \n\ +B1:=A>B AND REF(A,1)REF(B,1);\n\ +INDEXCLOSE:INDEXC,EXDATA;\n\ +INDEXOPEN:INDEXO,EXDATA;\n\ +DRAWICON(REF(B1,1),L*0.97,13);\n\ +DRAWICON(REF(S1,1),H*1.03,14);" + }; + + return data; +} + +JSIndexScript.prototype.Zealink_Index11 = function () +{ + let data = + { + Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, FloatPrecision: 0, + Args: [], + Script: //脚本 +"JJ:=(CLOSE+HIGH+LOW)/3;\n\ +A:=EMA(JJ,10);\n\ +B:=REF(A,1);\n\ +M1:=EMA(CLOSE,3);\n\ +M2:=EMA(CLOSE,8); \n\ +M3:=EMA(M2,13);\n\ +M4:=EMA(M2,55);\n\ +持股区域:=STICKLINE(A>B,A,B,2,0),COLORYELLOW; \n\ +持币区域:=STICKLINE(A100,VR14-100,0);\n\ +VR16:=HHV(H,34);\n\ +VR17:=EMA((VR11-VR13)/(VR16-VR13)*100,8);\n\ +VR18:=EMA(VR17,5);\n\ +\n\ +B1:=A>B AND REF(A,1)REF(B,1);\n\ +\n\ +B9:=BARSLAST(REF(B1,1) AND (REF(VR17>VR18,1) OR MIN(VR17,VR18)>REF(MAX(VR17,VR18),1)));\n\ +S9:=BARSLAST(REF(S1,1) AND (REF(VR17VR18,1) OR MIN(VR17,VR18)>REF(MAX(VR17,VR18),1)) AND REF(B9,2)>=S9,L*0.97,13);\n\ +DRAWICON(REF(S1,1) AND (REF(VR17=B9,H*1.03,14);\n\ +\n\ +DRAWKLINE_IF(VR17>VR18,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\ +DRAWKLINE_IF(VR17X_3 AND X_2>X_4,X_2+X_3/2+X_5/4,IF(X_3>X_4 AND X_3>X_2,X_3+X_2/2+X_5/4,X_4+X_5/4));\n\ +X_7:=CLOSE-X_1+(CLOSE-OPEN)/2+X_1-REF(OPEN,1);\n\ +X_8:=8*X_7/X_6*MAX(X_2,X_3);\n\ +ASI:SUM(X_8,0),LINETHICK2;\n\ +MASI:MA(ASI,N1),LINETHICK2;" + }; + + return data; +} + +/* +History +The Donchian Channels (DC) indicator was created by the famous commodities trader Richard Donchian. Donchian would become known as The Father of Trend Following. + +Calculation +For this example, a 20 day period is used which is a very commonly used timeframe. + +Upper Channel = 20 Day High +Lower Channel = 20 Day Low +Middle Channel = (20 Day High + 20 Day Low)/2 +*/ +JSIndexScript.prototype.DC = function () +{ + let data = + { + Name: 'DC', Description: '唐奇安通道', IsMainIndex: true, + Args: [{ Name: 'N1', Value: 20 }], + Script: //脚本 +"UPPER:HHV(H,N1),COLORBLUE,LINETHICK2;\n\ +LOWER:LLV(L,N1),COLORBLUE,LINETHICK2;\n\ +MIDDLE:(UPPER+LOWER)/2,COLORRED,LINETHICK3;" + }; + + return data; +} + +/* +双重指数移动均线(DEMA)由Patrick Mulloy开发并于1994年2月在"股票与商品期货的技术分析"杂志中出版。 +用于平滑价格系列并被直接应用到金融证券的价格图表中。此外,它还用于平滑其他指标的价值。 + +DEMA的优势是在锯齿状的价格移动中清除错误信号并允许趋势强劲时保持仓位。 + +计算 +该指标基于指数移动平均线 (EMA). 从EMA值中查看价格偏差错误: +err(i) = Price(i) - EMA(Price, N, i) + +此处: +err(i) ― 当前EMA误差; +Price(i) ― 当前价格; +EMA(Price, N, i) ― 价格系列的以N为周期的EMA的当前值。 + +添加指数平均线错误值到价格指数移动平均数值,可以获得EDMA; +DEMA(i) = EMA(Price,N,i)+ EMA(err,N,i) = EMA(Price,N,i)+EMA(Price-EMA(Price,N,i),N,i) = += 2*EMA(Price,N,i)-EMA(Price-EMA(Price,N,i),N,i)=2*EMA(Price,N,i)-EMA2(Price,N,i) + +此处: +EMA(err, N, i) ― 误差err的指数均线的当前值; +EMA2(Price, N, i) ― 价格的二重连续平滑的当前值。 +*/ +JSIndexScript.prototype.DEMA = function () +{ + let data = + { + Name: 'DEMA', Description: '双重指数移动均线', IsMainIndex: true, + Args: [{ Name: 'N1', Value: 10 }], + Script: //脚本 +"ERR:=C-EMA(C,N1);\n\ +DEMA:EMA(C,10)+EMA(ERR,N1);" + }; + + return data; +} + +/* +Calculation +There are five steps in calculating VWAP: + +Calculate the Typical Price for the period. + [(High + Low + Close)/3)] +Multiply the Typical Price by the period Volume. + (Typical Price x Volume) +Create a Cumulative Total of Typical Price. + Cumulative(Typical Price x Volume) +Create a Cumulative Total of Volume. + Cumulative(Volume) +Divide the Cumulative Totals. + VWAP = Cumulative(Typical Price x Volume) / Cumulative(Volume) +*/ + +JSIndexScript.prototype.VWAP = function () +{ + let data = + { + Name: 'VWAP', Description: '成交量加权平均价', IsMainIndex: true, + Args: [{ Name: 'N1', Value: 10 }], + Script: //脚本 +"PRICE:=(H+L+C)/3;\n\ +T2:=VOL*PRICE;\n\ +VWAP:SUM(T2,0)/SUM(VOL,0);" + }; + + return data; +} + +JSIndexScript.prototype.SQJZ = function () +{ + let data = + { + Name: 'SQJZ', Description: '神奇九转', IsMainIndex: true, + Script: //脚本 +"B:=C=9 AND REFXV(COUNT(B,9),8)=9);\n\ +DRAWNUMBER(B1 AND REF(B,1)=0,L,1),COLORMAGENTA;\n\ +B2:=(N=5 AND REFXV(COUNT(B,6),4)=6) OR (N=6 AND REFXV(COUNT(B,7),5)=7) OR (N=7 AND REFXV(COUNT(B,8),6)=8) OR (N>=8 AND REFXV(COUNT(B,9),7)=9);\n\ +DRAWNUMBER(B2 AND REF(B,2)=0,L,2),COLORMAGENTA;\n\ +B8:=(N=1 AND COUNT(B,8)=8) OR (N>=2 AND REFXV(COUNT(B,9),1)=9);\n\ +DRAWNUMBER(B8 AND REF(B,8)=0,L,8),COLORMAGENTA;\n\ +B9:=(N>=1 AND COUNT(B,9)=9);\n\ +DRAWNUMBER(B9 AND REF(B,9)=0,L,9),COLORBROWN;\n\ +S:=C>REF(C,4);\n\ +S1:=(N=6 AND REFXV(COUNT(S,6),5)=6) OR (N=7 AND REFXV(COUNT(S,7),6)=7) OR (N=8 AND REFXV(COUNT(S,8),7)=8) OR (N>=9 AND REFXV(COUNT(S,9),8)=9);\n\ +DRAWNUMBER(S1 AND REF(S,1)=0,H,1),COLORMAGENTA,DRAWABOVE;\n\ +S2:=(N=5 AND REFXV(COUNT(S,6),4)=6) OR (N=6 AND REFXV(COUNT(S,7),5)=7) OR (N=7 AND REFXV(COUNT(S,8),6)=8) OR (N>=8 AND REFXV(COUNT(S,9),7)=9);\n\ +DRAWNUMBER(S2 AND REF(S,2)=0,H,2),COLORMAGENTA,DRAWABOVE;\n\ +S8:=(N=1 AND COUNT(S,8)=8) OR (N>=2 AND REFXV(COUNT(S,9),1)=9);\n\ +DRAWNUMBER(S8 AND REF(S,8)=0,H,8),COLORMAGENTA,DRAWABOVE;\n\ +S9:=(N>=1 AND COUNT(S,9)=9);\n\ +DRAWNUMBER(S9 AND REF(S,9)=0,H,9),COLORGREEN,DRAWABOVE;" + }; + + return data; +} + +JSIndexScript.prototype.XT = function () +{ + let data = + { + Name: 'XT', Description: '箱体', IsMainIndex: true, + Args: [{ Name: 'N', Value: 10 }], + Script: //脚本 +"【箱顶】:PEAK(CLOSE,N,1)*0.98;\n\ +【箱底】:TROUGH(CLOSE,N,1)*1.02;\n\ +【箱高】:100*(【箱顶】-【箱底】)/【箱底】,NODRAW;" + }; + + return data; +} + +JSIndexScript.prototype.CFJT = function () +{ + let data = + { + Name: 'CFJT', Description: '财富阶梯', IsMainIndex: true, + Script: //脚本 +"突破:=REF(EMA(C,14),1);\n\ +A1X:=(EMA(C,10)-突破)/突破*100;\n\ +多方:=IF(A1X>=0,REF(EMA(C,10),BARSLAST(CROSS(A1X,0))+1),DRAWNULL);\n\ +空方:=IF(A1X<0,REF(EMA(C,10),BARSLAST(CROSS(0,A1X))+1),DRAWNULL);\n\ +STICKLINE(A1X>=0,多方,突破,110,0),COLORRED;\n\ +STICKLINE(A1X<0,空方,突破,110,0),COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.CYX = function () +{ + let data = + { + Name: 'CYX', Description: '撑压线', IsMainIndex: true, + Args: [{ Name: 'N', Value: 7 }], + Script: //脚本 +"Z1:=STRCAT(HYBLOCK,' ');\n\ +Z2:=STRCAT(Z1,DYBLOCK);\n\ +Z3:=STRCAT(Z2,' ');\n\ +DRAWTEXT_FIX(ISLASTBAR,0,0,0,STRCAT(Z3,GNBLOCK)),COLOR00C0C0;\n\ +A1:=REF(H,N)=HHV(H,2*N+1);\n\ +B1:=FILTER(A1,N);\n\ +C1:=BACKSET(B1,N+1);\n\ +D1:=FILTER(C1,N);\n\ +A2:=REF(L,N)=LLV(L,2*N+1);\n\ +B2:=FILTER(A2,N);\n\ +C2:=BACKSET(B2,N+1);\n\ +D2:=FILTER(C2,N);\n\ +E1:=(REF(LLV(L,2*N),1)+REF(HHV(H,2*N),1))/2;\n\ +E2:=(H+L)/2;\n\ +H1:=(D1 AND NOT(D2 AND E1>=E2)) OR ISLASTBAR OR BARSCOUNT(C)=1;\n\ +L1:=(D2 AND NOT(D1 AND E1=E2);\n\ +X1:=REF(BARSLAST(H1),1)+1;\n\ +F1:=BACKSET(H1 AND COUNT(L1,X1)>0,LLVBARS(IF(L1,L,10000),X1));\n\ +G1:=F1>REF(F1,1);\n\ +I1:=BACKSET(G1,2);\n\ +LD:=I1>REF(I1,1);\n\ +L2:=LD OR ISLASTBAR OR BARSCOUNT(C)=1;\n\ +X2:=REF(BARSLAST(L2),1)+1;\n\ +F2:=BACKSET(L2 AND COUNT(H2,X2)>0,HHVBARS(IF(H2,H,0),X2));\n\ +G2:=F2>REF(F2,1);\n\ +I2:=BACKSET(G2,2);\n\ +HD:=I2>REF(I2,1);\n\ +R1:=BACKSET(ISLASTBAR,BARSLAST(HD)+1);\n\ +S1:=R1>REF(R1,1);\n\ +T1:=BACKSET(ISLASTBAR,BARSLAST(LD)+1);\n\ +U1:=T1>REF(T1,1);\n\ +R2:=BACKSET(S1,REF(BARSLAST(HD),1)+2);\n\ +S2:=R2>REF(R2,1);\n\ +T2:=BACKSET(U1,REF(BARSLAST(LD),1)+2);\n\ +U2:=T2>REF(T2,1);\n\ +DRAWLINE(S2,H,S1,H,1),LINETHICK2,COLORRED;\n\ +DRAWLINE(U2,L,U1,L,1),LINETHICK2,COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.WAVE = function () +{ + let data = + { + Name: 'WAVE', Description: '波浪分析', IsMainIndex: true, + Args: [{ Name: 'N', Value: 5 }], + Script: //脚本 +"ZIG(3,N);" + }; + + return data; +} + +JSIndexScript.prototype.ShareholderCount=function() +{ + let data = + { + Name: '散户线', Description: '散户线', IsMainIndex: false, + Script: //脚本 +"GPJYVALUE(1,1,1);" + }; + + return data; +} + +JSIndexScript.prototype.NXTS=function() +{ + let data = + { + Name: 'NXTS', Description: '牛熊天数', IsMainIndex: false, + Args: [{ Name: 'N', Value: 20 }], + Script: //脚本 +"Z:=ZIG(C,N);\n\ +高点:=Z>REF(Z,1) AND Z>REFX(Z,1);\n\ +低点:=ZND OR COUNT(高点,0)=0) AND ND>0 AND (HHV(C,ND)-REF(C,ND))/REF(C,ND)>(N/100);\n\ +N2:=NG0 AND (LLV(C,NG)-REF(C,NG))/REF(C,NG)>=-(N/100);\n\ +N3:=NG(N/100);\n\ +N5:=COUNT(低点,0)=0 AND NG>0 AND (LLV(C,NG)-REF(C,NG))/REF(C,NG)>=-(N/100);\n\ +N6:=COUNT(低点,0)=0 AND NG=0;\n\ +X1:=(NG0 AND (LLV(C,NG)-REF(C,NG))/REF(C,NG)<-(N/100);\n\ +X2:=NG>ND AND ND>0 AND (HHV(C,ND)-REF(C,ND))/REF(C,ND)<=(N/100);\n\ +X3:=NG>ND AND ND=0;\n\ +X4:=COUNT(高点 OR 低点,0)=0 AND (LLV(C,0)-REF(C,BARSSINCE(C)))/REF(C,BARSSINCE(C))<-(N/100);\n\ +X5:=COUNT(高点,0)=0 AND ND>0 AND (HHV(C,ND)-REF(C,ND))/REF(C,ND)<=(N/100);\n\ +X6:=COUNT(高点,0)=0 AND ND=0;\n\ +牛市天数:IF(N4 OR N5 OR N6,D0,IF(N1 OR N2 OR N3,DD,0)),COLORRED;\n\ +熊市天数:IF(X4 OR X5 OR X6,D0,IF(X1 OR X2 OR X3,DG,0)),COLORGREEN;" + }; + + return data; +} + +JSIndexScript.prototype.FKX=function() +{ + let data = + { + Name: 'FKX', Description: '反K线', IsMainIndex: false, + Script: //脚本 +"DRAWKLINE(-LOW, -OPEN, -HIGH, -CLOSE);" + }; + + return data; +} + +JSIndexScript.prototype.Margin4=function() +{ + let data = + { + Name: '两融资金', Description: '两融资金', IsMainIndex: false, + Script: //脚本 +"TMPV:=IF(FINANCE(3)==0,REF(SCJYVALUE(1,1,1),1),REF(GPJYVALUE(3,1,1),1));\n\ +TMPV1:=IF(FINANCE(3)==0,SCJYVALUE(1,1,0),GPJYVALUE(3,1,0));\n\ +两融:IF(TMPV==0 OR TMPV1==0,DRAWNULL,IF(FINANCE(3)==0,(SCJYVALUE(1,1,1)-REF(SCJYVALUE(1,1,1),1))/10000-(SCJYVALUE(1,2,1)-REF(SCJYVALUE(1,2,1),1))/10000,((GPJYVALUE(3,1,1)-REF(GPJYVALUE(3,1,1),1))-((GPJYVALUE(3,2,1)*C/10000-(REF(GPJYVALUE(3,2,1),1)*REF(C,1)/10000)))))),NODRAW;\n\ +STICKLINE(两融>0,0,两融,2,0),COLORRED;\n\ +STICKLINE(两融<0,0,两融,2,0),COLORCYAN;" + }; + + return data; +} + +JSIndexScript.prototype.ZSDB=function() +{ + let data = + { + Name: 'ZSDB', Description: '指数对比(副图,需下载日线)', IsMainIndex: false, + Script: //脚本 +"A:=REF(INDEXC,1);\n\ +指数涨幅:IF(A>0,(INDEXC-A)*100/A,0),NODRAW;\n\ +DRAWKLINE(INDEXH,INDEXO,INDEXL,INDEXC);" + }; + + return data; +} + +JSIndexScript.prototype.NineTurns=function() +{ + let data = + { + Name: '神奇九转', Description: '九转指标', IsMainIndex: true, + Script: //脚本 +"A1:=C>REF(C,4);\n\ +A2:=CREF(C,4);\n\ +D1:=B2 AND REF(B1,1);\n\ +D2:=B2 AND REF(D1,1);\n\ +D3:=B2 AND REF(D2,1);\n\ +D4:=B2 AND REF(D3,1);\n\ +D5:=B2 AND REF(D4,1);\n\ +D6:=B2 AND REF(D5,1);\n\ +D7:=B2 AND REF(D6,1);\n\ +D8:=B2 AND REF(D7,1);\n\ +D9:=B2 AND REF(D8,1);\n\ +D10:=B2 AND REF(D9,1);\n\ +D11:=B2 AND REF(D10,1);\n\ +D12:=B2 AND REF(D11,1);\n\ +D13:=B2 AND REF(D12,1);\n\ +D14:=B2 AND REF(D13,1);\n\ +DRAWTEXT(D1,H*1.010,'1'),COLORBLUE;\n\ +DRAWTEXT(D2,H*1.010,'2'),COLORBLUE;\n\ +DRAWTEXT(D3,H*1.010,'3'),COLORBLUE;\n\ +DRAWTEXT(D4,H*1.010,'4'),COLORBLUE;\n\ +DRAWTEXT(D5,H*1.010,'5'),COLORBLUE;\n\ +DRAWTEXT(D6,H*1.010,'6'),COLORBLUE;\n\ +DRAWTEXT(D7,H*1.010,'7'),COLORBLUE;\n\ +DRAWTEXT(D8,H*1.010,'8'),COLORBLUE;\n\ +DRAWTEXT(D9,H*1.010,'9'),COLORGREEN;" + }; + + return data; +} + + + +JSIndexScript.prototype.TEST = function () +{ + let data = + { + Name: 'TEST', Description: '测试脚本', IsMainIndex: false, + Args: [{ Name: 'N', Value: 10 }], + Script: //脚本 + "DRAWCHANNEL(OPEN>C,H,L, 'RGB(255,94,102)', 2 ,'5,5','RGBA(58,20,62,0.3)' );" + }; + + return data; +} + + + +/* + Copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 封装行情类图形控件 (H5版本) +*/ + + +//日志输出类 +if (!JSConsole) +{ + var JSConsole= + { + Chart:{ Log:console.log, Warn:console.warn }, //图形日志 + Complier:{ Log:console.log, Warn:console.warn } //编译器日志 + }; +} + + +function JSChart(divElement, bOffscreen) +{ + this.DivElement=divElement; + this.DivToolElement=null; //工具条 + this.JSChartContainer; //画图控件 + + //h5 canvas + this.CanvasElement=document.createElement("canvas"); + this.CanvasElement.className='jschart-drawing'; + this.CanvasElement.id=Guid(); + this.CanvasElement.setAttribute("tabindex",0); + if (this.CanvasElement.style) this.CanvasElement.style.outline='none'; + if(divElement.hasChildNodes()) + { + JSConsole.Chart.Log("[JSChart::JSChart] divElement hasChildNodes", divElement.childNodes); + } + divElement.appendChild(this.CanvasElement); + + //离屏 + this.OffscreenCanvasElement; + if (bOffscreen==true) this.OffscreenCanvasElement=document.createElement("canvas"); + + //改参数div + this.ModifyIndexDialog=new ModifyIndexDialog(divElement); + this.ChangeIndexDialog=new ChangeIndexDialog(divElement); + this.MinuteDialog=new MinuteDialog(divElement); + + this.OnSize=function(option) //{ Type:1 新版本OnSize Redraw:是否重绘 } + { + //画布大小通过div获取 + var height=parseInt(this.DivElement.style.height.replace("px","")); + if (this.ToolElement) + { + //TODO调整工具条大小 + height-=this.ToolElement.style.height.replace("px",""); //减去工具条的高度 + } + + this.CanvasElement.height=height; + this.CanvasElement.width=parseInt(this.DivElement.style.width.replace("px","")); + this.CanvasElement.style.width=this.CanvasElement.width+'px'; + this.CanvasElement.style.height=this.CanvasElement.height+'px'; + + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + this.CanvasElement.height*=pixelTatio; + this.CanvasElement.width*=pixelTatio; + + if (this.OffscreenCanvasElement) + { + this.OffscreenCanvasElement.height=this.CanvasElement.height; + this.OffscreenCanvasElement.width=this.CanvasElement.width; + } + + JSConsole.Chart.Log(`[JSChart::OnSize] devicePixelRatio=${window.devicePixelRatio}, height=${this.CanvasElement.height}, width=${this.CanvasElement.width}`); + + if (option && option.Redraw==false) return; + + if (this.JSChartContainer) + { + if (this.JSChartContainer.OnSize && option && option.Type==1) + { + this.JSChartContainer.OnSize(); + } + else + { + if (this.JSChartContainer.Frame) this.JSChartContainer.Frame.SetSizeChage(true); + this.JSChartContainer.Draw(); + } + } + } + + //手机屏需要调整 间距 + this.AdjustChartBorder=function(chart) + { + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + + chart.Frame.ChartBorder.Left*=pixelTatio; + chart.Frame.ChartBorder.Right*=pixelTatio; + chart.Frame.ChartBorder.Top*=pixelTatio; + chart.Frame.ChartBorder.Bottom*=pixelTatio; + } + + this.AdjustTitleHeight=function(chart) + { + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + + for(var i=0;i=0) chart.DragMode=option.KLine.DragMode; + if (option.KLine.Right>=0) chart.Right=option.KLine.Right; + if (option.KLine.Period>=0) chart.Period=option.KLine.Period; + if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false); + if (option.KLine.IndexTreeApiUrl) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl; + if (option.KLine.KLineDoubleClick==false) chart.MinuteDialog=this.MinuteDialog=null; + if (option.KLine.IndexTreeApiUrl!=null) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl; + if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false; + if (option.KLine.MaxRequestMinuteDayCount>0) chart.MaxRequestMinuteDayCount=option.KLine.MaxRequestMinuteDayCount; + if (option.KLine.DrawType) chart.KLineDrawType=option.KLine.DrawType; + if (option.KLine.FirstShowDate>19910101) chart.CustomShow={ Date:option.KLine.FirstShowDate, PageSize:option.KLine.PageSize }; + if (option.KLine.RightSpaceCount>0) chart.RightSpaceCount=option.KLine.RightSpaceCount; + if (option.KLine.ZoomType>0) chart.ZoomType=option.KLine.ZoomType; + if (option.KLine.DataWidth>=1) chart.KLineSize={ DataWidth:option.KLine.DataWidth }; + if (IFrameSplitOperator.IsNumber(option.KLine.RightFormula)) chart=RightFormula=option.KLine.RightFormula; + } + + if (option.EnableFlowCapital) + { + var item=option.EnableFlowCapital; + if (item.BIT==true) chart.EnableFlowCapital.BIT=item.BIT; + } + + if (IFrameSplitOperator.IsBool(option.EnableBorderDrag)) + { + chart.EnableBorderDrag=option.EnableBorderDrag; + } + + if (option.Page) + { + if (option.Page.Day && option.Page.Day.Enable==true) chart.Page.Day.Enable=true; + if (option.Page.Minute && option.Page.Minute.Enable==true) chart.Page.Minute.Enable=true; + } + + if (option.DragDownload) + { + if (option.DragDownload.Day && option.DragDownload.Day.Enable==true) chart.DragDownload.Day.Enable=true; + if (option.DragDownload.Minute && option.DragDownload.Minute.Enable==true) chart.DragDownload.Minute.Enable=true; + } + + if (option.Language) + { + if (option.Language==='CN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + else if(option.Language==='EN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID; + } + + if (option.SourceDatatLimit) chart.SetSourceDatatLimit(option.SourceDatatLimit); + + if (option.DrawPicture) //画图工具 + { + if (option.DrawPicture.StorageKey && chart.ChartDrawStorage) chart.ChartDrawStorage.Load(option.DrawPicture.StorageKey); + } + + if (option.DrawTool) //画图工具 + { + if (option.DrawTool.StorageKey && chart.ChartDrawStorage) chart.ChartDrawStorage.Load(option.DrawTool.StorageKey); + } + + if (IFrameSplitOperator.IsNumber(option.StepPixel)) chart.StepPixel=option.StepPixel; + if (option.ZoomStepPixel>0) chart.ZoomStepPixel=option.ZoomStepPixel; + if (option.IsApiPeriod==true) chart.IsApiPeriod=option.IsApiPeriod; + + if (!option.Windows || option.Windows.length<=0) return null; + + //创建子窗口 + chart.Create(option.Windows.length, option.Listener); + + if (option.Border) + { + var item=option.Border; + if (IFrameSplitOperator.IsNumber(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + else option.Border.Left=chart.Frame.ChartBorder.Left; + if (IFrameSplitOperator.IsNumber(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + else option.Border.Right=chart.Frame.ChartBorder.Right; + if (IFrameSplitOperator.IsNumber(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + else option.Border.Top=chart.Frame.ChartBorder.Top; + if (IFrameSplitOperator.IsNumber(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + else option.Border.Bottom=chart.Frame.ChartBorder.Bottom; + + if (item.AutoLeft) chart.Frame.AutoLeftBorder=item.AutoLeft; + if (item.AutoRight) chart.Frame.AutoRightBorder=item.AutoRight; + } + + this.AdjustChartBorder(chart); + + if (option.KLine) + { + if (option.KLine.PageSize > 0) //一屏显示的数据个数 + { + let pageSize = chart.GetMaxMinPageSize(); + if (pageSize.Max>10 && pageSize.Max < option.KLine.PageSize) chart.PageSize = pageSize.Max; + else if (pageSize.Min>10 && pageSize.Min> option.KLine.PageSize) chart.PageSize=pageSize.Min; + else chart.PageSize = option.KLine.PageSize; + } + } + + //取消显示十字光标刻度信息 + if (option.IsCorssOnlyDrawKLine===true) chart.ChartCorssCursor.IsOnlyDrawKLine=option.IsCorssOnlyDrawKLine; + if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsClickShowCorssCursor==true) chart.IsClickShowCorssCursor=option.IsClickShowCorssCursor; + if (option.CorssCursorInfo) + { + var item=option.CorssCursorInfo; + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left=option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right=option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom=option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss===false) chart.ChartCorssCursor.IsShowCorss=option.CorssCursorInfo.IsShowCorss; + if (option.CorssCursorInfo.IsShowClose == true) chart.ChartCorssCursor.IsShowClose = option.CorssCursorInfo.IsShowClose; //Y轴显示收盘价 + if (option.CorssCursorInfo.PressTime) chart.PressTime=option.CorssCursorInfo.PressTime; //长按显示十字光标的时间 + if (IFrameSplitOperator.IsNumber(option.CorssCursorInfo.HPenType)) chart.ChartCorssCursor.HPenType=option.CorssCursorInfo.HPenType; + if (option.CorssCursorInfo.VPenType>0) chart.ChartCorssCursor.VPenType=option.CorssCursorInfo.VPenType; + if (IFrameSplitOperator.IsNumber(item.VLineType)) chart.ChartCorssCursor.VLineType=item.VLineType; + if (option.CorssCursorInfo.DateFormatType>0) chart.ChartCorssCursor.StringFormatX.DateFormatType=option.CorssCursorInfo.DateFormatType; + if (IFrameSplitOperator.IsBool(item.IsDrawXRangeBG)) chart.ChartCorssCursor.IsDrawXRangeBG=item.IsDrawXRangeBG; + if (IFrameSplitOperator.IsBool(option.CorssCursorInfo.IsFixXLastTime)) chart.ChartCorssCursor.IsFixXLastTime=option.CorssCursorInfo.IsFixXLastTime; + } + + //保存十字光标文字高度 + option.CorssCursor={}; + option.CorssCursor.TitleHeight=chart.ChartCorssCursor.TextHeight; + + + + if (option.Frame) + { + for(var i=0;i=0) chart.Frame.SubFrame[i].Frame.ChartBorder.TopSpace=item.TopSpace; + if (item.BottomSpace>=0) chart.Frame.SubFrame[i].Frame.ChartBorder.BottomSpace=item.BottomSpace; + + if (item.RightTextPosition>0) chart.Frame.SubFrame[i].Frame.YTextPosition[1]=item.RightTextPosition; + if (item.LeftTextPosition>0) chart.Frame.SubFrame[i].Frame.YTextPosition[0]=item.LeftTextPosition; + + if (item.IsShowXLine==false) chart.Frame.SubFrame[i].Frame.IsShowXLine=item.IsShowXLine; + if (item.IsShowYLine==false) chart.Frame.SubFrame[i].Frame.IsShowYLine=item.IsShowYLine; + if (IFrameSplitOperator.IsNumber(item.YTextBaseline)) chart.Frame.SubFrame[i].Frame.YTextBaseline=item.YTextBaseline; + + if (item.YCoordinateType>0) chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=item.YCoordinateType; + if (item.IsYReverse==true) chart.Frame.SubFrame[0].Frame.CoordinateType=1; //反转坐标 + if (IFrameSplitOperator.IsNumber(item.PercentageTextFormat)) subFrame.YSplitOperator.PercentageTextFormat=item.PercentageTextFormat; //百分比坐标格式 + + if (item.DefaultYMaxMin) chart.Frame.SubFrame[i].Frame.YSplitOperator.DefaultYMaxMin=item.DefaultYMaxMin; + if (IFrameSplitOperator.IsBool(item.EnableRemoveZero)) chart.Frame.SubFrame[i].Frame.YSplitOperator.EnableRemoveZero=item.EnableRemoveZero; + if (IFrameSplitOperator.IsPlusNumber(item.MinYDistance)) chart.Frame.SubFrame[i].Frame.MinYDistance=item.MinYDistance; + if (IFrameSplitOperator.IsNumber(item.BorderLine)) chart.Frame.SubFrame[i].Frame.BorderLine=item.BorderLine; + if (IFrameSplitOperator.IsBool(item.IsShowIndexTitle)) chart.Frame.SubFrame[i].Frame.IsShowIndexTitle=item.IsShowIndexTitle; + if (IFrameSplitOperator.IsBool(item.IsDrawTitleBottomLine)) subFrame.IsDrawTitleBottomLine=item.IsDrawTitleBottomLine; + } + } + + if (option.KLine) + { + if (option.KLine.ShowKLine == false) chart.ChartPaint[0].IsShow = false; + if (option.KLine.InfoPosition>0) chart.ChartPaint[0].InfoPosition=option.KLine.InfoPosition; + if (option.KLine.IsShowMaxMinPrice == false) chart.ChartPaint[0].IsShowMaxMinPrice=option.KLine.IsShowMaxMinPrice; + } + + if(option.KLineTitle) + { + if(option.KLineTitle.IsShowName==false) chart.TitlePaint[0].IsShowName=false; + if(option.KLineTitle.IsShowSettingInfo==false) chart.TitlePaint[0].IsShowSettingInfo=false; + if(option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false; + } + + //叠加股票 + + if (option.Overlay) + { + for(var i=0;i0) chart.WindowIndex[i].StringFormat=item.StringFormat; + if (item.FloatPrecision>=0) chart.WindowIndex[i].FloatPrecision=item.FloatPrecision; + } + + } + + if (item.Modify!=null) chart.Frame.SubFrame[i].Frame.ModifyIndex=item.Modify; + if (item.Change!=null) chart.Frame.SubFrame[i].Frame.ChangeIndex=item.Change; + if (item.Close!=null) chart.Frame.SubFrame[i].Frame.CloseIndex=item.Close; + if (item.Overlay!=null) chart.Frame.SubFrame[i].Frame.OverlayIndex=item.Overlay; + if (item.IsDrawTitleBG==true) chart.Frame.SubFrame[i].Frame.IsDrawTitleBG=item.IsDrawTitleBG; + + if (IFrameSplitOperator.IsNumber(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight; + else item.TitleHeight=chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight; + if (item.IsShowTitleArraw==false) chart.Frame.SubFrame[i].Frame.IsShowTitleArraw=false; + if (item.IsShowIndexName==false) chart.Frame.SubFrame[i].Frame.IsShowIndexName=false; + if (item.IsShowOverlayIndexName==false) chart.Frame.SubFrame[i].Frame.IsShowOverlayIndexName=false; + if (item.IndexParamSpace>=0) chart.Frame.SubFrame[i].Frame.IndexParamSpace=item.IndexParamSpace; + if (item.OverlayIndexType) + { + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.Position)) chart.Frame.SubFrame[i].Frame.OverlayIndexType.Position=item.OverlayIndexType.Position; + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.LineSpace)) chart.Frame.SubFrame[i].Frame.OverlayIndexType.LineSpace=item.OverlayIndexType.LineSpace; + } + } + + //叠加指标宽度 + if (option.OverlayIndexFrameWidth>40) chart.OverlayIndexFrameWidth=option.OverlayIndexFrameWidth; + + //叠加指标 + if (IFrameSplitOperator.IsNonEmptyArray(option.OverlayIndex)) + { + for(var i=0;i=chart.Frame.SubFrame.length) continue; + + var itemString = JSON.stringify(item); + var obj = JSON.parse(itemString); + if (item.Index) obj.IndexName=item.Index; + if (item.Windows>=0) obj.WindowIndex=item.Windows; + chart.CreateOverlayWindowsIndex(obj); + } + + } + + this.AdjustTitleHeight(chart); + + return chart; + } + + //自定义指数历史K线图 + this.CreateCustomKLineChartContainer=function(option) + { + var chart=new CustomKLineChartContainer(this.CanvasElement); + + //创建改参数div + chart.ModifyIndexDialog=this.ModifyIndexDialog; + chart.ChangeIndexDialog=this.ChangeIndexDialog; + chart.MinuteDialog=this.MinuteDialog; + + //右键菜单 + if (option.IsShowRightMenu==true) chart.RightMenu=new KLineRightMenu(this.DivElement); + + if (option.KLine) //k线图的属性设置 + { + if (option.KLine.DragMode>=0) chart.DragMode=option.KLine.DragMode; + if (option.KLine.Right>=0) chart.Right=option.KLine.Right; + if (option.KLine.Period>=0) chart.Period=option.KLine.Period; + if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false); + if (option.KLine.IndexTreeApiUrl) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl; + if (option.KLine.KLineDoubleClick==false) chart.MinuteDialog=this.MinuteDialog=null; + if (option.KLine.IndexTreeApiUrl!=null) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl; + if (option.KLine.PageSize>0) chart.PageSize=option.KLine.PageSize; + if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false; + } + + if (option.CustomStock) chart.CustomStock=option.CustomStock; + if (option.QueryDate) chart.QueryDate=option.QueryDate; + + if (!option.Windows || option.Windows.length<=0) return null; + + //创建子窗口 + chart.Create(option.Windows.length); + + if (option.Border) + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + } + + if (option.IsShowCorssCursorInfo==false) //取消显示十字光标刻度信息 + { + chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo; + } + + if (option.Frame) + { + for(var i=0;i0) windowsCount+=option.Windows.length; //指标窗口从第3个窗口开始 + if (option.EnableScrollUpDown==true) chart.EnableScrollUpDown=option.EnableScrollUpDown; + if (option.DisableMouse==true) chart.DisableMouse=option.DisableMouse; + if (option.ScriptError) chart.ScriptErrorCallback=option.ScriptError; //指标执行错误回调 + if (IFrameSplitOperator.IsString(option.SplashTitle)) chart.LoadDataSplashTitle=option.SplashTitle; + if (IFrameSplitOperator.IsBool(option.EnableSelectRect)) chart.EnableSelectRect=option.EnableSelectRect; //是否启用区间选择 + if (IFrameSplitOperator.IsBool(option.EnableZoomIndexWindow)) chart.EnableZoomIndexWindow=option.EnableZoomIndexWindow; + + chart.SelectRectDialog=new MinuteSelectRectDialog(this.DivElement); + + + if (option.Minute) //分钟走势图属性设置 + { + if (option.Minute.IsShowTooltip==false) chart.IsShowTooltip=false; + if (option.Minute.DragMode>=0) chart.DragMode=option.Minute.DragMode; + } + + if (option.Language) + { + if (option.Language==='CN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + else if(option.Language==='EN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID; + } + + if (option.Info && option.Info.length>0) chart.SetMinuteInfo(option.Info,false); + + if (option.DrawTool) //画图工具 + { + if (option.DrawTool.StorageKey && chart.ChartDrawStorage) chart.ChartDrawStorage.Load(option.DrawTool.StorageKey); + } + + if (option.BeforeOpen) //集合竞价 + { + var item=option.BeforeOpen; + + if (IFrameSplitOperator.IsBool(item.IsShow)) chart.IsShowBeforeData=item.IsShow; + if (IFrameSplitOperator.IsNumber(item.Width)) chart.ExtendWidth.Left=item.Width; + } + + if (option.AfterClose) //收盘集合竞价 + { + var item=option.AfterClose; + if (IFrameSplitOperator.IsBool(item.IsShow)) chart.IsShowAfterData=item.IsShow; + if (IFrameSplitOperator.IsNumber(item.Width)) chart.ExtendWidth.Right=item.Width; + } + + chart.Create(windowsCount,option.Listener); //创建子窗口 + + if (option.CorssCursorInfo) + { + var item=option.CorssCursorInfo; + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left=option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right=option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom=option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss===false) chart.ChartCorssCursor.IsShowCorss=option.CorssCursorInfo.IsShowCorss; + if (option.CorssCursorInfo.RightTextFormat>0) chart.ChartCorssCursor.TextFormat.Right=option.CorssCursorInfo.RightTextFormat; + if (option.CorssCursorInfo.IsOnlyDrawMinute == true) chart.ChartCorssCursor.IsOnlyDrawMinute = option.CorssCursorInfo.IsOnlyDrawMinute; //Y轴显示收盘价 + if (IFrameSplitOperator.IsBool(option.CorssCursorInfo.IsFixXLastTime)) chart.ChartCorssCursor.IsFixXLastTime=option.CorssCursorInfo.IsFixXLastTime; + } + + if (option.MinuteInfo) chart.CreateMinuteInfo(option.MinuteInfo); + + if (option.IsShowRightMenu==true) chart.RightMenu=new MinuteRightMenu(this.DivElement); + + if (option.DayCount>1) chart.DayCount=option.DayCount; + + if (option.Border) + { + var item=option.Border; + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + + if (item.AutoLeft) chart.Frame.AutoLeftBorder=item.AutoLeft; + if (item.AutoRight) chart.Frame.AutoRightBorder=item.AutoRight; + } + + if (option.SplashTitle) chart.ChartSplashPaint.SplashTitle=option.SplashTitle; + + if (IFrameSplitOperator.IsBool(option.EnableBorderDrag)) + { + chart.EnableBorderDrag=option.EnableBorderDrag; + } + + this.AdjustChartBorder(chart); + + if (option.Frame) + { + for(var i=0;i=0) chart.Frame.SubFrame[i].Height = item.Height; + if (item.Custom) chart.Frame.SubFrame[i].Frame.YSplitOperator.Custom=item.Custom; + if (item.RightTextFormat>0) chart.Frame.SubFrame[i].Frame.YSplitOperator.RightTextFormat=item.RightTextFormat; + if (IFrameSplitOperator.IsNumber(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight; + if (IFrameSplitOperator.IsNumber(item.BorderLine)) chart.Frame.SubFrame[i].Frame.BorderLine=item.BorderLine; + if (IFrameSplitOperator.IsBool(item.EnableRemoveZero)) chart.Frame.SubFrame[i].Frame.YSplitOperator.EnableRemoveZero=item.EnableRemoveZero; + if (IFrameSplitOperator.IsNumber(item.FloatPrecision)) chart.Frame.SubFrame[i].Frame.YSplitOperator.FloatPrecision=item.FloatPrecision; + if (IFrameSplitOperator.IsNumber(item.YTextBaseline)) chart.Frame.SubFrame[i].Frame.YTextBaseline=item.YTextBaseline; + } + + chart.UpdateXShowText(); + } + + if (option.ExtendChart) + { + for(var i=0;i0) chart.Frame.SubFrame[0].Frame.YSplitOperator.SplitType=option.MinuteLine.SplitType; + } + + if(option.MinuteTitle) + { + var item=option.MinuteTitle; + if(IFrameSplitOperator.IsBool(item.IsShowName)) chart.TitlePaint[0].IsShowName=item.IsShowName; + if(IFrameSplitOperator.IsBool(item.IsShowDate)) chart.TitlePaint[0].IsShowDate=item.IsShowDate; + if(IFrameSplitOperator.IsBool(item.IsShowTime)) chart.TitlePaint[0].IsShowTime=item.IsShowTime; + if(IFrameSplitOperator.IsBool(item.IsTitleShowLatestData)) chart.IsTitleShowLatestData=item.IsTitleShowLatestData; + //if(option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false; + } + + if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsShowBeforeData===true) chart.IsShowBeforeData=option.IsShowBeforeData; + + //分钟数据指标从第3个指标窗口设置 + if (IFrameSplitOperator.IsNonEmptyArray(option.Windows)) + { + let scriptData = new JSIndexScript(); + for(var i=0;i0) chart.WindowIndex[index].StringFormat=item.StringFormat; + if (item.FloatPrecision>=0) chart.WindowIndex[index].FloatPrecision=item.FloatPrecision; + } + } + + if (item.Modify!=null) chart.Frame.SubFrame[index].Frame.ModifyIndex=item.Modify; + if (item.Change!=null) chart.Frame.SubFrame[index].Frame.ChangeIndex=item.Change; + if (item.Close!=null) chart.Frame.SubFrame[index].Frame.CloseIndex=item.Close; + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[index].Frame.ChartBorder.TitleHeight=item.TitleHeight; + } + } + + this.AdjustTitleHeight(chart); + + //叠加指标 + if (IFrameSplitOperator.IsNonEmptyArray(option.OverlayIndex)) + { + for(var i=0;i=chart.Frame.SubFrame.length) continue; + + var itemString = JSON.stringify(item); + var obj = JSON.parse(itemString); + if (item.Index) obj.IndexName=item.Index; + if (item.Windows>=0) obj.WindowIndex=item.Windows; + chart.CreateOverlayWindowsIndex(obj); + } + } + + return chart; + } + + //历史分钟走势图 + this.CreateHistoryMinuteChartContainer=function(option) + { + var chart=new HistoryMinuteChartContainer(this.CanvasElement); + + var windowsCount=2; + if (option.Windows && option.Windows.length>0) windowsCount+=option.Windows.length; //指标窗口从第3个窗口开始 + + chart.Create(windowsCount); //创建子窗口 + + if (option.IsShowCorssCursorInfo==false) //取消显示十字光标刻度信息 + { + chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo; + } + + if (option.Border) + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + } + + let scriptData = new JSIndexScript(); + if (option.Windows) + { + for(var i=0;i=0) chart.Right=option.KLine.Right; + if (option.KLine.Period>=0) chart.Period=option.KLine.Period; + if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false); + if (option.KLine.PageSize>0) chart.PageSize=option.KLine.PageSize; + if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false; + if (option.KLine.MaxRequestMinuteDayCount>0) chart.MaxRequestMinuteDayCount=option.KLine.MaxRequestMinuteDayCount; + if (option.KLine.DrawType) chart.KLineDrawType=option.KLine.DrawType; + if (option.KLine.RightSpaceCount>0) chart.RightSpaceCount=option.KLine.RightSpaceCount; + } + + if (option.Train) + { + if (option.Train.DataCount) chart.TrainDataCount=option.Train.DataCount; + if (option.Train.Callback) chart.TrainCallback=option.Train.Callback; + if (option.Train.StartDate) chart.TrainStartDate=option.Train.StartDate; + } + + if (!option.Windows || option.Windows.length<=0) return null; + + //创建子窗口 + chart.Create(option.Windows.length); + + if (option.Border) + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + } + + this.AdjustChartBorder(chart); + + if (option.IsShowCorssCursorInfo==false) chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo; //取消显示十字光标刻度信息 + if (option.IsCorssOnlyDrawKLine===true) chart.ChartCorssCursor.IsOnlyDrawKLine=option.IsCorssOnlyDrawKLine; + if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsClickShowCorssCursor==true) chart.IsClickShowCorssCursor=option.IsClickShowCorssCursor; + if (option.CorssCursorInfo) + { + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left=option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right=option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom=option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss===false) chart.ChartCorssCursor.IsShowCorss=option.CorssCursorInfo.IsShowCorss; + if (option.CorssCursorInfo.IsShowClose == true) chart.ChartCorssCursor.IsShowClose = option.CorssCursorInfo.IsShowClose; //Y轴显示收盘价 + if (option.CorssCursorInfo.PressTime) chart.PressTime=option.CorssCursorInfo.PressTime; //长按显示十字光标的时间 + if (option.CorssCursorInfo.HPenType>0) chart.ChartCorssCursor.HPenType=option.CorssCursorInfo.HPenType; + if (option.CorssCursorInfo.VPenType>0) chart.ChartCorssCursor.VPenType=option.CorssCursorInfo.VPenType; + } + + //保存十字光标文字高度 + option.CorssCursor={}; + option.CorssCursor.TitleHeight=chart.ChartCorssCursor.TextHeight; + + if (option.Frame) + { + for(var i=0;i0) chart.Frame.SubFrame[i].Height = item.Height; + + if (item.IsShowLeftText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText; //显示左边刻度 + if (item.IsShowRightText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowRightText=item.IsShowRightText; //显示右边刻度 + } + } + + if (option.KLine) + { + if (option.KLine.ShowKLine == false) chart.ChartPaint[0].IsShow = false; + if (option.KLine.InfoPosition>0) chart.ChartPaint[0].InfoPosition=option.KLine.InfoPosition; + if (option.KLine.IsShowMaxMinPrice == false) chart.ChartPaint[0].IsShowMaxMinPrice=option.KLine.IsShowMaxMinPrice; + } + + if(option.KLineTitle) //股票名称 日期 周期 + { + if(option.KLineTitle.IsShowName==false) chart.TitlePaint[0].IsShowName=false; + if(option.KLineTitle.IsShowSettingInfo==false) chart.TitlePaint[0].IsShowSettingInfo=false; + if(option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false; + } + + if (option.ExtendChart) + { + for(var i=0;i0) chart.ZoomStepPixel=option.ZoomStepPixel; + + chart.Create(option.Listener); + + if (option.Border) + { + if (IFrameSplitOperator.IsNumber(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + else option.Border.Left=chart.Frame.ChartBorder.Left; + if (IFrameSplitOperator.IsNumber(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + else option.Border.Right=chart.Frame.ChartBorder.Right; + if (IFrameSplitOperator.IsNumber(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + else option.Border.Top=chart.Frame.ChartBorder.Top; + if (IFrameSplitOperator.IsNumber(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + else option.Border.Bottom=chart.Frame.ChartBorder.Bottom; + } + + this.AdjustChartBorder(chart); + + if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + + if (option.CorssCursorInfo) + { + var item=option.CorssCursorInfo; + if (IFrameSplitOperator.IsNumber(item.HPenType)) chart.ChartCorssCursor.HPenType=item.HPenType; + if (IFrameSplitOperator.IsNumber(item.VPenType)) chart.ChartCorssCursor.VPenType=item.VPenType; + if (IFrameSplitOperator.IsBool(item.IsShowTooltip)) chart.ChartCorssCursor.IsShowTooltip=item.IsShowTooltip; + } + + if (option.Frame) + { + var item=option.Frame + if (item.SplitCount) chart.Frame.YSplitOperator.SplitCount=item.SplitCount; + if (IFrameSplitOperator.IsNumber(item.SplitType)) chart.Frame.YSplitOperator.SplitType=item.SplitType; + if (IFrameSplitOperator.IsNumber(item.Height)) chart.Frame.Height = item.Height; + if (IFrameSplitOperator.IsNumber(item.YLineType)) chart.Frame.YSplitOperator.LineType=item.YLineType; + if (IFrameSplitOperator.IsNumber(item.XLineType)) chart.Frame.XSplitOperator.LineType=item.XLineType; + if (Array.isArray(item.IgnoreYValue)) chart.Frame.YSplitOperator.IgnoreYValue=item.IgnoreYValue; + if (item.IsShowLeftText===false || item.IsShowLeftText===true) + { + chart.Frame.IsShowYText[0]=item.IsShowLeftText; + chart.Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText; //显示左边刻度 + } + if (item.IsShowRightText===false || item.IsShowRightText===true) + { + chart.Frame.IsShowYText[1]=item.IsShowRightText; + chart.Frame.YSplitOperator.IsShowRightText=item.IsShowRightText; //显示右边刻度 + } + + if (item.IsShowXLine==false) chart.Frame.IsShowXLine=item.IsShowXLine; + if (item.IsShowYLine==false) chart.Frame.IsShowYLine=item.IsShowYLine; + } + + return chart; + } + + //根据option内容绘制图形 + this.SetOption=function(option) + { + var chart=null; + switch(option.Type) + { + case "历史K线图": + case '历史K线图横屏': + chart=this.CreateKLineChartContainer(option); + break; + case "自定义指数历史K线图": + chart=this.CreateCustomKLineChartContainer(option); + break; + case "分钟走势图": + case "分钟走势图横屏": + chart=this.CreateMinuteChartContainer(option); + break; + case "历史分钟走势图": + chart=this.CreateHistoryMinuteChartContainer(option); + break; + case 'K线训练': + case 'K线训练横屏': + chart=this.CreateKLineTrainChartContainer(option); + break; + case "深度图": + chart=this.CreateDepthChartContainer(option); + break; + case "简单图形": + return this.CreateSimpleChart(option); + case "饼图": + case '雷达图': + return this.CreatePieChart(option); + case '地图': + return this.CreateMapChart(option); + default: + return false; + } + + if (!chart) return false; + + if (option.OnCreatedCallback) option.OnCreatedCallback(chart); + + //是否自动更新 + if (option.IsAutoUpdate!=null) chart.IsAutoUpdate=option.IsAutoUpdate; + if (option.AutoUpdateFrequency>0) chart.AutoUpdateFrequency=option.AutoUpdateFrequency; + //注册事件 + if (option.EventCallback) + { + for(var i=0;i0) + { + var item=this.Start[this.Start.length-1]; + var spanTime=time-item.Time; + if (spanTime>0 && spanTime<300) + { + this.Start.push({ X:x, Y:y, Time:time }); + } + else + { + this.Start=[]; + } + } + else + { + this.Start.push({ X:x, Y:y, Time:time }); + } + } + + this.IsVaildDBClick=function() + { + if (this.Start.length==2) return true; + + return false; + } + + this.AddTouchEnd=function(time) + { + if (this.Start.length<=0) return; + + var item=this.Start[this.Start.length-1]; + var spanTime=time-item.Time; + if (spanTime>=0 && spanTime<150) + { + + } + else + { + this.Start=[]; + } + } +} + +/* + 图形控件 +*/ +function JSChartContainer(uielement, OffscreenElement) +{ + this.ClassName='JSChartContainer'; + var _self = this; + this.Frame; //框架画法 + this.ChartPaint=new Array(); //图形画法 + this.ChartPaintEx=[]; //图形扩展画法 + this.ChartInfo=new Array(); //K线|走势图上信息地雷 + this.ChartInfoPaint; //信息地理 + this.ExtendChartPaint=new Array(); //扩展画法 + this.TitlePaint=new Array(); //标题画法 + this.OverlayChartPaint=new Array(); //叠加信息画法 + this.ChartDrawPicture=new Array(); //画图工具 + this.ChartDrawStorage; //画图工具保存 + this.ChartDrawOption={ IsLockScreen:false, Zoom:5 }; //画图工具设置 { IsLockScreen://是否锁住屏幕, Zoom: //线段|点放大倍数 } + this.CurrentChartDrawPicture=null; //当前的画图工具 + this.SelectChartDrawPicture=null; //当前选中的画图 + this.EnableShowCorssCursor={ DrawPicture:true }; //DrawPicture=画图是否显示十字光标 + this.ChartPictureMenu; //画图工具 单个图形设置菜单 + this.ChartCorssCursor; //十字光标 + this.IsClickShowCorssCursor=false; //手势点击显示十字光标 + this.ChartSplashPaint=null; //等待提示 + this.LoadDataSplashTitle="数据加载中"; //下载数据提示信息 + if (OffscreenElement) + { + this.Canvas=OffscreenElement.getContext("2d"); + this.OffscreenCanvasElement=OffscreenElement; + + this.ShowCanvas=uielement.getContext("2d"); + } + else + { + this.Canvas=uielement.getContext("2d"); //画布 + this.ShowCanvas=null; + } + + this.UIElement=uielement; + this.MouseDrag; + this.PhoneTouchInfo; //手机手势信息 + this.DragMode=1; //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择 3(CLICK_TOUCH_MODE_ID)=长按十字光标显示保留/点击十字光标消失 (使用TouchStatus) + this.EnableBorderDrag=true; //是否可以拖拽边框调整指标框高度 + this.BorderDrag; //{ Index:, } + this.YDrag; //{Index: } //y轴拖拽放大缩小 + this.TouchStatus={ CorssCursorShow:false }, //十字光标是否显示 + this.DragTimer; + this.EnableScrollUpDown=false; //是否可以上下滚动图形(手机端才有) + this.ClickChartTimer=null; //点击图形定时器,解决双击和单击K线事件 + + this.CursorIndex=0; //十字光标X轴索引 + this.LastPoint=new Point(); //鼠标位置 + this.IsForceLandscape=false; //是否强制横屏 + this.CorssCursorTouchEnd = false; //手离开屏幕自动隐藏十字光标 + this.IsTitleShowLatestData=false; //十字/手势不在K线图上,标题显示最新一个数据 + this.StepPixel=4; //移动一个数据需要的像素 0=自动模式(根据K线宽度+间距) + this.ZoomStepPixel=5; //放大缩小手势需要的最小像素 + this.TouchMoveMinAngle=70; //左右移动最小角度 + this.EnableAnimation=false; //是否开启动画 + + //tooltip提示信息 + this.Tooltip=document.createElement("div"); + this.Tooltip.className='jschart-tooltip'; + this.Tooltip.style.background=g_JSChartResource.TooltipBGColor; + this.Tooltip.style.opacity=g_JSChartResource.TooltipAlpha; + this.Tooltip.style["pointer-events"]="none"; + this.Tooltip.id=Guid(); + uielement.parentNode.appendChild(this.Tooltip); + this.IsShowTooltip=true; //是否显示K线tooltip + + //区间选择 + this.SelectRect=document.createElement("div"); + this.SelectRect.className="jschart-selectrect"; + this.SelectRect.style.background=g_JSChartResource.SelectRectBGColor; + this.SelectRect.style["pointer-events"]="none"; + //this.SelectRect.style.opacity=g_JSChartResource.SelectRectAlpha; + this.SelectRect.id=Guid(); + this.SelectRect.oncontextmenu=function() { return false; }; //屏蔽选中区域系统右键菜单 + uielement.parentNode.appendChild(this.SelectRect); + + //区间选择右键菜单 + this.SelectRectRightMenu; + + //坐标轴风格方法 double-更加数值型分割 price-更加股票价格分割 + this.FrameSplitData=new Map(); + this.FrameSplitData.set("double",new SplitData()); + this.FrameSplitData.set("price",new PriceSplitData()); + + //事件回调 + this.mapEvent=new Map(); //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,} + + this.IsOnTouch = false; //是否再操作数据 + this.TouchDrawCount = 0; //手势绘制次数 + this.DisableMouse=false; //禁止鼠标事件 + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.PressTime=500; + + this.NetworkFilter; //网络请求回调 function(data, callback); + this.LastMouseStatus={ } + this.ClickDownPoint; //鼠标点击坐标 {X, Y}, 鼠标放开以后清空为null + this.IsDestroy=false; //是否已经销毁了 + + this.EnableYDrag={ Left:false, Right:false }; //是否可以拖拽Y轴,放大缩小Y轴最大最小值 + this.EnableZoomIndexWindow=false; //是否支持双击缩放附图窗口 + + this.PhoneDBClick=new PhoneDBClick(); + + this.ChartDestory=function() //销毁 + { + this.IsDestroy=true; + this.StopAutoUpdate(); + + if (this.GetLatestVersionTimer!=null) + { + clearTimeout(this.GetLatestVersionTimer); + this.GetLatestVersionTimer=null; + } + } + + + this.GetLatestVersionTimer=null; //获取最新版本 + this.GetLatestVersion=function() + { + var waittimer=1000*60*3.5; + var value="aHR0cHM6Ly9ocWNoYXJ0LnplYWxpbmsuY29tL2FwaS9HZXRWZXJzaW9u"; + JSConsole.Chart.Log("[JSChartContainer::GetLatestVersion] wait for get hqchart latest version. ",waittimer); + this.GetLatestVersionTimer = setTimeout(()=> + { + var width=0, height=0; + if (this.Frame && this.Frame.ChartBorder) + { + width=this.Frame.ChartBorder.GetChartWidth(); + height=this.Frame.ChartBorder.GetChartHeight(); + } + + var url=`${atob(value)}?width=${width}&height=${height}&type=h5`; + + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success:function(data) + { + JSConsole.Chart.Log("[JSChartContainer::GetLatestVersion] hqchart latest version. ",data); + //TODO:判断是否是最新版本 + }, + error:function(request, textStatus, errorThrown) + { + JSConsole.Chart.Log("[JSChartContainer::GetLatestVersion] Get HQChart latest version failed.", request); + } + }); + + }, waittimer); + } + + //设置事件回调 + //{event:事件id, callback:回调函数} + this.AddEventCallback=function(object) + { + if (!object || !object.event || !object.callback) return; + + var data={Callback:object.callback, Source:object}; + this.mapEvent.set(object.event,data); + } + + this.RemoveEventCallback=function(eventid) + { + if (!this.mapEvent.has(eventid)) return; + + this.mapEvent.delete(eventid); + } + + this.GetEventCallback=function(id) //获取事件回调 + { + if (!this.mapEvent.has(id)) return null; + var item=this.mapEvent.get(id); + return item; + } + + //接收指标数据 + this.GetIndexEvent=function() + { + return this.GetEventCallback(JSCHART_EVENT_ID.RECV_INDEX_DATA); + } + + this.GetOverlayIndexEvent=function() + { + return this.GetEventCallback(JSCHART_EVENT_ID.RECV_OVERLAY_INDEX_DATA); + } + + //鼠标事件绑定 + uielement.onmousemove=(e)=>{ this.UIOnMouseMove(e);} + uielement.oncontextmenu=(e)=> { return this.UIOnContextMenu(e); } + uielement.ondblclick=(e)=>{ this.UIOnDblClick(e); } + uielement.onmousedown=(e)=> { this.UIOnMouseDown(e); } + uielement.onmouseout=(e)=>{ this.UIOnMounseOut(e); } + + this.UIOnMouseMove=function(e) + { + //JSConsole.Chart.Log('[JSChartContainer.UIOnMouseMove] e.clientX, e.clientY, left, top ',e.clientX, e.clientY, this.getBoundingClientRect().left,this.getBoundingClientRect().top); + var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速 + var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio; + var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio; + + //加载数据中,禁用鼠标事件 + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + if (this.DisableMouse==true) return; + if (this.BorderDrag) return; + if (this.YDrag) return; + + //保存最后一次鼠标移动信息 + var MoveStatus={ X:x, Y:y, IsInClient: this.IsMouseOnClient(x,y) }; + this.LastMouseStatus.OnMouseMove=MoveStatus; + //JSConsole.Chart.Log("[JSChartContainer::UIOnMouseMove] MoveStatus", MoveStatus); + + this.OnMouseMove(x,y,e); + } + + this.IsMouseOnClient=function(x,y) + { + var isInClient=false; + this.Canvas.beginPath(); + this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight()); + isInClient=this.Canvas.isPointInPath(x,y); + return isInClient; + } + + this.UIOnContextMenu=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var x = e.clientX-this.UIElement.getBoundingClientRect().left; + var y = e.clientY-this.UIElement.getBoundingClientRect().top; + + if(typeof(this.OnRightMenu)=='function') this.OnRightMenu(x,y,e); //右键菜单事件 + + return false; + } + + this.UIOnDblClick=function(e) + { + var pixelTatio = GetDevicePixelRatio(); + var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio; + var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio; + this.OnDoubleClick(x,y,e); + } + + //是否在拖拽Y轴上 + this.TryYDrag=function(x,y) + { + if (!this.EnableYDrag) return null; + if (!this.EnableYDrag.Left && !this.EnableYDrag.Right) return null; + if (!this.Frame || !this.Frame.PtInFrameY) return null; + + var dragY=this.Frame.PtInFrameY(x,y); + if (!dragY || dragY.Index<0) return null; + + if (dragY.Left && this.EnableYDrag.Left && this.Frame.IsEnableDragY(dragY.Index)) + { + return dragY; + } + + if (dragY.Right && this.EnableYDrag.Right && this.Frame.IsEnableDragY(dragY.Index)) + { + return dragY; + } + + return null; + } + + //是否可以上下拖拽 + this.TryUpDownDrag=function(x,y) + { + var windowIndex=this.Frame.PtInFrame(x,y); + if (windowIndex<0) return null; + + var item=this.Frame.SubFrame[windowIndex]; + if (!item || !item.Frame) return null; + + var frame=item.Frame; + if (!frame.YSplitOperator || !frame.YSplitOperator.FixedYMaxMin) return null; + + return { Index:windowIndex, X:x, Y:y }; + } + + this.TryRectSelectDrag=function(x,y) + { + var paint=this.GetRectSelectPaint(); + if (!paint) return null; + if (paint.GetPointCount()<=0) return null; + + var item=paint.PtInPaint(x,y); + + return { Item:item, X:x, Y:y }; + } + + this.UIOnMouseDown=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + this.ClickDownPoint={ X:e.clientX, Y:e.clientY }; + this.IsOnTouch=true; + this.BorderDrag=null; + this.YDrag=null; + this.UpDownDrag=null; + this.RectSelectDrag=null; + + var pixelTatio = GetDevicePixelRatio(); + var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio; + var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio; + + var button=this.Frame.PtInButtons(x,y); + if (button && this.ClickFrameButton) + { + this.ClickFrameButton(button); + return; + } + + if (this.TryClickLock) + { + //JSConsole.Chart.Log('[uielement.onmousedown] left, top ',e.clientX, e.clientY, this.getBoundingClientRect().left,this.getBoundingClientRect().top); + if (this.TryClickLock(x,y)) return; + } + + this.HideSelectRect(); + if (this.SelectRectRightMenu) this.SelectRectRightMenu.Hide(); + if (this.ChartPictureMenu) this.ChartPictureMenu.Hide(); + + var paint=this.GetRectSelectPaint(); + if (paint && paint.GetPointCount()>0) + { + var item=paint.PtInPaint(x,y); + if (item) + { + this.UIElement.style.cursor="ew-resize"; + this.RectSelectDrag={ Index:item.PointIndex }; + JSConsole.Chart.Log("[JSChartContainer::UIOnMouseDown] drag rect select ",item); + } + } + + if (!this.RectSelectDrag && this.ClearRectSelect(false)) this.Draw(); + + if (this.EnableBorderDrag && this.Frame) + { + var dragBorder=this.Frame.PtInFrameBorder(x,y); + if (dragBorder && dragBorder.Index>=0) + { + this.UIElement.style.cursor="n-resize"; + this.BorderDrag={ Index:dragBorder.Index }; + JSConsole.Chart.Log("[JSChartContainer::UIOnMouseDown] DragBorder ",dragBorder); + } + } + + //拖拽Y轴缩放 + if (!this.BorderDrag) + { + var dragY=this.TryYDrag(x,y); + if (dragY) + { + this.UIElement.style.cursor=dragY.Position==0 ? "n-resize":"row-resize"; + this.YDrag=dragY; + JSConsole.Chart.Log("[JSChartContainer::UIOnMouseDown] dragY ",dragY); + } + else + { + var dragUpDown=this.TryUpDownDrag(x,y); + if (dragUpDown) + { + this.UIElement.style.cursor="pointer"; + this.UpDownDrag=dragUpDown; + JSConsole.Chart.Log("[JSChartContainer::UIOnMouseDown] dragUpDown ",dragUpDown); + } + } + } + + if(this.DragMode==0) return; + + var drag= + { + "Click":{}, + "LastMove":{} //最后移动的位置 + }; + + drag.Click.X=e.clientX; + drag.Click.Y=e.clientY; + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + + this.MouseDrag=drag; + this.SelectChartDrawPicture=null; + + if (this.BorderDrag) + { + + } + else if (this.YDrag || this.UpDownDrag || this.RectSelectDrag) + { + + } + else if (this.CurrentChartDrawPicture) //画图工具模式 + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2) + this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y); + else + { + this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y); + //只有1个点 直接完成 + if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo(); + } + } + else //是否在画图工具上 + { + var drawPictrueData={}; + drawPictrueData.X=(e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio; + drawPictrueData.Y=(e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio; + if (this.GetChartDrawPictureByPoint(drawPictrueData)) + { + if (drawPictrueData.ChartDrawPicture.EnableMove==true) + { + drawPictrueData.ChartDrawPicture.Status=20; + drawPictrueData.ChartDrawPicture.ValueToPoint(); + drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex; + this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture; + this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture; + } + else + { + this.CurrentChartDrawPicture=null; + this.SelectChartDrawPicture=null; + } + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_DRAWPICTURE); //选中画图工具事件 + if (event && event.Callback) + { + var sendData={ DrawPicture: drawPictrueData.ChartDrawPicture }; + event.Callback(event,sendData,this); + } + else + { + this.OnSelectChartPicture(drawPictrueData.ChartDrawPicture); //选中画图工具事件 + } + } + else + { + if (this.ClickChartTimer!=null) + { + clearTimeout(this.ClickChartTimer); + this.ClickChartTimer=null; + } + + var self=this; + var ptClick={ X:this.ClickDownPoint.X, Y:this.ClickDownPoint.Y }; + this.ClickChartTimer = setTimeout(function() + { + self.TryClickPaintEvent(JSCHART_EVENT_ID.ON_CLICK_CHART_PAINT,ptClick,e); + }, 250); + + } + } + + document.onmousemove=(e)=>{ this.DocOnMouseMove(e); } + document.onmouseup=(e)=> { this.DocOnMouseUp(e); } + } + + this.DocOnMouseMove=function(e) + { + //加载数据中,禁用鼠标事件 + if (this.ChartSplashPaint && this.IsEnableSplash == true) return; + + var drag=this.MouseDrag; + if (!drag) return; + + var moveSetp=Math.abs(drag.LastMove.X-e.clientX); + var moveSetpY=Math.abs(drag.LastMove.Y-e.clientY); + var isDragSelectRect=(this.RectSelectDrag && this.RectSelectDrag.Index>=0); + + if (this.BorderDrag && this.BorderDrag.Index>=0) + { + if(Math.abs(drag.LastMove.Y-e.clientY)<5) return; + + var yMove=e.clientY-drag.LastMove.Y; + + this.OnMoveFromeBorder(this.BorderDrag.Index, yMove); + + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + } + else if (this.YDrag && this.YDrag.Index>=0) + { + if(moveSetpY<5) return; + + var yMove=e.clientY-drag.LastMove.Y; + + //this.UIElement.style.cursor="n-resize"; + + JSConsole.Chart.Log("[JSChartContainer::DocOnMouseMove] YDrag ",this.YDrag,yMove); + this.OnZoomUpDownFrameY(this.YDrag, yMove); + + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + } + else if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==1 || drawPicture.Status==2) + { + if(Math.abs(drag.LastMove.X-e.clientX)<5 && Math.abs(drag.LastMove.Y-e.clientY)<5) return; + if(this.SetChartDrawPictureSecondPoint(e.clientX,e.clientY)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==3) + { + if(this.SetChartDrawPictureThirdPoint(e.clientX,e.clientY)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==20) //画图工具移动 + { + if(Math.abs(drag.LastMove.X-e.clientX)<5 && Math.abs(drag.LastMove.Y-e.clientY)<5) return; + + if(this.MoveChartDrawPicture(e.clientX-drag.LastMove.X,e.clientY-drag.LastMove.Y)) + { + this.DrawDynamicInfo(); + } + } + + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + } + else if (this.DragMode==1 || isDragSelectRect) //数据上下左右拖拽 + { + this.OnDragMode_One({X:moveSetp, Y:moveSetpY}, e); + } + else if (this.DragMode==2) //区间选择 + { + var yMoveSetp=Math.abs(drag.LastMove.Y-e.clientY); + + if (moveSetp<5 && yMoveSetp<5) return; + + this.UIElement.style.cursor="default"; + + var x=drag.Click.X-uielement.getBoundingClientRect().left; + var y=drag.Click.Y-uielement.getBoundingClientRect().top; + var x2=e.clientX-uielement.getBoundingClientRect().left; + var y2=e.clientY-uielement.getBoundingClientRect().top; + this.ShowSelectRect(x,y,x2,y2); + + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + + } + } + + this.OnDragMode_One=function(moveData, e) + { + var moveSetp=moveData.X; + var moveSetpY=moveData.Y; + var drag=this.MouseDrag; + + if (moveSetp<5 && moveSetpY<5) return; + + var bNeedDraw=false; + var bUpDownY=false; + if (moveSetpY>=5) + { + if (this.UpDownDrag && this.UpDownDrag.Index>=0) + { + var yMove=e.clientY-drag.LastMove.Y; + JSConsole.Chart.Log("[JSChartContainer::OnDragMode_One] UpDownDrag ",this.UpDownDrag,yMove); + this.UIElement.style.cursor="pointer"; + if (this.OnUpDonwFrameY(this.UpDownDrag, yMove)) + { + bNeedDraw=true; + bUpDownY=true; + } + drag.LastMove.Y=e.clientY; + } + } + + if (moveSetp>=5) + { + var isLeft=true; + if (drag.LastMove.X=0) + { + var obj={ X:e.clientX, Y:e.clientY, PointIndex:this.RectSelectDrag.Index, Name:"MoveRectSelectLine" }; + if (this.MoveRectSelectPoint(obj)) bNeedDraw=true; + } + else + { + this.UIElement.style.cursor="pointer"; + var oneStepWidth=this.GetMoveOneStepWidth(); + if (moveSetp=0); + var bClearDrawPicture=true; + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3) + { + drawPicture.PointStatus=drawPicture.Status; + if (this.FinishChartDrawPicturePoint()) + this.DrawDynamicInfo(); + else + bClearDrawPicture=false; + } + else if (drawPicture.Status==20) + { + if (this.FinishMoveChartDrawPicture()) + this.DrawDynamicInfo(); + } + } + else if (isDragSelectRect) //区间选择拖动范围 + { + if (this.OnDragSelectRectMouseUp) this.OnDragSelectRectMouseUp(e); + } + else if (IsMinuteChart) + { + if (this.OnMinuteSelectRectMouseUp) this.OnMinuteSelectRectMouseUp(e); + } + else if (this.DragMode==2) //区间选择 + { + var drag=this.MouseDrag; + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + + var selectData=new SelectRectData(); + var pixelTatio = GetDevicePixelRatio(); + //区间起始位置 结束位子 + selectData.XStart=(drag.Click.X-uielement.getBoundingClientRect().left)*pixelTatio; + selectData.YStart=(drag.Click.Y-uielement.getBoundingClientRect().top)*pixelTatio; + selectData.XEnd=(drag.LastMove.X-uielement.getBoundingClientRect().left)*pixelTatio; + selectData.YEnd=(drag.LastMove.Y-uielement.getBoundingClientRect().top)*pixelTatio; + selectData.JSChartContainer=this; + selectData.Stock={Symbol:this.Symbol, Name:this.Name}; + + if (!this.BorderDrag && this.GetSelectRectData(selectData)) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SELECT_RECT); + var paint=this.GetRectSelectPaint(); + if (event && event.Callback) + { + var data= + { + X:drag.LastMove.X-uielement.getBoundingClientRect().left, + Y:drag.LastMove.Y-uielement.getBoundingClientRect().top, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + event.Callback(event,data,this); + } + + if (IsMinuteChart) //分时图直接显示显示区间选择 + { + this.HideSelectRect(); + this.UpdateSelectRect(selectData.Start,selectData.End); + } + else + { + if (this.SelectRectRightMenu) + { + e.data= + { + Chart:this, + X:drag.LastMove.X-uielement.getBoundingClientRect().left, + Y:drag.LastMove.Y-uielement.getBoundingClientRect().top, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + this.SelectRectRightMenu.DoModal(e); + } + } + } + else + { + this.TryClickPaintEvent(JSCHART_EVENT_ID.ON_CLICKUP_CHART_PAINT,this.ClickDownPoint,e); + } + } + else + { + this.TryClickPaintEvent(JSCHART_EVENT_ID.ON_CLICKUP_CHART_PAINT,this.ClickDownPoint,e); + this.ClickEvent(e); + } + + //清空数据 + JSConsole.Chart.Log('[KLineChartContainer::document.onmouseup]',e); + this.UIElement.style.cursor="default"; + this.MouseDrag=null; + this.ClickDownPoint=null; + this.IsOnTouch=false; + if (this.BorderDrag && this.BorderDrag.Index>=0) this.Frame.SaveSubFrameHeightRate(); //拖拽指标窗口高度以后保存 + this.BorderDrag=null; + this.YDrag=null; + this.UpDownDrag=null; + this.RectSelectDrag=null; + if (bClearDrawPicture===true) this.CurrentChartDrawPicture=null; + } + + this.UIOnMounseOut=function(e) + { + JSConsole.Chart.Log('[KLineChartContainer::UIOnMounseOut]',e); + this.UIOnMouseMove(e); + } + + //点击事件 + this.ClickEvent=function(e) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK); + if (!event || !event.Callback) return false; + if (this.ClickDownPoint.X!=e.clientX || this.ClickDownPoint.Y!=e.clientY) return false; + var pixelTatio = GetDevicePixelRatio(); + var x=(e.clientX-uielement.getBoundingClientRect().left)*pixelTatio; + var y=(e.clientY-uielement.getBoundingClientRect().top)*pixelTatio; + var data= { X:e.clientX, Y:e.clientY, FrameID:-1 }; + + var isInClient=false; + this.Canvas.beginPath(); + this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight()); + isInClient=this.Canvas.isPointInPath(x,y); + if (isInClient) + { + var yValueExtend={}; + var yValue=this.Frame.GetYData(y,yValueExtend); + + if (IFrameSplitOperator.IsNumber(yValueExtend.FrameID) && yValueExtend.FrameID>=0) + { + var xValue=this.Frame.GetXData(x); + data.FrameID=yValueExtend.FrameID; + data.Data={ X:xValue, Y:yValue } ; + } + } + + event.Callback(event, data, this); + return true; + } + + this.PtInChartPaintTooltip=function(x,y, toolTip) + { + for(var i=0;i { this.OnTouchStart(e); } + uielement.ontouchmove=(e)=> {this.OnTouchMove(e); } + uielement.ontouchend=(e)=> {this.OnTouchEnd(e); } + + this.AddPhoneDBClickInfo=function(x,y,time) + { + var item={ X:x, Y:y, Time:time }; + this.PhoneDBClickInfo.Start.push(item); + } + + this.ClearPhoneDBClickInfo=function() + { + this.PhoneDBClickInfo.Start=[]; + } + + this.IsShortPhoneClick=function(x,y,time) + { + + } + + this.OnTouchStart=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + this.IsOnTouch=true; + this.TouchDrawCount=0; + this.PhonePinch=null; + this.StopDragTimer(); + + var isSingleTouch=this.IsSingleTouch(e); + if (this.EnableScrollUpDown==false || !isSingleTouch || //多点触屏 + (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID && this.TouchStatus.CorssCursorShow==true)) //十字光标显示,不能滚动页面 + { + if (e.cancelable) e.preventDefault(); + } + + if (this.IsPhoneDragging(e)) + { + if (this.TryClickLock || this.TryClickIndexTitle) //指标枷锁区域 , 指标标题点击 + { + var touches = this.GetToucheData(e, this.IsForceLandscape); + var pt=this.PointAbsoluteToRelative(touches[0].clientX, touches[0].clientY, true); + var x = pt.X; + var y = pt.Y; + if (this.TryClickLock && this.TryClickLock(x, y)) return; + if (this.TryClickIndexTitle && this.TryClickIndexTitle(x,y)) return; + } + + var bStartTimer=true; + if (this.ChartDrawOption.IsLockScreen) + { + bStartTimer=false; + } + else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) + { + if (this.TouchStatus.CorssCursorShow==true) bStartTimer=false; + } + else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID) + { + bStartTimer=false; + } + else + { + if (!isSingleTouch) bStartTimer=false; + } + + var drag= + { + "Click":{}, + "LastMove":{} //最后移动的位置 + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + drag.Click.X=touches[0].clientX; + drag.Click.Y=touches[0].clientY; + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + + this.MouseDrag=drag; + this.PhoneTouchInfo={ Start:{X:touches[0].clientX, Y:touches[0].clientY }, End:{ X:touches[0].clientX, Y:touches[0].clientY } }; + if (this.SelectChartDrawPicture) this.SelectChartDrawPicture.IsSelected=false; + this.SelectChartDrawPicture=null; + var isDrawPictrue=false; + if (this.CurrentChartDrawPicture) //画图工具模式 + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2) + this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y,true); + else + { + this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y,true); + //只有1个点 直接完成 + if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo({Corss:false, Tooltip:false}); + } + + if (e.cancelable) e.preventDefault(); + return; + } + else + { + var pt=this.PointAbsoluteToRelative(touches[0].clientX,touches[0].clientY, true); + var drawPictrueData={ X:pt.X, Y:pt.Y }; + var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速 + if (this.GetChartDrawPictureByPoint(drawPictrueData)) + { + drawPictrueData.ChartDrawPicture.Status=20; + drawPictrueData.ChartDrawPicture.ValueToPoint(); + drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex; + drawPictrueData.ChartDrawPicture.IsSelected=true; + this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture; + this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture; + let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_DRAWPICTURE); //选中画图工具事件 + if (event && event.Callback) + { + let sendData={ DrawPicture: drawPictrueData.ChartDrawPicture }; + event.Callback(event,sendData,this); + } + + if (e.cancelable) e.preventDefault(); + return; + } + } + + if (bStartTimer) + { + //长按2秒,十字光标 + var self=this; + this.DragTimer=setTimeout(function() + { + if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y) //手指没有移动,出现十字光标 + { + var mouseDrag=self.MouseDrag; + self.MouseDrag=null; + if (self.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) self.TouchStatus.CorssCursorShow=true; //十字显示 + self.MoveCorssCursor(drag.Click,e);//移动十字光标 + } + + }, self.PressTime); + } + + if (this.EnableZoomIndexWindow) + { + this.PhoneDBClick.AddTouchStart(touches[0].clientX, touches[0].clientY, Date.now()); + JSConsole.Chart.Log("[JSChartContainer::OnTouchStart] PhoneDBClick ", this.PhoneDBClick); + } + + if (this.ChartDrawOption.IsLockScreen) + { + this.MouseDrag=null; + } + else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) + { + + } + else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID) + { + this.MouseDrag=null; + this.MoveCorssCursor(drag.Click,e); + } + else if (this.IsClickShowCorssCursor) + { + this.MoveCorssCursor(drag.Click,e); + } + + this.TouchEvent({ EventID:JSCHART_EVENT_ID.ON_PHONE_TOUCH, FunctionName:"OnTouchStart"}, e); + } + else if (this.IsPhonePinching(e)) + { + var phonePinch= + { + "Start":{}, + "Last":{} + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + + this.PhonePinch=phonePinch; + this.SelectChartDrawPicture=null; + } + } + + this.OnTouchMove=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + if (this.IsPhoneDragging(e)) + { + var drag=this.MouseDrag; + if (drag==null) + { + if (this.IsForceLandscape) y=uielement.getBoundingClientRect().width-touches[0].clientY; //强制横屏Y计算 + if (!this.ChartDrawOption.IsLockScreen) this.MoveCorssCursor({X:touches[0].clientX, Y:touches[0].clientY},e); + } + else + { + var moveAngle=this.GetMoveAngle(drag.LastMove,{X:touches[0].clientX, Y:touches[0].clientY}); + var moveSetp=Math.abs(drag.LastMove.X-touches[0].clientX); + var moveUpDown=Math.abs(drag.LastMove.Y-touches[0].clientY); + moveSetp=parseInt(moveSetp); + var isMoveCorssCursor=(this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID && this.TouchStatus.CorssCursorShow==true); //是否移动十字光标 + + //JSConsole.Chart.Log("[JSChartContainer::uielement.ontouchmove] moveAngle , moveUpDown", moveAngle,moveUpDown); + + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==1 || drawPicture.Status==2) + { + if(moveSetp<5 && moveUpDown<5) return; + if(this.SetChartDrawPictureSecondPoint(touches[0].clientX,touches[0].clientY,true)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==3) + { + if(this.SetChartDrawPictureThirdPoint(touches[0].clientX,touches[0].clientY,true)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==20) //画图工具移动 + { + if(moveSetp<5 && moveUpDown<5) return; + + if(this.MoveChartDrawPicture(touches[0].clientX-drag.LastMove.X,touches[0].clientY-drag.LastMove.Y,true)) + { + this.DrawDynamicInfo(); + } + } + + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + } + else if (isMoveCorssCursor) //点击模式下 十字光标显示 左右移动十字光标 + { + var mouseDrag=this.MouseDrag; + this.MouseDrag=null; + this.MoveCorssCursor(drag.Click,e); //移动十字光标 + } + else if (this.DragMode==1 || isMoveCorssCursor==false) //数据左右拖拽 + { + if ( ((moveUpDown>0 && moveSetp<=3) || moveAngle<=this.TouchMoveMinAngle) && this.EnableScrollUpDown==true ) + { + this.StopDragTimer(); + return; + } + + if (moveSetp<5 || moveAngle<=this.TouchMoveMinAngle) + { + this.PreventTouchEvent(e); + return; + } + + var isLeft=true; + if (drag.LastMove.X0) //放大 + { + var cursorIndex={}; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomUp(cursorIndex)) return; + this.CursorIndex=cursorIndex.Index; + this.UpdatePointByCursorIndex(); + this.UpdataDataoffset(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + this.StopDragTimer(); + } + else //缩小 + { + var cursorIndex={}; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomDown(cursorIndex)) return; + this.CursorIndex=cursorIndex.Index; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + this.StopDragTimer(); + } + + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + } + + this.PreventTouchEvent(e); + } + + this.OnTouchEnd=function(e) + { + JSConsole.Chart.Log('[KLineChartContainer:OnTouchEnd]',e); + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var bClearDrawPicture=true; + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3) + { + drawPicture.PointStatus=drawPicture.Status; + if (this.FinishChartDrawPicturePoint()) + this.DrawDynamicInfo(); + else + bClearDrawPicture=false; + } + else if (drawPicture.Status==20) + { + if (this.FinishMoveChartDrawPicture()) + this.DrawDynamicInfo(); + } + } + + if (this.EnableZoomIndexWindow) + { + var time=Date.now(); + this.PhoneDBClick.AddTouchEnd(time); + if (this.PhoneDBClick.IsVaildDBClick()) + { + this.OnTouchDBClick(this.PhoneDBClick.Start); + this.PhoneDBClick.Clear(); + } + } + + this.IsOnTouch = false; + if (bClearDrawPicture===true) this.CurrentChartDrawPicture=null; + this.StopDragTimer(); + this.TouchEvent({ EventID:JSCHART_EVENT_ID.ON_PHONE_TOUCH, FunctionName:"OnTouchEnd"}, e); + this.OnTouchFinished(); + this.TouchDrawCount=0; + } + + this.OnTouchDBClick=function(points) + { + var pt=this.PointAbsoluteToRelative(points[0].X, points[0].Y, true); + var x=pt.X, y=pt.Y; + JSConsole.Chart.Log(`[KLineChartContainer:OnTouchDBClick] Phone dbclick absolute [${x},${y}], soruce [${points[0].X},${points[0].Y}]`); + + var frameId=this.Frame.PtInFrame(x,y); + JSConsole.Chart.Log("[KLineChartContainer::OnTouchDBClick] frameId",frameId); + if (frameId>=this.Frame.ZoomStartWindowIndex) + { + if (this.ZoomIndexWindow(frameId, {X:x, Y:y})) + { + this.Frame.SetSizeChage(true); + this.Draw(); + return true; + } + } + } + + //手势事件 + this.TouchEvent=function(obj,e) + { + var eventID=obj.EventID; + var event=this.GetEventCallback(eventID); + if (!event || !event.Callback) return false; + var drag=this.PhoneTouchInfo + if (!drag || !drag.Start || !drag.End ) return false; + var pixelTatio = GetDevicePixelRatio(); + var clientX=drag.End.X/pixelTatio; + var clientY=drag.End.Y/pixelTatio; + var x=drag.End.X-this.UIElement.getBoundingClientRect().left*pixelTatio; + var y=drag.End.Y-this.UIElement.getBoundingClientRect().top*pixelTatio; + var data= + { + X:clientX, Y:clientY, FrameID:-1, FunctionName:obj.FunctionName, + Drag: + { + Start:{ X:drag.Start.X/pixelTatio, Y:drag.Start.Y/pixelTatio }, + End:{ X:drag.End.X/pixelTatio, Y:drag.End.Y/pixelTatio } + } + }; + + var isInClient=false; + this.Canvas.beginPath(); + this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight()); + isInClient=this.Canvas.isPointInPath(x,y); + + if (isInClient) + { + var yValueExtend={}; + var yValue=this.Frame.GetYData(y,yValueExtend); + + if (IFrameSplitOperator.IsNumber(yValueExtend.FrameID) && yValueExtend.FrameID>=0) + { + var xValue=this.Frame.GetXData(x); + data.FrameID=yValueExtend.FrameID; + data.Data={ X:xValue, Y:yValue } ; + } + } + + event.Callback(event, data, this); + return true; + } + + this.MoveCorssCursor=function(point,e) + { + var pixelTatio = GetDevicePixelRatio(); + var x = point.X-this.UIElement.getBoundingClientRect().left*pixelTatio; + var y = point.Y-this.UIElement.getBoundingClientRect().top*pixelTatio; + if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) this.TouchStatus.CorssCursorShow=true; //十字显示 + this.OnMouseMove(x,y,e,true); + } + + this.DrawEmpty=function() + { + if (this.UIElement.width<=0 || this.UIElement.height<=0) return; + this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height); + if (this.Frame) + { + this.Frame.ScreenImageData=null; + this.Frame.Draw(); + } + } + + this.Draw=function() + { + if (this.ChartCorssCursor) this.ChartCorssCursor.Status=0; + if (this.UIElement.width<=0 || this.UIElement.height<=0) return; + + this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height); + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + this.Canvas.lineWidth=pixelTatio; //手机端需要根据分辨率比调整线段宽度 + + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) + { + this.Frame.ClearCoordinateText(); + this.Frame.Draw( { IsEnableSplash:this.ChartSplashPaint.IsEnableSplash} ); + this.ChartSplashPaint.Draw(); + return; + } + + //框架 + this.Frame.SetDrawDepthMap(()=> + { + for(var i=0;i=0 对应的指标窗口中ID + if (option && option.ParentFunction=='OnMouseMove' && option.Point) + { + ptPosition=this.Frame.PtInFrame(option.Point.X, option.Point.Y); + } + + var drawStatus=this.GetDrawStatus(); + drawStatus.DrawName="DrawDynamicInfo"; + for(var i=0;i=0) //在K线内部移动,调整K线索引 + this.CursorIndex=this.Frame.GetXData(x); + + var paint=this.GetRectSelectPaint(); + if (paint && paint.GetPointCount()>0) + { + var item=paint.PtInPaint(x,y); + if (item) + { + mouseStatus={ Cursor:"ew-resize", Name:"DragRectSelect"}; + JSConsole.Chart.Log("[JSChartContainer::OnMouseMove] drag rect select ",item); + } + } + + if (this.EnableBorderDrag && this.Frame) + { + var dragBorder=this.Frame.PtInFrameBorder(x,y); + if (dragBorder && dragBorder.Index>=0) + { + mouseStatus={ Cursor:"n-resize", Name:"DragBorder"}; + JSConsole.Chart.Log("[JSChartContainer::OnMouseMove] drag border ",dragBorder); + } + } + + if (this.EnableYDrag && this.Frame && !mouseStatus) + { + var dragY=this.TryYDrag(x,y); + if (dragY) + { + mouseStatus={ Cursor:dragY.Position==0 ? "n-resize":"row-resize", Name:"DragY"}; + JSConsole.Chart.Log("[JSChartContainer::OnMouseMove] drag y ",dragY); + } + } + + var button=this.Frame.PtInButtons(x,y); + if (button) + { + mouseStatus={ Cursor:"pointer", Name:"FrameButton"}; + JSConsole.Chart.Log("[JSChartContainer::OnMouseMove] frame button ", button); + } + + var bDrawPicture=false; //是否正在画图 + if (this.CurrentChartDrawPicture) + { + if (this.CurrentChartDrawPicture.SetLastPoint) this.CurrentChartDrawPicture.SetLastPoint({X:x,Y:y}); + bDrawPicture=true; + } + else + { + var drawPictrueData={}; + drawPictrueData.X=x; + drawPictrueData.Y=y; + if (this.GetChartDrawPictureByPoint(drawPictrueData) && + drawPictrueData.ChartDrawPicture && drawPictrueData.ChartDrawPicture.EnableMove==true) + { + + if (drawPictrueData.PointIndex===100) this.UIElement.style.cursor="move"; + else this.UIElement.style.cursor="pointer"; + bDrawPicture=true; + } + else + { + if (!this.MouseDrag) this.UIElement.style.cursor="default"; + } + } + + var clientPos=this.PtInClient(x,y); + var option={ ParentFunction:'OnMouseMove', Point:{X:x, Y:y}, IsPhone:isPhone===true, ClientPos:clientPos }; + this.DrawDynamicInfo(option); + if (mouseStatus) this.UIElement.style.cursor=mouseStatus.Cursor; + + if (this.IsShowTooltip && bDrawPicture==false) + { + var toolTip=this.GetChartTooltipData(x,y); + + if (toolTip && toolTip.Data) + { + if (isPhone===true) + { + var touche=e.touches[0]; + var xTooltip = touche.clientX-this.UIElement.getBoundingClientRect().left; + var yTooltip = touche.clientY-this.UIElement.getBoundingClientRect().top; + } + else + { + var xTooltip = e.clientX-this.UIElement.getBoundingClientRect().left; + var yTooltip = e.clientY-this.UIElement.getBoundingClientRect().top; + } + this.ShowTooltip(xTooltip,yTooltip,toolTip); + } + else + { + this.HideTooltip(); + } + } + } + + this.OnKeyDown=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var keyID = e.keyCode ? e.keyCode :e.which; + switch(keyID) + { + case 37: //left + if (this.CursorIndex<=0.99999) + { + if (!this.DataMoveLeft()) + { //左移数据到头了 触发下载新数据 + if (this.DragDownloadData) this.DragDownloadData(); + break; + } + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + } + else + { + --this.CursorIndex; + this.UpdatePointByCursorIndex(); + this.DrawDynamicInfo(); + this.ShowTooltipByKeyDown(); + } + break; + case 39: //right + var xPointcount=0; + if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount; + else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount; + if (this.CursorIndex+1>=xPointcount) + { + if (!this.DataMoveRight()) break; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + } + else + { + //判断是否在最后一个数据上 + var data=null; + if (this.Frame.Data) data=this.Frame.Data; + else data=this.Frame.SubFrame[0].Frame.Data; + if (!data) break; + if (this.CursorIndex+data.DataOffset+1>=data.Data.length) break; + + ++this.CursorIndex; + this.UpdatePointByCursorIndex(); + this.DrawDynamicInfo(); + this.ShowTooltipByKeyDown(); + } + break; + case 38: //up + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Keyboard===false) break; + var cursorIndex={ ZoomType:this.ZoomType }; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomUp(cursorIndex)) break; + this.CursorIndex=cursorIndex.Index; + this.UpdatePointByCursorIndex(); + this.UpdataDataoffset(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + break; + case 40: //down + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Keyboard===false) break; + var cursorIndex={ ZoomType:this.ZoomType }; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomDown(cursorIndex)) break; + this.CursorIndex=cursorIndex.Index; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + break; + case 46: //del + if (!this.SelectChartDrawPicture) break; + var drawPicture=this.SelectChartDrawPicture; + JSConsole.Chart.Log(drawPicture,"drawPicturedrawPicturedrawPicture") + this.SelectChartDrawPicture=null; + this.ClearChartDrawPicture(drawPicture); //删除选中的画图工具 + break; + case 32: //space + this.OnMarkRectSelect(e); + break; + default: + return; + } + + //不让滚动条滚动 + if(e.preventDefault) e.preventDefault(); + else e.returnValue = false; + } + + this.OnDoubleClick=function(x,y,e) + { + //JSConsole.Chart.Log(e); + } + + this.ZoomIndexWindow=function(frameID, option) //最大化/最小化指标窗口 + { + if (frameID<0 || frameID>=this.Frame.SubFrame.length) return false; + + return this.Frame.ZoomIndexWindow(frameID, option); + } + + this.RemoveMinSizeWindows=function() //清空最小化窗口 + { + if (!this.Frame.ZoomWindowsInfo) return; + + var aryDeleteIndex=[], aryIndex=[]; //删除的索引, 保留的索引 + for(var i=0;i10) + { + this.Canvas.beginPath(); + if (this.Frame.IsHScreen===true) + { + this.Canvas.rect(border.Left,border.Top,border.Right-border.Left,border.TopEx-border.Top); + } + else + { + this.Canvas.rect(border.Left,border.Top,border.LeftEx-border.Left,border.Bottom-border.Top); + } + + if (this.Canvas.isPointInPath(x,y)) return 2; + } + + if (this.Frame.ChartBorder.RightExtendWidth>10) + { + this.Canvas.beginPath(); + if (this.Frame.IsHScreen===true) + { + this.Canvas.rect(border.Left,border.BottomEx,border.Right-border.Left,border.Bottom-border.BottomEx); + } + else + { + this.Canvas.rect(border.RightEx,border.Top,border.Right-border.RightEx,border.Bottom-border.Top); + } + + if (this.Canvas.isPointInPath(x,y)) return 3; + } + + return -1; + } + + this.OnMoveFromeBorder=function(index, yMove) + { + if (!this.Frame) return; + + if (!this.Frame.OnMoveFromeBorder(index,yMove)) return ; + + //this.Frame.SetSizeChage(true); + this.Frame.SetFrameBorderSizeChange(); + this.Frame.ReDrawToolbar(); + this.Draw(); + } + + this.OnZoomUpDownFrameY=function(obj, yMove) + { + if (!this.Frame) return; + + if (!this.Frame.OnZoomUpDownFrameY(obj,yMove)) return ; + + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.OnUpDonwFrameY=function(obj, yMove) + { + if (!this.Frame) return false; + + if (!this.Frame.OnUpDonwFrameY(obj,yMove)) return false; + + this.Frame.SetSizeChage(true); + + return true; + } + + this.CancelZoomUpDownFrameY=function(obj) + { + if (!this.Frame) return; + + if (!this.Frame.CancelZoomUpDownFrameY(obj)) return ; + + this.UpdateFrameMaxMin(); + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.IsKLineContainer=function() + { + if (this.ClassName=='KLineChartContainer' || this.ClassName=='KLineChartHScreenContainer' || + this.ClassName=="KLineTrainChartContainer" || this.ClassName=="CustomKLineChartContainer" ) return true; + + return false; + } + + this.UpdatePointByCursorIndex=function(type) //type 1=根据十字光标更新 2=强制取消十字光标 + { + var pt={X:null, Y:null}; + pt.X=this.Frame.GetXFromIndex(this.CursorIndex); + var index=Math.abs(this.CursorIndex-0.5); + if (this.IsKLineContainer()) index=this.CursorIndex; + + var data=this.Frame.Data; + if (data.DataOffset+index0) textHeight=format.Height; //新版本高度有格式化类计算完成 + var width=format.Width; + + this.Tooltip.style.width = width+"px"; + this.Tooltip.style.height =textHeight+"px"; + //JSConsole.Chart.Log(`[JSChartContainer::ShowTooltip] left=${left} top=${top} xMove=${xMove}` ); + //if (toolTip.ChartPaint.Name=="Overlay-KLine") this.Tooltip.style.height =220+"px"; + this.Tooltip.style.position = "absolute"; + //JSConsole.Chart.Log('[JSChartContainer::ShowTooltip] getBoundingClientRect() ',this.UIElement.getBoundingClientRect()) + if (left+width>this.UIElement.getBoundingClientRect().width) + this.Tooltip.style.left = (left-width) + "px"; + else + this.Tooltip.style.left = left + "px"; + + if (top+xMove+textHeight>this.UIElement.getBoundingClientRect().height-5) + this.Tooltip.style.top = (top-textHeight)+ "px"; + else this.Tooltip.style.top = (top + xMove)+ "px"; + + this.Tooltip.className='jschart-tooltip'; + this.Tooltip.innerHTML=format.Text; + this.Tooltip.style.display = "block"; + } + else if (toolTip.Type===1) //信息地雷提示信息 + { + var scrollPos=GetScrollPosition(); + var left = x; + var top = y; + + var format=g_DivTooltipDataForamt.Create('KLineInfoDataStringFormat'); + format.Value=toolTip; + format.Symbol=this.Symbol; + format.LanguageID=this.LanguageID; + if (!format.Operator()) return; + var width=format.Width; + + this.Tooltip.className='jchart-klineinfo-tooltip'; + this.Tooltip.style.position = "absolute"; + if(left+width>this.UIElement.getBoundingClientRect().width){ + this.Tooltip.style.left = (left-width) + "px"; + }else{ + this.Tooltip.style.left = left + "px"; + } + + this.Tooltip.style.top = (top +xMove)+ "px"; + this.Tooltip.style.width = width+"px"; + this.Tooltip.style.height =null; + this.Tooltip.innerHTML=format.Text; + this.Tooltip.style.display = "block"; + } + else if (toolTip.Type==2) //指标信息 + { + var left = x; + var top = y; + + var format=g_DivTooltipDataForamt.Create('KLineTradeDataStringFormat'); + format.Value=toolTip; + format.Symbol=this.Symbol; + format.LanguageID=this.LanguageID; + if (!format.Operator()) return; + var width=format.Width; + + this.Tooltip.className='jchart-klinetrade-tooltip'; + this.Tooltip.style.position = "absolute"; + this.Tooltip.style.left = left + "px"; + this.Tooltip.style.top = (top +xMove)+ "px"; + this.Tooltip.style.width = width+"px"; + this.Tooltip.style.height =null; + this.Tooltip.innerHTML=format.Text;; + this.Tooltip.style.display = "block"; + } + else if (toolTip.Type==3) //分时图异动信息 + { + var left = x; + var top = y; + + var format=g_DivTooltipDataForamt.Create('MinuteInfoDataStringFormat'); + format.Value=toolTip; + format.Symbol=this.Symbol; + format.LanguageID=this.LanguageID; + if (!format.Operator()) return; + var width=format.Width; + + this.Tooltip.className='jchart-minuteinfo-tooltip'; //分时图异动 + this.Tooltip.style.position = "absolute"; + this.Tooltip.style.left = left + "px"; + this.Tooltip.style.top = (top +xMove)+ "px"; + this.Tooltip.style.width = width+"px"; + this.Tooltip.style.height =null; + this.Tooltip.innerHTML=format.Text;; + this.Tooltip.style.display = "block"; + } + else if (toolTip.Type==4) //icon图标信息 + { + var left = x; + var top = y; + + var format=g_DivTooltipDataForamt.Create('IconDataStringFormat'); + format.Value=toolTip; + format.Symbol=this.Symbol; + format.LanguageID=this.LanguageID; + if (!format.Operator()) return; + var width=format.Width; + + this.Tooltip.className='jchart-iconinfo-tooltip'; //图标信息 + this.Tooltip.style.position = "absolute"; + this.Tooltip.style.left = left + "px"; + this.Tooltip.style.top = (top +xMove)+ "px"; + this.Tooltip.style.width = width+"px"; + this.Tooltip.style.height =null; + this.Tooltip.innerHTML=format.Text;; + this.Tooltip.style.display = "block"; + } + } + + this.HideTooltip=function() + { + if (this.Tooltip.style.display!="none") this.Tooltip.style.display = "none"; + } + + this.ShowSelectRect=function(x,y,x2,y2) + { + var left = x; + var top = y; + + var border=this.Frame.ChartBorder.GetBorder(); + + var borderRight=border.Right; + var borderLeft=border.Left; + + if (x>borderRight) x=borderRight; + if (x2>borderRight) x2=borderRight; + + if (xx2) left=x2; + if (y>y2) top=y2; + + var width=Math.abs(x-x2); + var height=Math.abs(y-y2); + + this.SelectRect.style.width = width+"px"; + this.SelectRect.style.height =height+"px"; + this.SelectRect.style.position = "absolute"; + this.SelectRect.style.left = left +"px"; + this.SelectRect.style.top = top +"px"; + this.SelectRect.style.display = "block"; + } + + this.UpdateSelectRect=function(start,end) + { + if (!this.ChartPaint[0].Data) return; + var paint=this.GetRectSelectPaint(); + if (!paint) return; + + var data=this.ChartPaint[0].Data; + var startItem=data.Data[start]; + var endItem=data.Data[end]; + + JSConsole.Chart.Log('[JSChartContainer::UpdateSelectRect]',startItem,endItem); + paint.SetPoint(startItem, { Index:0 }); + paint.SetPoint(endItem, { Index:1 }); + + this.Draw(); + } + + this.HideSelectRect=function() + { + this.SelectRect.style.display = "none"; + } + + this.ResetFrameXYSplit=function() + { + if (typeof(this.Frame.ResetXYSplit)=='function') + this.Frame.ResetXYSplit(); + } + + this.UpdateFrameMaxMin=function() + { + var frameMaxMinData=new Array(); + + var chartPaint=new Array(); + + for(var i=0;irange.Min) frameItem.Range.Min=range.Min; + } + else + { + frameItem={ OverlayFrame:[] }; + frameItem.Frame=paint.ChartFrame; + frameItem.Range=range; + frameMaxMinData.push(frameItem); + } + } + + var mapFrame=new Map(); + for(var i=0;irange.Min) frameItem.Range.Min=range.Min; + } + } + } + + for(var i=0;i=data.Data.length) return false; + + ++data.DataOffset; + return true; + } + + this.UpdataDataoffset=function() + { + var data=null; + if (this.Frame.Data) + data=this.Frame.Data; + else + data=this.Frame.SubFrame[0].Frame.Data; + + if (!data) return; + + for(var i in this.ChartPaint) + { + var item =this.ChartPaint[i]; + if (!item.Data) continue; + item.Data.DataOffset=data.DataOffset; + } + + for(var i in this.OverlayChartPaint) + { + var item =this.OverlayChartPaint[i]; + if (!item.Data) continue; + item.Data.DataOffset=data.DataOffset; + } + + //叠加指标当前显示的数据偏移 + for (var i in this.Frame.SubFrame) + { + var subFrame=this.Frame.SubFrame[i]; + for(var j in subFrame.OverlayIndex) + { + var overlayItem=subFrame.OverlayIndex[j]; + for(var k in overlayItem.ChartPaint) + { + var item=overlayItem.ChartPaint[k]; + if (!item.Data) continue; + item.Data.DataOffset=data.DataOffset; + } + } + } + + } + + this.GetMoveOneStepWidth=function() + { + if (IFrameSplitOperator.IsPlusNumber(this.StepPixel)) return this.StepPixel; + + var pixelRatio=GetDevicePixelRatio(); + var mainFrame=this.Frame.SubFrame[0].Frame; + var dataWidth=mainFrame.DataWidth; + var distanceWidth=mainFrame.DistanceWidth; + var oneStepWidth=this.StepPixel; + var oneStepWidth=(dataWidth+distanceWidth)/pixelRatio; + if (oneStepWidth<1) oneStepWidth=1; + + return oneStepWidth; + } + + this.DataMove=function(step,isLeft) + { + var oneStepWidth=this.GetMoveOneStepWidth(); + var moveStep=step; + step=parseInt(step/oneStepWidth); //除以4个像素 + if (step<=0) return false; + + var data=null; + if (!this.Frame.Data) data=this.Frame.Data; + else data=this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + + var xPointcount=0; + if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount; + else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount; + if (!xPointcount) return false; + + if (this.Frame.SubFrame && this.Frame.SubFrame.length>0 && this.Frame.SubFrame[0].Frame) + { + var fristFrame=this.Frame.SubFrame[0].Frame; + if (fristFrame.DataWidth<=1 || fristFrame.DistanceWidth<=1) //K线在缩放很小的时候 移动加速 + { + if (IFrameSplitOperator.IsPlusNumber(this.StepPixel)) + step=moveStep*this.StepPixel; + } + + } + + if (isLeft) //--> + { + if (this.RightSpaceCount>0) + { + if (xPointcount+data.DataOffset>=data.Data.length+this.RightSpaceCount-1) return false; + data.DataOffset+=step; + + if (data.DataOffset+xPointcount>=data.Data.length+this.RightSpaceCount) + data.DataOffset=data.Data.length-(xPointcount-this.RightSpaceCount); + } + else + { + if (xPointcount+data.DataOffset>=data.Data.length) return false; + data.DataOffset+=step; + + if (data.DataOffset+xPointcount>=data.Data.length) + data.DataOffset=data.Data.length-xPointcount; + } + return true; + } + else //<-- + { + if (data.DataOffset<=0) return false; + + data.DataOffset-=step; + if (data.DataOffset<0) data.DataOffset=0; + + return true; + } + } + + //获取鼠标在当前子窗口id + this.GetSubFrameIndex=function(x,y) + { + if (!this.Frame.SubFrame || this.Frame.SubFrame.length<=0) return -1; + + for(var i in this.Frame.SubFrame) + { + var frame=this.Frame.SubFrame[i].Frame; + var left=frame.ChartBorder.GetLeft(); + var top=frame.ChartBorder.GetTop(); + var height=frame.ChartBorder.GetHeight(); + var width=frame.ChartBorder.GetWidth(); + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,width,height); + if (this.Canvas.isPointInPath(x,y)) return parseInt(i); + + } + return 0; + } + + //根据X坐标获取数据索引 + this.GetDataIndexByPoint=function(x) + { + var frame=this.Frame; + if (this.Frame.SubFrame && this.Frame.SubFrame.length>0) frame=this.Frame.SubFrame[0].Frame; + + var data=null; + if (this.Frame.Data) + data=this.Frame.Data; + else + data=this.Frame.SubFrame[0].Frame.Data; + + if (!data || !frame) return; + + var index=parseInt(frame.GetXData(x)); + + //JSConsole.Chart.Log('x='+ x +' date='+data.Data[data.DataOffset+index].Date); + return data.DataOffset+index; + } + + //获取主数据 + this.GetSelectRectData=function(selectData) + { + if (Math.abs(selectData.XStart-selectData.XEnd)<5) return false; + + var startClientPos=this.PtInClient(selectData.XStart, selectData.YStart); + var endClientPos=this.PtInClient(selectData.XEnd, selectData.YEnd); + + var data=null; + if (this.Frame.Data) + data=this.Frame.Data; + else + data=this.Frame.SubFrame[0].Frame.Data; + + if (!data) return false; + + var start=this.GetDataIndexByPoint(selectData.XStart); + var end=this.GetDataIndexByPoint(selectData.XEnd); + + if (Math.abs(start-end)<2) return false; + + selectData.Data=data; + if (start>end) + { + selectData.Start=end; + selectData.End=start; + } + else + { + selectData.Start=start; + selectData.End=end; + } + + return true; + } + + //获取当前的点对应的 画图工具的图形 + //data.X data.Y 鼠标位置 返回 data.ChartDrawPicture 数据在画图工具 data.PointIndex 在画图工具对应点索引 + this.GetChartDrawPictureByPoint=function(data) + { + for(var i in this.ChartDrawPicture) + { + var item =this.ChartDrawPicture[i]; + var pointIndex=item.IsPointIn(data.X,data.Y); + if (pointIndex===false) continue; + + if (pointIndex>=0) + { + data.ChartDrawPicture=item; + data.PointIndex=pointIndex; + return true; + } + } + + return false; + } + + // 保存图片 + this.SaveToImage = function (format,colorGB) + { + if (this.UIElement.width<=0 || this.UIElement.height<=0) return null; + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) return null; // 数据加载中不能保存 + + JSConsole.Chart.Log('[JSChartContainer::SaveToImage]', this.UIElement); + var clrBG='rgb(255,255,255)'; + if (colorGB) clrBG=colorGB; + this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height); + this.Canvas.fillStyle=clrBG; + this.Canvas.fillRect(0,0,this.UIElement.width,this.UIElement.height); //画一个背景色, 不然是一个黑的背景 + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + this.Canvas.lineWidth=pixelTatio; //手机端需要根据分辨率比调整线段宽度 + + this.Frame.Draw(); //框架 + + for (var i in this.ChartPaint) //框架内图形 + { + var item=this.ChartPaint[i]; + if (item.IsDrawFirst) + item.Draw(); + } + + for(var i in this.ChartPaint) + { + var item=this.ChartPaint[i]; + if (!item.IsDrawFirst) + item.Draw(); + } + + for(var i in this.ChartPaintEx) + { + var item=this.ChartPaintEx[i]; + item.Draw(); + } + + for(var i in this.OverlayChartPaint) //叠加股票 + { + var item=this.OverlayChartPaint[i]; + item.Draw(); + } + + + for(var i in this.ExtendChartPaint) //固定扩展图形 + { + var item=this.ExtendChartPaint[i]; + if (!item.IsDynamic && item.IsAnimation==false) item.Draw(); + } + + if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal(); + this.Frame.DrawLock(); + + for(var i in this.ExtendChartPaint) //动态扩展图形 + { + var item=this.ExtendChartPaint[i]; + if (item.IsDynamic && item.DrawAfterTitle===false) item.Draw(); + } + + if (this.LastPoint.X!=null || this.LastPoint.Y!=null) + { + if (this.ChartCorssCursor) //十字光标不画 + { + this.ChartCorssCursor.LastPoint=this.LastPoint; + this.ChartCorssCursor.CursorIndex=this.CursorIndex; + } + } + + for(var i in this.TitlePaint) + { + var item=this.TitlePaint[i]; + if (!item.IsDynamic) continue; + + item.CursorIndex=this.CursorIndex; + item.Draw(); + } + + for(var i in this.ExtendChartPaint) //动态扩展图形 + { + var item=this.ExtendChartPaint[i]; + if (item.IsDynamic && item.DrawAfterTitle===true && item.IsAnimation==false) item.Draw(); + } + + if (this.EnableAnimation) + { + for(var i in this.ExtendChartPaint) //动画 + { + var item=this.ExtendChartPaint[i]; + if (item.IsAnimation===true) item.Draw(); + } + } + + for(var i in this.ChartDrawPicture) + { + var item=this.ChartDrawPicture[i]; + item.Draw(); + } + + if (this.CurrentChartDrawPicture && this.CurrentChartDrawPicture.Status!=10) + { + this.CurrentChartDrawPicture.Draw(); + } + + var dataURL=this.UIElement.toDataURL(format ? format:'image/png', 1.0); + JSConsole.Chart.Log('[JSChartContainer::SaveToImage] data= ', dataURL); + return dataURL; + } + + + this.SaveToImageUrl=function(obj,callback) //obj:{Format: 图片格式, ColorGB: 背景色}, callback:function(bSuccess,obj) + { + if (!obj) obj={Format:'image/png', ColorGB:'rgb(255,255,255)'}; + var imageData=this.SaveToImage(obj.Format, obj.ColorGB); + var postData={"Base64":imageData, "BucketName":"downloadcache", "Path":"hqchart/hq_snapshot"}; + var url=g_JSChartResource.Domain+'/API/FileUploadForBase64'; + + JSNetwork.HttpRequest({ + url: url, + method: "POST", + dataType: "json", + data: postData, + success: function (data) + { + JSConsole.Chart.Log('[JSChartContainer::SaveToImageUrl] recv data', data); + var result={Path:data.relativeurl, Domain:'https://opensourcedownload.zealink.com'}; + result.Url=`${result.Domain}/${result.Path}`; + if (callback) callback(true,result,''); + }, + error: function (request) + { + JSConsole.Chart.Log('[JSChartContainer::SaveToImageUrl] error ', request); + if (callback) callback(false,null,'upload failed'); + } + }); + + } + + this.SetLanguage=function(language) + { + var languageID=null; + switch(language) + { + case 'EN': + languageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID; + break; + case 'CN': + languageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + break; + default: + console.warn(`[JSChartContainer::SetLanguage] language=${language} error`); + return; + } + + if (this.LanguageID==languageID) return; + + this.LanguageID=languageID; + if (this.ChartCorssCursor && this.ChartCorssCursor.StringFormatY) this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + + for(var i in this.TitlePaint) + { + var item=this.TitlePaint[i]; + if (item) item.LanguageID=this.LanguageID; + } + + if (this.Frame && this.Frame.SetLanguage) this.Frame.SetLanguage(this.LanguageID); + + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.ReloadTiltePaintResource=function(resource) //重新加载配置 + { + for(var i in this.TitlePaint) + { + var item=this.TitlePaint[i]; + if (item.ReloadResource) item.ReloadResource(resource); + } + } + + this.ReloadExtendChartPaintResource=function(resource) //扩展画法重新加载配置 + { + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + if (item.ReloadResource) item.ReloadResource(resource); + } + } + + this.ReloadResource=function(option) + { + this.ReloadBorder(option); + this.ReloadTiltePaintResource(option.Resource); + this.ReloadChartPaint(option.Resource); + this.ReloadFrame(option.Resource); + this.ReloadExtendChartPaintResource(option.Resource); + this.ReloadChartCorssCursor(option,option.Resource); + + if (option.Update && this.Update) this.Update( {UpdateCursorIndexType:2} ); //是否立即更新并重绘 + else if (option.Draw==true) this.Draw(); //是否立即重绘 + } + + this.ReloadBorder=function(option) //根据页面缩放调整对应边框的尺长 + { + if (!option) return; + + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + if (option.Border) + { + var item=option.Border; + if (IFrameSplitOperator.IsNumber(item.Left)) this.Frame.ChartBorder.Left=item.Left*pixelTatio; + if (IFrameSplitOperator.IsNumber(item.Right)) this.Frame.ChartBorder.Right=item.Right*pixelTatio; + if (IFrameSplitOperator.IsNumber(item.Top)) this.Frame.ChartBorder.Top=item.Top*pixelTatio; + if (IFrameSplitOperator.IsNumber(item.Bottom)) this.Frame.ChartBorder.Bottom=item.Bottom*pixelTatio; + } + + for(var i in option.Windows) + { + var item=option.Windows[i]; + if (i>=this.Frame.SubFrame.length) continue; + var subFrame=this.Frame.SubFrame[i]; + var border=subFrame.Frame.ChartBorder; + if (IFrameSplitOperator.IsNumber(item.TitleHeight)) border.TitleHeight=item.TitleHeight*pixelTatio; + } + + for(var i in option.Frame) + { + var item=option.Frame[i]; + if (i>=this.Frame.SubFrame.length) continue; + + var subFrame=this.Frame.SubFrame[i]; + var border=subFrame.Frame.ChartBorder; + if (item.TopSpace>=0) border.TopSpace=item.TopSpace*pixelTatio; + if (item.BottomSpace>=0) border.BottomSpace=item.BottomSpace*pixelTatio; + } + } + + this.ReloadFrame=function(resource) + { + for(var i in this.Frame.SubFrame) + { + var item=this.Frame.SubFrame[i]; + var subFrame=item.Frame; + if (subFrame && subFrame.ReloadResource) subFrame.ReloadResource(resource); + } + } + + this.ReloadChartPaint=function(resource) + { + for(var i in this.ChartPaint) + { + var item=this.ChartPaint[i]; + if (item && item.ReloadResource) item.ReloadResource(resource); + } + } + + this.ReloadChartCorssCursor=function(option, resource) + { + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + if (option && option.CorssCursor) + { + var item=option.CorssCursor; + if (IFrameSplitOperator.IsNumber(item.TitleHeight)) this.ChartCorssCursor.TextHeight=item.TitleHeight*pixelTatio; //十字光标文本信息高度 + } + + if (this.ChartCorssCursor.ReloadResource) this.ChartCorssCursor.ReloadResource(resource); + } + + this.SetDepthMapData=function(depthData, bDraw) + { + for(var i in depthData) + { + var item=depthData[i]; + for(var j in this.ExtendChartPaint) + { + var chart=this.ExtendChartPaint[j]; + if (chart.ID==item.ID) + { + chart.Data=item.Data; + break; + } + } + } + + if (bDraw) this.Draw(); + } + + //画图工具 + //把X, Y绝对位置转成的相对位置的点 + this.PointAbsoluteToRelative=function(x, y, isPhone) + { + var pt={ X:x, Y:y }; + var pixelTatio = GetDevicePixelRatio(); //x,y是原始坐标 需要乘以放大倍速 + var uiRect=this.UIElement.getBoundingClientRect(); //dom返回的是没有放大倍数的值 + + if (isPhone) + { + pt.X=x-uiRect.left*pixelTatio; //手机端 dom返回的是没有放大倍数的值 + pt.Y=y-uiRect.top*pixelTatio; + } + else + { + pt.X=(x-uiRect.left)*pixelTatio; + pt.Y=(y-uiRect.top)*pixelTatio; + } + + return pt; + } + + this.SetChartDrawPictureFirstPoint=function(x,y, isPhone) + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (!this.Frame.SubFrame || this.Frame.SubFrame.length<=0) return false; + + //相对坐标 + var pt=this.PointAbsoluteToRelative(x, y, isPhone); + var xFixed=pt.X; + var yFixed=pt.Y; + + for(var i in this.Frame.SubFrame) + { + var frame=this.Frame.SubFrame[i].Frame; + var left=frame.ChartBorder.GetLeft(); + var top=frame.ChartBorder.GetTopEx(); + var height=frame.ChartBorder.GetHeight(); + var width=frame.ChartBorder.GetWidth(); + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,width,height); + if (this.Canvas.isPointInPath(xFixed,yFixed)) + { + drawPicture.Frame=frame; + break; + } + } + + if (!drawPicture.Frame) return false; + + drawPicture.Point[0]=new Point(); + drawPicture.Point[0].X=xFixed; + drawPicture.Point[0].Y=yFixed; + drawPicture.Status=1; //第1个点完成 + return true; + } + + this.SetChartDrawPictureSecondPoint=function(x,y,isPhone) + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + //相对坐标 + var pt=this.PointAbsoluteToRelative(x, y, isPhone); + + drawPicture.Point[1]=new Point(); + drawPicture.Point[1].X=pt.X; + drawPicture.Point[1].Y=pt.Y; + + drawPicture.Status=2; //设置第2个点 + return true; + } + + //设置第3个点 + this.SetChartDrawPictureThirdPoint=function(x,y,isPhone) + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + //相对坐标 + var pt=this.PointAbsoluteToRelative(x, y, isPhone); + + drawPicture.Point[2]=new Point(); + drawPicture.Point[2].X=pt.X; + drawPicture.Point[2].Y=pt.Y; + + drawPicture.Status=3; //设置第3个点 + return true; + } + + //xStep,yStep 移动的偏移量 + this.MoveChartDrawPicture=function(x,y,isPhone) + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + var pixelTatio = GetDevicePixelRatio(); //x,y 需要乘以放大倍速 + if (isPhone) pixelTatio=1; + var xStep=x*pixelTatio; + var yStep=y*pixelTatio; + //JSConsole.Chart.Log("xStep="+xStep+" yStep="+yStep); + drawPicture.Move(xStep,yStep); + + return true; + } + + this.FinishChartDrawPicturePoint=function() + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (drawPicture.PointCount!=drawPicture.Point.length) return false; + if (drawPicture.ClassName=="ChartDrawRuler") //尺子不用保存的 + { + this.CurrentChartDrawPicture=null; + return true; + } + + drawPicture.Status=10; //完成 + drawPicture.PointToValue(); + + this.ChartDrawPicture.push(drawPicture); + this.CurrentChartDrawPicture=null; + + //通知上层画好了 + let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_FINISH_DRAWPICTURE); //完成画图工具事件 + if (event && event.Callback) + { + let sendData={ DrawPicture: drawPicture }; + event.Callback(event,sendData,this); + } + else if (drawPicture.FinishedCallback) drawPicture.FinishedCallback(drawPicture); + + if (this.ChartDrawStorage) this.ChartDrawStorage.SaveDrawData(drawPicture); + + return true; + } + + //选中画图工具 出现单个图形设置菜单 + this.OnSelectChartPicture=function(chart) + { + JSConsole.Chart.Log('[JSChartContainer::OnSelectChartPicture',chart); + if (!this.ChartPictureMenu) this.ChartPictureMenu=new ChartPictureSettingMenu(this.UIElement.parentNode); + + var event={ data: { ChartPicture:chart, HQChart:this}}; + this.ChartPictureMenu.DoModal(event); + } + + this.FinishMoveChartDrawPicture=function() + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (drawPicture.PointCount!=drawPicture.Point.length) return false; + + drawPicture.Status=10; //完成 + drawPicture.PointToValue(); + + if (this.ChartDrawStorage) this.ChartDrawStorage.SaveDrawData(drawPicture); + + this.CurrentChartDrawPicture=null; + return true; + } + + //清空所有的画线工具 + this.ClearChartDrawPicture=function(drawPicture) + { + if (!drawPicture) + { + this.ChartDrawPicture=[]; + if (this.ChartDrawStorage) this.ChartDrawStorage.Clear(); + this.Draw(); + } + else + { + for(var i in this.ChartDrawPicture) + { + if (this.ChartDrawPicture[i]==drawPicture) + { + if (this.ChartDrawStorage) this.ChartDrawStorage.DeleteDrawData(drawPicture); + this.ChartDrawPicture.splice(i,1); + this.Draw(); + } + } + } + } + + this.SetChartDrawOption=function(option) + { + if (IFrameSplitOperator.IsBool(option.IsLockScreen)) this.ChartDrawOption.IsLockScreen=option.IsLockScreen; + if (IFrameSplitOperator.IsNumber(option.Zoom) && option.Zoom>=0) this.ChartDrawOption.Zoom=option.Zoom; + } + + //是否显示十字光标的十字线 + this.EnableShowCorssCursorLine=function(bShow) + { + if (!this.ChartCorssCursor) return; + + this.ChartCorssCursor.IsShowCorss=bShow; + } + + //获取扩展画法 + this.GetExtendChartByClassName=function(name) + { + for(var i=0; i=this.ExtendChartPaint.length) return; + if (this.ExtendChartPaint[data.Index]!=data.Chart) return; + + if (typeof(data.Chart.Clear)=='function') data.Chart.Clear(); + this.ExtendChartPaint.splice(data.Index,1); + } + + //全屏提示信息 { Title:提示信息, Draw:false/true 是否立即重绘, } + this.EnableSplashScreen=function(option) + { + if (!this.ChartSplashPaint) return; + if (!option) return; + + if (IFrameSplitOperator.IsString(option.Title)) this.ChartSplashPaint.SetTitle(option.Title); + this.ChartSplashPaint.EnableSplash(true); + + if (option.Draw===false) return; + this.Draw(); + } + + this.SetSubFrameOption=function(subFrame, option) + { + if (!option) return; + + if (option.Window) + { + var item=option.Window; + if (item.Modify!=null) subFrame.Frame.ModifyIndex=item.Modify; + if (item.Change!=null) subFrame.Frame.ChangeIndex=item.Change; + if (item.Close!=null) subFrame.Frame.CloseIndex=item.Close; + if (item.Overlay!=null) subFrame.Frame.OverlayIndex=item.Overlay; + if (item.IsDrawTitleBG==true) subFrame.Frame.IsDrawTitleBG=item.IsDrawTitleBG; + + if (item.OverlayIndexType) + { + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.Position)) subFrame.Frame.OverlayIndexType.Position=item.OverlayIndexType.Position; + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.LineSpace)) subFrame.Frame.OverlayIndexType.LineSpace=item.OverlayIndexType.LineSpace; + } + } + + if (IFrameSplitOperator.IsNumber(option.SplitCount)) subFrame.Frame.YSplitOperator.SplitCount=option.SplitCount; + if (IFrameSplitOperator.IsNumber(option.TitleHeight)) subFrame.Frame.ChartBorder.TitleHeight=option.TitleHeight; + if (IFrameSplitOperator.IsBool(option.IsShowTitleArraw)) subFrame.Frame.IsShowTitleArraw=option.IsShowTitleArraw; + if (IFrameSplitOperator.IsBool(option.IsShowIndexName)) subFrame.Frame.IsShowIndexName=option.IsShowIndexName; + if (IFrameSplitOperator.IsBool(option.IsShowOverlayIndexName)) subFrame.Frame.IsShowOverlayIndexName=option.IsShowOverlayIndexName; + if (IFrameSplitOperator.IsNumber(option.IndexParamSpace)) subFrame.Frame.IndexParamSpace=option.IndexParamSpace; + if (IFrameSplitOperator.IsBool(option.IsShowXLine)) subFrame.Frame.IsShowXLine=option.IsShowXLine; + if (IFrameSplitOperator.IsBool(option.IsShowYLine)) subFrame.Frame.IsShowYLine=option.IsShowYLine; + if (IFrameSplitOperator.IsBool(option.IsShowIndexTitle)) subFrame.Frame.IsShowIndexTitle=option.IsShowIndexTitle; + + if (IFrameSplitOperator.IsBool(option.IsShowLeftText)) + { + subFrame.Frame.IsShowYText[0]=option.IsShowLeftText; + subFrame.Frame.YSplitOperator.IsShowLeftText=option.IsShowLeftText; //显示左边刻度 + } + if (IFrameSplitOperator.IsBool(option.IsShowRightText)) + { + subFrame.Frame.IsShowYText[1]=option.IsShowRightText; + subFrame.Frame.YSplitOperator.IsShowRightText=option.IsShowRightText; //显示右边刻度 + } + + } + + //增加一个指标窗口 + this.AddIndexWindow=function(indexName,option) + { + //查找系统指标 + let scriptData = new JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + + this.RemoveMinSizeWindows(); //清空隐藏的指标 + + var index=this.Frame.SubFrame.length; + var subFrame=this.CreateSubFrameItem(index); + var pixelRatio=GetDevicePixelRatio(); + subFrame.Frame.ChartBorder.TitleHeight*=pixelRatio; + this.Frame.SubFrame[index]=subFrame; + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[index].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + this.TitlePaint[index+1]=titlePaint; + + this.SetSubFrameOption(subFrame,option); + + //最后一个显示X轴坐标 + for(var i=0;i=0) indexData.FloatPrecision=option.FloatPrecision; + if (option.StringFormat>0) indexData.StringFormat=option.StringFormat; + if (option.Args) indexData.Args=option.Args; + } + + this.WindowIndex[index] = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args,indexData); //脚本执行 + if (this.ClassName=="MinuteChartContainer" || this.ClassName=="MinuteChartHScreenContainer") + var bindData=this.SourceData; + else + var bindData=this.ChartPaint[0].Data; + + this.BindIndexData(index,bindData); //执行脚本 + } + + //区间选择 + this.GetRectSelectPaint=function() + { + for(var i=0;i=kData.Data.length) dataIndex=kData.Data.length-1; + + var item = kData.Data[dataIndex]; + JSConsole.Chart.Log("[KLineChartContainer::MoveRectSelectPoint] point, item", obj.PointIndex, item); + + if (!paint.SetPoint(item,{ Index: obj.PointIndex })) return false; + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DRAG_SELECT_RECT); + if (event) + { + var selectData=paint.GetSelectRectData(); + var data={ X:obj.X, Y:obj.Y, SelectData:selectData, RectSelectPaint:paint }; + event.Callback(event,data,this); + } + + return true; + } + + this.ClearRectSelect=function(bEnforce) + { + var paint=this.GetRectSelectPaint(); + if (!paint) return false; + if (bEnforce) paint.PreventClose=false; + return paint.ClearPoint(); + } + + //删除叠加指标, 没有重回 + this.DeleteOverlayIndex=function(identify, windowIndex) + { + var findIndex=null; + if (IFrameSplitOperator.IsNumber(windowIndex)) + { + if (windowIndex>=0 && windowIndex=this.Frame.SubFrame.length ) return false; + if ( !IFrameSplitOperator.IsNumber(dest.WindowIndex) || dest.WindowIndex<0 || dest.WindowIndex>=this.Frame.SubFrame.length ) return false; + + var item=this.Frame.SubFrame[src.WindowIndex]; + var findOverlayItem=null; + for(var i=0; i=this.Frame.SubFrame.length ) return false; + + var item=this.Frame.SubFrame[src.WindowIndex]; + var findOverlayItem=null; + for(var i=0; i { return this.GetEventCallback(id); } + this.TitlePaint[index+1]=titlePaint; + + this.SetSubFrameOption(subFrame, option); + + //最后一个显示X轴坐标 + for(var i=0;i0) return true; + } + return false; +} + +function OnKeyDown(e) //键盘事件 +{ + if(this.JSChartContainer && this.JSChartContainer.OnKeyDown) + this.JSChartContainer.OnKeyDown(e); +} + +function OnWheel(e) //上下滚动事件 +{ + if(this.JSChartContainer && this.JSChartContainer.OnWheel) + this.JSChartContainer.OnWheel(e); +} + +function ToFixed(number, precision) +{ + var b = 1; + if (isNaN(number)) return number; + if (number < 0) b = -1; + var multiplier = Math.pow(10, precision); + var value=Math.round(Math.abs(number) * multiplier) / multiplier * b; + + if (/^(\d+(?:\.\d+)?)(e)([\-]?\d+)$/.test(value)) + var s=value.toFixed2(precision); + else + var s = value.toString(); + + var rs = s.indexOf('.'); + if (rs < 0 && precision>0) + { + rs = s.length; + s += '.'; + } + + while (s.length <= rs + precision) + { + s += '0'; + } + + + + return s; +} + +Number.prototype.toFixed2=Number.prototype.toFixed; //备份下老的 +Number.prototype.toFixed = function( precision ) +{ + return ToFixed(this,precision); +} + +function Guid() +{ + function S4() + { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + } + return "guid" + (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); +} + +function GetScrollPosition() +{ + var scrollPos={}; + var scrollTop=0; + var scrollLeft=0; + if(document.documentElement && document.documentElement.scrollTop) + { + scrollTop=document.documentElement.scrollTop; + scrollLeft=document.documentElement.scrollLeft; + }else if(document.body) + { + scrollTop=document.body.scrollTop; + scrollLeft=document.body.scrollLeft; + } + + scrollPos.Top=scrollTop; + scrollPos.Left=scrollLeft; + return scrollPos; +} + +//修正线段有毛刺 +function ToFixedPoint(value) +{ + return parseInt(value)+0.5; +} + +//修正粗线段毛刺 +function ToFixedPoint2(width, value) +{ + var fixValue=(width % 2)===0 ? Math.floor(value):Math.floor(value) + 0.5; //毛边修正 + return fixValue; +} + +function ToFixedRect(value) +{ + // With a bitwise or. + //rounded = (0.5 + somenum) | 0; + // A double bitwise not. + //rounded = ~~ (0.5 + somenum); + // Finally, a left bitwise shift. + //value*=GetDevicePixelRatio(); + var rounded; + return rounded = (0.5 + value) << 0; +} + + + +function Point() +{ + this.X; + this.Y; +} + +function SelectRectData() +{ + this.Data; //主数据 + this.JSChartContainer; //行情控件 + + this.Start; //数据起始位子 + this.End; //数据结束位置 + + this.XStart;//X坐标起始位置 + this.YStart; + this.XEnd; //X位置结束为止 + this.YEnd; +} + +//坐标信息 +function CoordinateInfo() +{ + this.Value; //坐标数据 + this.Message=[]; //坐标输出文字信息 0=左 1=右 2=内左 3=内右 + this.TextColor=g_JSChartResource.FrameSplitTextColor //文字颜色 + this.Font=g_JSChartResource.FrameSplitTextFont; //字体 + this.LineColor=g_JSChartResource.FrameSplitPen; //线段颜色 + this.LineType=1; //线段类型 -1 不画线段 2 虚线 8,9=集合竞价坐标 + this.ExtendData; //扩展属性 + //百分比 { PriceColor:, PercentageColor:, SplitColor:, Font: } +} + + +//边框信息 +function ChartBorder() +{ + this.UIElement; + + //四周间距 + this.Left=50; + this.Right=80; + this.Top=50; + this.Bottom=50; + this.TitleHeight=24; //标题高度 + this.TopSpace=0; + this.BottomSpace=0; + + this.LeftExtendWidth=0; //左边扩展图形宽度 + this.RightExtendWidth=0; + + this.MultiDayMinute={ Count:1, Left:0, Right:0 } // { Count:天数, Left:, Right: } + + this.GetBorder=function() + { + var data= + { + Left:this.Left, + LeftEx:this.Left+this.LeftExtendWidth, + Right:this.UIElement.width-this.Right, + RightEx:this.UIElement.width-this.Right-this.RightExtendWidth, + + Top:this.Top, + TopEx:this.Top+this.TitleHeight+this.TopSpace, + TopTitle:this.Top+this.TitleHeight, + Bottom:this.UIElement.height-this.Bottom, + BottomEx:this.UIElement.height-this.Bottom-this.BottomSpace, + + ChartWidth:this.UIElement.width, + ChartHeight:this.UIElement.height + }; + + if (this.MultiDayMinute && this.MultiDayMinute.Count>1 && ( this.MultiDayMinute.Left>0 || this.MultiDayMinute.Right>0 )) + { + var frameWidth=this.UIElement.width-this.Left-this.Right; //坐标框子宽度 + var dayWidth=frameWidth/this.MultiDayMinute.Count; // 每天的框子的宽度 + + var dayBorder=[]; + for(var i=0;i10) + { + canvas.beginPath(); + if (isHScreen===true) + { + canvas.rect(border.Left,border.Top,border.Right-border.Left,border.TopEx-border.Top); + } + else + { + canvas.rect(border.Left,border.Top,border.LeftEx-border.Left,border.Bottom-border.Top); + } + + if (canvas.isPointInPath(x,y)) return 2; + } + + if (this.RightExtendWidth>10) + { + canvas.beginPath(); + if (isHScreen===true) + { + canvas.rect(border.Left,border.BottomEx,border.Right-border.Left,border.Bottom-border.BottomEx); + } + else + { + canvas.rect(border.RightEx,border.Top,border.Right-border.RightEx,border.Bottom-border.Top); + } + + if (canvas.isPointInPath(x,y)) return 3; + } + + return -1; + } + + this.GetChartWidth=function() + { + return this.UIElement.width; + } + + this.GetChartHeight=function() + { + return this.UIElement.height; + } + + this.GetLeft=function() + { + return this.Left+this.LeftExtendWidth; + } + + this.GetRight=function() + { + return this.UIElement.width-this.Right-this.RightExtendWidth; + } + + this.GetTop=function() + { + return this.Top; + } + + this.GetTopEx=function() //去掉标题,上面间距 + { + return this.Top+this.TitleHeight+this.TopSpace; + } + + this.GetTopTitle=function() //去掉标题 + { + return this.Top+this.TitleHeight; + } + + this.GetBottom=function() + { + return this.UIElement.height-this.Bottom; + } + + this.GetBottomEx=function() + { + return this.UIElement.height-this.Bottom-this.BottomSpace; + } + + this.GetWidth=function() + { + return this.UIElement.width-this.Left-this.Right-this.LeftExtendWidth-this.RightExtendWidth; + } + + this.GetHeight=function() + { + return this.UIElement.height-this.Top-this.Bottom; + } + + this.GetHeightEx=function() //去掉标题的高度, 上下间距 + { + return this.UIElement.height-this.Top-this.Bottom-this.TitleHeight-this.TopSpace-this.BottomSpace; + } + + this.GetRightEx=function() //横屏去掉标题高度的 上面间距 + { + return this.UIElement.width-this.Right-this.TitleHeight- this.TopSpace; + } + + this.GetWidthEx=function() //横屏去掉标题宽度 上下间距 + { + return this.UIElement.width-this.Left-this.Right-this.TitleHeight- this.TopSpace - this.BottomSpace; + } + + this.GetLeftEx = function () //横屏 + { + return this.Left+this.BottomSpace; + } + + this.GetRightTitle = function ()//横屏 + { + return this.UIElement.width - this.Right - this.TitleHeight; + } + + this.GetTitleHeight=function() + { + return this.TitleHeight; + } +} + +function IChartFramePainting() +{ + this.HorizontalInfo=new Array(); //Y轴 + this.VerticalInfo=new Array(); //X轴 + this.ClassName='IChartFramePainting'; + + this.Canvas; //画布 + + this.Identify; //窗口标识 + + this.ChartBorder; + this.PenBorder=g_JSChartResource.FrameBorderPen; //边框颜色 + this.TitleBGColor=g_JSChartResource.FrameTitleBGColor; //标题背景色 + this.IsShow=true; //是否显示 + this.SizeChange=true; //大小是否改变 + this.XYSplit=true; //XY轴坐标信息改变 + + this.HorizontalMax; //Y轴最大值 + this.HorizontalMin; //Y轴最小值 + this.XPointCount=10; //X轴数据个数 + + this.YSplitOperator; //Y轴分割 + this.XSplitOperator; //X轴分割 + this.Data; //主数据 + + this.IsLocked=false; //是否上锁 + this.LockPaint = null; + + this.YSpecificMaxMin=null; //指定Y轴最大最小值 + this.IsShowBorder = true; //是否显示边框 + this.IsShowTitleArraw=true; //是否显示指标信息上涨下跌箭头 + this.IsShowIndexName=true; //是否显示指标名字 + this.IsShowOverlayIndexName=true; //是否显示叠加指标名字 + this.OverlayIndexType= { Position:0, LineSpace:5 }; + this.IndexParamSpace=2; //指标参数数值显示间距 + this.IsShowIndexTitle=true; //显示整个指标标题信息 + this.IsDrawTitleBottomLine=false; + + this.BorderLine=null; //1=上 2=下 4=左 8=右 + this.Buttons=[]; //按钮事件 + + this.IsMinSize=false; //窗口是否最小化 + + + this.PtInButtons=function(x,y) //坐标是否在按钮上 + { + for(var i=0;i0) //上 + { + this.Canvas.moveTo(left,top); + this.Canvas.lineTo(right,top); + } + + if ((this.BorderLine&2)>0) //下 + { + this.Canvas.moveTo(left,bottom); + this.Canvas.lineTo(right,bottom); + } + + if ((this.BorderLine&4)>0) //左 + { + this.Canvas.moveTo(left,top); + this.Canvas.lineTo(left,bottom); + } + + if ((this.BorderLine&8)>0) //右 + { + this.Canvas.moveTo(right,top); + this.Canvas.lineTo(right,bottom); + } + + this.Canvas.stroke(); + } + + + } + + //画标题背景色 + this.DrawTitleBG=function() + { + if (this.ChartBorder.TitleHeight<=0) return; + + var border=this.GetBorder(); + + var left=ToFixedPoint(border.Left); + var top=ToFixedPoint(border.Top); + var right=ToFixedPoint(border.Right); + var bottom=ToFixedPoint(this.ChartBorder.GetTopTitle()); + var width=right-left; + var height=bottom-top; + + this.Canvas.fillStyle=this.TitleBGColor; + this.Canvas.fillRect(left,top,width,height); + + if (this.IsDrawTitleBottomLine) + { + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + this.Canvas.moveTo(left,ToFixedPoint(border.TopTitle)); + this.Canvas.lineTo(right,ToFixedPoint(border.TopTitle)); + this.Canvas.stroke(); + } + } + + this.DrawLock=function() + { + if (this.IsLocked) + { + if (this.LockPaint == null) + this.LockPaint = new ChartLock(); + this.LockPaint.Canvas=this.Canvas; + this.LockPaint.ChartBorder=this.ChartBorder; + this.LockPaint.ChartFrame=this; + this.LockPaint.Draw(true); + } + } + + this.CalculateLock=function() + { + if (this.IsLocked) + { + if (this.LockPaint == null) + this.LockPaint = new ChartLock(); + this.LockPaint.Canvas=this.Canvas; + this.LockPaint.ChartBorder=this.ChartBorder; + this.LockPaint.ChartFrame=this; + this.LockPaint.Draw(false); + } + } + + //设施上锁 + this.SetLock=function(lockData) + { + if (!lockData) //空数据不上锁 + { + this.IsLocked=false; + return; + } + + this.IsLocked=true; + if (!this.LockPaint) this.LockPaint=new ChartLock(); //创建锁 + + if (lockData.Callback) this.LockPaint.Callback=lockData.Callback; //回调 + if (lockData.IndexName) this.LockPaint.IndexName=lockData.IndexName; //指标名字 + if (lockData.ID) this.LockPaint.LockID=lockData.ID; //锁ID + if (lockData.BG) this.LockPaint.BGColor=lockData.BG; //背景色 + if (lockData.Text) this.LockPaint.Title= lockData.Text; + if (lockData.TextColor) this.LockPaint.TextColor=lockData.TextColor; + if (lockData.Font) this.LockPaint.Font=lockData.Font; + if (lockData.Count) this.LockPaint.LockCount=lockData.Count; + if (lockData.MinWidth>0) this.LockPaint.MinWidth=lockData.MinWidth; + } + + this.GetLockRect=function() + { + if (!this.IsLocked) return null; + if (!this.LockPaint) return null; + return this.LockPaint.LockRect; + } + + this.ReloadResource=function(resource) + { + if (!resource) + { + this.PenBorder=g_JSChartResource.FrameBorderPen; //边框颜色 + this.TitleBGColor=g_JSChartResource.FrameTitleBGColor; //标题背景色 + } + + for(var i in this.HorizontalInfo) + { + var item=this.HorizontalInfo[i]; + if (item.Font) item.Font=g_JSChartResource.FrameSplitTextFont; //字体 + if (item.TextColor) item.TextColor=g_JSChartResource.FrameSplitTextColor //文字颜色 + if (item.LineColor) item.LineColor=g_JSChartResource.FrameSplitPen; //线段颜色 + } + + for(var i in this.VerticalInfo) + { + var item=this.VerticalInfo[i]; + if (item.Font) item.Font=g_JSChartResource.FrameSplitTextFont; //字体 + if (item.TextColor) item.TextColor=g_JSChartResource.FrameSplitTextColor //文字颜色 + if (item.LineColor) item.LineColor=g_JSChartResource.FrameSplitPen; //线段颜色 + } + } + + this.GetFontHeight=function(font) + { + return GetFontHeight(this.Canvas, font, "擎"); + } + + //左右刻度文字宽度 + this.GetScaleTextWidth=function() + { + + } +} + +//空框架只画边框 +function NoneFrame() +{ + this.newMethod=IChartFramePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="NoneFrame"; + + this.Snapshot=function() + { + + } + + this.SetSizeChage=function(sizeChange) + { + this.SizeChange=sizeChange; + + //画布的位置 + this.Position={ + X:this.ChartBorder.UIElement.offsetLeft, + Y:this.ChartBorder.UIElement.offsetTop, + W:this.ChartBorder.UIElement.clientWidth, + H:this.ChartBorder.UIElement.clientHeight + }; + } +} + +function AverageWidthFrame() +{ + this.newMethod=IChartFramePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="AverageWidthFrame"; + this.DataWidth=50*GetDevicePixelRatio(); + this.DistanceWidth=10*GetDevicePixelRatio(); + this.MinXDistance = 30*GetDevicePixelRatio(); //X轴刻度最小间距 + this.MinYDistance=12*GetDevicePixelRatio(); //Y轴刻度最小间距 + this.CoordinateType=0; //坐标类型 0=普通坐标 1=反转坐标 + this.IsShowYText=[true,true]; //是否显示Y轴坐标坐标 [0=左侧] [1=右侧] + this.XBottomOffset=g_JSChartResource.Frame.XBottomOffset; //X轴文字显示向下偏移 + this.YTextTopOffset=g_JSChartResource.Frame.YTopOffset; //Y轴顶部文字向下偏移 + this.YTextPosition=[0,0], //是坐标否强制画在内部 [0=左侧] [1=右侧] 1=OUT" , 2=INSIDE + this.IsShowXLine=true; //是否显示X轴刻度线 + this.IsShowYLine=true; + this.YInsideOffset=0; + this.YTextBaseline=0; //0=居中 1=上部 (目前就支持内部刻度) + this.MultiTextFormat=0; //多行刻度信息显示模式 0=显示第1行 1=价格/百分比 2=显示2行 + this.RightTextMaxWidth=0; + + this.ShortYLineLength=5; + this.ShortXLineLength=5; + this.DrawDepthMapCallback; //绘制深度图 + + this.DrawFrame=function() + { + if (this.XPointCount>0) + { + let dInterval=this.ChartBorder.GetWidth()/(6*this.XPointCount); //分6份, 数据4 间距2 + this.DistanceWidth=2*dInterval; + this.DataWidth=4*dInterval; + } + + this.DrawHorizontal(); + this.DrawVertical(); + } + + //isLimit 是否限制在当前坐标下 + this.GetYFromData=function(value, isLimit) + { + if (this.Logarithmic && this.GetYLogarithmicFromData) + { + return this.GetYLogarithmicFromData(value, isLimit); + } + + if (isLimit===false) + { + if (this.CoordinateType==1) + { + var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return this.ChartBorder.GetTopEx()+height; + } + else + { + var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return this.ChartBorder.GetBottomEx()-height; + } + } + else + { + if (this.CoordinateType==1) + { + if(value<=this.HorizontalMin) return this.ChartBorder.GetTopEx(); + if(value>=this.HorizontalMax) return this.ChartBorder.GetBottomEx(); + + var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return this.ChartBorder.GetTopEx()+height; + } + else + { + if(value<=this.HorizontalMin) return this.ChartBorder.GetBottomEx(); + if(value>=this.HorizontalMax) return this.ChartBorder.GetTopEx(); + + var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return this.ChartBorder.GetBottomEx()-height; + } + } + } + + //画Y轴 + this.DrawHorizontal=function() + { + this.RightTextMaxWidth=0; + if (!IFrameSplitOperator.IsNonEmptyArray(this.HorizontalInfo)) return; + + var border=this.ChartBorder.GetBorder(); + var left=border.Left; + var right=border.Right + var bottom = border.Bottom + var top = this.ChartBorder.GetTopTitle(); + var borderRight=this.ChartBorder.Right; + var borderLeft=this.ChartBorder.Left; + + var isDrawLeft=borderLeft>10 && this.IsShowYText[0]===true && this.YTextPosition[0]!=2; + var isDrawRight=borderRight>10 && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + + var yPrev=null; //上一个坐标y的值 + var pixelRatio=GetDevicePixelRatio(); + var itemHeight=(border.BottomEx-border.TopEx)/this.HorizontalInfo.length; + var aryMultiText=[]; + for(var i=this.HorizontalInfo.length-1; i>=0; --i) //从上往下画分割线 + { + var item=this.HorizontalInfo[i]; + var y=this.GetYFromData(item.Value); + if (y!=null && yPrev!=null && Math.abs(y-yPrev)0) + { + if (g_JSChartResource.FrameYLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameYLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(left,yFixed); + this.Canvas.lineTo(right,yFixed); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.beginPath(); + this.Canvas.moveTo(left,yFixed); + this.Canvas.lineTo(right,yFixed); + this.Canvas.stroke(); + } + } + } + + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + this.Canvas.textBaseline = "middle"; + } + + //坐标信息 左边 间距小于10 不画坐标 + this.Canvas.strokeStyle=item.TextColor; + this.Canvas.fillStyle=item.TextColor; + + if (item.Message[0]!=null && isDrawLeft) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.textAlign="right"; + this.Canvas.fillText(item.Message[0],left-2,yText); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1]!=null && isDrawRight) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + var xText=right; + if (item.LineType==3) + { + var lineLength=this.ShortYLineLength*GetDevicePixelRatio(); + this.Canvas.beginPath(); + this.Canvas.moveTo(xText,yFixed); + this.Canvas.lineTo(xText+lineLength,yFixed); + this.Canvas.stroke(); + + xText+=lineLength; + } + + this.Canvas.textAlign="left"; + if (Array.isArray(item.Message[1])) + { + if (this.MultiTextFormat==1) //显示1行 格式:价格/百分比 + { + if (item.ExtendData) + { + if (item.ExtendData.Font) this.Canvas.font=item.ExtendData.Font; + } + + var textData= + { + Text: + [ + {Text:item.Message[1][0], Width:this.Canvas.measureText(item.Message[1][0]).width }, + {Text:item.Message[1][1], Width:this.Canvas.measureText(item.Message[1][1]).width } + ], + X:xText+2, + Y:yText, + TextBaseline:this.Canvas.textBaseline, + Item:item + } + aryMultiText.push(textData); + } + else if (this.MultiTextFormat==2) //显示2行 + { + this.Canvas.fillText(item.Message[1][0],xText+2,yText); + var lineHeight=this.Canvas.measureText('M').width; + if (itemHeight>lineHeight*2) this.Canvas.fillText(item.Message[1][1],xText+2,yText+lineHeight); + } + else //显示第1行 + { + this.Canvas.fillText(item.Message[1][0],xText+2,yText); + } + } + else + { + this.Canvas.fillText(item.Message[1],xText+2,yText); + } + + } + + yPrev=y; + } + + if (IFrameSplitOperator.IsNonEmptyArray(aryMultiText) && this.MultiTextFormat==1) this.DrawHorizontalMuText(aryMultiText) + } + + this.DrawHorizontalMuText=function(aryText) + { + var maxWidth=[null,null]; + for(var i=0;i= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + if (item.Message[2]) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + this.Canvas.textBaseline = "middle"; + } + + var textObj={ X:left, Y:yText, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) + { + this.Canvas.fillText(item.Message[2], left + 1*pixelTatio, yText); + if (yInsideText==null || yInsideText>yText) + { + this.YInsideOffset=this.Canvas.measureText(item.Message[2]).width+4*GetDevicePixelRatio(); + yInsideText=yText; + } + + } + } + + if (item.Message[3]) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + this.Canvas.textBaseline = "middle"; + } + var textWidth = this.Canvas.measureText(item.Message[3]).width; + var textObj={ X:right-textWidth, Y:yText, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) + this.Canvas.fillText(item.Message[3], right - 1*pixelTatio, yText); + } + yPrev = y; + } + } + + //Y刻度画在左边内部 + this.DrawInsideHorizontal = function () + { + if (this.IsHScreen===true) return; //横屏不画 + if (this.IsMinSize) return; + if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return; + + this.DrawInsideClientHorizontal(); + + var border=this.ChartBorder.GetBorder(); + + var left = border.Left + var right = border.Right; + var bottom = border.Bottom; + var top = border.TopTitle; + var borderRight = this.ChartBorder.Right; + var borderLeft = this.ChartBorder.Left; + var titleHeight = this.ChartBorder.TitleHeight; + + var isDrawLeft= (borderLeft<10 || this.YTextPosition[0]==2) && this.IsShowYText[0]===true; + var isDrawRight= (borderRight<10 || this.YTextPosition[1]==2) && this.IsShowYText[1]===true; + + if ( isDrawLeft || isDrawRight ) + { + var pixelTatio = GetDevicePixelRatio(); + var yPrev = null; //上一个坐标y的值 + var yInsideText=null; + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + //坐标信息 左边 间距小于10 画在内部 + if (item.Message[0] != null && isDrawLeft) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + if (this.YTextBaseline==1) this.Canvas.textBaseline = "bottom"; + else this.Canvas.textBaseline = "middle"; + } + + var textObj={ X:left, Y:yText, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) + { + this.Canvas.fillText(item.Message[0], left + 1*pixelTatio, yText); + if (yInsideText==null || yInsideText>yText) + { + this.YInsideOffset=this.Canvas.measureText(item.Message[0]).width+4*GetDevicePixelRatio(); + yInsideText=yText; + } + + } + } + + if (item.Message[1] != null && isDrawRight) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + if (this.YTextBaseline==1) this.Canvas.textBaseline = "bottom"; + else this.Canvas.textBaseline = "middle"; + } + + if (Array.isArray(item.Message[1])) var text=item.Message[1][0]; + else var text=item.Message[1]; + + var textWidth = this.Canvas.measureText(text).width; + var textObj={ X:right-textWidth, Y:yText, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) + this.Canvas.fillText(text, right - 1*pixelTatio, yText); + } + yPrev = y; + } + } + } + + this.GetXFromIndex=function(index) + { + var count=this.XPointCount; + + if (count==1) + { + if (index==0) return this.ChartBorder.GetLeft(); + else return this.ChartBorder.GetRight(); + } + else if (count<=0) + { + return this.ChartBorder.GetLeft(); + } + else if (index>=count) + { + return this.ChartBorder.GetRight(); + } + else + { + var offset=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*index/count; + return offset; + } + } + + //画X轴 + this.DrawVertical=function() + { + var border=this.GetBorder(); + var top=border.TopTitle; + var bottom=border.Bottom; + var right=border.RightEx; + var pixelRatio = GetDevicePixelRatio(); //获取设备的分辨率 + //JSConsole.Chart.Log('[AverageWidthFrame.DrawVertical] bottom',bottom); + if (this.ChartBorder.Bottom<=5*GetDevicePixelRatio()) return; //高度不够 不显示 + + var xPrev=null; //上一个坐标x的值 + var textRightPrev=null; //上一次刻度输出右边x坐标 + for(var i in this.VerticalInfo) + { + var x=this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x>right) break; + if (xPrev!=null && Math.abs(x-xPrev)0) //实线 + { + if (g_JSChartResource.FrameXLineDash) + { + this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor; + this.Canvas.setLineDash(g_JSChartResource.FrameXLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,top); + this.Canvas.lineTo(xFixed,bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,top); + this.Canvas.lineTo(xFixed,bottom); + this.Canvas.stroke(); + } + } + } + + + if (this.VerticalInfo[i].Message[0]!=null) + { + if (this.VerticalInfo[i].Font!=null) + this.Canvas.font=this.VerticalInfo[i].Font; + + var textLeft=0; + this.Canvas.fillStyle=item.TextColor; + this.Canvas.strokeStyle=item.TextColor; + var testWidth=this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (x= this.ChartBorder.GetChartWidth()) + { + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline="top"; + textLeft=x-testWidth; + } + else + { + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="top"; + textLeft=x-(testWidth/2); + } + + if (textRightPrev==null || textLeft>textRightPrev) + { + var yText=bottom; + if (item.LineType==3) + { + var lineLength=this.ShortXLineLength*pixelRatio; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,yText); + this.Canvas.lineTo(xFixed,yText+lineLength); + this.Canvas.stroke(); + + yText+=lineLength+2*pixelRatio; + } + + this.Canvas.fillText(this.VerticalInfo[i].Message[0],x,yText+this.XBottomOffset); + textRightPrev=textLeft+testWidth; + } + } + + xPrev=x; + } + } + + //Y坐标转y轴数值 + this.GetYData=function(y,isLimit) + { + if (this.Logarithmic && this.GetYLogarithmicFromData) + { + return this.GetYLogarithmicData(y); + } + + if (this.CoordinateType==1) //反转坐标 + { + if (isLimit==false) + { + return (y-this.ChartBorder.GetTopEx())/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + else + { + if (ythis.ChartBorder.GetBottomEx()) return this.HorizontalMax; + + return (y-this.ChartBorder.GetTopEx())/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + } + else + { + if (isLimit==false) + { + return (this.ChartBorder.GetBottomEx()-y)/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + else + { + if (ythis.ChartBorder.GetBottomEx()) return this.HorizontalMin; + + return (this.ChartBorder.GetBottomEx()-y)/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + } + } + + //X坐标转x轴数值 + this.GetXData=function(x) + { + if (x<=this.ChartBorder.GetLeft()) return 0; + if (x>=this.ChartBorder.GetRight()) return this.XPointCount; + + return (x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth()); + } + + //字体外部设置好 + this.GetCustomItemTextInfo=function(item, bLeft,pixelTatio) + { + var text=bLeft?item.Message[0]:item.Message[1]; + var aryText=[]; + var width=0; + if (Array.isArray(text)) + { + for(var i=0;iwidth) width=value; + aryText.push({Text:text[i].Text, Width:value+2*pixelTatio}); + } + + if (width>0) width+=2*pixelTatio; + } + else + { + width=this.Canvas.measureText(text).width+2*pixelTatio; + aryText.push( {Text:text, Width:width} ); + } + + return { MaxWidth:width, Text:aryText }; + } + + this.DrawCustomItem=function(item) //显示自定义刻度 + { + //if (this.IsHScreen===true) return; //横屏不画 + if (!item.Message[1] && !item.Message[0]) return; + if (item.Value>this.HorizontalMax || item.ValuedefaultTextHeight? fontHeight:defaultTextHeight; + var bgColor=item.LineColor; + var rgb=this.RGBToStruct(item.LineColor); + if (rgb) bgColor=`rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`; //内部刻度 背景增加透明度 + + var yText=y; + for(var i=0;idefaultTextHeight? fontHeight:defaultTextHeight; + + var yText=y; + for(var i=0;idefaultTextHeight? fontHeight:defaultTextHeight; + var bgColor=item.LineColor; + var rgb=this.RGBToStruct(item.LineColor); + if (rgb) bgColor=`rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`; //内部刻度 背景增加透明度 + + var yText=y; + for(var i=0;idefaultTextHeight? fontHeight:defaultTextHeight; + + var yText=y; + for(var i=0;i10*pixelTatio && this.IsShowYText[0]===true && this.YTextPosition[0]!=2; + var isDrawRight=borderBottom>10*pixelTatio && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + } + else + { + var borderRight=this.ChartBorder.Right; + var borderLeft=this.ChartBorder.Left; + var isDrawLeft=borderLeft>10 && this.IsShowYText[0]===true && this.YTextPosition[0]!=2; + var isDrawRight=borderRight>10 && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + } + + if (!isDrawRight && !isDrawLeft) return null; + + var width={ Left:null, Right:null }; + var rightExtendWidth=0; + for(var i=0;i`; + const changeButton=``; + const closeButton=``; + + var spanIcon=modifyButton+changeButton; + if (this.CloseIndex) + { + spanIcon+=closeButton; + } + + //var scrollPos=GetScrollPosition(); + //left = left+scrollPos.Left; + //top = top+scrollPos.Top; + divToolbar.style.left = left + "px"; + divToolbar.style.top = top + "px"; + divToolbar.style.width=toolbarWidth+"px"; //宽度先不调整吧 + divToolbar.style.height=(toolbarHeight/pixelTatio)+'px'; //只调整高度 + divToolbar.innerHTML=spanIcon; + + var chart=this.ChartBorder.UIElement.JSChartContainer; + var identify=this.Identify; + if (!this.ModifyIndex) //隐藏'改参数' + $("#"+divToolbar.id+" .index_param").hide(); + else if (typeof(this.ModifyIndexEvent)=='function') //绑定点击事件 + $("#"+divToolbar.id+" .index_param").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify + },this.ModifyIndexEvent); + + if (!this.ChangeIndex) //隐藏'换指标' + { + $("#"+divToolbar.id+" .index_change").hide(); + } + else if (typeof(this.ChangeIndexEvent)=='function') + { + $("#"+divToolbar.id+" .index_change").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify, + IsOverlay:false + },this.ChangeIndexEvent); + } + + $("#"+divToolbar.id+" .index_close").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify + }, + function(event) + { + var hqChart=event.data.Chart; + var id=event.data.Identify; + hqChart.RemoveIndexWindow(id); + }); + + divToolbar.style.display = "block"; + } + + this.DrawMultiDayBeforeDataBG=function(border) + { + var dayBorder=border.DayBorder; + var top=ToFixedPoint(border.Top); + var bottom=ToFixedPoint(border.Bottom); + var height=bottom-top; + this.Canvas.fillStyle=this.BeforeBGColor; + this.Canvas.strokeStyle=this.MultiDayBorderPen; + for(var i in dayBorder) + { + var drawCount=0; + var item=dayBorder[i]; + var left=ToFixedPoint(item.Left); + var right=ToFixedPoint(item.LeftEx); + var width=right-left; + if (width>3) + { + this.Canvas.fillRect(left,top,width,height); + ++drawCount; + } + + var left=ToFixedPoint(item.RightEx); + var right=ToFixedPoint(item.Right); + var width=right-left; + if (width>3) + { + this.Canvas.fillRect(left,top,width,height); + ++drawCount; + } + + if (drawCount==2 && i!=dayBorder.length-1) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(right,top); + this.Canvas.lineTo(right,bottom); + this.Canvas.stroke(); + } + } + } + + this.DrawCallAuctionVertical=function(verticalInfo, callAuctionData, border, isBeforeClose) + { + if (!callAuctionData) return; + + var top=border.TopTitle; + var bottom=border.Bottom; + var left=border.Left; + var right=border.LeftEx; + var pixelRatio = GetDevicePixelRatio(); //获取设备的分辨率 + + var xPrev=null; //上一个坐标x的值 + var textRightPrev=null; //上一次刻度输出右边x坐标 + + for(var i in verticalInfo) + { + var item=verticalInfo[i]; + var x=this.GetLeftExtendXFromIndex(item.Value,callAuctionData); + if (x>right) break; + if (xPrev!=null && Math.abs(x-xPrev)0) //实线 + { + if (g_JSChartResource.FrameXLineDash) + { + this.Canvas.strokeStyle=item.LineColor; + this.Canvas.setLineDash(g_JSChartResource.FrameXLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,top); + this.Canvas.lineTo(xFixed,bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.strokeStyle=item.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,top); + this.Canvas.lineTo(xFixed,bottom); + this.Canvas.stroke(); + } + } + } + + + if (item.Message[0]!=null && this.ChartBorder.Bottom>5*pixelRatio) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + var textLeft=0; + this.Canvas.fillStyle=item.TextColor; + this.Canvas.strokeStyle=item.TextColor; + var testWidth=this.Canvas.measureText(item.Message[0]).width; + if (x= border.ChartWidth) + { + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline="top"; + textLeft=x-testWidth; + } + else + { + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="top"; + textLeft=x-(testWidth/2); + } + + if (textRightPrev==null || textLeft>textRightPrev) + { + var yText=bottom; + if (item.LineType==3) + { + var lineLength=this.ShortXLineLength*pixelRatio; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,yText); + this.Canvas.lineTo(xFixed,yText+lineLength); + this.Canvas.stroke(); + + yText+=lineLength+2*pixelRatio; + } + + this.Canvas.fillText(item.Message[0],x,yText+this.XBottomOffset); + textRightPrev=textLeft+testWidth; + } + } + + xPrev=x; + } + } + + //画集合竞价背景 + this.DrawBeforeDataBG=function() + { + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) + { + this.DrawMultiDayBeforeDataBG(border); + return; + } + + if (this.ChartBorder.LeftExtendWidth<10 && this.ChartBorder.RightExtendWidth<10) return; + //if (this.Identify>=2) return; + + var top=ToFixedPoint(border.Top); + var bottom=ToFixedPoint(border.Bottom); + this.Canvas.fillStyle=this.BeforeBGColor; + + if (this.ChartBorder.LeftExtendWidth>10) + { + var left=ToFixedPoint(border.Left); + var right=ToFixedPoint(this.ChartBorder.GetLeft()); + var width=right-left; + var height=bottom-top; + + this.Canvas.fillRect(left,top,width,height); + + //绘制关闭图标 + if (this.Identify==0) + { + this.Canvas.font=this.BeforeCloseIcon.Size+'px '+this.BeforeCloseIcon.Family; + this.Canvas.fillStyle=this.BeforeCloseIcon.Color; + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline='top'; + this.Canvas.fillText(this.BeforeCloseIcon.Text,border.Left,border.TopEx); + + var rect={ Left:border.Left, Top:border.TopEx, Width:this.BeforeCloseIcon.Size, Height:this.BeforeCloseIcon.Size }; + this.Buttons.push( { Rect:rect, Name:"集合竞价关闭按钮" , ID:JSCHART_BUTTON_ID.CLOSE_BEFOREOPEN_ID }); + } + + this.DrawCallAuctionVertical(this.BeforeOpenVerticalInfo, this.YSplitOperator.BeforeOpenData, border, true); + } + + if (this.ChartBorder.RightExtendWidth>10) + { + this.Canvas.fillStyle=this.BeforeBGColor; + var left=ToFixedPoint(this.ChartBorder.GetRight()); + var right=ToFixedPoint(border.Right); + var width=right-left; + var height=bottom-top; + + this.Canvas.fillRect(left,top,width,height); + } + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); + } + + this.GetMultiDayXFromIndex=function(index, border) + { + var dayBorder=border.DayBorder; + var minuteIndex=index%this.MinuteCount; + var dayIndex=parseInt(index/this.MinuteCount); + if (dayIndex>=dayBorder.length) dayIndex=dayBorder.length-1; + + var client=dayBorder[dayIndex]; + var count=this.MinuteCount-1; + + if (minuteIndex>=count) + { + return client.RightEx; + } + else + { + var width=(client.RightEx-client.LeftEx); + var offset=client.LeftEx+width*minuteIndex/count; + return offset; + } + } + + this.GetXFromIndex=function(index) + { + var count=this.XPointCount-1; + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetMultiDayXFromIndex(index, border); + + if (count==1) + { + if (index==0) return border.LeftEx; + else return border.RightEx; + } + else if (count<=0) + { + return border.LeftEx; + } + else if (index>=count) + { + return border.RightEx; + } + else + { + var width=(border.RightEx-border.LeftEx); + var offset=border.LeftEx+width*index/count; + return offset; + } + } + + this.GetMultiDayXData=function(x,border) + { + var dayBorder=border.DayBorder; + for(var i=0;i=client.Left && x<=client.Right) + { + var count=this.MinuteCount-1; + var dayMinuteCount=this.MinuteCount*i; + if (x<=client.LeftEx) return 0+dayMinuteCount; + if (x>=client.RightEx) return count+dayMinuteCount; + + var width=client.RightEx-client.LeftEx; + return (x-client.LeftEx)*(count*1.0/width)+dayMinuteCount; + } + } + } + + //X坐标转x轴数值 + this.GetXData=function(x) + { + var count=this.XPointCount-1; + if (count<0) count=0; + + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetMultiDayXData(x, border); + + if (x<=border.LeftEx) return 0; + if (x>=border.RightEx) return count; + + var width=border.RightEx-border.LeftEx; + return (x-border.LeftEx)*(count*1.0/width); + } + + this.DrawCustomHorizontal=function() //Y轴刻度定制显示 + { + if (this.IsMinSize) return; + for(var i in this.CustomHorizontalInfo) + { + var item=this.CustomHorizontalInfo[i]; + switch(item.Type) + { + case 0: + case 1: + this.DrawCustomItem(item); //自定义刻度 + break; + } + } + } + + this.GetLeftExtendXFromIndex=function(index, obj) + { + var count=obj.TotalCount-1; + + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetLeftExtendMultiDayXFromIndex(index, obj, border); + + var left=border.Left; + var width=this.ChartBorder.LeftExtendWidth; + + var offset=left+width*index/count; + return offset; + } + + this.GetLeftExtendMultiDayXFromIndex=function(index, obj, border) + { + var dayBorder=border.DayBorder; + var client=dayBorder[obj.Index]; + var count=obj.TotalCount-1; + + var left=client.Left; + var right=client.LeftEx; + var width=right-left; + + var offset=left+width*index/count; + return offset; + } + + this.GetLeftExtendMultiDayXData=function(x, obj, border) + { + var dayBorder=border.DayBorder; + for(var i in dayBorder) + { + var client=dayBorder[i]; + if (x>=client.Left && x<=client.LeftEx) + { + var count=obj[i].TotalCount-1; + var left=client.Left; + var right=client.LeftEx; + + var width=right-left; + var index=(x-left)*(count*1.0/width); + + return { DayIndex:parseInt(i), DataIndex:index }; + } + } + } + + this.GetLeftExtendXData=function(x, obj) + { + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetLeftExtendMultiDayXData(x, obj, border); + + var count=obj.TotalCount-1; + if (count<0) count=0; + + var left=border.Left; + var right=border.LeftEx; + + if (x<=left) return 0; + if (x>=right) return count; + + var width=right-left; + return (x-left)*(count*1.0/width); + } + + this.GetLeftExtendYFromData=function(value,isLimit,obj) + { + if (!obj || !obj.Range) return this.GetYFromData(value,isLimit); + + var range=obj.Range; + var border=this.ChartBorder.GetBorder(); + var height=border.BottomEx-border.TopEx; + var offset=height*(value-range.Min)/(range.Max-range.Min); + return border.BottomEx-offset; + } + + this.GetLeftExtendYData=function(y, isLimit, obj) + { + if (!obj || !obj.Range) return this.GetYData(y,isLimit); + + var range=obj.Range; + var border=this.ChartBorder.GetBorder(); + var height=border.BottomEx-border.TopEx; + return (border.BottomEx-y)/height*(range.Max-range.Min)+range.Min; + } + + this.GetRightExtendMultiDayXFromIndex=function(index, obj, border) + { + var dayBorder=border.DayBorder; + var client=dayBorder[obj.Index]; + var count=obj.TotalCount-1; + + var left=client.RightEx; + var right=client.Right; + var width=right-left; + + var offset=left+width*index/count; + return offset; + } + + this.GetRightExtendXFromIndex=function(index, obj) + { + var count=obj.TotalCount-1; + + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetRightExtendMultiDayXFromIndex(index, obj, border); + + var left=border.RightEx; + var width=this.ChartBorder.RightExtendWidth; + + var offset=left+width*index/count; + return offset; + } + + this.GetRightExtendYFromData=function(value,isLimit,obj) + { + return this.GetLeftExtendYFromData(value,isLimit,obj) + } + + this.GetRightExtendYData=function(y, isLimit, obj) + { + if (!obj || !obj.Range) return this.GetYData(y,isLimit); + + return this.GetLeftExtendYData(y, isLimit, obj); + } + + this.GetRightExtendMultiDayXData=function(x, obj, border) + { + var dayBorder=border.DayBorder; + for(var i in dayBorder) + { + var client=dayBorder[i]; + if (x>=client.RightEx && x<=client.Right) + { + var count=obj[i].TotalCount-1; + var left=client.RightEx; + var right=client.Right; + + var width=right-left; + var index=(x-left)*(count*1.0/width); + + return { DayIndex:parseInt(i), DataIndex:index }; + } + } + } + + this.GetRightExtendXData=function(x, obj) + { + var border=this.ChartBorder.GetBorder(); + if (border.DayBorder) return this.GetRightExtendMultiDayXData(x, obj, border); + + var count=obj.TotalCount-1; + if (count<0) count=0; + + var left=border.RightEx; + var right=border.Right; + + if (x<=left) return 0; + if (x>=right) return count; + + var width=right-left; + return (x-left)*(count*1.0/width); + } +} + +//走势图 横屏框架 +function MinuteHScreenFrame() +{ + this.newMethod=MinuteFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="MinuteHScreenFrame"; + this.IsHScreen=true; //是否是横屏 + + //画标题背景色 + this.DrawTitleBG=function() + { + if (this.ChartBorder.TitleHeight<=0) return; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=ToFixedPoint(border.RightEx); + var top=ToFixedPoint(border.Top); + var bottom=ToFixedPoint(border.Bottom); + var width=this.ChartBorder.TitleHeight; + var height=bottom-top; + + this.Canvas.fillStyle=this.TitleBGColor; + this.Canvas.fillRect(left,top,width,height); + } + + this.DrawToolbar=function() + { + return; + } + + //画集合竞价背景 + this.DrawBeforeDataBG=function() + { + if (this.ChartBorder.LeftExtendWidth<10 && this.ChartBorder.RightExtendWidth<10) return; + var border=this.ChartBorder.GetHScreenBorder(); + + var left=ToFixedPoint(border.Left); + var right=ToFixedPoint(border.Right); + this.Canvas.fillStyle=this.BeforeBGColor; + + if (this.ChartBorder.LeftExtendWidth>10) + { + var top=ToFixedPoint(border.Top); + var bottom=ToFixedPoint(border.TopEx); + var width=right-left; + var height=bottom-top; + + this.Canvas.fillRect(left,top,width,height); + } + + if (this.ChartBorder.RightExtendWidth>10) + { + var top=border.BottomEx; + var bottom=border.Bottom; + var width=right-left; + var height=bottom-top; + + this.Canvas.fillRect(left,top,width,height); + } + } + + //Y坐标转y轴数值 + this.GetYData=function(x) + { + var border=this.ChartBorder.GetHScreenBorder(); + if (xborder.RightEx) return this.HorizontalMax; + + var width=border.RightEx-border.LeftEx; + return (x-border.LeftEx)/width*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + + //X坐标转x轴数值 + this.GetXData=function(y) + { + var border=this.ChartBorder.GetHScreenBorder(); + var count=this.XPointCount-1; + if (count<0) count=0; + + if (y<=border.TopEx) return 0; + if (y>=border.BottomEx) return count; + + var height=(border.BottomEx-border.TopEx); + return (y-border.TopEx)*(count*1.0/height); + } + + this.GetXFromIndex=function(index) + { + var count=this.XPointCount-1; + var border=this.ChartBorder.GetHScreenBorder(); + if (count==1) + { + if (index==0) return border.TopEx; + else return border.BottomEx; + } + else if (count<=0) + { + return border.TopEx; + } + else if (index>=count) + { + return border.BottomEx; + } + else + { + var height=border.BottomEx-border.TopEx; + var offset=border.TopEx+height*index/count; + return offset; + } + } + + + this.GetYFromData=function(value) + { + var border=this.ChartBorder.GetHScreenBorder(); + if(value<=this.HorizontalMin) return border.LeftEx; + if(value>=this.HorizontalMax) return border.RightEx; + + var width=(border.RightEx-border.LeftEx)*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return border.LeftEx+width; + } + + this.DrawVolTitle=function(symbol) + { + if (!MARKET_SUFFIX_NAME.IsShowMinuteVolTitle(symbol)) return; + + if (this.Identify==1) //显示"成交量" + { + var pixelRatio=GetDevicePixelRatio(); + var left=this.ChartBorder.GetRight()-2*pixelRatio; + var top=this.ChartBorder.GetTopEx()+2*pixelRatio; + + var xText=left,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + var x=this.YInsideOffset,y=0; + this.Canvas.textAlign='left'; + this.Canvas.textBaseline='top'; + if (g_JSChartResource.Minute.VolBarColor || g_JSChartResource.Minute.VolTitleColor) + { + if (g_JSChartResource.Minute.VolBarColor) this.Canvas.fillStyle=g_JSChartResource.Minute.VolBarColor + else this.Canvas.fillStyle=g_JSChartResource.Minute.VolTitleColor; + var languageID=this.YSplitOperator.LanguageID; + var text=g_JSChartLocalization.GetText('MVol-Vol',languageID); + this.Canvas.fillText(text,x,y); + x+=this.Canvas.measureText(text).width; + x+=6*GetDevicePixelRatio(); + } + + if (this.IsShowPositionTitle) + { + text=g_JSChartLocalization.GetText('MVol-Position',languageID); + this.Canvas.fillStyle=g_JSChartResource.Minute.PositionColor; + this.Canvas.fillText(text,x,y); + } + + this.Canvas.restore(); + } + } + + //画Y轴 + this.DrawHorizontal=function() + { + var border=this.ChartBorder.GetHScreenBorder(); + + var top=border.Top; + var bottom=border.Bottom; + var left=border.Left; + var right=border.Right; + var borderTop=this.ChartBorder.Top; + var borderBottom=this.ChartBorder.Bottom; + + var yPrev=null; //上一个坐标y的值 + for(var i=this.HorizontalInfo.length-1; i>=0; --i) //从左往右画分割线 + { + var item=this.HorizontalInfo[i]; + var y=this.GetYFromData(item.Value); + if (y!=null && Math.abs(y-yPrev)= right - 2) + { + this.Canvas.textBaseline = 'top'; + y = right; + } + else if (y <= left + 2) + { + this.Canvas.textBaseline = 'bottom'; + y=left; + if (y != null && Math.abs(y - yPrev) < 2*this.MinYDistance) continue; //两个坐标在近了 就不画了 + } + else + { + this.Canvas.textBaseline = "middle"; + } + + //坐标信息 左边 间距小于10 不画坐标 + if (item.Message[0]!=null && borderTop>10) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="right"; + + var xText=y,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1]!=null && borderBottom>10) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="left"; + + var xText=y,yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], 2, 0); + this.Canvas.restore(); + } + + yPrev=y; + } + } + + this.DrawInsideClientHorizontal=function() + { + var border=this.GetBorder(); + var left=border.Left; + var right=border.Right; + var bottom=border.BottomEx; + var top=border.TopEx; + + var pixelTatio = GetDevicePixelRatio(); + var yPrev = null; //上一个坐标y的值 + var yInsideText=null; + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + if (!item || !item.Message[2] || !item.Message[3]) continue; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + if (y >= right - 2) + { + this.Canvas.textBaseline = 'top'; + y = right; + } + else if (y <= left + 2) + { + this.Canvas.textBaseline = 'bottom'; + y=left; + if (y != null && Math.abs(y - yPrev) < 2*this.MinYDistance) continue; //两个坐标在近了 就不画了 + } + else + { + this.Canvas.textBaseline = "middle"; + } + + if (item.Message[2]) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + + var xText=y,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[2], 2, 0); + this.Canvas.restore(); + } + + if (item.Message[3]) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + + var xText=y,yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[3], -2, 0); + this.Canvas.restore(); + + } + yPrev = y; + } + } + + //画X轴 + this.DrawVertical=function() + { + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.Left; + var right=border.Right; + var bottom=border.Bottom; + + var xPrev=null; //上一个坐标x的值 + for(var i in this.VerticalInfo) + { + var x=this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x>bottom) break; + if (xPrev!=null && Math.abs(x-xPrev)= this.ChartBorder.GetChartHeight()) + { + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "top"; + } + else + { + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="top"; + } + + var xText=left,yText=x; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.VerticalInfo[i].Message[0], 0, 0); + this.Canvas.restore(); + } + + xPrev=x; + } + } + + //Y刻度画在左边内部 + this.DrawInsideHorizontal = function () + { + if (this.IsMinSize) return; + if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return; + + this.DrawInsideClientHorizontal(); + + var border=this.ChartBorder.GetHScreenBorder(); + var left = border.Left; + var right = border.RightEx; + var top=border.Top; + var bottom=border.Bottom; + var borderTop=this.ChartBorder.Top; + var borderBottom=this.ChartBorder.Bottom; + var titleHeight = this.ChartBorder.TitleHeight; + var pixelTatio = GetDevicePixelRatio(); + + if ( (borderTop<10*pixelTatio && this.IsShowYText[0]===true) || (borderBottom<10*pixelTatio && this.IsShowYText[1]===true) ) + { + var pixelTatio = GetDevicePixelRatio(); + var yPrev = null; //上一个坐标y的值 + var yInsideText=null; + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + //坐标信息 左边 间距小于10 画在内部 + if (item.Message[0] != null && borderTop < 10*pixelTatio && this.IsShowYText[0]===true) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + if (y >= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + + var textObj={ X:left, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ; + var xText=y,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], 2*pixelTatio, 0); + this.Canvas.restore(); + + if (yInsideText==null || yInsideText= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[1]).width; + var textObj={ X:right-textWidth, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ; + + var xText=y,yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], -2*pixelTatio, 0); + this.Canvas.restore(); + } + yPrev = y; + } + } + } + + this.GetLeftExtendXFromIndex=function(index, obj) + { + var count=obj.TotalCount-1; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.Top; + var width=this.ChartBorder.LeftExtendWidth; + + var offset=left+width*index/count; + return offset; + } + + this.GetLeftExtendXData=function(y, obj) + { + var count=obj.TotalCount-1; + if (count<0) count=0; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.Top; + var right=border.TopEx; + + if (y<=left) return 0; + if (y>=right) return count; + + var width=right-left; + return (y-left)*(count*1.0/width); + } + + this.GetLeftExtendYFromData=function(value,isLimit,obj) + { + if (!obj || !obj.Range) return this.GetYFromData(value,isLimit); + + var range=obj.Range; + var border=this.ChartBorder.GetHScreenBorder(); + var width=border.RightEx-border.LeftEx; + var offset=width*(value-range.Min)/(range.Max-range.Min); + return border.LeftEx+offset; + } + + this.GetLeftExtendYData=function(x, isLimit, obj) + { + if (!obj || !obj.Range) return this.GetYData(y,isLimit); + + var range=obj.Range; + var border=this.ChartBorder.GetHScreenBorder(); + var width=border.RightEx-border.LeftEx; + return (x-border.LeftEx)/width*(range.Max-range.Min)+range.Min; + } + + this.GetRightExtendXFromIndex=function(index, obj) + { + var count=obj.TotalCount-1; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.BottomEx; + var width=this.ChartBorder.RightExtendWidth; + + var offset=left+width*index/count; + return offset; + } + + this.GetRightExtendXData=function(y, obj) + { + var count=obj.TotalCount-1; + if (count<0) count=0; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.BottomEx; + var right=border.Bottom; + + if (y<=left) return 0; + if (y>=right) return count; + + var width=right-left; + return (y-left)*(count*1.0/width); + } + + this.GetRightExtendYFromData=function(value,isLimit,obj) + { + if (!obj || !obj.Range) return this.GetYFromData(value,isLimit); + + return this.GetLeftExtendYFromData(value,isLimit,obj); + } + + +} + +function OverlayMinuteFrame() +{ + this.newMethod=MinuteFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="OverlayMinuteFrame"; + this.IsShow=true; //坐标是否显示 + this.IsShareY=false; //使用和主框架公用Y轴 + + this.Draw=function() + { + this.SplitXYCoordinate(); + if (this.IsShow) + { + + } + + this.SizeChange=false; + this.XYSplit=false; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + + if (this.IsShareY) //和主图指标共享Y轴坐标 + { + this.HorizontalMax=this.MainFrame.HorizontalMax; + this.HorizontalMin=this.MainFrame.HorizontalMin; + this.HorizontalInfo=[]; + for(var i in this.MainFrame.HorizontalInfo) + { + var item=this.MainFrame.HorizontalInfo[i]; + this.HorizontalInfo.push(item); + } + } + else //独立Y轴坐标 + { + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + } + + // if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); 子坐标和主坐标X轴一致 所以不用计算 + } +} + +function OverlayMinuteHScreenFrame() +{ + this.newMethod=MinuteHScreenFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="OverlayMinuteHScreenFrame"; + this.IsShow=true; //坐标是否显示 + + this.Draw=function() + { + this.SplitXYCoordinate(); + if (this.IsShow) + { + + } + + this.SizeChange=false; + this.XYSplit=false; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + + if (this.IsShareY) //和主图指标共享Y轴坐标 + { + this.HorizontalMax=this.MainFrame.HorizontalMax; + this.HorizontalMin=this.MainFrame.HorizontalMin; + this.HorizontalInfo=[]; + for(var i in this.MainFrame.HorizontalInfo) + { + var item=this.MainFrame.HorizontalInfo[i]; + this.HorizontalInfo.push(item); + } + } + else //独立Y轴坐标 + { + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + } + + // if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); 子坐标和主坐标X轴一致 所以不用计算 + } +} + +//K线框架 +function KLineFrame() +{ + this.newMethod=AverageWidthFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='KLineFrame'; + this.ToolbarID=Guid(); //工具条Div id + + this.ModifyIndex=true; //是否显示'改参数'菜单 + this.ChangeIndex=true; //是否显示'换指标'菜单 + this.CloseIndex=true; //是否显示'关闭指标窗口'菜单 + this.OverlayIndex=false; //是否显示叠加指标 + + this.ModifyIndexEvent; //改参数 点击事件 + this.ChangeIndexEvent; //换指标 点击事件 + this.ToolbarRect=null; //保存工具条的位置 + this.ReDrawToolbar=false; + + this.LastCalculateStatus={ Width:0, XPointCount:0 }; //最后一次计算宽度的状态 + + this.CustomHorizontalInfo=[]; //定制Y轴刻度 + this.IsDrawTitleBG=false; + + this.CustomVerticalInfo=[]; //定制X轴刻度 Type:0, Date:, Time: , Line:{ Color:线段颜色, Type:线段类型 0 直线 1 虚线 } + // Type:1, Space: 第几个空白间距, Line:{ Color:线段颜色, Type:线段类型 0 直线 1 虚线 } + this.DrawCustomVerticalEvent; + this.RightSpaceCount=0; + + this.Logarithmic=null; //{Up:上部 , Donw:下部 , OpenPrice:第一个开盘价} + + this.DrawToolbar=function() + { + if (typeof($)=="undefined") return; + + if (!this.ChartBorder.UIElement || !this.ChartBorder.UIElement.parentNode) return; + + var divToolbar=document.getElementById(this.ToolbarID); + if (divToolbar && this.SizeChange==false && this.ReDrawToolbar==false) return; + + if (!divToolbar) + { + + divToolbar=document.createElement("div"); + divToolbar.className='klineframe-toolbar'; + divToolbar.id=this.ToolbarID; + divToolbar.oncontextmenu = function() { return false;}; //屏蔽右键系统菜单 + //为divToolbar添加属性identify + divToolbar.setAttribute("identify",this.Identify.toString()); + this.ChartBorder.UIElement.parentNode.appendChild(divToolbar); + } + + if ((!this.ModifyIndex && !this.ChangeIndex && !this.OverlayIndex && !this.CloseIndex)) + { + if (divToolbar.style.display!='none') + divToolbar.style.display='none'; + return; + } + + //使用外城div尺寸 画图尺寸是被放大的 + var pixelTatio = GetDevicePixelRatio(); + var chartWidth=parseInt(this.ChartBorder.UIElement.parentElement.style.width.replace("px","")); + var chartHeight=parseInt(this.ChartBorder.UIElement.parentElement.style.height.replace("px","")); + //JSConsole.Chart.Log('[KLineFrame::DrawToolbar] ',chartWidth,chartHeight,pixelTatio); + + var toolbarWidth=100; + var toolbarHeight=this.ChartBorder.GetTitleHeight(); + var left=chartWidth-(this.ChartBorder.Right/pixelTatio)-toolbarWidth; + var top=this.ChartBorder.GetTop()/pixelTatio; + + if (this.ToolbarRect) + { + //尺寸变动移动才重新设置DOM + if (this.ToolbarRect.Left==left && this.ToolbarRect.Top==top && + this.ToolbarRect.Width==toolbarWidth && this.ToolbarRect.Height==toolbarHeight/pixelTatio) + { + return; + } + } + + this.ToolbarRect={ Left:left, Top:top, Width:toolbarWidth, Height:toolbarHeight/pixelTatio }; + + const modifyButton=``; + const changeButton=``; + const overlayButton=``; + const closeButton=``; + + var spanIcon=modifyButton+changeButton+overlayButton; + + if (this.Identify!==0 && this.CloseIndex) //第1个窗口不能关闭 + { + spanIcon+=closeButton; + } + + //var scrollPos=GetScrollPosition(); + //left = left+scrollPos.Left; + //top = top+scrollPos.Top; + divToolbar.style.left = left + "px"; + divToolbar.style.top = top + "px"; + divToolbar.style.width=toolbarWidth+"px"; //宽度先不调整吧 + divToolbar.style.height=(toolbarHeight/pixelTatio)+'px'; //只调整高度 + divToolbar.innerHTML=spanIcon; + + var chart=this.ChartBorder.UIElement.JSChartContainer; + var identify=this.Identify; + if (!this.ModifyIndex) //隐藏'改参数' + $("#"+divToolbar.id+" .index_param").hide(); + else if (typeof(this.ModifyIndexEvent)=='function') //绑定点击事件 + $("#"+divToolbar.id+" .index_param").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify + },this.ModifyIndexEvent); + + if (!this.ChangeIndex) //隐藏'换指标' + { + $("#"+divToolbar.id+" .index_change").hide(); + } + else if (typeof(this.ChangeIndexEvent)=='function') + { + $("#"+divToolbar.id+" .index_change").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify, + IsOverlay:false + },this.ChangeIndexEvent); + } + + if (!this.OverlayIndex) + { + $("#"+divToolbar.id+" .index_overlay").hide(); + } + else + { + $("#"+divToolbar.id+" .index_overlay").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify, + IsOverlay:true + },this.ChangeIndexEvent); + } + + $("#"+divToolbar.id+" .index_close").click( + { + Chart:this.ChartBorder.UIElement.JSChartContainer, + Identify:this.Identify + }, + function(event) + { + var hqChart=event.data.Chart; + var id=event.data.Identify; + hqChart.RemoveIndexWindow(id); + }); + + divToolbar.style.display = "block"; + } + + this.DrawFrame=function() + { + if (!this.IsMinSize) + { + this.SplitXYCoordinate(); + + if (this.SizeChange==true) + { + this.CalculateDataWidth(); + if (this.Logarithmic) this.SplitLogarithmicXYCoordinate(); + } + + if (this.DrawDepthMapCallback) this.DrawDepthMapCallback(); + + this.DrawTitleBG(); + this.DrawHorizontal(); + this.DrawVertical(); + } + + if (this.SizeChange==true || this.ReDrawToolbar==true) + { + this.DrawToolbar(); //大小变动才画工具条 + this.ReDrawToolbar=false; + } + } + + //isLimit 是否限制在当前屏坐标下 + this.GetXFromIndex=function(index,isLimit) + { + if (isLimit===false) + { + if (index>=0) + { + var offset=this.ChartBorder.GetLeft()+g_JSChartResource.FrameLeftMargin+this.DistanceWidth/2+this.DataWidth/2; + for(var i=1;i<=index;++i) + { + offset+=this.DistanceWidth+this.DataWidth; + } + } + else + { + var offset=this.ChartBorder.GetLeft()-(this.DistanceWidth/2+this.DataWidth+this.DistanceWidth); + var absIndex=Math.abs(index); + for(var i=1;i this.xPointCount - 1) index = this.xPointCount - 1; + + var offset=this.ChartBorder.GetLeft()+g_JSChartResource.FrameLeftMargin+this.DistanceWidth/2+this.DataWidth/2; + for(var i=1;i<=index;++i) + { + offset+=this.DistanceWidth+this.DataWidth; + } + } + + return offset; + } + + //X坐标转x轴数值 isLimit=是否限制在当前屏坐标下 + this.GetXData=function(x,isLimit) + { + var distanceWidth=this.DistanceWidth; + var dataWidth=this.DataWidth; + var left=this.ChartBorder.GetLeft()+g_JSChartResource.FrameLeftMargin; + + if (isLimit==false) + { + if (x-10000) + { + if (xPoint<=x) + break; + xPoint-=(dataWidth+distanceWidth); + --index; + } + + return index; + } + else + { + var index=0; + var xPoint=left+distanceWidth/2+dataWidth+distanceWidth; + while(index<10000) //自己算x的数值 + { + if (xPoint>=x) break; + xPoint+=(dataWidth+distanceWidth); + ++index; + } + + return index; + } + } + else + { + if (x<=this.ChartBorder.GetLeft()) return 0; + if (x>=this.ChartBorder.GetRight()) return this.XPointCount-1; + + var right=this.ChartBorder.GetRight()-g_JSChartResource.FrameRightMargin; + var index=0; + var xPoint=left+distanceWidth/2+dataWidth+distanceWidth; + while(xPoint=x) break; + xPoint+=(dataWidth+distanceWidth); + ++index; + } + + //var test=(x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth()); + return index; + } + } + + //计算数据宽度 + this.CalculateDataWidth=function() + { + if (this.XPointCount<2) return; + + //JSConsole.Chart.Log(`[KLineFrame::CalculateDataWidth] ZoomIndex=${this.ZoomIndex}, XPointCount=${this.XPointCount}, DataWidth=${this.DataWidth}, DistanceWidth=${this.DistanceWidth}`); + var width=this.GetFrameWidth()-g_JSChartResource.FrameMargin; + + if (this.ZoomIndex>=0 && this.LastCalculateStatus.Width==width && this.LastCalculateStatus.XPointCount==this.XPointCount) //宽度没变 尝试使用原来的柱子宽度 + { + var caclWidth=(this.DistanceWidth/2+g_JSChartResource.FrameLeftMargin)+(this.DataWidth + this.DistanceWidth)*(this.XPointCount-1); + var caclWidth2=(this.DataWidth + this.DistanceWidth) * this.XPointCount; + if (caclWidth<= width) //当前的柱子宽度够用 就不调整了 + return; + } + + this.LastCalculateStatus.Width=width; + this.LastCalculateStatus.XPointCount=this.XPointCount; + for(var i=0;i width) + { + this.DistanceWidth -= 0.01; + break; + } + this.DistanceWidth += 0.01; + } + } + + //当前坐标信息 是否覆盖最大 最小值输出 + this.IsOverlayMaxMin=function(obj) + { + if (!this.ChartKLine) return false; + if (!this.ChartKLine.Max || !this.ChartKLine.Min) return false; + + var textWidth=this.Canvas.measureText(obj.Text.Value).width+4; //刻度文字宽度 + if (obj.Text.TextAlign==='right') obj.X-=textWidth; + var max=this.ChartKLine.Max, min=this.ChartKLine.Min; + var isOverlayMax=false, isOverlayMin=false; + const textHeight=20; //字体高度 + if (max.X>=obj.X && max.X<=obj.X+textWidth) //最大值X 坐标不在 刻度文字范围内 + { + var y1=max.Y+textHeight, y2=max.Y-textHeight; + if ( (y1>=obj.Y-textHeight && y1<=obj.Y+textHeight) || (y2>=obj.Y-textHeight && y2<=obj.Y+textHeight)) + isOverlayMax=true; + } + + if (isOverlayMax==true) return true; + + if (min.X>=obj.X && min.X<=obj.X+textWidth) + { + var y1=min.Y+textHeight, y2=min.Y-textHeight; + if ( (y1>=obj.Y-textHeight && y1<=obj.Y+textHeight) || (y2>=obj.Y-textHeight && y2<=obj.Y+textHeight)) + isOverlayMin=true; + } + + return isOverlayMax || isOverlayMin; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); + if (this.Logarithmic) this.SplitLogarithmicXYCoordinate(); + } + + this.SplitLogarithmicXYCoordinate=function() + { + var up=this.Logarithmic.Up; + var down=this.Logarithmic.Down; + var logHeight=0; //对数额外高度 + var count=0; + var maxCount=Math.max(up.length, down.length); + + var aryLogHeight=[]; + for(var i=0;i=0;--i) //上部 + { + var item=down[i]; + item.Height=perHeight+(heightRate*item.LogHeight); + item.Top=itemTop; + item.Bottom=item.Top+item.Height; + itemTop=item.Bottom; + } + + var itemBottom=bottom; + for(var i=up.length-1;i>=0;--i) //下部 + { + var item=up[i]; + item.Height=perHeight+(heightRate*item.LogHeight) + item.Bottom=itemBottom; + item.Top=itemBottom-item.Height; + itemBottom=item.Top; + } + } + else + { + var itemTop=top; + for(var i=up.length-1;i>=0;--i) + { + var item=up[i]; + item.Height=perHeight+(heightRate*item.LogHeight); + item.Top=itemTop; + item.Bottom=item.Top+item.Height; + itemTop=item.Bottom; + } + + var itemBottom=bottom; + for(var i=down.length-1;i>=0;--i) + { + var item=down[i]; + item.Height=perHeight+(heightRate*item.LogHeight) + item.Bottom=itemBottom; + item.Top=itemBottom-item.Height; + itemBottom=item.Top; + } + } + JSConsole.Chart.Log("[KLineFrame::SplitLogarithmicXYCoordinate]", this.Logarithmic); + } + + this.GetYLogarithmicFromData=function(value, isLimit) + { + if (this.CoordinateType==1) + { + if(value<=this.HorizontalMin) return this.ChartBorder.GetTopEx(); + if(value>=this.HorizontalMax) return this.ChartBorder.GetBottomEx(); + + if (value>this.Logarithmic.OpenPrice) + { + var up=this.Logarithmic.Up; + for(var i in up) + { + var item=up[i]; + if (value>=item.Start && value<=item.End) + { + var itemHeight=item.Bottom-item.Top; + var height=itemHeight*(value-item.Start)/(item.End-item.Start); + return item.Top+height; + } + } + } + else + { + var down=this.Logarithmic.Down; + for(var i in down) + { + var item=down[i]; + if (value>=item.Start && value<=item.End) + { + var itemHeight=item.Bottom-item.Top; + var height=itemHeight*(value-item.Start)/(item.End-item.Start); + return item.Top+height; + } + } + } + } + else + { + if(value<=this.HorizontalMin) return this.ChartBorder.GetBottomEx(); + if(value>=this.HorizontalMax) return this.ChartBorder.GetTopEx(); + + if (value>this.Logarithmic.OpenPrice) + { + var up=this.Logarithmic.Up; + for(var i in up) + { + var item=up[i]; + if (value>=item.Start && value<=item.End) + { + var itemHeight=item.Bottom-item.Top; + var height=itemHeight*(value-item.Start)/(item.End-item.Start); + return item.Bottom-height; + } + } + } + else + { + var down=this.Logarithmic.Down; + for(var i in down) + { + var item=down[i]; + if (value>=item.Start && value<=item.End) + { + var itemHeight=item.Bottom-item.Top; + var height=itemHeight*(value-item.Start)/(item.End-item.Start); + return item.Bottom-height; + } + } + } + } + } + + this.GetYLogarithmicData=function(y) + { + if (this.CoordinateType==1) //反转坐标 + { + if (ythis.ChartBorder.GetBottomEx()) return this.HorizontalMax; + + var up=this.Logarithmic.Up; + for(var i in up) + { + var item=up[i]; + if (y>=item.Top && y<=item.Bottom) + { + return (y-item.Top)/item.Height*(item.End-item.Start)+item.Start; + } + } + + var down=this.Logarithmic.Down; + for(var i in down) + { + var item=down[i]; + if (y>=item.Top && y<=item.Bottom) + { + return (y-item.Top)/item.Height*(item.End-item.Start)+item.Start; + } + } + } + else + { + if (ythis.ChartBorder.GetBottomEx()) return this.HorizontalMin; + + var up=this.Logarithmic.Up; + for(var i in up) + { + var item=up[i]; + if (y>=item.Top && y<=item.Bottom) + { + return (item.Bottom-y)/item.Height*(item.End-item.Start)+item.Start; + } + } + + var down=this.Logarithmic.Down; + for(var i in down) + { + var item=down[i]; + if (y>=item.Top && y<=item.Bottom) + { + return (item.Bottom-y)/item.Height*(item.End-item.Start)+item.Start; + } + } + } + } + + this.CalculateCount=function(zoomIndex) + { + var width=this.GetFrameWidth()-g_JSChartResource.FrameMargin; + return parseInt(width/(ZOOM_SEED[zoomIndex][0] + ZOOM_SEED[zoomIndex][1])); + } + + this.ZoomUp=function(cursorIndex) + { + if (this.ZoomIndex<=0) return false; + if (this.Data.DataOffset<0) return false; + var dataCount=this.Data.Data.length; + var maxDataCount=dataCount+this.RightSpaceCount; + + var rightSpaceCount=0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index; + if (lastDataIndex>=dataCount) + { + rightSpaceCount=lastDataIndex-(this.Data.Data.length-1); //计算右边预留空间 + lastDataIndex=this.Data.Data.length-1; + if (rightSpaceCount>this.RightSpaceCount) rightSpaceCount=this.RightSpaceCount; + } + + var xPointCount=this.CalculateCount(this.ZoomIndex-1); + JSConsole.Chart.Log(`[KLineFrame::ZoomUp] old status. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${this.Data.Data.length} rightSpaceCount=${rightSpaceCount}`); + + var isShowAll=false; + --this.ZoomIndex; + if (cursorIndex.IsLockRight==true) //固定右边 + { + var rightDataIndex=this.Data.DataOffset + this.XPointCount; //最右边的数据索引 + if (xPointCount>rightDataIndex) + { + xPointCount=rightDataIndex; + this.XPointCount=xPointCount; + this.Data.DataOffset=0; + } + else + { + var dataOffset=lastDataIndex - (xPointCount-rightSpaceCount)+1; + this.XPointCount=xPointCount; + this.Data.DataOffset=dataOffset; + if (this.Data.DataOffset<0) this.Data.DataOffset=0; + } + } + else if (xPointCount>=maxDataCount) + { + //xPointCount=maxDataCount; + //this.XPointCount=xPointCount; + this.Data.DataOffset=0; + this.XPointCount=xPointCount; + isShowAll=true; + JSConsole.Chart.Log(`[KLineFrame::ZoomUp] Show all data. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount}`); + } + else + { + var dataOffset=lastDataIndex - (xPointCount-rightSpaceCount)+1; + if (cursorIndex.ZoomType==1) //以十字光标为中心左右放大 + { + var moveOffset=(this.XPointCount-xPointCount)+1; + var leftOffset=parseInt(cursorIndex.Index/this.XPointCount*moveOffset); + var rightOffset=moveOffset-leftOffset; + var offset=this.Data.DataOffset+leftOffset; + if (offset=ZOOM_SEED.length) return false; + if (this.Data.DataOffset<0) return false; + var dataCount=this.Data.Data.length; + var maxDataCount=dataCount+this.RightSpaceCount; + //if (this.XPointCount>=maxDataCount) return false; + + var rightSpaceCount=0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + if (lastDataIndex>=this.Data.Data.length) + { + rightSpaceCount=lastDataIndex-(this.Data.Data.length-1); //计算右边预留空间 + lastDataIndex=this.Data.Data.length-1; + if (rightSpaceCount>this.RightSpaceCount) rightSpaceCount=this.RightSpaceCount; + } + + var xPointCount=this.CalculateCount(this.ZoomIndex+1); + var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index; + + JSConsole.Chart.Log(`[KLineFrame::ZoomDown] old status. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${this.Data.Data.length} lastCursorIndex=${lastCursorIndex} rightSpaceCount=${rightSpaceCount}`); + + var isShowAll=false; + ++this.ZoomIndex; + if (cursorIndex.IsLockRight==true) //固定右边 + { + var rightDataIndex=this.Data.DataOffset + this.XPointCount; //最右边的数据索引 + if (xPointCount>rightDataIndex) + { + xPointCount=rightDataIndex; + this.XPointCount=xPointCount; + this.Data.DataOffset=0; + } + else + { + var dataOffset=lastDataIndex - (xPointCount-rightSpaceCount)+1; + this.XPointCount=xPointCount; + this.Data.DataOffset=dataOffset; + if (this.Data.DataOffset<0) this.Data.DataOffset=0; + } + } + else if (xPointCount>=maxDataCount) //所有数据无法显示完一屏 + { + //xPointCount=maxDataCount; + this.XPointCount=xPointCount; + this.Data.DataOffset=0; + isShowAll=true; //数据铺满全屏, 不需要调整宽度 + JSConsole.Chart.Log(`[KLineFrame::ZoomDown] Show all data. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount}`); + } + else + { + var dataOffset=lastDataIndex - (xPointCount-rightSpaceCount)+1; + if (cursorIndex.ZoomType==1) //当前十字光标位置左右同时缩小 + { + var moveOffset=(xPointCount-this.XPointCount)+1; + var leftOffset=parseInt(cursorIndex.Index/this.XPointCount*moveOffset); + var rightOffset=moveOffset-leftOffset; + var dataOffset=this.Data.DataOffset-leftOffset; + if (dataOffset+(xPointCount-this.RightSpaceCount)>=dataCount) dataOffset=this.Data.DataOffset-moveOffset; + } + + this.XPointCount=xPointCount; + this.Data.DataOffset=dataOffset; + if (this.Data.DataOffset<0) this.Data.DataOffset=0; + + JSConsole.Chart.Log(`[KLineFrame::ZoomDown] calculate. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount} DataOffset=${this.Data.DataOffset}`); + } + + this.DataWidth = ZOOM_SEED[this.ZoomIndex][0]; + this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1]; + if (!isShowAll) + { + var width=this.GetFrameWidth()-g_JSChartResource.FrameMargin; + this.TrimKLineDataWidth(width); + } + this.LastCalculateStatus.XPointCount=this.XPointCount; + cursorIndex.Index=lastCursorIndex-this.Data.DataOffset; + + return true; + } + + this.GetFrameWidth=function() + { + if (this.IsHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + return border.BottomEx-border.TopEx; + //return this.ChartBorder.GetHeight(); + } + else + { + var border=this.ChartBorder.GetBorder(); + return border.RightEx-border.LeftEx; + //return this.ChartBorder.GetWidth(); + } + } + + this.DrawCustomHorizontal=function() //Y轴刻度定制显示 + { + if (this.IsMinSize) return; + for(var i in this.CustomHorizontalInfo) + { + var item=this.CustomHorizontalInfo[i]; + switch(item.Type) + { + case 0: //最新价格刻度 + case 1: //固定价格刻度 + this.DrawCustomItem(item); + break; + } + } + } + + this.DrawCustomVerticalItem=function(item) + { + this.Canvas.save(); + if (item.Data.Line.Type==1) this.Canvas.setLineDash([5,5]); //虚线 + this.Canvas.strokeStyle=item.Data.Line.Color; + this.Canvas.beginPath(); + if (item.IsHScreen) + { + this.Canvas.moveTo(item.Top,ToFixedPoint(item.X)); + this.Canvas.lineTo(item.Bottom,ToFixedPoint(item.X)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(item.X),item.Top); + this.Canvas.lineTo(ToFixedPoint(item.X),item.Bottom); + } + this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.DrawCustomVertical=function() //X轴定制刻度显示 + { + if (!this.CustomVerticalInfo) return; + if (this.CustomVerticalInfo.length<=0) return; + if (!this.Data) return; + + var isHScreen=this.IsHScreen; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + + var dataWidth=this.DataWidth; + var distanceWidth=this.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + + if (isHScreen) + { + xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + top=this.ChartBorder.GetLeftEx(); + bottom=this.ChartBorder.GetRightEx(); + } + + var j=0; + for(var i=this.Data.DataOffset;i10*pixelTatio && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + } + else + { + var borderRight=this.ChartBorder.Right; + var isDrawRight=borderRight>10 && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + } + + if (!isDrawRight) return null; + + var width={ Left:null, Right:0 }; + for(var i=0;i=0; --i) //从上往下画分割线 + { + var item=this.HorizontalInfo[i]; + var y=this.GetYFromData(item.Value); + if (y!=null && Math.abs(y-yPrev)= bottom - 2) this.Canvas.textBaseline = 'bottom'; + else if (y <= top + 2) this.Canvas.textBaseline = 'top'; + else this.Canvas.textBaseline = "middle"; + + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + this.Canvas.moveTo(right-2,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + this.Canvas.stroke(); + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1]!=null && borderRight>10) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + var text=item.Message[1]; + if (Array.isArray(item.Message[1])) text=item.Message[1][0]; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="left"; + this.Canvas.fillText(text,right+2,y); + } + + yPrev=y; + } + } + + //画X轴 + this.DrawVertical=function() + { + var border=this.ChartBorder.GetBorder(); + var top=border.TopEx; + //var left=this.ChartBorder.GetLeft(); + var right=border.Right; + var bottom=border.BottomEx; + right+=this.RightOffset; + + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(right),ToFixedPoint(top)); + this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(bottom)); + this.Canvas.stroke(); + } +} + +//K线横屏框架 +function KLineHScreenFrame() +{ + this.newMethod=KLineFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='KLineHScreenFrame'; + this.IsHScreen=true; //是否是横屏 + + //画标题背景色 + this.DrawTitleBG=function() + { + if (this.ChartBorder.TitleHeight<=0) return; + + var border=this.ChartBorder.GetHScreenBorder(); + var left=ToFixedPoint(border.RightTitle); + var top=ToFixedPoint(border.Top); + var bottom=ToFixedPoint(border.Bottom); + var width=this.ChartBorder.TitleHeight; + var height=bottom-top; + + this.Canvas.fillStyle=this.TitleBGColor; + this.Canvas.fillRect(left,top,width,height); + } + + this.DrawToolbar=function() + { + return; + } + + this.GetYFromData=function(value,isLimit) + { + var border=this.ChartBorder.GetHScreenBorder(); + if (isLimit===false) + { + var width=(border.RightEx-border.LeftEx)*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return border.LeftEx+width; + } + else + { + if(value<=this.HorizontalMin) return border.LeftEx; + if(value>=this.HorizontalMax) return border.RightEx; + + var width=(border.RightEx-border.LeftEx)*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); + return border.LeftEx+width; + } + } + + //画Y轴 + this.DrawHorizontal=function() + { + var border=this.ChartBorder.GetHScreenBorder(); + var top=border.Top; + var bottom=border.Bottom; + var borderTop=this.ChartBorder.Top; + var borderBottom=this.ChartBorder.Bottom; + var left=border.Left; + var right=border.Right; + + var yPrev=null; //上一个坐标y的值 + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + + var isDrawLeft=borderTop>10*pixelTatio && this.IsShowYText[0]===true && this.YTextPosition[0]!=2; + var isDrawRight=borderBottom>10*pixelTatio && this.IsShowYText[1]===true && this.YTextPosition[1]!=2; + + for(var i=this.HorizontalInfo.length-1; i>=0; --i) //从左往右画分割线 + { + var item=this.HorizontalInfo[i]; + var y=this.GetYFromData(item.Value); + if (y!=null && Math.abs(y-yPrev)0) + { + this.Canvas.strokeStyle=item.LineColor; + if (g_JSChartResource.FrameYLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameYLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y),top); + this.Canvas.lineTo(ToFixedPoint(y),bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y),top); + this.Canvas.lineTo(ToFixedPoint(y),bottom); + this.Canvas.stroke(); + } + } + } + + //坐标信息 左边 间距小于10 不画坐标 + if (item.Message[0]!=null && isDrawLeft) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + + var xText=y,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1]!=null && isDrawRight) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + var text; + if (Array.isArray(item.Message[1])) //横屏只支持单行 + { + text=item.Message[1][0]; + } + else + { + text=item.Message[1]; + } + + var xText=y,yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(text, 2, 0); + this.Canvas.restore(); + } + + yPrev=y; + } + } + + //Y刻度画在左边内部 + this.DrawInsideHorizontal = function () + { + if (this.IsMinSize) return; + if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return; + + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRightEx(); + var top=this.ChartBorder.GetTop(); + var bottom=this.ChartBorder.GetBottom(); + var borderTop=this.ChartBorder.Top; + var borderBottom=this.ChartBorder.Bottom; + var titleHeight = this.ChartBorder.TitleHeight; + var pixelTatio = GetDevicePixelRatio(); + + var isDrawLeft= (borderTop<10*pixelTatio || this.YTextPosition[0]==2) && this.IsShowYText[0]===true; + var isDrawRight= (borderBottom<10*pixelTatio || this.YTextPosition[1]==2) && this.IsShowYText[1]===true; + + if ( isDrawLeft || isDrawRight ) + { + var pixelTatio = GetDevicePixelRatio(); + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + //坐标信息 左边 间距小于10 画在内部 + if (item.Message[0] != null && isDrawLeft) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + if (y >= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + + var textObj={ X:left, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ; + var xText=y,yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + if (item.Message[1] != null && isDrawRight) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + if (y >= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[1]).width; + var textObj={ X:right-textWidth, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ; + + var xText=y,yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], 2, 0); + this.Canvas.restore(); + } + yPrev = y; + } + } + } + + this.GetXFromIndex=function(index) + { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var border=this.ChartBorder.GetHScreenBorder(); + var offset=border.TopEx+ g_JSChartResource.FrameLeftMargin + this.DistanceWidth/2+this.DataWidth/2; + for(var i=1;i<=index;++i) + { + offset+=this.DistanceWidth+this.DataWidth; + } + + return offset; + } + + //画X轴 + this.DrawVertical=function() + { + var border=this.ChartBorder.GetHScreenBorder(); + var left=border.Left; + var right=border.RightTitle; + var bottom=border.Bottom; + var pixelRatio = GetDevicePixelRatio(); //获取设备的分辨率 + var xPrev=null; //上一个坐标x的值 + for(var i in this.VerticalInfo) + { + var x=this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x>=bottom) break; + if (xPrev!=null && Math.abs(x-xPrev)<80) continue; + + var item=this.VerticalInfo[i]; + if (item.LineType==2) + { + this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor; + this.Canvas.setLineDash([5*pixelRatio,5*pixelRatio]); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(left,ToFixedPoint(x)); + this.Canvas.lineTo(right,ToFixedPoint(x)); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else if (item.LineType>0) + { + this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor; + if (g_JSChartResource.FrameXLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameXLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(left,ToFixedPoint(x)); + this.Canvas.lineTo(right,ToFixedPoint(x)); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else //实线 + { + this.Canvas.beginPath(); + this.Canvas.moveTo(left,ToFixedPoint(x)); + this.Canvas.lineTo(right,ToFixedPoint(x)); + this.Canvas.stroke(); + } + } + + + if (this.VerticalInfo[i].Message[0]!=null) + { + if (this.VerticalInfo[i].Font!=null) + this.Canvas.font=this.VerticalInfo[i].Font; + + this.Canvas.fillStyle=this.VerticalInfo[i].TextColor; + var testWidth=this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (xborder.RightEx) return this.HorizontalMax; + + var width=border.RightEx-border.LeftEx; + return (x-border.LeftEx)/width*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin; + } + + //X坐标转x轴数值 + this.GetXData=function(y) + { + var border=this.ChartBorder.GetHScreenBorder(); + if (y<=border.TopEx) return 0; + if (y>=border.BottomEx) return this.XPointCount-1; + + var left=border.TopEx; + var right=border.BottomEx; + var distanceWidth=this.DistanceWidth; + var dataWidth=this.DataWidth; + + var index=0; + var xPoint=left+distanceWidth/2+ dataWidth+distanceWidth; + while(xPointy) break; + xPoint+=(dataWidth+distanceWidth); + ++index; + } + return index; + + //return (y-this.ChartBorder.GetTop())*(this.XPointCount*1.0/this.ChartBorder.GetHeight()); + } + + //计算数据宽度 + /* + this.CalculateDataWidth=function() + { + if (this.XPointCount<2) return; + + var width=this.ChartBorder.GetHeight()-g_JSChartResource.FrameMargin; + + for(var i=0;i=dataCount) return false; + + var rightSpaceCount=0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index; + if (lastDataIndex>=dataCount) + { + rightSpaceCount=lastDataIndex-(dataCount-1); //计算右边预留空间 + lastDataIndex=dataCount-1; + if (rightSpaceCount>this.RightSpaceCount) rightSpaceCount=this.RightSpaceCount; + } + + var xPointCount=this.CalculateCount(this.ZoomIndex-1); + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomUp] old status. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${this.Data.Data.length} rightSpaceCount=${rightSpaceCount}`); + + --this.ZoomIndex; + if (xPointCount>=maxDataCount) + { + xPointCount=maxDataCount; + this.XPointCount=xPointCount; + this.Data.DataOffset=0; + + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomUp] Show all data. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount}`); + } + else + { + this.XPointCount=xPointCount; + this.Data.DataOffset = lastDataIndex - (this.XPointCount-rightSpaceCount)+1; + + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomUp] calculate. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount} DataOffset=${this.Data.DataOffset}`); + } + + this.DataWidth = ZOOM_SEED[this.ZoomIndex][0]; + this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1]; + var width=this.ChartBorder.GetHeight()-g_JSChartResource.FrameMargin; + this.TrimKLineDataWidth(width); + this.LastCalculateStatus.XPointCount=this.XPointCount; + cursorIndex.Index=lastCursorIndex-this.Data.DataOffset; + + return true; + } + + this.ZoomDown=function(cursorIndex) + { + if (this.ZoomIndex+1>=ZOOM_SEED.length) return false; + if (this.Data.DataOffset<0) return false; + var dataCount=this.Data.Data.length; + var maxDataCount=dataCount+this.RightSpaceCount; + if (this.XPointCount>=dataCount) return false; + + var rightSpaceCount=0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + if (lastDataIndex>=this.Data.Data.length) + { + rightSpaceCount=lastDataIndex-(this.Data.Data.length-1); //计算右边预留空间 + lastDataIndex=this.Data.Data.length-1; + if (rightSpaceCount>this.RightSpaceCount) rightSpaceCount=this.RightSpaceCount; + } + + var xPointCount=this.CalculateCount(this.ZoomIndex+1); + var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index; + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomDown] old status. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${this.Data.Data.length} lastCursorIndex=${lastCursorIndex} rightSpaceCount=${rightSpaceCount}`); + + ++this.ZoomIndex; + if (xPointCount>=maxDataCount) + { + xPointCount=maxDataCount; + this.XPointCount=xPointCount; + this.Data.DataOffset=0; + + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomDown] Show all data. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount}`); + } + else + { + this.XPointCount=xPointCount; + this.Data.DataOffset = lastDataIndex - (this.XPointCount-rightSpaceCount)+1; + + JSConsole.Chart.Log(`[KLineHScreenFrame::ZoomDown] calculate. XPointCount=${xPointCount} ZoomIndex=${this.ZoomIndex} DataCount= ${dataCount} DataOffset=${this.Data.DataOffset}`); + } + + this.DataWidth = ZOOM_SEED[this.ZoomIndex][0]; + this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1]; + var width=this.ChartBorder.GetHeight()-g_JSChartResource.FrameMargin; + this.TrimKLineDataWidth(width); + this.LastCalculateStatus.XPointCount=this.XPointCount; + cursorIndex.Index=lastCursorIndex-this.Data.DataOffset; + + return true; + } + */ +} + + +function OverlayKLineHScreenFrame() +{ + this.newMethod=KLineHScreenFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='OverlayKLineHScreenFrame'; + this.MainFrame=null; //主框架 + this.IsShareY=false; //使用和主框架公用Y轴 + this.RightOffset=50; + this.PenBorder=g_JSChartResource.OverlayFrame.BolderPen; //'rgb(0,0,0)' + this.IsShow=true; //坐标是否显示 + this.Title=null; + this.TitleColor=g_JSChartResource.OverlayFrame.TitleColor; + this.TitleFont=g_JSChartResource.OverlayFrame.TitleFont; + + this.Draw=function() + { + this.SplitXYCoordinate(); + + if (this.IsShow) + { + this.DrawVertical(); + this.DrawHorizontal(); + this.DrawTitle(); + } + + this.SizeChange=false; + this.XYSplit=false; + } + + this.DrawTitle=function() //画标题 + { + /* + if (!this.Title) return; + var top = this.ChartBorder.GetTopTitle(); + var bottom = this.ChartBorder.GetBottom(); + var right=this.ChartBorder.GetRight(); + right+=this.RightOffset; + + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.font=this.TitleFont; + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="top"; + + var xText=right-2,yText=top+(bottom-top)/2; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.Title, 0, 0); + this.Canvas.restore(); + */ + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + if (this.IsShareY) //和主图指标共享Y轴坐标 + { + this.HorizontalMax=this.MainFrame.HorizontalMax; + this.HorizontalMin=this.MainFrame.HorizontalMin; + this.HorizontalInfo=[]; + for(var i in this.MainFrame.HorizontalInfo) + { + var item=this.MainFrame.HorizontalInfo[i]; + this.HorizontalInfo.push(item); + } + } + else + { + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + } + // if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); 子坐标和主坐标X轴一致 所以不用计算 + } + + //画Y轴 + this.DrawHorizontal=function() + { + /* + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var bottom = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetTopTitle(); + var borderRight=this.ChartBorder.Right; + right+=this.RightOffset; + + var yPrev=null; //上一个坐标y的值 + for(var i=this.HorizontalInfo.length-1; i>=0; --i) //从上往下画分割线 + { + var item=this.HorizontalInfo[i]; + var y=this.GetYFromData(item.Value); + if (y!=null && Math.abs(y-yPrev)= bottom - 2) this.Canvas.textBaseline = 'bottom'; + else if (y <= top + 2) this.Canvas.textBaseline = 'top'; + else this.Canvas.textBaseline = "middle"; + + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + this.Canvas.moveTo(right-2,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + this.Canvas.stroke(); + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1]!=null && borderRight>10) + { + if (item.Font!=null) this.Canvas.font=item.Font; + + this.Canvas.fillStyle=item.TextColor; + this.Canvas.textAlign="left"; + this.Canvas.fillText(item.Message[1],right+2,y); + } + + yPrev=y; + } + */ + } + + //画X轴 + this.DrawVertical=function() + { + /* + var top=this.ChartBorder.GetTopEx(); + //var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var bottom=this.ChartBorder.GetBottomEx(); + right+=this.RightOffset; + + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(right),ToFixedPoint(top)); + this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(bottom)); + this.Canvas.stroke(); + */ + } + + this.GetScaleTextWidth=function() + { + return { TextWidth:0 }; + } +} + +function SubFrameItem() +{ + this.Frame; + this.Height; + this.OverlayIndex=[]; //叠加指标 + this.Interval=60; //子坐标间间距 +} + +function OverlayIndexItem() +{ + this.Frame; + this.ChartPaint=[]; + this.Identify=Guid(); + this.Scprit; //脚本 + + this.UpdateFrameMaxMin=function() //调整坐标最大 最小值 + { + var value={ Max:null, Min:null } + if (this.Frame.IsShareY) //共享Y轴坐标 + { + this.Frame.XYSplit=true; + return; + } + + if (this.Frame.YSpecificMaxMin) //固定坐标 + { + value.Max=this.Frame.YSpecificMaxMin.Max; + value.Min=this.Frame.YSpecificMaxMin.Min; + } + else + { + for(var i in this.ChartPaint) + { + var paint=this.ChartPaint[i]; + var range=paint.GetMaxMin(); + + if (IFrameSplitOperator.IsNumber(range.Max)) + { + if (value.Max==null || value.Maxrange.Min) value.Min=range.Min; + } + } + } + + if (value.Max!=null && value.Min!=null) + { + this.Frame.HorizontalMax=value.Max; + this.Frame.HorizontalMin=value.Min; + this.Frame.XYSplit=true; + } + } +} + +//行情框架 +function HQTradeFrame() +{ + this.SubFrame=new Array(); //SubFrameItem 数组 + this.SizeChange=true; //大小是否改变 + this.ChartBorder; + this.Canvas; //画布 + this.ScreenImageData; //截图 + this.Data; //主数据 + this.Position; //画布的位置 + this.SizeChange=true; + this.MinSubFrameHeight=g_JSChartResource.DragSubFrameBorder.MinFrameHeight; + this.DragBorderHeight=g_JSChartResource.DragSubFrameBorder.TopBorderHeight; //拖拽边框高度 + + this.AutoLeftBorder=null; //{ Blank:10 留白宽度, MinWidth:最小宽度 } + this.AutoRightBorder=null; //{ Blank:10 留白宽度, MinWidth:最小宽度 } + this.OverlayBlankWidth=40; + this.AuotRightWidth; //右边主坐标刻度宽度 + + this.ZoomWindowsInfo=null; //附图指标缩放,备份信息 + this.ZoomStartWindowIndex=1; //允许缩放窗口起始位置 + + this.OnMoveFromeBorder=function(index, yMove) + { + if (this.SubFrame.length<=0) return false; + + var topFrame=this.SubFrame[index]; + var bottomFrame=null; + for(var i=index+1;i0) + { + bottomFrame=item; + break; + } + } + + if (!topFrame || !bottomFrame) return false; + + var bottomBackup=topFrame.Frame.ChartBorder.Bottom; + var topBackup=bottomFrame.Frame.ChartBorder.Top; + + topFrame.Frame.ChartBorder.Bottom-=yMove; + bottomFrame.Frame.ChartBorder.Top+=yMove; + + var height=topFrame.Frame.ChartBorder.GetHeightEx(); + var height2=bottomFrame.Frame.ChartBorder.GetHeightEx(); + + //缩小的时候 小于最小高度 不处理 + if ((height0)) + { + topFrame.Frame.ChartBorder.Bottom=bottomBackup; + bottomFrame.Frame.ChartBorder.Top=topBackup; + return false; + } + + return true; + } + + this.ReDrawToolbar=function() + { + for(var i in this.SubFrame) + { + this.SubFrame[i].Frame.ReDrawToolbar=true; + } + } + + this.SetFrameBorderSizeChange=function() + { + var firstFrame=this.SubFrame[0]; + if (!firstFrame || !firstFrame.Frame) return; + + var splitOper=firstFrame.Frame.YSplitOperator; + if (!splitOper) return; + if (splitOper.CoordinateType==2) //对数坐标 需要重新计算Y轴分割 + { + firstFrame.Frame.XYSplit=true; + } + } + + this.CancelZoomUpDownFrameY=function(obj) + { + var index=obj.Index; + if (this.SubFrame.length<=0) return false; + if (!this.SubFrame[index]) return false; + + var subFrame=this.SubFrame[index]; + var frame=subFrame.Frame; + var splitOper=frame.YSplitOperator; + + if (splitOper.FixedYMaxMin) + { + splitOper.FixedYMaxMin=null; + splitOper.EnableZoomUpDown=false; + frame.XYSplit=true; + for(var i in subFrame.OverlayIndex) + { + var item=subFrame.OverlayIndex[i]; + if (item.Frame.IsShareY) item.Frame.XYSplit=true; + } + + JSConsole.Chart.Log(`[HQTradeFrame::CancelZoomUpDownFrameY]`); + return true; + } + + return false; + } + + this.OnZoomUpDownFrameY=function(obj, yMove) + { + var index=obj.Index; + if (this.SubFrame.length<=0) return false; + if (!this.SubFrame[index]) return false; + + var subFrame=this.SubFrame[index]; + var frame=subFrame.Frame; + var top=frame.ChartBorder.GetTopEx(); + var bottom=frame.ChartBorder.GetBottomEx(); + + var maxValue=frame.HorizontalMax; + var minValue=frame.HorizontalMin; + + var moveStep=(maxValue-minValue)*Math.abs(yMove)/(bottom-top); + + var splitOper=frame.YSplitOperator; + + var newFixedYMaxMin={ Max:maxValue, Min:minValue }; + if (obj.Position==1) + { + var step=yMove>0 ? -moveStep:moveStep; + newFixedYMaxMin.Max+=step; + } + else if (obj.Position==2) + { + var step=yMove>0 ? -moveStep:moveStep; + newFixedYMaxMin.Min+=step; + } + else if (obj.Position==0) + { + var step=yMove>0 ? moveStep:-moveStep; + newFixedYMaxMin.Max+=step; + newFixedYMaxMin.Min-=step; + } + else + { + return false; + } + + if (newFixedYMaxMin.Max>newFixedYMaxMin.Min) + { + splitOper.FixedYMaxMin=newFixedYMaxMin; + splitOper.EnableZoomUpDown=true; + frame.XYSplit=true; + + for(var i in subFrame.OverlayIndex) + { + var item=subFrame.OverlayIndex[i]; + if (item.Frame.IsShareY) item.Frame.XYSplit=true; + } + + JSConsole.Chart.Log(`[HQTradeFrame::OnZoomUpDownFrameY] Max=${newFixedYMaxMin.Max}, Min=${newFixedYMaxMin.Min}`); + return true; + } + + return false; + } + + this.OnUpDonwFrameY=function(obj, yMove) + { + var index=obj.Index; + if (this.SubFrame.length<=0) return false; + if (!this.SubFrame[index]) return false; + + var subFrame=this.SubFrame[index]; + var frame=subFrame.Frame; + var top=frame.ChartBorder.GetTopEx(); + var bottom=frame.ChartBorder.GetBottomEx(); + var splitOper=frame.YSplitOperator; + if (!splitOper) return false; + + var maxValue=frame.HorizontalMax; + var minValue=frame.HorizontalMin; + + var moveStep=(maxValue-minValue)*Math.abs(yMove)/(bottom-top); + var step=yMove>0 ? -moveStep:moveStep; + var newFixedYMaxMin={ Max:maxValue, Min:minValue }; + newFixedYMaxMin.Max-=step; + newFixedYMaxMin.Min-=step; + + splitOper.FixedYMaxMin=newFixedYMaxMin; + splitOper.EnableZoomUpDown=true; + frame.XYSplit=true; + + for(var i in subFrame.OverlayIndex) + { + var item=subFrame.OverlayIndex[i]; + if (item.Frame.IsShareY) item.Frame.XYSplit=true; + } + + JSConsole.Chart.Log(`[HQTradeFrame::OnUpDonwFrameY] Max=${newFixedYMaxMin.Max}, Min=${newFixedYMaxMin.Min}, yMove=${yMove}, moveStep=${moveStep}`); + return true; + } + + this.ClearUpDonwFrameYData=function(option) //清空上下拖拽的数据 + { + if (this.SubFrame.length<=0) return; + + if (option) + { + var index=option.Index; + if (index<0 || index>=this.SubFrame.length) return; + + var item=this.SubFrame[index]; + if (!item || !item.Frame || !item.Frame.YSplitOperator) return; + + var splitOper=item.Frame.YSplitOperator; + if (splitOper.EnableZoomUpDown==true) splitOper.FixedYMaxMin=null; + } + else + { + for(var i=0;i0) max+=this.OverlayBlankWidth + + for(var j=0; jvalue) value=this.AutoLeftBorder.MinWidth; + } + if (this.IsHScreen) this.ChartBorder.Top=value; + else this.ChartBorder.Left=value; + + for(var i=0; ivalue) value=this.AutoRightBorder.MinWidth; + } + + this.AuotRightWidth=value; + if (IFrameSplitOperator.IsPlusNumber(textWidth.OverlayRight)) + { + value+=this.OverlayBlankWidth; + this.AuotRightWidth=value; + value+=textWidth.OverlayRight; + } + + if (this.GetExtendChartRightWidth) + { + var extendWidth=this.GetExtendChartRightWidth(); + value+=extendWidth; + } + + if (this.IsHScreen) + { + if (this.ChartBorder.Bottom!=value) bSizeChange=true; + this.ChartBorder.Bottom=value; + } + else + { + if (this.ChartBorder.Right!=value) bSizeChange=true; + this.ChartBorder.Right=value; + } + for(var i=0; i0 && overlayItem.Frame) + { + overlayItem.Frame.RightOffset=rightOffset; + overlayItem.Frame.Draw(); + rightOffset+=overlayItem.RightWidth.Width; + } + } + + } + else + { + var rightOffset=item.Interval; + if (item.Frame.RightTextMaxWidth>rightOffset) rightOffset=item.Frame.RightTextMaxWidth; + for(var j in item.OverlayIndex) + { + var overlayItem=item.OverlayIndex[j]; + //把主坐标部分设置给子坐标下来 + overlayItem.Frame.DataWidth=item.Frame.DataWidth; + overlayItem.Frame.DistanceWidth=item.Frame.DistanceWidth; + overlayItem.Frame.XPointCount=item.Frame.XPointCount; + if (overlayItem.ChartPaint.length>0 && overlayItem.Frame) + { + overlayItem.Frame.RightOffset=rightOffset; + overlayItem.Frame.Draw(); + if (overlayItem.Frame.IsShow) rightOffset+=item.Interval; + } + } + } + } + + this.SizeChange=false; + } + + this.DrawOveraly=function() + { + for(var i in this.SubFrame) + { + var item=this.SubFrame[i]; + for(var j in item.OverlayIndex) + { + var overlayItem=item.OverlayIndex[j]; + for(var k in overlayItem.ChartPaint) + { + if (overlayItem.ChartPaint[k].IsShow) + overlayItem.ChartPaint[k].Draw(); + } + } + } + } + + this.DrawLock=function() + { + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + item.Frame.DrawLock(); + } + } + + this.CalculateLock=function() + { + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + item.Frame.CalculateLock(); + } + } + + this.DrawInsideHorizontal = function () + { + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + if (item.Frame.DrawInsideHorizontal) item.Frame.DrawInsideHorizontal(); + } + } + + this.DrawCustomHorizontal=function() //定制坐标 + { + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + if (item.Frame.DrawCustomHorizontal) item.Frame.DrawCustomHorizontal(); + } + } + + this.DrawEx=function(option) + { + for(var i in this.SubFrame) + { + var item = this.SubFrame[i]; + if ((item.Frame.ClassName=="MinuteFrame"||item.Frame.ClassName=="MinuteHScreenFrame") && i==1) + { + item.Frame.DrawVolTitle(option.Symbol); + } + } + } + + this.DrawCustomVertical=function(event) + { + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + item.Frame.DrawCustomVerticalEvent=event; + if (item.Frame.DrawCustomVertical) item.Frame.DrawCustomVertical(); + } + } + + this.SetSizeChage=function(sizeChange) + { + this.SizeChange=sizeChange; + + for(var i in this.SubFrame) + { + var item=this.SubFrame[i]; + item.Frame.SizeChange=sizeChange; + + for(var j in item.OverlayIndex) + { + var overlayItem=item.OverlayIndex[j]; + if (overlayItem.Frame) overlayItem.Frame.SizeChange=sizeChange; + } + } + + //画布的位置 + if (this.ChartBorder.UIElement) + { + this.Position={ + X:this.ChartBorder.UIElement.offsetLeft, + Y:this.ChartBorder.UIElement.offsetTop, + W:this.ChartBorder.UIElement.clientWidth, + H:this.ChartBorder.UIElement.clientHeight + }; + } + } + + this.SetDrawDepthMap=function(callback) + { + for(var i in this.SubFrame) + { + var item=this.SubFrame[i]; + item.Frame.DrawDepthMapCallback=callback; + } + } + + //图形快照 + this.Snapshot=function() + { + this.ScreenImageData=this.Canvas.getImageData(0,0,this.ChartBorder.GetChartWidth(),this.ChartBorder.GetChartHeight()); + } + + this.GetXData=function(x) + { + return this.SubFrame[0].Frame.GetXData(x); + } + + this.GetYData=function(y,outObject) //outObject 可以保存返回的额外数据 + { + var frame; + for(var i in this.SubFrame) + { + var item=this.SubFrame[i]; + var left=item.Frame.ChartBorder.GetLeft(); + var top=item.Frame.ChartBorder.GetTopEx(); + var width=item.Frame.ChartBorder.GetWidth(); + var height=item.Frame.ChartBorder.GetHeightEx(); + + item.Frame.Canvas.beginPath(); + item.Frame.Canvas.rect(left,top,width,height); + if (item.Frame.Canvas.isPointInPath(left,y)) + { + frame=item.Frame; + if (outObject) outObject.FrameID=parseInt(i); //转成整形 + break; + } + } + + if (frame!=null) + { + if (frame.RightFrame) outObject.RightYValue=frame.RightFrame.GetYData(y); //右侧子坐标 + + var yValue=frame.GetYData(y); + if (frame.YSplitOperator.CoordinateType==1) //百分比坐标 右边显示百分比信息 + { + var firstOpenPrice=frame.YSplitOperator.GetFirstOpenPrice(); + outObject.RightYValue=((yValue-firstOpenPrice)/firstOpenPrice*100).toFixed(2)+'%'; + } + return yValue; + } + } + + this.PtInFrame=function(x,y) //鼠标哪个指标窗口 + { + for(var i=0; i0) //第1个窗口主坐标已经算好了 + { + item.Frame.XPointCount= mainFrame.XPointCount; + item.Frame.ZoomIndex= mainFrame.ZoomIndex; + item.Frame.DataWidth= mainFrame.DataWidth; + item.Frame.DistanceWidth= mainFrame.DistanceWidth; + item.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width; + item.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount; + } + + for(var j in item.OverlayIndex) + { + var overlayItem=this.SubFrame[i].OverlayIndex[j]; + overlayItem.Frame.XPointCount= mainFrame.XPointCount; + overlayItem.Frame.ZoomIndex= mainFrame.ZoomIndex; + overlayItem.Frame.DataWidth= mainFrame.DataWidth; + overlayItem.Frame.DistanceWidth= mainFrame.DistanceWidth; + overlayItem.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width; + overlayItem.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount; + } + } + } + + //鼠标是否在边框上 + this.PtInFrameBorder=function(x,y) + { + var height=this.DragBorderHeight; + for(var i=0;imaxTopHegith) + { + var barTop=top+maxTopHegith; + var barBottom=bottom-maxTopHegith; + } + else + { + var internal=barHegith/3; + var barTop=top+internal; + var barBottom=bottom-internal; + } + + var position=0; + if (ybarBottom) position=2; + + if (rightWidth>=10) + { + item.Frame.Canvas.beginPath(); + item.Frame.Canvas.rect(right,top,rightWidth,(bottom-top)); + if (item.Frame.Canvas.isPointInPath(x,y)) + { + return { Index:i, Right:true, Left:false , Position:position }; //Position 1=上面 2 下面 0=中间(TODO) + } + } + + var leftWidth=item.Frame.ChartBorder.Left; + if (leftWidth>=10) + { + item.Frame.Canvas.beginPath(); + item.Frame.Canvas.rect(0,top,leftWidth,(bottom-top)); + if (item.Frame.Canvas.isPointInPath(x,y)) + { + return { Index:i, Right:false, Left:true, Position:position }; + } + } + + } + return null; + } + + this.SetDayCount=function(dayCount) + { + this.ChartBorder.MultiDayMinute.Count=dayCount; + for(var i=0;i0) + { + var minPrice=this.BidPrice[0]; + if (price0) + { + var maxPrice=this.AskPrice[this.AskPrice.length-1]; + if (price>maxPrice) + { + isAskPrice=true; + find=maxPrice; + } + } + } + + if (find==null) return null; + + var x=this.GetXFromIndex(find); + + return { X:x, Price:find, IsAsk:isAskPrice }; + } + + this.GetPrice=function(aryPrice, price) + { + if (!aryPrice || !Array.isArray(aryPrice) || aryPrice.length<=0) return null; + + if (pricearyPrice[aryPrice.length-1]) return null; + + var lastPrice=null; + for(var i in aryPrice) + { + var item=aryPrice[i]; + if (price==item) + { + return item; + } + + if (pricexRange.MaxDiffer) differ=xRange.MaxDiffer; + + xRange.Differ=differ; + xRange.Min=xRange.Center-xRange.Differ; + xRange.Max=xRange.Center+xRange.Differ; + + return true; + } +} + + +function SimpleChartFrame() +{ + this.newMethod=AverageWidthFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ScreenImageData; //截图 + this.Position; //画布的位置 + + this.DrawFrame=function() + { + if (this.XPointCount>0) + { + let dInterval=this.ChartBorder.GetWidth()/(6*this.XPointCount); //分6份, 数据4 间距2 + this.DistanceWidth=2*dInterval; + this.DataWidth=4*dInterval; + } + + this.SplitXYCoordinate(); + this.DrawHorizontal(); + this.DrawVertical(); + } + + this.GetXFromIndex=function(index) + { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var offset=this.ChartBorder.GetLeft()+2+this.DistanceWidth/2+this.DataWidth/2; + for(var i=1;i<=index;++i) + { + offset+=this.DistanceWidth+this.DataWidth; + } + + return offset; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate=function() + { + if (this.XYSplit==false) return; + if (this.YSplitOperator!=null) this.YSplitOperator.Operator(); + if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); + } + + //图形快照 + this.Snapshot=function() + { + this.ScreenImageData=this.Canvas.getImageData(0,0,this.ChartBorder.GetChartWidth(),this.ChartBorder.GetChartHeight()); + } + + this.SetSizeChage=function(sizeChange) + { + this.SizeChange=sizeChange; + + //画布的位置 + this.Position={ + X:this.ChartBorder.UIElement.offsetLeft, + Y:this.ChartBorder.UIElement.offsetTop, + W:this.ChartBorder.UIElement.clientWidth, + H:this.ChartBorder.UIElement.clientHeight + }; + } + + this.SetDrawDepthMap=function(callback) + { + + } + + this.PtInFrame=function(x,y) //鼠标哪个指标窗口 + { + var left=this.ChartBorder.GetLeft(); + var top=this.ChartBorder.GetTop(); + var width=this.ChartBorder.GetWidth(); + var height=this.ChartBorder.GetHeight(); + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,width,height); + if (this.Canvas.isPointInPath(x,y)) + return 0; + + return -1; + } + + this.PtInFrameBorder=function(x,y) + { + return null; + } +} + + +//历史K线数据 +function HistoryData() +{ + this.Date; + this.YClose; + this.Open; + this.Close; + this.High; + this.Low; + this.Vol; + this.Amount; + this.Time; //mmhh 或者 mmhhss + this.FlowCapital=0; //流通股本 + this.Position=null; //持仓量 + + //期货 + this.YFClose=null; //前结算价 + this.FClose=null; //结算价 + + //指数才有的数据 + this.Stop; //停牌家数 + this.Up; //上涨 + this.Down; //下跌 + this.Unchanged; //平盘 + + this.ExtendData; //扩展数据 + + this.BFactor; //前复权 + this.AFactor; //后复权 +} + +//数据复制 +HistoryData.Copy=function(data) +{ + var newData=new HistoryData(); + newData.Date=data.Date; + newData.YClose=data.YClose; + newData.Open=data.Open; + newData.Close=data.Close; + newData.High=data.High; + newData.Low=data.Low; + newData.Vol=data.Vol; + newData.Amount=data.Amount; + newData.Time=data.Time; + newData.FlowCapital=data.FlowCapital; + + newData.Position=data.Position; + newData.YFClose=data.YFClose; + newData.FClose=data.FClose; + + newData.Stop=data.Stop; + newData.Up=data.Up; + newData.Down=data.Down; + newData.Unchanged=data.Unchanged; + + return newData; +} + +//把数据 src 复制到 dest中 +HistoryData.CopyTo=function(dest,src) +{ + dest.Date=src.Date; + dest.YClose=src.YClose; + dest.Open=src.Open; + dest.Close=src.Close; + dest.High=src.High; + dest.Low=src.Low; + dest.Vol=src.Vol; + dest.Amount=src.Amount; + dest.Time=src.Time; + dest.FlowCapital=src.FlowCapital; + + dest.Position=src.Position; + dest.YFClose=src.YFClose; + dest.FClose=src.FClose; + + dest.Stop=src.Stop; + dest.Up=src.Up; + dest.Down=src.Down; + dest.Unchanged=src.Unchanged; +} + +//数据复权拷贝 +HistoryData.CopyRight=function(data,seed) +{ + var newData=new HistoryData(); + newData.Date=data.Date; + newData.YClose=data.YClose*seed; + newData.Open=data.Open*seed; + newData.Close=data.Close*seed; + newData.High=data.High*seed; + newData.Low=data.Low*seed; + + newData.Vol=data.Vol; + newData.Amount=data.Amount; + newData.FlowCapital=data.FlowCapital; + newData.Position=data.Position; + newData.YFClose=data.YFClose; + newData.FClose=data.FClose; + + return newData; +} + +function MinuteData() +{ + this.Close; + this.Open; + this.High; + this.Low; + this.Vol; //量 + this.Amount; //金额 + this.DateTime; + this.Increase; //涨幅 + this.Risefall; //涨跌 + this.AvPrice; //均价 + this.Lead=null; //领先指标 + this.Time; //时间 + this.Date; //日期 + this.Position=null; //持仓量 +} + +//盘前集合竞价 +function BeforeOpenData() +{ + this.Time; + this.Date; + this.Price; //匹配的价格 + this.AvPrice; //均价 + this.Vol=[]; //[0]=匹配量 [1]=未匹配量 + this.ColorID; //0=平盘 1=红 2=绿 3 ...自定义颜色 +} + +//收盘集合竞价 +function AfterCloseData() +{ + this.Time; + this.Date; + this.Price; //匹配的价格 + this.AvPrice; //均价 + this.Vol=[]; //[0]=匹配量 [1]=未匹配量 + this.ColorID; //0=平盘 1=红 2=绿 3 ...自定义颜色 +} + +//单指标数据 +function SingleData() +{ + this.Date; //日期 + this.Time; //时间 + this.Value; //数据 (可以是一个数组) +} + +var KLINE_INFO_TYPE= +{ + INVESTOR:1, //互动易 + ANNOUNCEMENT:2, //公告 + PFORECAST:3, //业绩预告 + + ANNOUNCEMENT_QUARTER_1:4, //一季度报 + ANNOUNCEMENT_QUARTER_2:5, //半年报 + ANNOUNCEMENT_QUARTER_3:6, //2季度报 + ANNOUNCEMENT_QUARTER_4:7, //年报 + + RESEARCH:8, //调研 + BLOCKTRADING:9, //大宗交易 + TRADEDETAIL:10, //龙虎榜 + + //公告预留类型 + ANNOUNCEMENT_EX_START:100, + ANNOUNCEMENT_EX_END:200, +} + +function KLineInfoData() +{ + this.ID; + this.Date; + this.Title; + this.InfoType; + this.ExtendData; //扩展数据 +} + +//外部数据计算方法接口 +function DataPlus () +{ + this.PeriodCallback=new Map(); + + this.GetPeriodCallback=function(period) + { + if (!this.PeriodCallback.has(period)) return null; + + return this.PeriodCallback.get(period); + } + + this.AddPeriodCallback=function(obj) + { + if (!IFrameSplitOperator.IsNumber(obj.Period) || !obj.Callback) return; + + var item={ Period:obj.Period, Callback:obj.Callback }; + this.PeriodCallback.set(obj.Period, item); + } + + this.RemovePeriodCallback=function(obj) + { + if (!this.PeriodCallback.has(obj.ID)) return; + this.PeriodCallback.delete(obj.ID); + } +}; + +DataPlus.GetMinutePeriodData=null; + +var g_DataPlus=new DataPlus(); + +/* +DataPlus.GetMinutePeriodData=function(period,data,self) +{ + +} +*/ + +function ChartData() +{ + this.Data=new Array(); + this.DataOffset=0; //数据偏移 + this.Period=0; //周期 0 日线 1 周线 2 月线 3年线 + this.Right=0; //复权 0 不复权 1 前复权 2 后复权 + this.Symbol; //股票代码 + + this.Data2=new Array(); //第1组数据 走势图:历史分钟数据 + + this.GetCloseMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Close; + } + result[i]=sum / dayCount; + } + return result; + } + + this.GetVolMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Vol; + } + result[i]=sum / dayCount; + } + return result; + } + + this.GetAmountMA=function(dayCount) + { + var result=new Array(); + for (var i = 0, len = this.Data.length; i < len; i++) + { + if (i < dayCount) + { + result[i]=null; + continue; + } + + var sum = 0; + for (var j = 0; j < dayCount; j++) + { + sum += this.Data[i - j].Amount; + } + result[i]=sum / dayCount; + } + return result; + } + + //获取收盘价 + this.GetClose=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Close; + } + + return result; + } + + this.GetYClose=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].YClose; + } + + return result; + } + + this.GetHigh=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].High; + } + + return result; + } + + this.GetLow=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Low; + } + + return result; + } + + this.GetOpen=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Open; + } + + return result; + } + + this.GetVol=function(unit) + { + var value=1; + if (IFrameSplitOperator.IsNumber(unit)>0) value=unit; + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Vol/value; + } + + return result; + } + + this.GetAmount=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Amount; + } + + return result; + } + + this.GetPosition=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].Position; + } + + return result; + } + + this.GetSettlementPrice=function() //结算价 + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].FClose; + } + + return result; + } + + this.GetIsEqual=function() + { + var result=[]; + for(var i in this.Data) + { + var item=this.Data[i]; + result[i]=(item.Close==item.Open? 1:0); + } + + return result; + } + + this.GetIsUp=function() + { + var result=[]; + for(var i in this.Data) + { + var item=this.Data[i]; + result[i]=(item.Close>item.Open? 1:0); + } + + return result; + } + + this.GetIsDown=function() + { + var result=[]; + for(var i in this.Data) + { + var item=this.Data[i]; + result[i]=(item.CloseCUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) + { + periodDataCount=period-CUSTOM_MINUTE_PERIOD_START; + return this.GetMinuteCustomPeriodData(periodDataCount); + } + else + return this.Data; + var bFirstPeriodData = false; + var newData = null; + var preTime=null; //上一次的计算时间 + for (var i = 0; i < this.Data.length; ) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var minData = this.Data[i]; + if (minData == null) + { + ++j; + continue; + } + if (minData.Time==925 && (preTime==null || preTime!=924)) //9:25, 9:30 不连续就不算个数 + { + + } + else if (minData.Time == 930 && (preTime==null || preTime!=929) ) + { + + } + else if (minData.Time == 1300 && (preTime==null || preTime!=1259) ) //1点的数据 如果不是连续的 就不算个数 + { + + } + else + ++j; + newData.Date = minData.Date; + newData.Time = minData.Time; + preTime=newData.Time; + if (minData.Open==null || minData.Close==null) + continue; + if (newData.Open==null || newData.Close==null) + { + newData.Open=minData.Open; + newData.High=minData.High; + newData.Low=minData.Low; + newData.YClose=minData.YClose; + newData.Close=minData.Close; + newData.Vol=minData.Vol; + newData.Amount=minData.Amount; + newData.FlowCapital=minData.FlowCapital; + newData.Position=minData.Position; + newData.YFClose=minData.YFClose; + newData.FClose=minData.FClose; + } + else + { + if (newData.HighminData.Low) + newData.Low=minData.Low; + newData.Close=minData.Close; + newData.Vol+=minData.Vol; + if (minData.Amount!=null) newData.Amount+=minData.Amount; + newData.Position=minData.Position; + newData.FlowCapital=minData.FlowCapital; + newData.FClose=minData.FClose; + } + + if (i+1 < this.Data.length) //判断下一个数据是否是不同日期的 + { + var nextItem=this.Data[i+1]; + if (nextItem && nextItem.Date!=minData.Date) //不同日期的, 周期结束 + { + ++i; + break; + } + } + } + } + return result; + } + + //自定义分钟 + this.GetMinuteCustomPeriodData=function(count) + { + var result = new Array(); + var periodDataCount = count; + var bFirstPeriodData = false; + var newData = null; + for (var i = 0; i < this.Data.length; ) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var minData = this.Data[i]; + if (minData == null) continue; + + newData.Date = minData.Date; + newData.Time = minData.Time; + if (minData.Open==null || minData.Close==null) continue; + if (newData.Open==null || newData.Close==null) + { + newData.Open=minData.Open; + newData.High=minData.High; + newData.Low=minData.Low; + newData.YClose=minData.YClose; + newData.Close=minData.Close; + newData.Vol=minData.Vol; + newData.Amount=minData.Amount; + newData.FlowCapital=minData.FlowCapital; + newData.Position=minData.Position; + newData.YFClose=minData.YFClose; + newData.FClose=minData.FClose; + } + else + { + if (newData.HighminData.Low) newData.Low=minData.Low; + newData.Close=minData.Close; + newData.Vol+=minData.Vol; + if (minData.Amount!=null) newData.Amount+=minData.Amount; //null数据不相加 + newData.Position=minData.Position; + newData.FlowCapital=minData.FlowCapital; + newData.FClose=minData.FClose; + } + } + } + return result; + } + //计算周,月,年 + this.GetDayPeriodData=function(period) + { + if (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END) + return this.GetDayCustomPeriodData(period-CUSTOM_DAY_PERIOD_START); + + var isBit=MARKET_SUFFIX_NAME.IsBIT(this.Symbol); + + var result=new Array(); + var index=0; + var startDate=0; + var weekCount=2; + var newData=null; + for(var i in this.Data) + { + var isNewData=false; + var dayData=this.Data[i]; + + switch(period) + { + case 1: //周线 + if (isBit) var fridayDate=ChartData.GetSunday(dayData.Date); //数字货币全年的 + else var fridayDate=ChartData.GetFirday(dayData.Date); + + if (fridayDate!=startDate) + { + isNewData=true; + startDate=fridayDate; + } + break; + case 21: //双周 + if (isBit) var fridayDate=ChartData.GetSunday(dayData.Date); //数字货币全年的 + else var fridayDate=ChartData.GetFirday(dayData.Date); + + if (fridayDate!=startDate) + { + ++weekCount; + if (weekCount>=2) + { + isNewData=true; + weekCount=0; + } + startDate=fridayDate; + } + break; + case 2: //月线 + if (parseInt(dayData.Date/100)!=parseInt(startDate/100)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 3: //年线 + if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 22://半年: + var halfYear=ChartData.GetHalfYear(dayData.Date); + if (halfYear!=startDate) + { + isNewData=true; + startDate=halfYear; + } + break; + case 9: //季线 + var now=ChartData.GetQuarter(dayData.Date); + now=parseInt(dayData.Date/10000)*10+now; + var last=ChartData.GetQuarter(startDate); + last=parseInt(startDate/10000)*10+last; + if (now!=last) + { + isNewData=true; + startDate=dayData.Date; + } + break; + } + + if (isNewData) + { + newData=new HistoryData(); + newData.Date=dayData.Date; + result.push(newData); + + if (dayData.Open==null || dayData.Close==null) continue; + + newData.Open=dayData.Open; + newData.High=dayData.High; + newData.Low=dayData.Low; + newData.YClose=dayData.YClose; + newData.Close=dayData.Close; + newData.Vol=dayData.Vol; + newData.Amount=dayData.Amount; + newData.FlowCapital=dayData.FlowCapital; + newData.Position=dayData.Position; + newData.YFClose=dayData.YFClose; + newData.FClose=dayData.FClose; + } + else + { + if (newData==null) continue; + if (dayData.Open==null || dayData.Close==null) continue; + + if (newData.Open==null || newData.Close==null) + { + newData.Open=dayData.Open; + newData.High=dayData.High; + newData.Low=dayData.Low; + newData.YClose=dayData.YClose; + newData.Close=dayData.Close; + newData.Vol=dayData.Vol; + newData.Amount=dayData.Amount; + newData.FlowCapital=dayData.FlowCapital; + newData.Position=dayData.Position; + newData.YFClose=dayData.YFClose; + newData.FClose=dayData.FClose; + } + else + { + if (newData.HighdayData.Low) newData.Low=dayData.Low; + + newData.Close=dayData.Close; + newData.Vol+=dayData.Vol; + if (dayData.Amount!=null) newData.Amount+=dayData.Amount; + newData.Position=dayData.Position; + newData.FlowCapital=dayData.FlowCapital; + newData.Date=dayData.Date; + newData.FClose=dayData.FClose; + } + } + } + + return result; + } + + this.GetDayCustomPeriodData=function(count) + { + var result = []; + var periodDataCount = count; + var bFirstPeriodData = false; + var newData = null; + for (var i = 0; i < this.Data.length; ) + { + bFirstPeriodData = true; + for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j) + { + if (bFirstPeriodData) + { + newData = new HistoryData(); + result.push(newData); + bFirstPeriodData = false; + } + var dayData = this.Data[i]; + if (dayData == null) continue; + + newData.Date = dayData.Date; + + if (dayData.Open==null || dayData.Close==null) continue; + if (newData.Open==null || newData.Close==null) + { + newData.Open=dayData.Open; + newData.High=dayData.High; + newData.Low=dayData.Low; + newData.YClose=dayData.YClose; + newData.Close=dayData.Close; + newData.Vol=dayData.Vol; + newData.Amount=dayData.Amount; + newData.FlowCapital=dayData.FlowCapital; + newData.Position=dayData.Position; + newData.YFClose=dayData.YFClose; + newData.FClose=dayData.FClose; + } + else + { + if (newData.HighdayData.Low) newData.Low=dayData.Low; + newData.Close=dayData.Close; + newData.Vol+=dayData.Vol; + if (dayData.Amount!=null) newData.Amount+=dayData.Amount; + newData.Position=dayData.Position; + newData.FlowCapital=dayData.FlowCapital; + newData.FClose=dayData.FClose; + } + } + } + return result; + } + + //周期数据 1=周 2=月 3=年 + this.GetPeriodData=function(period) + { + //外部自定义周期计算函数 + var itemCallback=g_DataPlus.GetPeriodCallback(period); + if (itemCallback) return itemCallback.Callback(period,this.Data,this); + + if (MARKET_SUFFIX_NAME.IsBIT(this.Symbol)) + { + if (period==5 || period==6 || period==7 || period==8 || (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)) //分钟K线 + { + var periodDataCount = 5; + if (period == 5) + periodDataCount = 5; + else if (period == 6) + periodDataCount = 15; + else if (period == 7) + periodDataCount = 30; + else if (period == 8) + periodDataCount = 60; + else if (period==11) + periodDataCount = 120; + else if (period==12) + periodDataCount = 240; + else if (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) + periodDataCount=period-CUSTOM_MINUTE_PERIOD_START; + + return this.GetMinuteCustomPeriodData(periodDataCount); + } + } + + //if (period==1 || period==2 || period==3 || period==9 || period==21 || period==22 || (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END)) + if (ChartData.IsDayPeriod(period,false)) return this.GetDayPeriodData(period); + + //if (period==5 || period==6 || period==7 || period==8 || period==11 || period==12 || (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)) return this.GetMinutePeriodData(period); + if (ChartData.IsMinutePeriod(period,false)) return this.GetMinutePeriodData(period); + } + + //复权 0 不复权 1 前复权 2 后复权 + this.GetRightData=function(right, option) + { + var result=[]; + if (this.Data.length<=0) return result; + + if (option && option.AlgorithmType==1) //使用复权因子计算 + { + for(var i=0;i=0; --index) + { + if (yClose!=this.Data[index].Close) break; + result[index]=HistoryData.Copy(this.Data[index]); + yClose=this.Data[index].YClose; + } + + for(; index>=0; --index) + { + var value=this.Data[index].Close; + if(yClose!=value && value!=0) + seed *= yClose/value; + + result[index]=HistoryData.CopyRight(this.Data[index],seed); + + yClose=this.Data[index].YClose; + } + } + else if (right==2) + { + var index=0; + var seed=1; + var close=this.Data[index].Close; + result[index]=HistoryData.Copy(this.Data[index]); + + for(++index;index=overlayData.length) + { + result[i]=new HistoryData(); + result[i].Date=date; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + result[i]=new HistoryData(); + result[i].Date=overlayData[j].Date; + result[i].YClose=overlayData[j].YClose; + result[i].Open=overlayData[j].Open; + result[i].High=overlayData[j].High; + result[i].Low=overlayData[j].Low; + result[i].Close=overlayData[j].Close; + result[i].Vol=overlayData[j].Vol; + result[i].Amount=overlayData[j].Amount; + + //涨跌家数数据 + result[i].Stop=overlayData[j].Stop; + result[i].Up=overlayData[j].Up; + result[i].Down=overlayData[j].Down; + result[i].Unchanged=overlayData[j].Unchanged; + + ++j; + ++i; + } + else if (overlayDate=overlayData.length) + { + result[i]=new HistoryData(); + result[i].Date=date; + result[i].Time=time; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + var overlayTime=overlayData[j].Time; + + if (overlayDate==date && overlayTime==time) + { + result[i]=new HistoryData(); + result[i].Date=overlayData[j].Date; + result[i].Time=overlayData[j].Time; + result[i].YClose=overlayData[j].YClose; + result[i].Open=overlayData[j].Open; + result[i].High=overlayData[j].High; + result[i].Low=overlayData[j].Low; + result[i].Close=overlayData[j].Close; + result[i].Vol=overlayData[j].Vol; + result[i].Amount=overlayData[j].Amount; + + //涨跌家数数据 + result[i].Stop=overlayData[j].Stop; + result[i].Up=overlayData[j].Up; + result[i].Down=overlayData[j].Down; + result[i].Unchanged=overlayData[j].Unchanged; + + ++j; + ++i; + } + else if (overlayDate=overlayData.length) + { + result[i]=null; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + var item=new SingleData(); + item.Date=overlayData[j].Date; + item.Value=overlayData[j].Value; + result[i]=item; + ++j; + ++i; + } + else if (overlayDate=overlayData.length) + { + result[i]=new SingleData(); + result[i].Date=date; + result[i].Value=emptyValue; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + var item=new SingleData(); + item.Date=overlayData[j].Date; + item.Value=overlayData[j].Value; + result[i]=item; + ++j; + ++i; + } + else if (overlayDate=data.length) break; + + var dateItem=data[j]; + + if (dateItem.Date==date) + { + dateItem.Index=i; + ++j; + } + else if (dateItem.Date=data.length) break; + + var dateTimeItem=data[j]; + + if (dateTimeItem.Date==date && dateTimeItem.Time==time) + { + dateTimeItem.Index=i; + ++j; + } + else if (dateTimeItem.Date=overlayData.length) + { + result[i]=null; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + var overlayTime=overlayData[j].Time; + const overlayItem=overlayData[j]; + + if (overlayDate==date && overlayTime==time) + { + var item=new SingleData(); + item.Date=overlayItem.Date; + item.Time=overlayItem.Time; + item.Value=overlayItem.Value; + result[i]=item; + ++j; + ++i; + } + else if (overlayDatefirstItem.Date || (date==firstItem.Date && time>=firstItem.Time)) + { + break; + } + } + + for(var j=0;idate) + { + var item=new SingleData(); + item.Date=date; + item.Value=nullValue; + result[i]=item; + ++i; + continue; + } + } + + if (j+1date || (tradeData[j].Date==date && tradeData[j].Time>time)) + { + var item=new SingleData(); + item.Date=date; + item.Time=time; + item.Value=nullValue; + result[i]=item; + ++i; + continue; + } + } + + if (j+1=overlayData.length) + { + result[i]=null; + ++i; + continue;; + } + + var overlayDate=overlayData[j].Date; + + if (overlayDate==date) + { + var item=new SingleData(); + item.Date=overlayData[j].Date; + item.Value=overlayData[j].Value; + item.Text=overlayData[j].Text; + result[i]=item; + ++j; + ++i; + } + else if (preDate!=null && preDateoverlayDate) + { + var item=new SingleData(); + item.Date=date; + item.OverlayDate=overlayData[j].Date; + item.Value=overlayData[j].Value; + item.Text=overlayData[j].Text; + result[i]=item; + ++j; + ++i; + } + else if (overlayDate=2) + { + isNewData=true; + weekCount=0; + } + startDate=fridayDate; + } + break; + case 2: //月线 + if (parseInt(dayData.Date/100)!=parseInt(startDate/100)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 3: //年线 + if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000)) + { + isNewData=true; + startDate=dayData.Date; + } + break; + case 9: //季线 + var now=ChartData.GetQuarter(dayData.Date); + now=parseInt(dayData.Date/10000)*10+now; + var last=ChartData.GetQuarter(startDate); + last=parseInt(startDate/10000)*10+last; + if (now!=last) + { + isNewData=true; + startDate=dayData.Date; + } + break; + } + + if (isNewData) + { + newData=new SingleData(); + newData.Date=dayData.Date; + newData.Value=dayData.Value; + result.push(newData); + } + else + { + if (newData==null) continue; + if (dayData.Value==null || isNaN(dayData.Value)) continue; + if (newData.Value==null || isNaN(newData.Value)) newData.Value=dayData.Value; + } + } + + return result; + } + + /* + 分钟数据方法 + this.GetClose() 每分钟价格 + this.GetVol() 每分钟成交量 + */ + + //分钟均线 + this.GetMinuteAvPrice=function() + { + var result=new Array(); + for(var i in this.Data) + { + result[i]=this.Data[i].AvPrice; + } + + return result; + } + + this.MergeMinuteData=function(data) //合并数据 + { + var sourceFirstItem=this.Data[0]; + var firstItemID=0; + var firstItem=null; + for(var i=0;isourceFirstItem.Date) + { + firstItemID=i; + firstItem=item; + break; + } + + if (item.Date==sourceFirstItem.Date && item.Time>=sourceFirstItem.Time) + { + firstItemID=i; + firstItem=item; + break; + } + } + + if (firstItem==null) return false; + + var index=null; + var bFind=false; //第1个数据是否完全匹配 + for(var i=this.Data.length-1; i>=0;--i) + { + var date=this.Data[i].Date; + var time=this.Data[i].Time; + + if (firstItem.Date>date || (firstItem.Date==date && firstItem.Time>=time) ) + { + index=i; + if (firstItem.Date==date && firstItem.Time==time) bFind=true; + break; + } + } + + if (index==null) return false; + + var j=index; //原始数据插入位置 + var i=firstItemID; //合并数据起始位置 + if (bFind==true) //第1个数据匹配,覆盖 + { + var item=data[i]; + if (j-1>0 && !item.YClose) item.YClose=this.Data[j-1].Close; //前收盘如果没有就是上一记录的收盘 + var newItem=HistoryData.Copy(item); + this.Data[j]=newItem; + ++j; + ++i; + } + else //从下一个数据开始插入 + { + ++j; + } + + for(;i=this.Data.length-1) + { + if (j-1>0 && !item.YClose) item.YClose=this.Data[j-1].YClose; //前收盘如果没有就是上一记录的收盘 + var newItem=HistoryData.Copy(item); + this.Data[j]=newItem; + ++j; + ++i; + } + else + { + var oldItem=this.Data[j]; + if (oldItem.Date==item.Date && oldItem.Time==item.Time) //更新数据 + { + HistoryData.CopyTo(oldItem,item); + ++j; + ++i; + } + else + { + ++j; + } + } + } + + //JSConsole.Chart.Log('[ChartData::MergeMinuteData] ', this.Data, data); + + return true; + } + + //跨周期转化 + this.ConverPeriod=function(data, curPeriod, changePeriod) //把数据用data, 日期时间不变, curPeriod=当前周期 changePeriod=需要转换周期 + { + var result=[]; + var tempItem=null; + var isMinute=ChartData.IsMinutePeriod(curPeriod,true); + var isMinute2=ChartData.IsMinutePeriod(changePeriod,true); + var isMimToMin=isMinute && isMinute2; + var isMinToDay=isMinute && !isMinute2; + var isDayToDay=!isMinute && !isMinute2; + + for(var i=0,j=0; iitem.Date) || (periodItem.Date==item.Date && periodItem.Time>=item.Time) ) + { + tempItem=periodItem; + break; + } + } + else if (isMinToDay || isDayToDay) //分钟 => 日线, 日线 => 日线 + { + if (periodItem.Date>=item.Date) + { + tempItem=periodItem; + break; + } + } + } + + var newItem=null; + if (tempItem) + { + newItem=HistoryData.Copy(tempItem); + newItem.PDate=tempItem.Date; + newItem.PTime=tempItem.Time; + } + else newItem=new HistoryData(); + + newItem.Date=item.Date; + if (isMimToMin || isMinToDay) newItem.Time=item.Time; + result.push(newItem); + } + + JSConsole.Chart.Log('[ChartData::ConverPeriod] result', result); + return result; + } + + this.GetRef=function(n) + { + let result=[]; + + for(var i=0 ;i 日线(原始) 日线(拟合 => 分钟(原始) + { + if (item.Date>=firstItem.Date) + { + indexStart = i; + break; + } + } + else if (isMinutePeriod[0] && isMinutePeriod[1]) //分钟(拟合 => 分钟(原始) + { + if (item.Date>firstItem.Date) + { + indexStart = i; + break; + } + + if (item.Date == firstItem.Date && item.Time >= firstItem.Time ) + { + indexStart = i; + break; + } + } + } + + for(var i=0, j=indexStart; i=indexCount) + { + var fitItem={ Date:item.Date, Time:item.Time, Index:-1 }; + aryFixDataID[i]=fitItem; + ++i; + continue; + } + + var destItem=ChartData.GetKLineDataTime(kLineData[j]); + if ( (isDayPeriod[0] && isDayPeriod[1]) || (isMinutePeriod[0] && isDayPeriod[1]) ) //日线(拟合) => 日线(原始) 日线(拟合 => 分钟(原始) + { + if (destItem.Date == item.Date) + { + var fitItem={ Date:item.Date, Time:item.Time, Index:j, Data2:destItem.Date, Time2:destItem.Time }; + aryFixDataID[i]=fitItem; + ++i; + } + else + { + if (j+1item.Date ) + { + var fitItem={ Date:item.Date, Time:item.Time, Index:j+1, Data2:nextDestItem.Date, Time2:nextDestItem.Time }; + aryFixDataID[i]=fitItem; + ++i; + } + else if (nextDestItem.Date <= item.Date ) + { + ++j; + } + else + { + var fitItem={ Date:item.Date, Time:item.Time, Index:-1 }; + aryFixDataID[i]=fitItem; + ++i; + } + } + else + { + ++j; + } + } + } + else if (isMinutePeriod[0] && isMinutePeriod[1]) //分钟(拟合 => 分钟(原始) + { + if (destItem.Date == item.Date && destItem.Time == item.Time) + { + var fitItem={ Date:item.Date, Time:item.Time, Index:j, Data2:destItem.Date, Time2:destItem.Time }; + aryFixDataID[i]=fitItem; + ++i; + } + else + { + if (j+1item.Date) || + (destItem.Date == item.Date && destItem.Time < item.Time && nextDestItem.Date == item.Date && nextDestItem.Time > item.Time) || + (destItem.Date == item.Date && destItem.Time < item.Time && nextDestItem.Date > item.Date) || + (destItem.Date < item.Date && nextDestItem.Date == item.Date && nextDestItem.Time > item.Time) ) + { + var fitItem={ Date:item.Date, Time:item.Time, Index:j+1, Data2:nextDestItem.Date, Time2:nextDestItem.Time }; + aryFixDataID[i]=fitItem; + ++i; + } + else if (nextDestItem.Date < item.Date || (nextDestItem.Date == item.Date && nextDestItem.Time <= item.Time) ) + { + ++j; + } + else + { + var fitItem={ Date:item.Date, Time:item.Time, Index:-1 }; + aryFixDataID[i]=fitItem; + ++i; + } + } + else + { + ++j; + } + } + } + } + + //拟合数据 + var result=[]; + for(var i in outVar) + { + var item=outVar[i]; + if (Array.isArray(item.Data)) + { + var data=[]; + result[i]={ Data:data, Name:item.Name } ; + for(var j=0;j=0 && dataItem.Index=0) continue; + + if (IFrameSplitOperator.IsNumber(findItem.Time)) + { + if (findItem.Date==item.Date && findItem.Time==item.Time) + { + findItem.Index=parseInt(i); + ++findCount; + break; + } + } + else + { + if (findItem.Date==item.Date) + { + findItem.Index=parseInt(i); + ++findCount; + break; + } + } + + if (findCount>=aryDateTime.length) break; + } + } + } + + //深拷贝数据 + this.CloneData=function(className) + { + var result=[]; + if (className=="HistoryData") + { + for(var i in this.Data) + { + var item=this.Data[i]; + var newItem=HistoryData.Copy(item); + result.push(newItem); + } + } + return result; + } + + this.GetAPIDataIndex=function(date,time) + { + var result=[]; + if (date && time) + { + var count=this.Data.length; //原始K线数据 + var indexCount=date.length; //拟合数据 + var firstItem=ChartData.GetKLineDataTime(this.Data[0]); + var indexStart=indexCount; //拟合数据的起始位置 + + var indexStart=indexCount; //拟合数据的起始位置 + for(var i=0;ifirstItem.Date || (itemDate == firstItem.Date && itemTime >= firstItem.Time)) + { + indexStart = i; + break; + } + } + + for(var i=0, j=indexStart; i=indexCount) + { + var fitItem={ KDate:item.Date, KTime:item.Time, KIndex:i, Index:-1 }; + result[i]=fitItem; + ++i; + continue; + } + + var itemDate=date[j]; + var itemTime=time[j]; + if (itemDate == item.Date && itemTime == item.Time) + { + var fitItem={ KDate:item.Date, KTime:item.Time, KIndex:i, Index:j, Data:itemDate, Time:itemTime }; + result[i]=fitItem; + ++i; + } + else + { + if (j+1item.Date) || + (itemDate == item.Date && itemTime < item.Time && nextItemDate == item.Date && nextItemTime > item.Time) || + (itemDate == item.Date && itemTime < item.Time && nextItemDate > item.Date) || + (itemDate < item.Date && nextItemDate == item.Date && nextItemTime > item.Time) ) + { + var fitItem={ KDate:item.Date, KTime:item.Time, KIndex:i, Index:j, Data:itemDate, Time:itemTime }; + result[i]=fitItem; + ++i; + } + else if (nextItemDate < item.Date || (nextItemDate == item.Date && nextItemTime <= item.Time) ) + { + ++j; + } + else + { + var fitItem={ KDate:item.Date, KTime:item.Time, KIndex:i, Index:-1 }; + result[i]=fitItem; + ++i; + } + } + else + { + ++j; + } + } + } + } + else if (date) + { + var count=this.Data.length; //原始K线数据 + var indexCount=date.length; //拟合数据 + var firstItem=ChartData.GetKLineDataTime(this.Data[0]); + + var indexStart=indexCount; //拟合数据的起始位置 + for(var i=0;i=firstItem.Date) + { + indexStart = i; + break; + } + } + + for(var i=0, j=indexStart; i=indexCount) + { + var fitItem={ KDate:item.Date, KIndex:i, Index:-1 }; + result[i]=fitItem; + ++i; + continue; + } + + var destDate=date[j]; + if (destDate == item.Date) + { + var fitItem={ KDate:item.Date, KIndex:i, Index:j, Data:destDate }; + result[i]=fitItem; + ++i; + } + else + { + if (j+1item.Date ) + { + var fitItem={ KDate:item.Date, KIndex:i, Index:j, Data:destDate }; + result[i]=fitItem; + ++i; + } + else if (nextDestDate <= item.Date ) + { + ++j; + } + else + { + var fitItem={ KDate:item.Date, KIndex:i, Index:-1 }; + result[i]=fitItem; + ++i; + } + } + else + { + ++j; + } + } + } + } + return result; + } + + //K线数据拟合 + this.FixKData=function(aryKData, period) + { + if (ChartData.IsDayPeriod(period,true)) + { + return this.FixKData_Day(aryKData); + } + else if (ChartData.IsMinutePeriod(period,true)) + { + return this.FixKData_Minute(aryKData); + } + + return null; + } + + this.FixKData_Day=function(aryKData) + { + var result=[]; + var nOverlayDataCount=aryKData.length; + for(var i=0,j=0; ikItem.Date) + { + ++i; + continue; + } + } + + if (j+1kDateTime) + { + ++i; + continue; + } + } + + if (j+10) + { + var prevTimestamp=(24*60*60*1000)*(7-day); + timestamp+=prevTimestamp; + } + + date.setTime(timestamp); + var sundayDate= date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate(); + var week=date.getDay(); + return sundayDate; +} + +ChartData.GetQuarter=function(value) +{ + var month=parseInt(value%10000/100); + if (month==1 || month==2 || month==3) return 1; + else if (month==4 || month==5 || month==6) return 2; + else if (month==7 || month==8 || month==9) return 3; + else if (month==10 || month==11 || month==12) return 4; + else return 0; +} + +ChartData.GetHalfYear=function(value) +{ + var year=parseInt(value/10000); + var day=value%10000; + if (day<=630) return year*10000+630; + return year*10000+1231; +} + +//是否是日线周期 0=日线 1=周线 2=月线 3=年线 9=季线 21=双周 22=半年 [40001-50000) 自定义日线 (isIncludeBase 是否包含基础日线周期) +var CUSTOM_DAY_PERIOD_START=40000, CUSTOM_DAY_PERIOD_END=49999; +ChartData.IsDayPeriod=function(period, isIncludeBase) +{ + if (period==1 || period==2 || period==3 || period==9 || period==21 || period==22) return true; + if (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END) return true; + if (period==0 && isIncludeBase==true) return true; + + return false; +} + +//是否是分钟周期 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 11=120分钟 12=240分钟 [20001-30000) 自定义分钟 (isIncludeBase 是否包含基础1分钟周期) +var CUSTOM_MINUTE_PERIOD_START=20000, CUSTOM_MINUTE_PERIOD_END=29999; +ChartData.IsMinutePeriod=function(period,isIncludeBase) +{ + if (period==5 || period==6 || period==7 || period==8 ||period==11 || period==12) return true; + if (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) return true; + if (period==4 && isIncludeBase==true) return true; + + return false; +} + +//是否是秒周期 [30001-32000) +var CUSTOM_SECOND_PERIOD_START=30000, CUSTOM_SECOND_PERIOD_END=32000; +ChartData.IsSecondPeriod=function(period) +{ + if (period>CUSTOM_SECOND_PERIOD_START && period<=CUSTOM_SECOND_PERIOD_END) return true; + return false; +} + + +//是否是分笔图 10=分笔 +ChartData.IsTickPeriod=function(period) +{ + return period==10; +} + +//获取周期名字 +ChartData.GetPeriodName=function(period) +{ + var mapName=new Map( + [ + [0, '日线'],[1, '周线'],[2, '月线'],[3, '年线'],[9, '季线'], [21,'双周'],[22,"半年"], + [4, '1分'], [5, '5分'], [6, '15分'],[7, '30分'],[8, '60分'],[11, '2小时'],[12, '4小时'], + [10, '分笔'] + ]); + + if (mapName.has(period)) return mapName.get(period); + + return ''; +} + + +function TooltipData() //提示信息 +{ + this.ChartPaint; + this.Data; + this.Type=0; +} + +function Rect(x,y,width,height) +{ + this.X=x, + this.Y=y; + this.Width=width; + this.Height=height; +} + +//图新画法接口类 +function IChartPainting() +{ + this.Canvas; //画布 + this.ChartBorder; //边框信息 + this.ChartFrame; //框架画法 + this.Name; //名称 + this.ClassName='IChartPainting'; //类名 + this.Data=new ChartData(); //数据区 + + this.NotSupportMessage=null; + this.MessageFont=g_JSChartResource.Index.NotSupport.Font; + this.MessageColor=g_JSChartResource.Index.NotSupport.TextColor; + this.IsDrawFirst=false; + this.IsShow=true; + this.GetEventCallback; + + this.Draw=function() + { + + } + + this.GetBorder=function() + { + if (this.ChartFrame.IsHScreen) return this.ChartBorder.GetHScreenBorder(); + return this.ChartBorder.GetBorder(); + } + + this.ClipClient=function(isHScreen) //裁剪客户端 + { + if (isHScreen==true) + { + var left=this.ChartBorder.GetLeftEx(); + var right=this.ChartBorder.GetRightEx(); + var top=this.ChartBorder.GetTop(); + var bottom=this.ChartBorder.GetBottom(); + } + else + { + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + } + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,(right-left),(bottom-top)); + //this.Canvas.stroke(); //调试用 + this.Canvas.clip(); + } + + this.GetYFromData=function(value,isLimit) + { + return this.ChartFrame.GetYFromData(value,isLimit); + } + + this.IsMinuteFrame=function() + { + var isMinute=(this.ChartFrame.ClassName=="MinuteFrame" || this.ChartFrame.ClassName=="MinuteHScreenFrame" || + this.ChartFrame.ClassName=="OverlayMinuteFrame" || this.ChartFrame.ClassName=="OverlayMinuteHScreenFrame" ); + + return isMinute + } + + this.DrawNotSupportmessage=function() + { + this.Canvas.font=this.MessageFont; + this.Canvas.fillStyle=this.MessageColor; + + var left=this.ChartBorder.GetLeft(); + var width=this.ChartBorder.GetWidth(); + var top=this.ChartBorder.GetTopEx(); + var height=this.ChartBorder.GetHeightEx(); + + var x=left+width/2; + var y=top+height/2; + + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillText(this.NotSupportMessage,x,y); + } + + this.GetTooltipData=function(x,y,tooltip) + { + return false; + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + + if(!this.Data || !this.Data.Data) return range; + + for(var i=this.Data.DataOffset,j=0;ivalue) range.Min=value; + } + + return range; + } + + this.GetDynamicFont=function(dataWidth, distanceWidth, maxSize, minSize, zoom, fontname) //根据宽度自动获取对应字体 + { + var pixelTatio = GetDevicePixelRatio(); + maxSize*=pixelTatio; + minSize*=pixelTatio; + + if (maxSize==minSize) //固定大小 + { + var font=`${maxSize.toFixed(0)}px ${fontname}` ; + return font; + } + + var fontSize=(dataWidth+distanceWidth); + if (zoom) + { + if (zoom.Type==0) + { + if (zoom.Value>0) fontSize=(dataWidth*zoom.Value); + } + else if (zoom.Type==1) + { + if (zoom.Value>0) fontSize=(dataWidth+distanceWidth)*zoom.Value; + } + else if (zoom.Type==2) + { + if (IFrameSplitOperator.IsNumber(zoom.Value)) + fontSize=(dataWidth+distanceWidth) + (2*zoom.Value)*pixelTatio; + } + } + + if (fontSizemaxSize) fontSize=maxSize; + + var font=`${fontSize.toFixed(0)}px ${fontname}` ; + + /* + if (dataWidth < 5) font =4*pixelTatio + 'px Arial'; //字体根据数据宽度动态调整 + else if (dataWidth < 7) font = 6*pixelTatio +'px Arial'; + else if (dataWidth < 9) font = 8*pixelTatio +'px Arial'; + else if (dataWidth < 11) font =10*pixelTatio +'px Arial'; + else if (dataWidth < 13) font =12*pixelTatio +'px Arial'; + else if (dataWidth < 15) font =14*pixelTatio + 'px Arial'; + else font =16*pixelTatio + 'px Arial'; + */ + + return font; + } + + this.GetLockRect=function() + { + return this.ChartFrame.GetLockRect(); + } + + this.SetFillStyle=function(color, x0, y0, x1, y1) + { + if (Array.isArray(color)) + { + let gradient = this.Canvas.createLinearGradient(x0, y0, x1, y1); + var offset=1/(color.length); + for(var i in color) + { + gradient.addColorStop(i*offset, color[i]); + } + this.Canvas.fillStyle=gradient; + } + else + { + this.Canvas.fillStyle=color; + } + } + + this.GetDynamicIconSize=function(dataWidth, distanceWidth, maxSize, minSize, zoom) + { + var pixelTatio = GetDevicePixelRatio(); + maxSize*=pixelTatio; + minSize*=pixelTatio; + + if (maxSize==minSize) return maxSize; + + var iconSize=(dataWidth+distanceWidth)-2*pixelTatio; + + if (zoom) + { + if (zoom.Type==0) + { + if (zoom.Value>0) iconSize=(dataWidth*zoom.Value); + } + else if (zoom.Type==1) + { + if (zoom.Value>0) iconSize=(dataWidth+distanceWidth)*zoom.Value; + } + else if (zoom.Type==2) + { + if (IFrameSplitOperator.IsNumber(zoom.Value)) + iconSize=(dataWidth+distanceWidth) + (2*zoom.Value)*pixelTatio; + } + } + + if (iconSizemaxSize) iconSize=maxSize; + + return iconSize; + } + +} + + +//缩放因子 +/* +var ZOOM_SEED= +[ + [49,10], [46,9], [43,8], + [41,7.5], [39,7], [37,6], + [31,5.5], [27,5], [23,4.5], + [21,4], [18,3.5], [16,3], + [13,2.5], [11,2], [8,1.5], + [6,1], [3,0.6], [2.2,0.5], + //太多了卡, + //[1.1,0.3], + //[0.9,0.2], [0.7,0.15], + //[0.6,0.12], [0.5,0.1], [0.4,0.08], + //[0.3,0.06], [0.2,0.04], [0.1,0.02] +]; +*/ + + +var ZOOM_SEED= //0=柱子宽度 1=间距 +[ + [48,10], [44,10], + [40,9], [36,9], + [32,8], [28,8], + [24,7], [20,7], + [18,6], [16,6], + [14,5], [12,5], + [8,4], [6,4], [4,4], + + [3,3], + [3,1], [2,1], [1,1], [1,0], + + //[0.5,0],[0.4,0],[0.3,0],[0.2,0],[0.1,0] +]; + + +//K线画法 支持横屏 +function ChartKLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartKLine'; //类名 + this.Symbol; //股票代码 + this.DrawType=0; // 0=实心K线柱子 1=收盘价线 2=美国线 3=空心K线柱子 4=收盘价面积图 + this.CloseLineColor=g_JSChartResource.CloseLineColor; + this.CloseLineAreaColor=g_JSChartResource.CloseLineAreaColor; + this.CloseLineWidth=g_JSChartResource.CloseLineWidth; + this.UpColor=g_JSChartResource.UpBarColor; + this.DownColor=g_JSChartResource.DownBarColor; + this.UnchagneColor=g_JSChartResource.UnchagneBarColor; //平盘 + this.ColorData; //五彩K线颜色 >0:g_JSChartResource.UpBarColor 其他:g_JSChartResource.DownBarColor + this.TradeData; //交易系统 包含买卖数据{Buy:, Sell:, Name:指标名称 } + this.TradeIcon=g_JSChartResource.KLine.TradeIcon; + this.TooltipRect=[]; //2位数组 0 数据序号 1 区域 + this.InfoTooltipRect=[]; //2维数组 0 数据, 1 区域 + this.TradeIconTooltipRect=[]; //2维数组 0 数据, 1 区域 + + this.IsShowMaxMinPrice=true; //是否显示最大最小值 + this.IsShowKTooltip=true; //是否显示K线tooltip + this.TextFont=g_JSChartResource.KLine.MaxMin.Font; + this.TextColor=g_JSChartResource.KLine.MaxMin.Color; + + this.InfoData; //信息地雷 key=日期 value=信息数据 + this.InfoPosition=0; // 0=K线上 1 底部 + + this.PtMax; //最大值的位置 + this.PtMin; //最小值的位置 + + this.TickSymbol='╳'; //分笔显示的图标 + this.TickFontName='arial'; + this.Period; //周期 + this.ShowRange={ }; //K线显示范围 { Start:, End:, DataCount:, ShowCount: } + this.CustomKLine; //自定义K线, key=date*1000000+time, key={ Color:, DrawType: } + + /* + this.CustomKLine=new Map([ + [20210415*1000000, { Color:'rgb(255,0,255)', DrawType:3, BGColor:"rgba(135,206,250,0.5)" }], + [20210412*1000000, { DrawType:3 }], + [20210108*1000000, { DrawType:3,BGColor:"rgba(135,206,250,0.5)" }], + [20210122*1000000, {Color:'rgb(122,135,155)'}] + ]); + */ + + this.ReloadResource=function(resource) + { + this.TextFont=g_JSChartResource.KLine.MaxMin.Font; + this.TextColor=g_JSChartResource.KLine.MaxMin.Color; + + this.CloseLineColor=g_JSChartResource.CloseLineColor; + this.CloseLineAreaColor=g_JSChartResource.CloseLineAreaColor; + this.CloseLineWidth=g_JSChartResource.CloseLineWidth; + + this.UpColor=g_JSChartResource.UpBarColor; + this.DownColor=g_JSChartResource.DownBarColor; + this.UnchagneColor=g_JSChartResource.UnchagneBarColor; //平盘 + } + + this.ClearCustomKLine=function() + { + this.CustomKLine=null; + } + + this.GetCustomKLine=function(kItem) + { + if (!this.CustomKLine) return null; + if (!kItem) return null; + + var key=kItem.Date*1000000; + if (IFrameSplitOperator.IsNumber(kItem.Time)) key+=kItem.Time; + if (!this.CustomKLine.has(key)) return null; + + var value=this.CustomKLine.get(key); + return value; + } + + this.DrawAKLine=function() //美国线 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.BottomEx; + } + else + { + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + } + + var upColor=this.UpColor; + var downColor=this.DownColor; + var unchagneColor=this.UnchagneColor; + + var ptMax={X:null,Y:null,Value:null,Align:'left'}; + var ptMin={X:null,Y:null,Value:null,Align:'left'}; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low,false); + var yHigh=this.GetYFromData(data.High,false); + var yOpen=this.GetYFromData(data.Open,false); + var yClose=this.GetYFromData(data.Close,false); + + if (ptMax.Value==null || ptMax.Valuedata.Low) //求最小值 + { + ptMin.X=x; + ptMin.Y=yLow; + ptMin.Value=data.Low; + ptMin.Align=jdata.Close) this.Canvas.strokeStyle=this.DownColor; //阳线 + else this.Canvas.strokeStyle=this.UnchagneColor; //平线 + + if (this.ColorData) ///五彩K线颜色设置 + { + if (i0?this.UpColor:this.DownColor); + else + upColor=downColor=unchagneColor=this.DownColor; + } + + this.Canvas.beginPath(); //最高-最低 + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + + this.Canvas.stroke(); + + if (dataWidth>=4) + { + this.Canvas.beginPath(); //开盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yOpen),left); + this.Canvas.lineTo(ToFixedPoint(yOpen),x); + } + else + { + this.Canvas.moveTo(left,ToFixedPoint(yOpen)); + this.Canvas.lineTo(x,ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + + this.Canvas.beginPath(); //收盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yClose),right); + this.Canvas.lineTo(ToFixedPoint(yClose),x); + } + else + { + this.Canvas.moveTo(right,ToFixedPoint(yClose)); + this.Canvas.lineTo(x,ToFixedPoint(yClose)); + } + this.Canvas.stroke(); + } + + if(this.Data.DataType==0) + { + var infoItem={Xleft:left,XRight:right, YMax:yHigh, XCenter:x, YMin:yLow, DayData:data, Index:j}; + this.DrawInfo(infoItem); + } + } + + this.PtMax=ptMax; + this.PtMin=ptMin; + } + + this.DrawCloseArea=function() //收盘价面积图 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.BottomEx; + } + else + { + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + } + + + var bFirstPoint=true; + var firstPoint=null; + + this.Canvas.beginPath(); + this.Canvas.strokeStyle=this.CloseLineColor; + if (IFrameSplitOperator.IsNumber(this.CloseLineWidth)) this.Canvas.lineWidth=this.CloseLineWidth; + var ptLast=null; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yClose=this.GetYFromData(data.Close,false); + + if (bFirstPoint) + { + if (isHScreen) + { + this.Canvas.moveTo(yClose,x); + firstPoint={ X:yClose, Y:x }; + } + else + { + this.Canvas.moveTo(x,yClose); + firstPoint={ X:x, Y:yClose }; + } + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(yClose,x); + else this.Canvas.lineTo(x,yClose); + } + + if (i==this.Data.Data.length-1) + { + ptLast={ X:x, Y:yClose, KItem:data }; + } + } + + if (this.GetEventCallback) //通知外部绘制最后一个点 + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DRAW_KLINE_LAST_POINT); + if (event) + { + if (ptLast) var data={ LastPoint:{ X:ptLast.X, Y:ptLast.Y }, KItem:ptLast.KItem }; + else var data={ LastPoint:null, KItem:null }; + event.Callback(event,data,this); + } + } + + if (bFirstPoint) return; + + this.Canvas.stroke(); + if (isHScreen) + { + this.Canvas.lineTo(border.Left,x); + this.Canvas.lineTo(border.Left,firstPoint.Y); + } + else + { + this.Canvas.lineTo(x,border.Bottom); + this.Canvas.lineTo(firstPoint.X,border.Bottom); + } + this.Canvas.closePath(); + if (Array.isArray(this.CloseLineAreaColor)) + { + if (isHScreen) + { + let gradient = this.Canvas.createLinearGradient(this.ChartBorder.GetRightEx(),this.ChartBorder.GetTop(), this.ChartBorder.GetLeft(),this.ChartBorder.GetTop()); + gradient.addColorStop(0, this.CloseLineAreaColor[0]); + gradient.addColorStop(1, this.CloseLineAreaColor[1]); + this.Canvas.fillStyle=gradient; + } + else + { + let gradient = this.Canvas.createLinearGradient(firstPoint.X,this.ChartBorder.GetTopEx(), firstPoint.X,this.ChartBorder.GetBottom()); + gradient.addColorStop(0, this.CloseLineAreaColor[0]); + gradient.addColorStop(1, this.CloseLineAreaColor[1]); + this.Canvas.fillStyle=gradient; + } + } + else + { + this.Canvas.fillStyle=this.CloseLineAreaColor; + } + this.Canvas.fill(); + } + + this.DrawCloseLine=function() //收盘价线 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.BottomEx; + } + else + { + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + } + + var bFirstPoint=true; + this.Canvas.beginPath(); + this.Canvas.strokeStyle=this.CloseLineColor; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yClose=this.GetYFromData(data.Close,false); + + if (bFirstPoint) + { + if (isHScreen) this.Canvas.moveTo(yClose,x); + else this.Canvas.moveTo(x,yClose); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(yClose,x); + else this.Canvas.lineTo(x,yClose); + } + } + + if (bFirstPoint==false) this.Canvas.stroke(); + } + + this.DrawKBar=function() //蜡烛头 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + chartright=border.BottomEx; + } + + var ptMax={X:null,Y:null,Value:null,Align:'left'}; + var ptMin={X:null,Y:null,Value:null,Align:'left'}; + + var upColor=this.UpColor; + var downColor=this.DownColor; + var unchagneColor=this.UnchagneColor; + + this.ShowRange.Start=this.Data.DataOffset; + this.ShowRange.End=this.ShowRange.Start; + this.ShowRange.DataCount=0; + this.ShowRange.ShowCount=xPointCount; + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low, false); + var yHigh=this.GetYFromData(data.High, false); + var yOpen=this.GetYFromData(data.Open, false); + var yClose=this.GetYFromData(data.Close, false); + var y=yHigh; + + if (ptMax.Value==null || ptMax.Valuedata.Low) //求最小值 + { + ptMin.X=x; + ptMin.Y=yLow; + ptMin.Value=data.Low; + ptMin.Align=j0?this.UpColor:this.DownColor); + else + upColor=downColor=unchagneColor=this.DownColor; + } + + var kLineOption=this.GetCustomKLine(data); + + if (kLineOption) + { + var barColor=kLineOption.Color; + if (!barColor) + { + if (data.Opendata.Close) barColor=downColor; + else barColor=unchagneColor; + } + + var drawType=this.DrawType; + if (IFrameSplitOperator.IsNumber(kLineOption.DrawType)) drawType=kLineOption.DrawType; + + this.DrawKBar_Custom(data, dataWidth, barColor, drawType, kLineOption, x, y, left, right, yLow, yHigh, yOpen, yClose, border, isHScreen); + } + else if (data.Opendata.Close) //阴线 + { + this.DrawKBar_Down(data, dataWidth, downColor, this.DrawType, x, y, left, right, yLow, yHigh, yOpen, yClose, isHScreen); + } + else // 平线 + { + this.DrawKBar_Unchagne(data, dataWidth, unchagneColor, this.DrawType, x, y, left, right, yLow, yHigh, yOpen, yClose, isHScreen); + } + + if (this.IsShowKTooltip && !isHScreen) //添加tooltip区域 + { + var yTop=Math.min(yOpen,yClose); + var yBottom=Math.max(yOpen,yClose); + if (Math.abs(yOpen-yClose)<5) //高度太小了, 上下各+5px + { + yTop=Math.min(yHigh,yTop-5); + yBottom=Math.max(yLow,yBottom+5); + } + var rect=new Rect(left,yTop,dataWidth,yBottom-yTop); + //this.Canvas.fillStyle="rgb(0,0,100)"; + //this.Canvas.fillRect(rect.X,rect.Y,rect.Width,rect.Height); + this.TooltipRect.push([i,rect]); //[0]数据索引 [1]数据区域 + } + + if(this.Data.DataType==0) + { + var infoItem={Xleft:left,XRight:right, XCenter:x, YMax:yHigh, YMin:yLow, DayData:data, Index:j}; + this.DrawInfo(infoItem); + } + } + + this.PtMax=ptMax; + this.PtMin=ptMin; + } + + this.DrawKBar_Up=function(data, dataWidth, upColor, drawType, x, y, left, right, yLow, yHigh, yOpen, yClose, isHScreen) //阳线 + { + if (dataWidth>=4) + { + this.Canvas.strokeStyle=upColor; + if (data.High>data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(drawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(drawType==3?Math.min(yClose,yOpen):yClose)); + } + this.Canvas.stroke(); + y=yClose; + } + else + { + y=yClose; + } + + this.Canvas.fillStyle=upColor; + if (isHScreen) + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + } + } + } + else + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yOpen)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yOpen-y))); + } + } + } + + if (data.Open>data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(drawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(drawType==3?Math.max(yClose,yOpen):y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.strokeStyle=upColor; + this.Canvas.stroke(); + } + } + + this.DrawKBar_Down=function(data, dataWidth, downColor, drawType, x, y, left, right, yLow, yHigh, yOpen, yClose, isHScreen) //阴线 + { + if (dataWidth>=4) + { + this.Canvas.strokeStyle=downColor; + if (data.High>data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yOpen),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + y=yOpen; + } + else + { + y=yOpen + } + + this.Canvas.fillStyle=downColor; + if (isHScreen) + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yClose-y),ToFixedRect(dataWidth)); + } + else + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yClose)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yClose-y))); + } + + if (data.Open>data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.strokeStyle=downColor; + this.Canvas.stroke(); + } + } + + this.DrawKBar_Unchagne=function(data, dataWidth, unchagneColor, drawType, x, y, left, right, yLow, yHigh, yOpen, yClose, isHScreen) //平线 + { + if (dataWidth>=4) + { + this.Canvas.strokeStyle=unchagneColor; + this.Canvas.beginPath(); + if (data.High>data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yOpen,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yOpen); + } + y=yOpen; + } + else + { + y=yOpen; + } + + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(left)); + this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(right)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(left),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(y)); + } + + if (data.Open>data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + } + + this.Canvas.stroke(); + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.strokeStyle=unchagneColor; + this.Canvas.stroke(); + } + } + + this.DrawKBar_Custom=function(data, dataWidth, barColor, drawType, option, x, y, left, right, yLow, yHigh, yOpen, yClose, border, isHScreen) + { + if (option.BGColor) //画背景色 + { + this.Canvas.fillStyle=option.BGColor; + var distanceWidth=this.ChartFrame.DistanceWidth; + if (isHScreen) + { + var yLeft=left-(distanceWidth/2); + var yRight=right+(distanceWidth/2); + var xTop=border.RightEx; + var xBottom=border.LeftEx; + + this.Canvas.fillRect(ToFixedRect(xBottom),ToFixedRect(yLeft),ToFixedRect(xTop-xBottom),ToFixedRect(yRight-yLeft)); + } + else + { + var xLeft=left-(distanceWidth/2); + var xRight=right+(distanceWidth/2); + var yTop=border.TopEx; + var yBottom=border.BottomEx; + + this.Canvas.fillRect(ToFixedRect(xLeft),ToFixedRect(yTop),ToFixedRect(xRight-xLeft),ToFixedRect(yBottom-yTop)); + } + } + + if (dataWidth>=4) + { + this.Canvas.strokeStyle=barColor; + if (data.High>data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(drawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(drawType==3?Math.min(yClose,yOpen):yClose)); + } + this.Canvas.stroke(); + y=yClose; + } + else + { + y=yClose; + } + + this.Canvas.fillStyle=barColor; + if (isHScreen) + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + } + } + } + else + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yOpen)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yOpen-y))); + } + } + } + + if (data.Open>data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(drawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(drawType==3?Math.max(yClose,yOpen):y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.strokeStyle=barColor; + this.Canvas.stroke(); + } + } + + this.DrawTrade=function() //交易系统 + { + if (!this.TradeData) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + chartright=this.ChartBorder.GetBottom(); + } + + var sellData=this.TradeData.Sell; + var buyData=this.TradeData.Buy; + var arrowWidth=dataWidth; + if (arrowWidth>10) arrowWidth=10; + for(var i=this.Data.DataOffset,j=0;i0; + if (buyData && i0; + if (!sell && !buy) continue; + + var left=xOffset; + var right=xOffset+dataWidth; + if (right>chartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low,false); + var yHigh=this.GetYFromData(data.High,false); + var yOpen=this.GetYFromData(data.Open,false); + var yClose=this.GetYFromData(data.Close,false); + var y=yHigh; + + if (buy) + { + this.Canvas.fillStyle=this.UpColor; + this.Canvas.strokeStyle=this.UnchagneColor; + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yLow-1,x); + this.Canvas.lineTo(yLow-arrowWidth-1,x-arrowWidth/2); + this.Canvas.lineTo(yLow-arrowWidth-1,x+arrowWidth/2); + } + else + { + this.Canvas.moveTo(x,yLow+1); + this.Canvas.lineTo(x-arrowWidth/2,yLow+arrowWidth+1); + this.Canvas.lineTo(x+arrowWidth/2,yLow+arrowWidth+1); + } + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + + if (sell) + { + this.Canvas.fillStyle=this.DownColor; + this.Canvas.strokeStyle=this.UnchagneColor; + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh+1,x); + this.Canvas.lineTo(yHigh+arrowWidth+1,x-arrowWidth/2); + this.Canvas.lineTo(yHigh+arrowWidth+1,x+arrowWidth/2); + } + else + { + this.Canvas.moveTo(x,yHigh-1); + this.Canvas.lineTo(x-arrowWidth/2,yHigh-arrowWidth-1); + this.Canvas.lineTo(x+arrowWidth/2,yHigh-arrowWidth-1); + } + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + } + } + + this.DrawTick=function() //分笔图 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + var fontSize=parseInt(dataWidth); + if (fontSize<=1) fontSize=2; + else if (fontSize>=18) fontSize=18; + var bFirstPoint=true; + var pixelRatio=GetDevicePixelRatio(); + var textSize=fontSize*pixelRatio; + this.Canvas.beginPath(); + this.Canvas.font=fontSize*pixelRatio+'px '+this.TickFontName; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yClose=this.ChartFrame.GetYFromData(data.Close,false); + + if (data.Flag===0) this.Canvas.fillStyle=this.UpColor; + else if (data.Flag==1) this.Canvas.fillStyle=this.DownColor; + else this.Canvas.fillStyle=this.UnchagneColor; + + this.Canvas.textAlign='center' + this.Canvas.textBaseline='middle'; + if (isHScreen) this.Canvas.fillText(this.TickSymbol,yClose,x); + else this.Canvas.fillText(this.TickSymbol,x,yClose); + + if (this.IsShowKTooltip && !isHScreen) //添加tooltip区域 + { + var rect=new Rect(x-textSize/2,yClose-textSize/2,textSize,textSize); + //this.Canvas.fillStyle="rgb(0,0,100)"; + //this.Canvas.fillRect(rect.X,rect.Y,rect.Width,rect.Height); + this.TooltipRect.push([i,rect]); //[0]数据索引 [1]数据区域 + } + } + } + + this.ClipTickClient=function(isHScreen) + { + var border=this.GetBorder(); + if (isHScreen==true) + { + var left=this.ChartBorder.GetLeftEx(); + var right=this.ChartBorder.GetRightEx(); + var top=this.ChartBorder.GetTop(); + var bottom=this.ChartBorder.GetBottom(); + } + else + { + var left=border.Left; + var right=border.Right; + var top=border.TopTitle; + var bottom=border.Bottom; + } + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,(right-left),(bottom-top)); + this.Canvas.clip(); + } + + this.Draw=function() + { + this.TooltipRect=[]; + this.InfoTooltipRect=[]; + this.TradeIconTooltipRect=[]; + this.PtMax={X:null,Y:null,Value:null,Align:'left'}; //清空最大 + this.PtMin={X:null,Y:null,Value:null,Align:'left'}; //清空最小 + + this.ChartFrame.ChartKLine={Max:null, Min:null }; //保存K线上 显示最大最小值坐标 + + if (!this.IsShow) return; + + if (ChartData.IsTickPeriod(this.Period)) //分笔图 + { + this.Canvas.save(); + + + if (this.DrawType==1) + { + this.ClipClient(this.ChartFrame.IsHScreen); + this.DrawCloseLine(); + } + else + { + this.ClipTickClient(this.ChartFrame.IsHScreen); + this.DrawTick(); + } + + + this.Canvas.restore(); + return; + } + + this.Canvas.save(); + this.ClipClient(this.ChartFrame.IsHScreen); + + if (this.DrawType==1) + { + this.DrawCloseLine(); + this.Canvas.restore(); + return; + } + else if (this.DrawType==2) + { + this.DrawAKLine(); + } + else if (this.DrawType==4) + { + this.DrawCloseArea(); + } + else + { + this.DrawKBar(); + } + + if (this.TradeIcon) this.DrawTradeIcon() + else this.DrawTrade(); + this.Canvas.restore(); + + if (this.IsShowMaxMinPrice) //标注最大值最小值 + { + if (this.ChartFrame.IsHScreen===true) this.HScreenDrawMaxMinPrice(this.PtMax,this.PtMin); + else this.DrawMaxMinPrice(this.PtMax,this.PtMin); + } + } + + this.DrawMaxMinPrice=function(ptMax,ptMin) + { + if (ptMax.X==null || ptMax.Y==null || ptMax.Value==null) return; + if (ptMin.X==null || ptMin.Y==null || ptMin.Value==null) return; + + var leftArrow=g_JSChartResource.KLine.MaxMin.LeftArrow; + var rightArrow=g_JSChartResource.KLine.MaxMin.RightArrow; + var highYOffset=g_JSChartResource.KLine.MaxMin.HighYOffset; + var lowYOffset=g_JSChartResource.KLine.MaxMin.LowYOffset; + + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + this.Canvas.font=this.TextFont; + this.Canvas.fillStyle=this.TextColor; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + + var ptTop=ptMax; + if (this.ChartFrame.CoordinateType==1) //反转坐标 + { + if (ptMax.YptMin.Y) ptTop=ptMin; + this.Canvas.textBaseline='bottom'; + } + + this.Canvas.textAlign=ptTop.Align; + var left=ptTop.Align=='left'?ptTop.X:ptTop.X; + if (IFrameSplitOperator.IsNumber(highYOffset)) ptTop.Y+=highYOffset; + var text=ptTop.Value.toFixed(defaultfloatPrecision); + if (ptTop.Align=='left') text=leftArrow+text; + else text=text+rightArrow; + if (ptTop.Y>(top-2)) + { + this.Canvas.fillText(text,left,ptTop.Y); + this.ChartFrame.ChartKLine.Max={X:left, Y:ptTop.Y, Text: { BaseLine:'bottom'}}; + } + + var ptBottom=ptMin; + if (this.ChartFrame.CoordinateType==1) + { + if (ptMin.Y>ptMax.Y) ptBottom=ptMax; + this.Canvas.textBaseline='bottom'; + } + else + { + if (ptMax.Y>ptMin.Y) ptTop=ptMin; + this.Canvas.textBaseline='top'; + } + this.Canvas.textAlign=ptBottom.Align; + + var left=ptBottom.Align=='left'?ptBottom.X:ptBottom.X; + if (IFrameSplitOperator.IsNumber(lowYOffset)) ptBottom.Y+=lowYOffset; + var text=ptMin.Value.toFixed(defaultfloatPrecision); + if (ptBottom.Align=='left') text=leftArrow+text; + else text=text+rightArrow; + if (ptBottom.Y<(bottom+1)) + { + this.Canvas.fillText(text,left,ptBottom.Y); + this.ChartFrame.ChartKLine.Min={X:left, Y:ptBottom.Y, Text:{ BaseLine:'top'}}; + } + } + + this.HScreenDrawMaxMinPrice=function(ptMax,ptMin) //横屏模式下显示最大最小值 + { + if (ptMax.X==null || ptMax.Y==null || ptMax.Value==null) return; + if (ptMin.X==null || ptMin.Y==null || ptMin.Value==null) return; + + var leftArrow=g_JSChartResource.KLine.MaxMin.LeftArrow; + var rightArrow=g_JSChartResource.KLine.MaxMin.RightArrow; + var highYOffset=g_JSChartResource.KLine.MaxMin.HighYOffset; + var lowYOffset=g_JSChartResource.KLine.MaxMin.LowYOffset; + + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + var xText=ptMax.Y; + var yText=ptMax.X; + if (IFrameSplitOperator.IsNumber(highYOffset)) xText+=highYOffset; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.font=this.TextFont; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.textAlign=ptMax.Align; + this.Canvas.textBaseline='bottom'; + var text=ptMax.Value.toFixed(defaultfloatPrecision); + if (ptMax.Align=='left') text=leftArrow+text; + else text=text+rightArrow; + this.Canvas.fillText(text,0,0); + this.Canvas.restore(); + + + var xText=ptMin.Y; + var yText=ptMin.X; + if (IFrameSplitOperator.IsNumber(lowYOffset)) xText+=lowYOffset; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.font=this.TextFont; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.textAlign=ptMin.Align; + this.Canvas.textBaseline='top'; + var text=ptMin.Value.toFixed(defaultfloatPrecision); + if (ptMin.Align=='left') text=leftArrow+text; + else text=text+rightArrow; + this.Canvas.fillText(text,0,0); + this.Canvas.restore(); + } + + //画某一天的信息地雷 + this.DrawInfo=function(item) + { + if (!this.InfoData || this.InfoData.length<=0) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var bottom=this.ChartBorder.GetBottom(); + var top=this.ChartBorder.GetTop(); + + var infoData=this.InfoData.get(item.DayData.Date.toString()); + if (!infoData || infoData.Data.length<=0) return; + + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + var iconSize=dataWidth+distanceWidth; + var minIconSize=18*pixelTatio; + var bShowNum=true; + if (iconSize<=15) bShowNum=false; + if (iconSize=2 && numText && bShowNum) //太小了 就不显示了 + { + var iconID=value.Data.length; + if (iconID>=numText.length) iconID=0; + this.Canvas.fillStyle=g_JSChartResource.KLine.NumIcon.Color; + var text=numText[iconID]; + this.Canvas.fillText(text,value.TextRect.X,value.TextRect.Y,iconSize); + } + + if (!isHScreen) this.InfoTooltipRect.push(value); //横屏没有tooltip + } + } + + //画交易图标 + this.DrawTradeIcon=function() + { + if (!this.TradeData) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + chartright=this.ChartBorder.GetBottom(); + } + + var sellData=this.TradeData.Sell; + var buyData=this.TradeData.Buy; + var iconSize=dataWidth+distanceWidth; + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + var iconSizeMax=24*pixelTatio,iconSizeMin=12*pixelTatio; + if (iconSizeiconSizeMax) iconSize=iconSizeMax; + this.Canvas.font=iconSize+'px '+this.TradeIcon.Family; + + for(var i=this.Data.DataOffset,j=0;i0; + if (buyData && i0; + if (!sell && !buy) continue; + + var left=xOffset; + var right=xOffset+dataWidth; + if (right>chartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low,false); + var yHigh=this.GetYFromData(data.High,false); + var yOpen=this.GetYFromData(data.Open,false); + var yClose=this.GetYFromData(data.Close,false); + var y=yHigh; + + if (buy) + { + this.Canvas.fillStyle=this.TradeIcon.Buy.Color; + + if (isHScreen) + { + this.Canvas.textAlign='right'; + this.Canvas.textBaseline='middle'; + this.Canvas.fillText(this.TradeIcon.Buy.HScreenText,yLow,x); + } + else + { + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='top'; + this.Canvas.fillText(this.TradeIcon.Buy.Text,x,yLow); + + var iconRect=new Rect(x-iconSize/2,yLow,iconSize,iconSize); + var iconData={ Data:{ Type:1, KData:data, Name:this.TradeData.Name, Param:this.TradeData.Param }, Rect:iconRect, TextRect:{X:x, Y:yLow} }; + this.TradeIconTooltipRect.push(iconData); + } + } + + if (sell) + { + this.Canvas.fillStyle=this.TradeIcon.Sell.Color; + if (isHScreen) + { + this.Canvas.textAlign='left'; + this.Canvas.textBaseline='middle'; + this.Canvas.fillText(this.TradeIcon.Sell.HScreenText,yHigh,x); + } + else + { + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='bottom'; + this.Canvas.fillText(this.TradeIcon.Sell.Text,x,yHigh); + + var iconRect=new Rect(x-iconSize/2,yHigh-iconSize,iconSize,iconSize); + var iconData={ Data:{ Type:2, KData:data, Name:this.TradeData.Name,Param:this.TradeData.Param }, Rect:iconRect, TextRect:{X:x, Y:yHigh} }; + this.TradeIconTooltipRect.push(iconData); + } + } + } + } + + this.GetTooltipData=function(x,y,tooltip) + { + for(var i in this.TradeIconTooltipRect) + { + var item=this.TradeIconTooltipRect[i]; + if (!item.Rect) continue; + var rect=item.Rect; + this.Canvas.beginPath(); + this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height); + if (this.Canvas.isPointInPath(x,y)) + { + JSConsole.Chart.Log('[ChartKLine::GetTooltipData] trade icon ', item); + tooltip.Data=item; + tooltip.ChartPaint=this; + tooltip.Type=2; //指标 + return true; + } + } + + for(var i in this.InfoTooltipRect) + { + var item=this.InfoTooltipRect[i]; + if (!item.Rect) continue; + var rect=item.Rect; + this.Canvas.beginPath(); + this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height); + if (this.Canvas.isPointInPath(x,y)) + { + //JSConsole.Chart.Log('[ChartKLine::GetTooltipData] info ', item); + tooltip.Data=item; + tooltip.ChartPaint=this; + tooltip.Type=1; //信息地雷 + return true; + } + } + + for(var i in this.TooltipRect) + { + var rect=this.TooltipRect[i][1]; + this.Canvas.beginPath(); + this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height); + if (this.Canvas.isPointInPath(x,y)) + { + var index=this.TooltipRect[i][0]; + tooltip.Data=this.Data.Data[index]; + tooltip.ChartPaint=this; + tooltip.Type=0; //K线信息 + return true; + } + } + + return false; + } + + //计算当天显示数据的最大最小值 + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Max=null; + range.Min=null; + if (this.DrawType==1 || this.DrawType==4 ) // 1=收盘价线 4=收盘价面积图 + { + for(var i=this.Data.DataOffset,j=0;idata.Close) range.Min=data.Close; + } + } + else + { + for(var i=this.Data.DataOffset,j=0;idata.Low) range.Min=data.Low; + } + } + + return range; + } + + //获取所有的价格 + this.GetAllPrice=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var setPrice=new Set(); + for(var i=this.Data.DataOffset,j=0;i=4) + { + if (data.High>data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):yClose)); + } + this.Canvas.stroke(); + y=yClose; + } + else + { + y=yClose; + } + + if (isHScreen) + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + } + } + } + else + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + } + else + { + if (this.DrawType==3) //空心柱 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yOpen)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yOpen-y))); + } + } + } + + if (data.Open>data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.stroke(); + } + } + + this.DrawDownBarItem=function(data, xOffset, dataWidth, option) + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var left=xOffset; + var right=xOffset+dataWidth; + var x=left+(right-left)/2; + + var yLow=this.ChartFrame.GetYFromData(data.Low); + var yHigh=this.ChartFrame.GetYFromData(data.High); + var yOpen=this.ChartFrame.GetYFromData(data.Open); + var yClose=this.ChartFrame.GetYFromData(data.Close); + var y=yHigh; + + if (dataWidth>=4) + { + if (data.High>data.Close) //上影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yOpen),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + y=yOpen; + } + else + { + y=yOpen + } + + if (isHScreen) + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yClose-y),ToFixedRect(dataWidth)); + } + else + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yClose)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yClose-y))); + } + + if (data.Open>data.Low) //下影线 + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + this.Canvas.stroke(); + } + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.stroke(); + } + } + + this.DrawUnChangeBarItem=function(data, xOffset, dataWidth, option) + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var left=xOffset; + var right=xOffset+dataWidth; + var x=left+(right-left)/2; + + var yLow=this.ChartFrame.GetYFromData(data.Low); + var yHigh=this.ChartFrame.GetYFromData(data.High); + var yOpen=this.ChartFrame.GetYFromData(data.Open); + var yClose=this.ChartFrame.GetYFromData(data.Close); + var y=yHigh; + + if (dataWidth>=4) + { + this.Canvas.beginPath(); + if (data.High>data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yOpen,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yOpen); + } + y=yOpen; + } + else + { + y=yOpen; + } + + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(left)); + this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(right)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(left),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(y)); + } + + if (data.Open>data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + } + + this.Canvas.stroke(); + } + else + { + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + this.Canvas.stroke(); + } + } + + this.DrawBar=function() + { + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + + if (this.IsHScreen) + { + xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + chartright=this.ChartBorder.GetBottom(); + } + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + if (itemOption.Color) + { + this.Canvas.strokeStyle=itemOption.Color; + this.Canvas.fillStyle=itemOption.Color; + } + else + { + this.Canvas.strokeStyle=this.Color; + this.Canvas.fillStyle=this.Color; + } + + if (data.Opendata.Close) //阴线 + { + this.DrawDownBarItem(data,xOffset,dataWidth,itemOption); + } + else //平线 + { + this.DrawUnChangeBarItem(data,xOffset,dataWidth,itemOption); + } + } + } + + this.GetMaxMin=function() + { + var range={Max:null,Min:null }; + return range; + } +} + +//K线叠加 支持横屏 +var OVERLAY_STATUS_ID= +{ + STATUS_NONE_ID:0, //空闲状态 + STATUS_REQUESTDATA_ID:1, //请求数据 + STATUS_RECVDATA_ID:2, //接收到数据 + STATUS_FINISHED_ID:3, //数据下载完成 +}; + +function ChartOverlayKLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartOverlayKLine'; //类名 + this.Color="rgb(65,105,225)"; + this.MainData; //主图K线数据 + this.SourceData; //叠加的原始数据 + this.Title; + this.DrawType=0; + this.CustomDrawType=null; //图形类型 + this.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID; + this.IsDelete=false; //是否已经删除 + + this.SetOption=function(option) + { + if (!option) return; + if (IFrameSplitOperator.IsNumber(option.DrawType)) this.CustomDrawType=option.DrawType; + } + + this.DrawKBar=function(firstOpen) //firstOpen 当前屏第1个显示数据 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + var drawType=this.DrawType; + if (this.CustomDrawType!=null) drawType=this.CustomDrawType; + + var isFristDraw=true; + var firstOverlayOpen=null; + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low/firstOverlayOpen*firstOpen,false); + var yHigh=this.GetYFromData(data.High/firstOverlayOpen*firstOpen,false); + var yOpen=this.GetYFromData(data.Open/firstOverlayOpen*firstOpen,false); + var yClose=this.GetYFromData(data.Close/firstOverlayOpen*firstOpen,false); + var y=yHigh; + + if (data.Open=4) + { + if (data.High>data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):yClose)); + } + y=yClose; + } + else + { + y=yClose; + } + + if (isHScreen) + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); //空心柱 + else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth)); + } + } + else + { + if (Math.abs(yOpen-y)<1) + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + } + else + { + if (drawType==3) this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y)); //空心柱 + else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y)); + } + } + + if (data.Open>data.Low) + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + } + } + else if (data.Open>data.Close) //阴线 + { + if (dataWidth>=4) + { + if (data.High>data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yOpen),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yOpen)); + } + y=yOpen; + } + else + { + y=yOpen + } + + if (isHScreen) + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth)); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yClose-y),ToFixedRect(dataWidth)); + } + else + { + if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1); //高度小于1,统一使用高度1 + else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),ToFixedRect(yClose-y)); + } + + if (data.Open>data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + } + } + else // 平线 + { + if (dataWidth>=4) + { + if (data.High>data.Close) //上影线 + { + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yOpen,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yOpen); + } + + y=yOpen; + } + else + { + y=yOpen; + } + + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(left)); + this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(right)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(left),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(y)); + } + + if (data.Open>data.Low) //下影线 + { + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow)); + } + } + } + else + { + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + } + } + + //添加tooltip区域 + { + var yTop=Math.min(yLow,yHigh,yOpen,yClose); + var yBottom=Math.max(yLow,yHigh,yOpen,yClose); + var rect=new Rect(left,yTop,dataWidth,yBottom-yTop); + //this.Canvas.fillStyle="rgb(0,0,100)"; + //this.Canvas.fillRect(rect.X,rect.Y,rect.Width,rect.Height); + this.TooltipRect.push([i,rect]); //[0]数据索引 [1]数据区域 + } + } + + if (isFristDraw==false) this.Canvas.stroke(); + } + + this.DrawAKLine=function(firstOpen) //美国线 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + var firstOverlayOpen=null; + this.Canvas.strokeStyle=this.Color; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yLow=this.GetYFromData(data.Low/firstOverlayOpen*firstOpen,false); + var yHigh=this.GetYFromData(data.High/firstOverlayOpen*firstOpen,false); + var yOpen=this.GetYFromData(data.Open/firstOverlayOpen*firstOpen,false); + var yClose=this.GetYFromData(data.Close/firstOverlayOpen*firstOpen,false); + + this.Canvas.beginPath(); //最高-最低 + if (isHScreen) + { + this.Canvas.moveTo(yHigh,ToFixedPoint(x)); + this.Canvas.lineTo(yLow,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yHigh); + this.Canvas.lineTo(ToFixedPoint(x),yLow); + } + + this.Canvas.stroke(); + + if (dataWidth>=4) + { + this.Canvas.beginPath(); //开盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yOpen),left); + this.Canvas.lineTo(ToFixedPoint(yOpen),x); + } + else + { + this.Canvas.moveTo(left,ToFixedPoint(yOpen)); + this.Canvas.lineTo(x,ToFixedPoint(yOpen)); + } + this.Canvas.stroke(); + + this.Canvas.beginPath(); //收盘 + if (isHScreen) + { + this.Canvas.moveTo(ToFixedPoint(yClose),right); + this.Canvas.lineTo(ToFixedPoint(yClose),x); + } + else + { + this.Canvas.moveTo(right,ToFixedPoint(yClose)); + this.Canvas.lineTo(x,ToFixedPoint(yClose)); + } + this.Canvas.stroke(); + } + } + + } + + this.DrawCloseLine=function(firstOpen) //收盘价线 + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + var firstOverlayOpen=null; + var bFirstPoint=true; + this.Canvas.strokeStyle=this.Color; + this.Canvas.beginPath(); + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + var yClose=this.GetYFromData(data.Close/firstOverlayOpen*firstOpen,false); + + if (bFirstPoint) + { + if (isHScreen) this.Canvas.moveTo(yClose,x); + else this.Canvas.moveTo(x,yClose); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(yClose,x); + else this.Canvas.lineTo(x,yClose); + } + } + + if (bFirstPoint==false) this.Canvas.stroke(); + } + + this.Draw=function() + { + this.TooltipRect=[]; + this.InfoTooltipRect=[]; + if (!this.MainData || !this.Data) return; + + var xPointCount=this.ChartFrame.XPointCount; + var firstOpen=null; //主线数据第1个开盘价 + for(var i=this.Data.DataOffset,j=0;ilow) range.Min=low; + } + + return range; + } + + this.GetTooltipData=function(x,y,tooltip) + { + for(var i in this.TooltipRect) + { + var rect=this.TooltipRect[i][1]; + this.Canvas.beginPath(); + this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height); + if (this.Canvas.isPointInPath(x,y)) + { + var index=this.TooltipRect[i][0]; + tooltip.Data=this.Data.Data[index]; + tooltip.ChartPaint=this; + return true; + } + } + return false; + } +} + +//分钟成交量 支持横屏 +function ChartMinuteVolumBar() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartMinuteVolumBar'; //类名 + + this.UpColor = g_JSChartResource.UpBarColor; //上涨 + this.DownColor = g_JSChartResource.DownBarColor; //下跌 + this.UnchangeColor=g_JSChartResource.UnchagneBarColor; //平盘 + + this.CustomColor=g_JSChartResource.Minute.VolBarColor; //自定义颜色 + + this.YClose; //前收盘 + this.Symbol; + this.BeforeOpenData; //盘前数据 + this.AfterCloseData; //盘后数据 + this.BeforeVolColor=g_JSChartResource.Minute.Before.VolColor; + + this.MultiDayBeforeOpenData; //多日分时图 盘前数据 数组 1天一个 + this.MultiDayAfterCloseData; //多日分时图 盘后数据 数组 1天一个 + + this.Draw=function() + { + var isHScreen = (this.ChartFrame.IsHScreen === true); + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var chartright = border.BottomEx; + } + else + { + var border=this.ChartBorder.GetBorder(); + var chartright=border.RightEx; + } + + this.DrawBeforeOpen(); + this.DrawMultiDayBeforeOpen(); + + var xPointCount=this.ChartFrame.XPointCount; + var yBottom=this.ChartFrame.GetYFromData(0); + var yPrice=this.YClose; //上一分钟的价格 + var data=this.Data; + var bShowColorBar=MARKET_SUFFIX_NAME.IsShowMinuteColorVolBar(this.Symbol); + + if (bShowColorBar) this.Canvas.strokeStyle=this.CustomColor; + for(var i=data.DataOffset,j=0;ichartright) break; + + //价格>=上一分钟价格 红色 否则绿色 + if (!bShowColorBar) this.Canvas.strokeStyle = price >= yPrice ? this.UpColor:this.DownColor; + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yBottom,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yBottom); + } + this.Canvas.stroke(); + if (price) yPrice=price; + } + + this.DrawAfterClose(); + this.DrawMultiDayAfterClose(); + } + + this.DrawBeforeOpen=function() + { + if (this.ChartBorder.LeftExtendWidth<10) return; + if (!this.BeforeOpenData) return; + + this.DrawCallAuction(-1,this.BeforeOpenData,true); + } + + this.DrawAfterClose=function() + { + if (this.ChartBorder.RightExtendWidth<10) return; + if (!this.AfterCloseData) return; + + this.DrawCallAuction(-1,this.AfterCloseData,false); + } + + this.DrawMultiDayBeforeOpen=function() + { + if (this.ChartBorder.MultiDayMinute.Count<=1 || this.ChartBorder.MultiDayMinute.Left<=0) return; + if (!this.MultiDayBeforeOpenData) return; + + for(var i in this.MultiDayBeforeOpenData) + { + var dayItem=this.MultiDayBeforeOpenData[i]; + this.DrawCallAuction(parseInt(i), dayItem, true); + } + } + + this.DrawMultiDayAfterClose=function() + { + if (this.ChartBorder.MultiDayMinute.Count<=1 || this.ChartBorder.MultiDayMinute.Right<=0) return; + if (!this.MultiDayAfterCloseData) return; + + for(var i in this.MultiDayAfterCloseData) + { + var dayItem=this.MultiDayAfterCloseData[i]; + this.DrawCallAuction(parseInt(i), dayItem, false); + } + } + + this.DrawCallAuction=function(indexDay, callAutionData, isBeforeOpen) + { + if (!callAutionData) return; + + callAutionData.Index=indexDay; + var border=this.GetBorder(); + var isHScreen=(this.ChartFrame.IsHScreen===true); + var yPrice=this.YClose; //上一分钟的价格 + var yBottom=this.ChartFrame.GetYFromData(0); + if (callAutionData.Ver==1.0) + { + for(var i in callAutionData.Data) + { + var item=callAutionData.Data[i]; + if (!item || !IFrameSplitOperator.IsNumber(item.Vol[0])) continue; + + if (isBeforeOpen) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(item.Vol[0]); + } + else + { + var x=this.ChartFrame.GetRightExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetRightExtendYFromData(item.Vol[0]); + } + + + this.Canvas.strokeStyle = item.Price >= yPrice ? this.UpColor:this.DownColor; + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yBottom,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yBottom); + } + this.Canvas.stroke(); + if (item.Price) yPrice=item.Price; + } + } + else if (callAutionData.Ver==2.0 || callAutionData.Ver==3.0) + { + var range={ Max: callAutionData.VolMax, Min:callAutionData.VolMin } + + for(var i in callAutionData.Data) + { + var item=callAutionData.Data[i]; + if (!item) continue; + if (IFrameSplitOperator.IsPlusNumber(item.Vol[0])) + { + if (isBeforeOpen) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(item.Vol[0],false, { Range:range }); + } + else + { + var x=this.ChartFrame.GetRightExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetRightExtendYFromData(item.Vol[0],false, { Range:range }); + } + + this.Canvas.strokeStyle = this.GetBarColor(item.ColorID); + + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yBottom,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yBottom); + } + this.Canvas.stroke(); + } + + if (IFrameSplitOperator.IsPlusNumber(item.Vol[1])) + { + if (isBeforeOpen) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(item.Vol[1],false, { Range:range }); + } + else + { + var x=this.ChartFrame.GetRightExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetRightExtendYFromData(item.Vol[1],false, { Range:range }); + } + + this.Canvas.strokeStyle = this.GetBarColor(item.ColorID); + this.Canvas.beginPath(); + if (isHScreen) + { + y-=yBottom; + y=border.RightEx-y; + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(border.RightEx,ToFixedPoint(x)); + } + else + { + y-=yBottom; + y=border.TopEx-y; + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),border.TopEx); + } + this.Canvas.stroke(); + } + } + } + } + + this.GetBarColor=function(id) + { + switch(id) + { + case 0: + return this.UnchangeColor; + case 1: + return this.UpColor; + case 2: + return this.DownColor; + } + + if (this.BeforeVolColor && Array.isArray(this.BeforeVolColor)) + { + var index=id-3; + if (index>=0 && index0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + if (this.IsDotLine) this.Canvas.setLineDash(g_JSChartResource.DOTLINE.LineDash); //画虚线 + var bFirstPoint=true; + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + if (bFirstPoint) + { + this.Canvas.strokeStyle=this.Color; + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.moveTo(y,x); //横屏坐标轴对调 + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (bHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + + if (drawCount>0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + //无效数不画 + this.DrawStraightLine=function() + { + var bHScreen=(this.ChartFrame.IsHScreen===true); + var isMinute=this.IsMinuteFrame(); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + + if (bHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var chartright=border.BottomEx; + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + else + { + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + } + + var lockRect=this.GetLockRect(); + if (lockRect) + { + if (bHScreen) chartright=lockRect.Top; + else chartright=lockRect.Left; + } + + this.Canvas.save(); + this.ClipClient(bHScreen); + if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + this.Canvas.strokeStyle=this.Color; + if (this.IsDotLine) this.Canvas.setLineDash(g_JSChartResource.DOTLINE.LineDash); //画虚线 + + var bFirstPoint=true; + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;i0) this.Canvas.stroke(); + bFirstPoint=true; + drawCount=0; + continue; + } + + if (isMinute) + { + var x=this.ChartFrame.GetXFromIndex(j); + } + else + { + var left=xOffset; + var right=xOffset+dataWidth; + if (right>chartright) break; + var x=left+(right-left)/2; + } + + var y=this.GetYFromData(value,false); + + if (x>chartright) break; + + if (bFirstPoint) + { + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.moveTo(y,x); //横屏坐标轴对调 + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (bHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + + if (drawCount>0) this.Canvas.stroke(); + this.Canvas.restore(); + } +} + +//子线段 +function ChartSubLine() +{ + this.newMethod=ChartLine; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartSubLine'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + this.LineWidth; //线段宽度 + this.DrawType=0; //画图方式 0=无效数平滑 1=无效数不画断开 + this.IsDotLine=false; //虚线 + + this.SubFrame={ Max:null,Min:null }; + + this.Draw=function() + { + if (!this.IsShow) return; + if (!this.Data || !this.Data.Data) return; + + this.CalculateDataMaxMin(); + + switch(this.DrawType) + { + case 0: + return this.DrawLine(); + case 1: + return this.DrawStraightLine(); + } + } + + this.GetYFromData=function(value) + { + var bHScreen = (this.ChartFrame.IsHScreen === true); + + if (bHScreen) + { + if (value <= this.SubFrame.Min) return this.ChartBorder.GetLeftEx(); + if (value >= this.SubFrame.Max) return this.ChartBorder.GetRightEx(); + + var width = this.ChartBorder.GetWidthEx() * (value - this.SubFrame.Min) / (this.SubFrame.Max - this.SubFrame.Min); + return this.ChartBorder.GetLeftEx() + width; + } + else + { + if(value<=this.SubFrame.Min) return this.ChartBorder.GetBottomEx(); + if(value>=this.SubFrame.Max) return this.ChartBorder.GetTopEx(); + + var height=this.ChartBorder.GetHeightEx()*(value-this.SubFrame.Min)/(this.SubFrame.Max-this.SubFrame.Min); + return this.ChartBorder.GetBottomEx()-height; + } + } + + this.CalculateDataMaxMin=function() + { + this.SubFrame={ Max:null,Min:null }; + + var bHScreen=(this.ChartFrame.IsHScreen===true); + var chartright=this.ChartBorder.GetRight(); + if (bHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + if (this.SubFrame.Min==null || this.SubFrame.Min>value) this.SubFrame.Min=value; + if (this.SubFrame.Max==null || this.SubFrame.Max0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + this.Canvas.strokeStyle=this.Color; + if (this.IsDotLine) this.Canvas.setLineDash([3,5]); //画虚线 + + var bFirstPoint=true; + var drawCount=0,rate=0; + for(var i=firstData.DataOffset,j=firstData.Index;i0) this.Canvas.stroke(); + bFirstPoint=true; + drawCount=0; + continue; + } + + var x=this.ChartFrame.GetXFromIndex(j); + var diff=value-firstData.OverlayValue; + if (firstData.OverlayValue!=0) rate= 1+ diff/Math.abs(firstData.OverlayValue); + else rate=1+diff; + var fixedValue=firstData.MainValue*rate; + //var fixedValue=value/firstData.OverlayValue*firstData.MainValue; + var y=this.GetYFromData(fixedValue); + + if (x>chartright) break; + + if (bFirstPoint) + { + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.moveTo(y,x); //横屏坐标轴对调 + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (bHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + + if (drawCount>0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + + var firstData=this.GetFirstVaildIndex(); + if (!firstData) return range; + + for(var i=firstData.DataOffset,j=firstData.Index; ivalue) range.Min=value; + } + + return range; + } +} + +//彩色线段 +function ChartPartLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartPartLine'; //类名 + this.LineWidth; //线段宽度 + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + this.DrawLine(); + } + + this.DrawLine=function() + { + var bHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (bHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + this.Canvas.save(); + if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + var bFirstPoint=true; + var drawCount=0; + var lastColor; + var lastPoint={X:null,Y:null}; + var isPerNull=false; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + if (color!=lastColor || isPerNull==true) + { + if (lastColor && drawCount>0) this.Canvas.stroke(); + + drawCount=0; + lastColor=color; + this.Canvas.strokeStyle=color; + this.Canvas.beginPath(); + + if (lastPoint.X!=null && lastPoint.Y!=null) //接着上一个点连线 + { + if (bHScreen) this.Canvas.moveTo(lastPoint.Y,lastPoint.X); //横屏坐标轴对调 + else this.Canvas.moveTo(lastPoint.X,lastPoint.Y); + + if (bHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + + ++drawCount; + } + else + { + if (bHScreen) this.Canvas.moveTo(y,x); //横屏坐标轴对调 + else this.Canvas.moveTo(x,y); + } + } + else + { + if (bHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + ++drawCount; + } + + lastPoint.X=x; + lastPoint.Y=y; + isPerNull=false; + } + + if (drawCount>0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;iitem.Value) range.Min = item.Value; + } + + return range; + } +} + +//斜率线 DRAWSL() +function ChartSlopeLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartSlopeLine'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + this.IsDotLine=false; + this.LineWidth; + this.Option; //[ { Slope:slope, Length:len, Direct:direct } ] + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + this.Canvas.save(); + this.Canvas.strokeStyle=this.Color; + if (this.IsDotLine) this.Canvas.setLineDash(g_JSChartResource.DOTLINE.LineDash); //画虚线 + + var left=this.ChartBorder.GetLeft(); + var top=this.ChartBorder.GetTopEx(); + var right=this.ChartBorder.GetRight(); + var bottom=this.ChartBorder.GetBottom(); + + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + + this.Canvas.beginPath(); + this.Canvas.rect(left,top,(right-left),(bottom-top)); + this.Canvas.clip(); + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + var x2=Math.sqrt((z*z)/(1+option.Slope*option.Slope)); + var y2=x2*option.Slope; + + this.Canvas.beginPath(); + if (option.Direct==2) + { + this.Canvas.moveTo(x-x2,y+y2); + this.Canvas.lineTo(x+x2,y-y2); + } + else if (option.Direct==1) + { + this.Canvas.moveTo(x,y); + this.Canvas.lineTo(x-x2,y+y2); + } + else + { + this.Canvas.moveTo(x,y); + this.Canvas.lineTo(x+x2,y-y2); + } + + this.Canvas.stroke(); + } + + this.Canvas.restore(); + } +} + + +//POINTDOT 圆点 支持横屏 +function ChartPointDot() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartPointDot'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + this.Radius=1; //点半径 + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var bHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (bHScreen===true) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + this.Canvas.save(); + this.Canvas.fillStyle=this.Color; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + this.Canvas.beginPath(); + if (bHScreen) this.Canvas.arc(y, x, this.Radius, 0, Math.PI*2, true); + else this.Canvas.arc(x, y, this.Radius, 0, Math.PI*2, true); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.Canvas.restore(); + } +} + +//通达信语法 STICK 支持横屏 +function ChartStick() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color="rgb(255,193,37)"; //线段颜色 + this.LineWidth; //线段宽度 + this.ClassName='ChartStick'; + + this.DrawLine=function() + { + if (!this.Data || !this.Data.Data) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen===true) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + this.Canvas.save(); + if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + var bFirstPoint=true; + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + if (bFirstPoint) + { + this.Canvas.strokeStyle=this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + + if (drawCount>0) this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.DrawStick=function() + { + if (!this.Data || !this.Data.Data) return; + var bHScreen=(this.ChartFrame.IsHScreen===true); + var chartright=this.ChartBorder.GetRight(); + if (bHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + var yBottom=this.ChartBorder.GetBottom(); + var xLeft=this.ChartBorder.GetLeft(); + + this.Canvas.save(); + this.Canvas.strokeStyle=this.Color; + if (this.LineWidth) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + var lineWidth=this.Canvas.lineWidth; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + this.Canvas.beginPath(); + if (bHScreen) + { + this.Canvas.moveTo(xLeft,x); + this.Canvas.lineTo(y,x); + this.Canvas.stroke(); + } + else + { + var xFix=ToFixedPoint2(lineWidth, x); + this.Canvas.moveTo(xFix,y); + this.Canvas.lineTo(xFix,yBottom); + } + this.Canvas.stroke(); + } + + this.Canvas.restore(); + } + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + this.DrawStick(); + } +} + +//通达信语法 LINESTICK 支持横屏 +function ChartLineStick() +{ + this.newMethod=ChartStick; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartLineStick'; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + this.DrawStick(); + this.DrawLine(); + } +} + +//通达信语法 VOLSTICK 支持横屏 +function ChartVolStick() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.UpColor=g_JSChartResource.UpBarColor; + this.DownColor=g_JSChartResource.DownBarColor; + this.HistoryData; //历史数据 + this.KLineDrawType=0; + this.ClassName='ChartVolStick'; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.ChartFrame.IsHScreen===true) + { + this.HScreenDraw(); + return; + } + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + var xPointCount=this.ChartFrame.XPointCount; + var lockRect=this.GetLockRect(); + if (lockRect) chartright=lockRect.Left; + var isMinute=this.IsMinuteFrame(); + + var yBottom=this.ChartFrame.GetYFromData(0); + + if (dataWidth>=4) + { + yBottom=ToFixedRect(yBottom); + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + var y=this.ChartFrame.GetYFromData(value); + var barColor=this.GetBarColor(kItem); + var bUp=barColor.IsUp; + this.Canvas.fillStyle=barColor.Color; + + var height=ToFixedRect(Math.abs(yBottom-y)>=1?yBottom-y:1);//高度调整为整数, 如果小于1, 统一使用1 + y=yBottom-height; + if (bUp && (this.KLineDrawType==1 || this.KLineDrawType==2 || this.KLineDrawType==3)) //空心柱子 + { + this.Canvas.strokeStyle=this.UpColor; + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),height); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left),y,ToFixedRect(dataWidth),height); + } + } + } + else //太细了直接话线 + { + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + var barColor=this.GetBarColor(kItem); + this.Canvas.strokeStyle=barColor.Color; + + var x=this.ChartFrame.GetXFromIndex(j); + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yBottom); + this.Canvas.stroke(); + } + } + } + + this.HScreenDraw=function() //横屏画法 + { + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.ChartBorder.GetHScreenBorder(); + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartBottom=border.BottomEx; + var xPointCount=this.ChartFrame.XPointCount; + var lockRect=this.GetLockRect(); + if (lockRect) chartBottom=lockRect.Top; + + var yBottom=this.ChartFrame.GetYFromData(0); + + if (dataWidth>=4) + { + yBottom=ToFixedRect(yBottom); + for(var i=this.Data.DataOffset,j=0;ichartBottom) break; + + var y=this.ChartFrame.GetYFromData(value); + var bUp=false; + if (kItem.Close>=kItem.Open) + { + bUp=true; + this.Canvas.fillStyle=this.UpColor; + } + else + { + this.Canvas.fillStyle=this.DownColor; + } + + var height=ToFixedRect(y-yBottom); //高度调整为整数 + if (bUp && (this.KLineDrawType==1 || this.KLineDrawType==2 || this.KLineDrawType==3)) //空心柱子 + { + this.Canvas.strokeStyle=this.UpColor; + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(yBottom),ToFixedPoint(left),height,ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(yBottom,ToFixedRect(left),height,ToFixedRect(dataWidth)); + } + } + } + else //太细了直接话线 + { + for(var i=this.Data.DataOffset,j=0;ichartBottom) break; + + if (kItem.Close>kItem.Open) + this.Canvas.strokeStyle=this.UpColor; + else + this.Canvas.strokeStyle=this.DownColor; + + var x=this.ChartFrame.GetXFromIndex(j); + this.Canvas.beginPath(); + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(yBottom,ToFixedPoint(x)); + this.Canvas.stroke(); + } + } + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=0; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;i=kItem.Open) return { Color:this.UpColor, IsUp:true }; //颜色, 是否是上涨 + else return { Color:this.DownColor, IsUp:false }; + } +} + +// VERTLINE(HIGH>=HHV(HIGH,20),1)表示在创20天新高画垂直虚线。 +// 支持横屏 +function ChartVericaltLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartVericaltLine'; //类名 + this.LineWidth=2; + this.LineCap; + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + var isHScreen=this.ChartFrame.IsHScreen==true; + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.GetBorder(); + var xPointCount=this.ChartFrame.XPointCount; + + if (isHScreen) + { + var top=border.RightEx; + var bottom=border.LeftEx; + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + else + { + var top=border.TopEx; + var bottom=border.BottomEx; + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + + this.Canvas.save(); + this.Canvas.strokeStyle=this.Color; + if (this.LineType) this.Canvas.setLineDash(this.LineType); + if (this.LineCap) this.Canvas.lineCap = this.LineCap; + + this.Canvas.beginPath(); + var lineWidth=this.Canvas.lineWidth; + if (this.LineWidth>0) + { + lineWidth=this.LineWidth * GetDevicePixelRatio(); + this.Canvas.lineWidth=lineWidth; + } + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;i0) this.Canvas.stroke(); + + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + return { Min:null, Max:null }; + } +} + +//HORLINE 支持横屏 +function ChartHorizontalLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartHorizontalLine'; //类名 + this.LineWidth=2; + this.LineCap; + this.ExtendType=0; //1=表示向左延长,2=表示向右延长,3=表示左右延长 + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + var isHScreen=this.ChartFrame.IsHScreen==true; + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.GetBorder(); + + var xPointCount=this.ChartFrame.XPointCount; + if (isHScreen) + { + var left=border.TopEx; + var right=border.BottomEx; + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + else + { + var left=border.LeftEx + var right=border.RightEx; + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + + this.Canvas.save(); + this.Canvas.strokeStyle=this.Color; + if (this.LineType) this.Canvas.setLineDash(this.LineType); + if (this.LineCap) this.Canvas.lineCap = this.LineCap; + + this.Canvas.beginPath(); + var lineWidth=this.Canvas.lineWidth; + if (this.LineWidth>0) + { + lineWidth=this.LineWidth * GetDevicePixelRatio(); + this.Canvas.lineWidth=lineWidth; + } + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;i0) this.Canvas.stroke(); + + this.Canvas.restore(); + } +} + + + +//线段 多数据(一个X点有多条Y数据) 支持横屏 +function ChartLineMultiData() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartLineMultiData'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + + this.Draw=function() + { + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + var bFirstPoint=true; + var drawCount=0; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + for(var index in aryValue) + { + var value =aryValue[index].Value; + + var y=this.ChartFrame.GetYFromData(value); + + if (bFirstPoint) + { + this.Canvas.strokeStyle=this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + } + + if (drawCount>0) + this.Canvas.stroke(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + + if(!this.Data || !this.Data.Data) return range; + + for(var i=this.Data.DataOffset,j=0;ivalue) range.Min=value; + } + } + + return range; + } +} + +//柱子 支持横屏 +function ChartStickLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartStickLine'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + this.BarType=0; //柱子类型 0=实心 1=空心 -1=画虚线空心柱 + this.LineDotted=[3,3]; //虚线设置 + this.Width=0; //柱子宽度 0=1 3,50=k线宽度 101=K线宽度+间距宽度 + + this.SetEmptyBar=function() //设置空心柱子 + { + if (this.BarType!=1 && this.BarType!=-1) return false; + + this.Canvas.lineWidth=GetDevicePixelRatio(); + this.Canvas.strokeStyle=this.Color; + var emptyBGColor=g_JSChartResource.EmptyBarBGColor; + if (emptyBGColor) this.Canvas.fillStyle=emptyBGColor; + if (this.BarType==-1) //虚线 + { + this.Canvas.setLineDash(this.LineDotted); //虚线 + } + + return true; + } + + this.IsEmptyBar=function() + { + return (this.BarType==1 || this.BarType==-1); + } + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + var zoomIndex=this.ChartFrame.ZoomIndex; + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0; + + var isMinute=this.IsMinuteFrame(); + + this.Canvas.save(); + var bFillBar=false; + var bFillKLine=false; + var emptyBGColor=g_JSChartResource.EmptyBarBGColor; + + if (isMinute) + { + if (this.Width>1) this.Canvas.lineWidth=2*GetDevicePixelRatio(); + else this.Canvas.lineWidth=GetDevicePixelRatio(); + this.Canvas.strokeStyle=this.Color; + } + else if(this.Width==0) + { + this.SetEmptyBar(); + } + else if (this.Width==3 || this.Width==50) //3和50 K线宽度 + { + if (dataWidth>=4) + { + bFillKLine=true; + this.SetEmptyBar(); + if (!this.IsEmptyBar()) this.Canvas.fillStyle=this.Color; + this.Canvas.strokeStyle=this.Color; + } + else //太细了 画竖线 + { + this.Canvas.lineWidth=GetDevicePixelRatio(); + this.Canvas.strokeStyle=this.Color; + } + } + else if (this.Width==101) + { + var lineWidth=dataWidth+distanceWidth+1*GetDevicePixelRatio(); + this.Canvas.lineWidth=lineWidth; + this.Canvas.strokeStyle=this.Color; + } + else if (this.Width<=3) + { + var minWidth=2*GetDevicePixelRatio(); + var barWidth=dataWidth*(this.Width/3); + if (barWidthchartright) break; + + if (bFillBar) + { + if (isHScreen) + { + var left=x-barWidth/2; + var width=barWidth; + if (this.IsEmptyBar()) //空心 + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(Math.min(y,y2)),ToFixedPoint(left),ToFixedRect(Math.abs(y-y2)),ToFixedRect(width)); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(Math.min(y,y2)),ToFixedRect(left),ToFixedRect(Math.abs(y-y2)),ToFixedRect(width)); + } + } + else + { + var left=x-barWidth/2; + var width=barWidth; + if (left+width>chartright) width=chartright-left; //不要超过右边框子 + if (this.IsEmptyBar()) //空心 + { + if (emptyBGColor) + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,y2)),ToFixedRect(width),ToFixedRect(Math.abs(y-y2))); + + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(Math.min(y,y2)),ToFixedRect(width),ToFixedRect(Math.abs(y-y2))); + this.Canvas.stroke(); + } + else + { + this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,y2)),ToFixedRect(width),ToFixedRect(Math.abs(y-y2))); + } + } + } + else if (bFillKLine) + { + if (this.IsEmptyBar()) //空心 + { + if (isHScreen) + { + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(Math.min(y,y2)),ToFixedPoint(xOffset),ToFixedRect(Math.abs(y-y2)),ToFixedRect(dataWidth)); + this.Canvas.stroke(); + } + else + { + if (emptyBGColor) + this.Canvas.fillRect(ToFixedRect(xOffset),ToFixedRect(Math.min(y,y2)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(y-y2))); + + this.Canvas.beginPath(); + this.Canvas.rect(ToFixedPoint(xOffset),ToFixedPoint(Math.min(y,y2)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(y-y2))); + this.Canvas.stroke(); + } + } + else + { + if (isHScreen) + this.Canvas.fillRect(ToFixedRect(Math.min(y,y2)),ToFixedRect(xOffset),ToFixedRect(Math.abs(y-y2)),ToFixedRect(dataWidth)); + else + this.Canvas.fillRect(ToFixedRect(xOffset),ToFixedRect(Math.min(y,y2)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(y-y2))); + } + } + else + { + if (isHScreen) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(y,ToFixedPoint(x)); + this.Canvas.lineTo(y2,ToFixedPoint(x)); + this.Canvas.stroke(); + } + else + { + var xFix=parseInt(x.toString())+0.5; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix,y); + this.Canvas.lineTo(xFix,y2); + this.Canvas.stroke(); + } + } + } + + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + + if(!this.Data || !this.Data.Data) return range; + + for(var i=this.Data.DataOffset,j=0;ivalueMin) range.Min=valueMin; + } + + return range; + } +} + +function ChartText() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartText'; //类名 + this.TextFont="14px 微软雅黑"; + + this.Draw=function() + { + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + + for(var i in this.Data.Data) + { + var value=this.Data.Data[i]; + if (value==null) continue; + + var price=value.Value; + var position=value.Position; + + if (position=='Left') + { + var x=this.ChartFrame.GetXFromIndex(0); + var y=this.ChartFrame.GetYFromData(price); + + if (x>chartright) continue; + + this.Canvas.textAlign='left'; + this.Canvas.textBaseline='middle'; + this.Canvas.fillStyle=value.Color; + this.Canvas.font=this.TextFont; + this.Canvas.fillText(value.Message,x,y); + } + + } + + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + + if(!this.Data || !this.Data.Data) return range; + + for(var i in this.Data.Data) + { + var data=this.Data.Data[i]; + if (data==null || isNaN(data.Value)) continue; + + var value=data.Value; + + if (range.Max==null) range.Max=value; + if (range.Min==null) range.Min=value; + + if (range.Maxvalue) range.Min=value; + } + + return range; + } +} + +/* + 文字输出 支持横屏 + 数组(Data)不为null的数据中输出 this.Text文本 +*/ +function ChartSingleText() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartSingleText'; //类名 + this.Color="rgb(255,193,37)"; //线段颜色 + this.TextFont="14px 微软雅黑"; //线段宽度 + this.Text; + this.TextAlign='left'; + this.Direction=0; //0=middle 1=bottom 2=top + this.YOffset=0; //连线 + this.Position; //指定输出位置 + this.IconFont; //Iconfont + this.IconSize= + { + Max: g_JSChartResource.DRAWICON.Icon.MaxSize, Min:g_JSChartResource.DRAWICON.Icon.MinSize, //图标的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWICON.Icon.Zoom.Type , Value:g_JSChartResource.DRAWICON.Icon.Zoom.Value }, //放大倍数 + YOffset:g_JSChartResource.DRAWICON.Icon.YOffset //Direction==2的向下偏移 + }; + + this.TextSize= + { + Max: g_JSChartResource.DRAWICON.Text.MaxSize, Min:g_JSChartResource.DRAWICON.Text.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWICON.Text.Zoom.Type , Value:g_JSChartResource.DRAWICON.Text.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWICON.Text.FontName, + YOffset:g_JSChartResource.DRAWICON.Text.YOffset + } + + this.ReloadResource=function(resource) + { + if (this.Name=="DRAWTEXT") + { + this.TextSize= + { + Max: g_JSChartResource.DRAWTEXT.MaxSize, Min:g_JSChartResource.DRAWTEXT.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWTEXT.Zoom.Type , Value:g_JSChartResource.DRAWTEXT.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWTEXT.FontName, + YOffset:g_JSChartResource.DRAWTEXT.YOffset + } + } + else if (this.Name=="DRAWNUMBER") + { + this.TextSize= + { + Max: g_JSChartResource.DRAWNUMBER.MaxSize, Min:g_JSChartResource.DRAWNUMBER.MinSize, //字体的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWNUMBER.Zoom.Type , Value:g_JSChartResource.DRAWNUMBER.Zoom.Value }, //放大倍数 + FontName:g_JSChartResource.DRAWNUMBER.FontName, + YOffset:g_JSChartResource.DRAWNUMBER.YOffset + } + } + } + + this.SuperGetMaxMin=this.GetMaxMin; + this.GetMaxMin=function() + { + if ( this.Name=="DRAWTEXT_FIX" || this.Name=='DRAWNUMBER_FIX') //固定位置的 没有大小值 + { + return { Min:null,Max:null }; + } + else if (this.Name=="DRAWTEXTREL" || this.Name=="DRAWTEXTABS") + { + return { Min:null,Max:null }; + } + else + { + return this.SuperGetMaxMin(); + } + } + + this.DrawRectText=function() + { + if (!this.DrawData) return; + var isHScreen=(this.ChartFrame.IsHScreen===true) + var border=this.ChartFrame.GetBorder(); + + + if (this.Name=="DRAWTEXTREL") + { + if (isHScreen) + { + var height=border.RightTitle-border.LeftEx; + var width=border.BottomEx-border.TopEx; + var x=this.DrawData.Point.X/1000*width+border.TopEx; + var y=border.RightTitle-this.DrawData.Point.Y/1000*width; + } + else + { + var width=border.RightEx-border.LeftEx; + var height=border.BottomEx-border.TopTitle; + var x=this.DrawData.Point.X/1000*width+border.LeftEx; + var y=this.DrawData.Point.Y/1000*height+border.TopTitle; + } + + } + else if (this.Name=="DRAWTEXTABS") + { + if (isHScreen) + { + var x=this.DrawData.Point.X+border.TopEx; + var y=border.RightTitle-this.DrawData.Point.Y; + } + else + { + var x=this.DrawData.Point.X+border.LeftEx; + var y=this.DrawData.Point.Y+border.TopTitle; + } + } + else + { + return; + } + + if (this.Direction==1) this.Canvas.textBaseline='bottom'; + else if (this.Direction==2) this.Canvas.textBaseline='top'; + else this.Canvas.textBaseline='middle'; + this.Canvas.textAlign='left'; + this.Canvas.font=this.TextFont; + this.Canvas.fillStyle=this.Color; + this.DrawText(this.DrawData.Text,x,y,isHScreen); + } + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.Name=="DRAWTEXTREL" || this.Name=="DRAWTEXTABS") + { + this.DrawRectText(); + return; + } + + if (this.Position) + { + this.DrawPosition(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true) + var isMinute=this.IsMinuteFrame(); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=this.ChartBorder.GetRight(); + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + if (isHScreen) + { + chartright=this.ChartBorder.GetBottom(); + top=this.ChartBorder.GetRightEx(); + bottom=this.ChartBorder.GetLeftEx(); + xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + } + var xPointCount=this.ChartFrame.XPointCount; + + var isArrayText=Array.isArray(this.Text); + var pixelTatio = GetDevicePixelRatio(); + + if (this.Direction==1) this.Canvas.textBaseline='bottom'; + else if (this.Direction==2) this.Canvas.textBaseline='top'; + else this.Canvas.textBaseline='middle'; + + if (this.IconFont) + { + this.Color=this.IconFont.Color; + this.Text=this.IconFont.Text; + + var iconSize=this.GetDynamicIconSize(dataWidth,distanceWidth,this.IconSize.Max,this.IconSize.Min,this.IconSize.Zoom); + this.Canvas.font=iconSize+'px '+this.IconFont.Family; + } + else + { + this.TextFont=this.GetDynamicFont(dataWidth,distanceWidth,this.TextSize.Max,this.TextSize.Min,this.TextSize.Zoom,this.TextSize.FontName); + this.Canvas.font=this.TextFont; + } + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + } + + var y=this.ChartFrame.GetYFromData(value); + + if (x>chartright) break; + + this.Canvas.textAlign=this.TextAlign; + this.Canvas.fillStyle=this.Color; + + if (this.YOffset>0 && this.Direction>0) + { + var yPrice=y; + + this.Canvas.setLineDash([5,10]); + this.Canvas.strokeStyle=this.Color; + this.Canvas.beginPath(); + if (isHScreen) + { + if (this.Direction==1) + { + y=top-this.YOffset*pixelTatio; + yPrice+=5*pixelTatio; + } + else + { + y=bottom+this.YOffset*pixelTatio; + yPrice-=5*pixelTatio; + } + this.Canvas.moveTo(ToFixedPoint(yPrice),ToFixedPoint(x)); + this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(x)); + } + else + { + if (this.Direction==1) + { + y=top+this.YOffset*pixelTatio; + yPrice+=5*pixelTatio; + } + else + { + y=bottom-this.YOffset*pixelTatio; + yPrice-=5*pixelTatio; + } + + this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(yPrice)); + this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(y)); + } + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + + if (isArrayText) + { + var text=this.Text[i]; + if (!text) continue; + if (this.Name=='DRAWNUMBER') + { + if (this.Direction==1) y-=g_JSChartResource.DRAWABOVE.YOffset*pixelTatio; + else if (this.Direction==2) y+=this.TextSize.YOffset*pixelTatio; + } + this.DrawText(text,x,y,isHScreen); + } + else + { + if (this.Name=='DRAWICON') + { + if (this.Direction==1) y-=g_JSChartResource.DRAWABOVE.YOffset*pixelTatio; + else if (this.Direction==2) + { + if (this.IconFont) y+=this.IconSize.YOffset*pixelTatio; + else y+=this.TextSize.YOffset*pixelTatio; + } + } + else if (this.Name=="DRAWTEXT") + { + if (this.Direction==1) y-=g_JSChartResource.DRAWABOVE.YOffset*pixelTatio; + else if (this.Direction==2) y+=this.TextSize.YOffset*pixelTatio; + } + //JSConsole.Chart.Log('[ChartSingleText::Draw] ',this.Direction,this.Text) + this.DrawText(this.Text,x,y,isHScreen); + } + } + } + + this.DrawPosition=function() //绘制在指定位置上 + { + if (!this.Text) return; + var isHScreen=(this.ChartFrame.IsHScreen===true) + if (isHScreen) + { + var y=this.ChartBorder.GetRightEx()-this.ChartBorder.GetWidthEx()*this.Position.Y; + var x=this.ChartBorder.GetTop()+this.ChartBorder.GetHeight()*this.Position.X; + } + else + { + var x=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*this.Position.X; + var y=this.ChartBorder.GetTopEx()+this.ChartBorder.GetHeightEx()*this.Position.Y; + } + + this.Canvas.fillStyle=this.Color; + + //TYPE:0为左对齐,1为右对齐. + if (this.Position.Type==0) this.Canvas.textAlign='left'; + else if (this.Position.Type==1) this.Canvas.textAlign='right'; + else this.Canvas.textAlign='center'; + + if (this.Direction==1) this.Canvas.textBaseline='bottom'; + else if (this.Direction==2) this.Canvas.textBaseline='top'; + else this.Canvas.textBaseline='middle'; + + if (Array.isArray(this.Text)) + { + if (!this.Data || !this.Data.Data) return; + var xPointCount=this.ChartFrame.XPointCount; + for(var i=this.Data.DataOffset,j=0; ivalueMin) range.Min=valueMin; + } + + return range; + } +} + +//分钟线 支持横屏 +function ChartMinutePriceLine() +{ + this.newMethod=ChartLine; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartMinutePriceLine'; //类名 + this.YClose; + this.IsDrawArea=true; //是否画价格面积图 + this.AreaColor='rgba(0,191,255,0.1)'; + this.IsShowLead=false; + this.LeadData; + this.UpColor=g_JSChartResource.UpBarColor; + this.DownColor=g_JSChartResource.DownBarColor; + + this.BeforeOpenData; //盘前数据 Data:[] 数据, TotalCount:一共的数据个数 + this.BeforeLineColor=g_JSChartResource.Minute.Before.LineColor; + this.BeforeAvPriceColor=g_JSChartResource.Minute.Before.AvPriceColor; + this.BeforePoint={ Color:g_JSChartResource.Minute.Before.Point.Color, Radius:g_JSChartResource.Minute.Before.Point.Radius }; + + this.AfterCloseData; //盘后数据 + + this.MultiDayBeforeOpenData; //多日分时图 盘前数据 数组 1天一个 + this.MultiDayAfterCloseData; //多日分时图 盘后数据 数组 1天一个 + + this.ColorLineData; //自定义价格线分段颜色 + this.Source; //原始分钟数据 + + this.Draw=function() + { + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.IsShow) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen===true) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + var minuteCount=this.ChartFrame.MinuteCount; + var bottom=this.ChartBorder.GetBottomEx(); + var left=this.ChartBorder.GetLeftEx(); + var data=this.Data; + + this.DrawBeforeOpen(); //盘前 + this.DrawMultiDayBeforeOpen(); + + if (this.IsShowLead) this.DrawLead(); //领先指标 + + if (!data) return; + + var bFirstPoint=true; + var ptFirst={}; //第1个点 + var ptLast={}; //最后一个点 + var drawCount=0; + for(var i=data.DataOffset,j=0;i=minuteCount) //上一天的数据和这天地数据线段要断开 + { + bFirstPoint=true; + this.Canvas.stroke(); + if (this.IsDrawArea) //画面积 + { + if (isHScreen) + { + this.Canvas.lineTo(left,x); + this.Canvas.lineTo(left,ptFirst.X); + this.SetFillStyle(this.AreaColor,this.ChartBorder.GetRightEx(),bottom,this.ChartBorder.GetLeftEx(),bottom); + } + else + { + this.Canvas.lineTo(x,bottom); + this.Canvas.lineTo(ptFirst.X,bottom); + this.SetFillStyle(this.AreaColor, left,this.ChartBorder.GetTopEx(), left,bottom); + } + + this.Canvas.fill(); + } + drawCount=0; + } + } + + if (drawCount>0) + { + this.Canvas.stroke(); + if (this.IsDrawArea) //画面积 + { + if (isHScreen) + { + this.Canvas.lineTo(left,x); + this.Canvas.lineTo(left,ptFirst.X); + this.SetFillStyle(this.AreaColor,this.ChartBorder.GetRightEx(),bottom,this.ChartBorder.GetLeftEx(),bottom); + } + else + { + this.Canvas.lineTo(x,bottom); + this.Canvas.lineTo(ptFirst.X,bottom); + this.SetFillStyle(this.AreaColor,left,this.ChartBorder.GetTopEx(), left,bottom); + } + + this.Canvas.fill(); + } + } + + this.DrawColorLine(); + this.DrawAfterClose(); //收盘集合竞价 + this.DrawMultiDayAfterClose(); + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DRAW_MINUTE_LAST_POINT); + if (event) + { + var data={ LastPoint:{X:ptLast.X, Y:ptLast.Y}, Price:ptLast.Price }; + event.Callback(event,data,this); + } + } + } + + //画领先指标 + this.DrawLead=function() + { + if (!this.LeadData) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + //if (isHScreen) return; + //var dataWidth=this.ChartFrame.DataWidth; + //var distanceWidth=this.ChartFrame.DistanceWidth; + //var chartright=this.ChartBorder.GetRight(); + + var xPointCount=this.ChartFrame.XPointCount; + var minuteCount=this.ChartFrame.MinuteCount; + var bottom=this.ChartBorder.GetBottomEx(); + var top=this.ChartBorder.GetTopEx(); + if (isHScreen===true) top=this.ChartBorder.GetRightEx(); + + if (xPointCount>minuteCount) return; + + var aryLead=[]; //{X: Value:} + var max=null, min=null; + var yCenter=this.ChartFrame.GetYFromData(this.YClose); + var leadHeight=(yCenter-top)/3; + var data=this.LeadData; + for(var i=data.DataOffset,j=0;ivalue) min=value; + + aryLead.push({X:x, Value:value}) + } + + if (aryLead.length<=0) return; + var maxValue=Math.max(Math.abs(max),Math.abs(min)); + + for(var i in aryLead) + { + var item=aryLead[i]; + if (item.Value>0) this.Canvas.strokeStyle=this.UpColor; + else this.Canvas.strokeStyle=this.DownColor; + + var y=yCenter-(item.Value*leadHeight/maxValue); + var x=ToFixedPoint(item.X); + this.Canvas.beginPath(); + if (isHScreen===true) + { + this.Canvas.moveTo(yCenter,x); + this.Canvas.lineTo(y,x); + } + else + { + this.Canvas.moveTo(x,yCenter); + this.Canvas.lineTo(x,y); + } + this.Canvas.stroke(); + } + } + + this.DrawBeforeOpen=function() + { + if (this.ChartBorder.LeftExtendWidth<10) return; + if (!this.BeforeOpenData) return; + + this.DrawCallAuction(-1, this.BeforeOpenData, true); + } + + this.DrawAfterClose=function() + { + if (this.ChartBorder.RightExtendWidth<10) return; + if (!this.AfterCloseData) return; + + this.DrawCallAuction(-1, this.AfterCloseData, false); + } + + this.DrawMultiDayBeforeOpen=function() + { + if (this.ChartBorder.MultiDayMinute.Count<=1 || this.ChartBorder.MultiDayMinute.Left<=0) return; + if (!this.MultiDayBeforeOpenData) return; + + for(var i in this.MultiDayBeforeOpenData) + { + var dayItem=this.MultiDayBeforeOpenData[i]; + this.DrawCallAuction(parseInt(i), dayItem, true); + } + } + + this.DrawMultiDayAfterClose=function() + { + if (this.ChartBorder.MultiDayMinute.Count<=1 || this.ChartBorder.MultiDayMinute.Right<=0) return; + if (!this.MultiDayAfterCloseData) return; + + for(var i in this.MultiDayAfterCloseData) + { + var dayItem=this.MultiDayAfterCloseData[i]; + this.DrawCallAuction(parseInt(i), dayItem, false); + } + } + + this.DrawCallAuction=function(indexDay, callAutionData, isBeforeOpen) + { + if (!callAutionData) return; + + callAutionData.Index=indexDay; + var isHScreen=(this.ChartFrame.IsHScreen===true); + var bFirstPoint=true; + var drawCount=0; + var callAutionInfo={ TotalCount:callAutionData.TotalCount }; + if (callAutionData.Ver==3.0) //均线 + { + for(var i in callAutionData.Data) + { + var item=callAutionData.Data[i]; + if (!item || !IFrameSplitOperator.IsNumber(item.AvPrice)) continue; + + if (isBeforeOpen) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(item.AvPrice); + } + else + { + var x=this.ChartFrame.GetRightExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetRightExtendYFromData(item.AvPrice); + } + + if (bFirstPoint) + { + this.Canvas.strokeStyle=this.BeforeAvPriceColor; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ++drawCount; + } + + if (drawCount>0) + { + this.Canvas.stroke(); + } + } + + var bFirstPoint=true; + var drawCount=0; + var aryPoint=[]; + for(var i in callAutionData.Data) + { + var item=callAutionData.Data[i]; + if (!item || !IFrameSplitOperator.IsNumber(item.Price)) + { + if (i==0 && isBeforeOpen && IFrameSplitOperator.IsNumber(this.YClose)) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(this.YClose); + + this.Canvas.strokeStyle=this.BeforeLineColor; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + } + + continue; + } + + if (isBeforeOpen) + { + var x=this.ChartFrame.GetLeftExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetLeftExtendYFromData(item.Price); + } + else + { + var x=this.ChartFrame.GetRightExtendXFromIndex(i,callAutionData); + var y=this.ChartFrame.GetRightExtendYFromData(item.Price); + } + + if (bFirstPoint) + { + this.Canvas.strokeStyle=this.BeforeLineColor; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + + aryPoint.push({X:x, Y:y }); + } + else + { + if (isHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + + aryPoint.push({X:x, Y:y }); + } + + ++drawCount; + } + + if (drawCount>0) + { + this.Canvas.stroke(); + } + + if (callAutionData.Ver==2.0 && this.BeforePoint.Radius>0) + { + this.Canvas.fillStyle=this.BeforePoint.Color; + for(var i in aryPoint) + { + var item=aryPoint[i]; + + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.arc(item.Y, item.X, this.BeforePoint.Radius, 0, 2 * Math.PI); + else this.Canvas.arc(item.X, item.Y, this.BeforePoint.Radius, 0, 2 * Math.PI); + this.Canvas.fill(); + } + + } + } + + this.FindColorLineItem=function(minuteItem) + { + if (!minuteItem || !this.ColorLineData) return null; + + for(var i in this.ColorLineData) + { + var item=this.ColorLineData[i]; + if (item.Date==minuteItem.Date && (minuteItem.Time>=item.Start && minuteItem.Time<=item.End)) + { + return item; + } + } + + return null; + } + + this.DrawColorLine=function() + { + if (!this.ColorLineData|| !this.Source || !this.Data) return; + var isHScreen=(this.ChartFrame.IsHScreen===true); + var border=this.ChartBorder.GetBorder(); + var xPointCount=this.ChartFrame.XPointCount; + var minuteCount=this.ChartFrame.MinuteCount; + var data=this.Data; + + var bFirstPoint=true; + var ptLast={}; //最后一个点 + var ptFirst={}; + var drawCount=0; + var preColor=null; + this.Canvas.save(); + + for(var i=data.DataOffset,j=0;i0) + { + this.Canvas.stroke(); + bFirstPoint=true; + } + continue; + } + + if (preColor && preColor!=colorItem.Color) + { + this.Canvas.stroke(); + bFirstPoint=true; + + /* + this.Canvas.strokeStyle=colorItem.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(ptLast.Y,ptLast.X); + else this.Canvas.moveTo(ptLast.X,ptLast.Y); + bFirstPoint=false; + preColor=colorItem.Color; + */ + } + + var x=this.ChartFrame.GetXFromIndex(j); + var y=this.ChartFrame.GetYFromData(value); + + if (bFirstPoint) + { + this.Canvas.strokeStyle=colorItem.Color; + if (IFrameSplitOperator.IsNumber(colorItem.LineWidth)) + this.Canvas.lineWidth=colorItem.LineWidth; + + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y,x); + else this.Canvas.moveTo(x,y); + bFirstPoint=false; + ptFirst={X:x,Y:y}; + preColor=colorItem.Color; + } + else + { + if (isHScreen) this.Canvas.lineTo(y,x); + else this.Canvas.lineTo(x,y); + } + + ptLast.X=x; + ptLast.Y=y; + ptLast.Price=value; + + ++drawCount; + } + + if (drawCount>0) + { + this.Canvas.stroke(); + } + + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + if (this.YClose==null) return range; + if (!this.IsShow) return range; + + range.Min=this.YClose; + range.Max=this.YClose; + + if (this.ChartBorder.LeftExtendWidth>10 && this.BeforeOpenData) + { + for(var i in this.BeforeOpenData.Data) + { + var item=this.BeforeOpenData.Data[i]; + if (!item) continue; + if (IFrameSplitOperator.IsNumber(item.Price)) + { + if (range.Max==null) range.Max=item.Price; + if (range.Min==null) range.Min=item.Price; + + if (range.Maxitem.Price) range.Min=item.Price; + } + + //集合竞价均线统计 + if (this.BeforeOpenData.Ver==3.0 && IFrameSplitOperator.IsNumber(item.AvPrice)) + { + if (range.Max==null) range.Max=item.AvPrice; + if (range.Min==null) range.Min=item.AvPrice; + + if (range.Maxitem.AvPrice) range.Min=item.AvPrice; + } + } + } + + + var data=this.Data; + for(var i=data.DataOffset,j=0;ivalue) range.Min=value; + } + + if (range.Max==this.YClose && range.Min==this.YClose) + { + range.Max=this.YClose+this.YClose*0.1; + range.Min=this.YClose-this.YClose*0.1; + return range; + } + + var distance=Math.max(Math.abs(this.YClose-range.Max),Math.abs(this.YClose-range.Min)); + range.Max=this.YClose+distance; + range.Min=this.YClose-distance; + + return range; + } + + this.GetTooltipData=function(x,y,tooltip) + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + if (isHScreen) return false; + if (!this.IsShow) return false; + if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false; + var position=this.ChartBorder.PtInClient(x,y,this.Canvas,isHScreen); + if (position!=1) return false; + + var data=this.Data; + var index=this.ChartFrame.GetXData(x); + var end=Math.ceil(index); + var start=Math.floor(index); + + if (end>=data.Data.length || start>=data.Data.length) return false; + + var lineWidth=5; + if (end==start) + { + var value=data.Data[start]; + } + else + { + var startValue=data.Data[start]; + var endValue=data.Data[end]; + var ptStart={X:this.ChartFrame.GetXFromIndex(start), Y:this.ChartFrame.GetYFromData(startValue)}; + var ptEnd={X:this.ChartFrame.GetXFromIndex(end), Y:this.ChartFrame.GetYFromData(endValue)}; + + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y+lineWidth); + this.Canvas.lineTo(ptStart.X,ptStart.Y-lineWidth); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y-lineWidth); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y+lineWidth); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x,y)) + { + tooltip.Data={ Index:index, Start:{ Index:start, Item:data.Data[start] }, End:{ Index:end, Value:data.Data[end]} }; + tooltip.ChartPaint=this; + tooltip.Type=5; //走势图线 + return true; + } + } + + return false; + } +} + +//分钟线叠加 支持横屏 +function ChartOverlayMinutePriceLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color="rgb(65,105,225)"; + this.MainData; //主图数据 + this.MainYClose; //主图股票的前收盘价 + this.SourceData; //原始数据 + + this.ClassName="ChartOverlayMinutePriceLine"; + this.Title; + this.Symbol; //叠加的股票代码 + this.YClose; //叠加的股票前收盘 + this.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID; + + this.Draw=function() + { + if (!this.Data) return; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen===true) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + var minuteCount=this.ChartFrame.MinuteCount; + + var bFirstPoint=true; + var drawCount=0; + var xOffset=0; + for(var i=this.Data.DataOffset+xOffset,j=0;i=minuteCount) //上一天的数据和这天地数据线段要断开 + { + bFirstPoint=true; + this.Canvas.stroke(); + drawCount=0; + } + } + + if (drawCount>0) this.Canvas.stroke(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + if (this.YClose==null) return range; + + range.Min=this.MainYClose; + range.Max=this.MainYClose; + for(var i=this.Data.DataOffset,j=0;ivalue) range.Min=value; + } + + if (range.Max==this.MainYClose && range.Min==this.MainYClose) + { + range.Max=this.MainYClose+this.MainYClose*0.1; + range.Min=this.MainYClose-this.MainYClose*0.1; + return range; + } + + var distance=Math.max(Math.abs(this.MainYClose-range.Max),Math.abs(this.MainYClose-range.Min)); + range.Max=this.MainYClose+distance; + range.Min=this.MainYClose-distance; + + //JSConsole.Chart.Log(`[ChartOverlayMinutePriceLine::GetMaxMin] max=${range.Max} min=${range.Min}`); + return range; + } + + this.GetTooltipData=function(x,y,tooltip) + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + if (isHScreen) return false; + if (!this.IsShow) return false; + if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false; + if (!IFrameSplitOperator.IsNumber(this.YClose) || !IFrameSplitOperator.IsNumber(this.MainYClose)) return false; + + var position=this.ChartBorder.PtInClient(x,y,this.Canvas,isHScreen); + if (position!=1) return false; + + var data=this.Data; + var index=this.ChartFrame.GetXData(x); + var end=Math.ceil(index); + var start=Math.floor(index); + + if (end>=data.Data.length || start>=data.Data.length) return false; + + var lineWidth=5; + if (end==start) + { + var value=data.Data[start]; + } + else + { + var startValue=data.Data[start].Close + startValue=startValue/this.YClose*this.MainYClose; + var endValue=data.Data[end].Close + endValue=endValue/this.YClose*this.MainYClose; + var ptStart={X:this.ChartFrame.GetXFromIndex(start), Y:this.ChartFrame.GetYFromData(startValue)}; + var ptEnd={X:this.ChartFrame.GetXFromIndex(end), Y:this.ChartFrame.GetYFromData(endValue)}; + + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y+lineWidth); + this.Canvas.lineTo(ptStart.X,ptStart.Y-lineWidth); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y-lineWidth); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y+lineWidth); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x,y)) + { + tooltip.Data={ Index:index, Start:{ Index:start, Item:data.Data[start] }, End:{ Index:end, Value:data.Data[end]} }; + tooltip.ChartPaint=this; + tooltip.Type=6; //走势图线 + //tooltip.Stock={Symbol:this.Symbol, Name:this.Title }; + return true; + } + } + + return false; + } +} + +//分钟信息地雷 支持横屏 +function ChartMinuteInfo() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMinuteInfo"; + this.Data=new Map() //Map key=date-time, value=[{Date, Time, Title, Type, ID:}] + this.SourceData; + this.ChartMinutePrice; + this.YClose; + this.HQChartBorder; + + this.TextColor=g_JSChartResource.MinuteInfo.TextColor; + this.Font=g_JSChartResource.MinuteInfo.Font; + this.PointColor=g_JSChartResource.MinuteInfo.PointColor; + this.LineColor=g_JSChartResource.MinuteInfo.LineColor; + this.TextBGColor=g_JSChartResource.MinuteInfo.TextBGColor; + this.PixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + this.TextHeight=20; + + this.TextRectCache=[]; + this.InfoDrawCache=[]; + this.FrameBottom; + this.FrameTop; + this.FrameLeft; + this.FrameRight; + this.YOffset=5; + this.IsHScreen=false; + this.IsDrawFull=false; //是否全屏画 + + this.TooltipRect=[]; //Rect + + this.SetOption=function(option) + { + if (option.TextColor) this.TextColor=option.TextColor; + if (option.TextBGColor) this.TextBGColor=option.TextBGColor; + if (option.Font) this.Font=option.Font; + if (option.PointColor) this.PointColor=option.PointColor; + if (option.LineColor) this.LineColor=option.LineColor; + if (option.TextHeight>0) this.TextHeight=option.TextHeight; + if (option.IsDrawFull==true) this.IsDrawFull=true; + } + + this.Draw=function() + { + this.TooltipRect=[]; + if (!this.ChartMinutePrice) return; + if (!this.Data || this.Data.size<=0) return; + + this.TextRectCache=[]; + this.InfoDrawCache=[]; + this.PixelTatio=GetDevicePixelRatio(); + this.YOffset=5*this.PixelTatio; + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + var xPointCount=this.ChartFrame.XPointCount; + var minuteCount=this.ChartFrame.MinuteCount; + + this.FrameBottom=this.ChartBorder.GetBottom(); + if (this.IsDrawFull && this.HQChartBorder) this.FrameBottom=this.HQChartBorder.GetBottom(); + this.FrameTop=this.ChartBorder.GetTop(); + this.FrameLeft=this.ChartBorder.GetLeft(); + this.FrameRight=this.ChartBorder.GetRight(); + if (this.IsHScreen) + { + this.FrameRight=this.ChartBorder.GetBottom(); + this.FrameLeft=this.ChartBorder.GetTop(); + this.FrameBottom=this.ChartBorder.GetLeft(); + this.FrameTop=this.ChartBorder.GetRight(); + } + + this.YClose=this.ChartMinutePrice.YClose; + + var data=this.ChartMinutePrice.Source; + + for(var i=data.DataOffset,j=0;ithis.YClose) + xData.X=xData.X.reverse(); + + var rtBorder={X:null, Y:y, Width:textWidth,Height:textHeight}; + if (!isDrawLeft) rtBorder.Y-=rtBorder.Height; + + this.FixHScreenTextRect(rtBorder,xData); + var InfoDrawItem={ Border:rtBorder, Start:{X:x,Y:y}, IsLeft:isDrawLeft, Title:showItem.Title }; + + this.InfoDrawCache.push(InfoDrawItem); + this.TextRectCache.push(rtBorder); + } + + this.DrawInfoLines=function(item) + { + var rtBorder=item.Border; + var isDrawLeft=item.IsLeft; + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(item.Start.X),item.Start.Y); + if (isDrawLeft) + { + this.Canvas.lineTo(ToFixedPoint(item.Start.X),rtBorder.Y); + } + else + { + if (this.IsHScreen) this.Canvas.lineTo(rtBorder.X,rtBorder.Y+rtBorder.Height); + else this.Canvas.lineTo(ToFixedPoint(item.Start.X),rtBorder.Y); + } + this.Canvas.stroke(); + + this.Canvas.fillStyle = this.PointColor; + this.Canvas.beginPath(); + this.Canvas.arc(item.Start.X,item.Start.Y, 5, 0, 2 * Math.PI); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.DrawInfoText=function(item) + { + var rtBorder=item.Border; + var x=rtBorder.X, y=rtBorder.Y; + if (item.BGColor) this.Canvas.fillStyle=item.BGColor + else this.Canvas.fillStyle=this.TextBGColor; + this.Canvas.fillRect(x, y, rtBorder.Width,rtBorder.Height); + + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.rect(x,y,rtBorder.Width,rtBorder.Height); + this.Canvas.stroke(); + + if (this.IsHScreen) + { + this.Canvas.save(); + this.Canvas.translate(rtBorder.X,rtBorder.Y); + this.Canvas.rotate(90 * Math.PI / 180); + x=0;y=0; + } + + this.Canvas.textAlign = 'left' + this.Canvas.textBaseline = 'middle'; + if (item.Color) this.Canvas.fillStyle=item.Color; + else this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + if (this.IsHScreen) this.Canvas.fillText(item.Title, x+2*this.PixelTatio, y-rtBorder.Width/2); + else this.Canvas.fillText(item.Title, x+2*this.PixelTatio, y+rtBorder.Height/2); + + if (this.IsHScreen) this.Canvas.restore(); + } + + this.FixTextRect=function(rect,yData) + { + for(var k in yData.Y) + { + var yItem=yData.Y[k]; + rect.Y=yItem.Value; + + var y; + for(var j=0;j<10;++j) + { + var isOverlap=false; + for(var i in this.TextRectCache) + { + var item=this.TextRectCache[i]; + if (this.IsOverlap(item, rect)) + { + isOverlap=true; + break; + } + } + + if (isOverlap==false) return; + + y=rect.Y; + y+=yItem.Offset; + if (y+rect.Height>this.FrameBottom || ythis.FrameTop) break; + + rect.X=x; + } + } + } + + this.IsOverlap=function(rc1, rc2) + { + if (rc1.X + rc1.Width > rc2.X &&rc2.X + rc2.Width > rc1.X &&rc1.Y + rc1.Height > rc2.Y &&rc2.Y + rc2.Height > rc1.Y) + return true; + else + return false; + } + + this.GetMaxMin=function() + { + var range={Min:null, Max:null}; + return range; + } + + this.GetTooltipData=function(x,y,tooltip) + { + for(var i in this.TooltipRect) + { + var item=this.TooltipRect[i]; + if (!item.Rect) continue; + var rect=item.Rect; + this.Canvas.beginPath(); + this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height); + if (this.Canvas.isPointInPath(x,y)) + { + //JSConsole.Chart.Log('[ChartMinuteInfo::GetTooltipData] info ', item); + tooltip.Data=item; + tooltip.ChartPaint=this; + tooltip.Type=3; //异动信息 + return true; + } + } + + return false; + } +} + +//MACD森林线 支持横屏 +function ChartMACD() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMACD"; + this.UpColor=g_JSChartResource.UpBarColor; + this.DownColor=g_JSChartResource.DownBarColor; + this.LineWidth=1; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.ChartFrame.IsHScreen===true) + { + this.HScreenDraw(); + return; + } + + var isMinute=this.IsMinuteFrame(); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + var xPointCount=this.ChartFrame.XPointCount; + var lockRect=this.GetLockRect(); + if (lockRect) chartright=lockRect.Left; + + var bFirstPoint=true; + var drawCount=0; + var yBottom=this.ChartFrame.GetYFromData(0); + + var lineWidth=this.LineWidth*GetDevicePixelRatio(); + if (this.LineWidth==50) lineWidth=dataWidth; + else if (lineWidth>dataWidth) lineWidth=dataWidth; + + var backupLineWidth=this.Canvas.lineWidth; + this.Canvas.lineWidth=lineWidth; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + } + var y=this.ChartFrame.GetYFromData(value); + + if (x>chartright) break; + + var xFix=ToFixedPoint2(lineWidth, x); //毛边修正 + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix,yBottom); + this.Canvas.lineTo(xFix,y); + + if (value>=0) this.Canvas.strokeStyle=this.UpColor; + else this.Canvas.strokeStyle=this.DownColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + } + + this.Canvas.lineWidth=backupLineWidth; + } + + this.HScreenDraw=function() + { + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var border=this.ChartBorder.GetHScreenBorder(); + var chartright=border.BottomEx; + var xPointCount=this.ChartFrame.XPointCount; + var lockRect=this.GetLockRect(); + if (lockRect) chartright=lockRect.Top; + + var yBottom=this.ChartFrame.GetYFromData(0); + + var lineWidth=this.LineWidth*GetDevicePixelRatio(); + if (this.LineWidth==50) lineWidth=dataWidth; + else if (lineWidth>dataWidth) lineWidth=dataWidth; + + var backupLineWidth=this.Canvas.lineWidth; + this.Canvas.lineWidth=lineWidth; + + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + this.Canvas.beginPath(); + this.Canvas.moveTo(yBottom,ToFixedPoint(x)); + this.Canvas.lineTo(y,ToFixedPoint(x)); + + if (value>=0) this.Canvas.strokeStyle=this.UpColor; + else this.Canvas.strokeStyle=this.DownColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + } + + this.Canvas.lineWidth=backupLineWidth; + } +} + +//柱子 +function ChartBar() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartBar"; + this.UpBarColor=g_JSChartResource.UpBarColor; + this.DownBarColor=g_JSChartResource.DownBarColor; + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + + var bFirstPoint=true; + var drawCount=0; + var yBottom=this.ChartFrame.GetYFromData(0); + if (dataWidth>=4) + { + yBottom=ToFixedRect(yBottom); //调整为整数 + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + var x=this.ChartFrame.GetXFromIndex(j); + var y=this.ChartFrame.GetYFromData(value); + + + if (value>0) this.Canvas.fillStyle=this.UpBarColor; + else this.Canvas.fillStyle=this.DownBarColor; + + //高度调整为整数 + var height=ToFixedRect(Math.abs(yBottom-y)); + if(yBottom-y>0) y=yBottom-height; + else y=yBottom+height; + this.Canvas.fillRect(ToFixedRect(left),y,ToFixedRect(dataWidth),height); + } + } + else //太细了 直接画柱子 + { + for(var i=this.Data.DataOffset,j=0;ichartright) break; + + var x=this.ChartFrame.GetXFromIndex(j); + var y=this.ChartFrame.GetYFromData(value); + + if (value>0) this.Canvas.strokeStyle=this.UpBarColor; + else this.Canvas.strokeStyle=this.DownBarColor; + + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x),y); + this.Canvas.lineTo(ToFixedPoint(x),yBottom); + this.Canvas.stroke(); + } + } + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=0; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;i1 && secondlinePoints.length>1) + { + this.DrawBand(firstlinePoints, secondlinePoints); + } + } + } + + this.DrawBand=function(aryFrist, arySecond) + { + this.Canvas.save(); + this.Canvas.beginPath(); + for(var i=0;i= 0; --i) + { + this.Canvas.lineTo(arySecond[i].x, arySecond[i].y); + } + this.Canvas.closePath(); + this.Canvas.clip(); + + this.Canvas.moveTo(aryFrist[0].x, this.ChartBorder.GetBottom()); + for (var i = 0; i < aryFrist.length; ++i) + { + this.Canvas.lineTo(aryFrist[i].x, aryFrist[i].y); + } + this.Canvas.lineTo(aryFrist[aryFrist.length-1].x, this.ChartBorder.GetBottom()); + this.Canvas.closePath(); + this.Canvas.fillStyle = this.FirstColor; + this.Canvas.fill(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(arySecond[0].x, this.ChartBorder.GetBottom()); + for (var i = 0; i < arySecond.length; ++i) + { + this.Canvas.lineTo(arySecond[i].x, arySecond[i].y); + } + this.Canvas.lineTo(arySecond[arySecond.length-1].x, this.ChartBorder.GetBottom()); + this.Canvas.closePath(); + this.Canvas.fillStyle = this.SecondColor; + this.Canvas.fill(); + + this.Canvas.restore(); + } + + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;ivalue.Value2?value.Value:value.Value2; + var minData = value.Value minData) + range.Min = minData; + } + + return range; + } +} + +// 线段围成的面积图 支持横屏 +function ChartLineArea() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = true; //面积图在K线前面画,否则回挡住K线的 + + this.ClassName="ChartLineArea"; + this.Color='rgb(56,67,99)'; + this.IsHScreen=false; + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var x = 0, y = 0, y2 = 0; + var aryPoint=[]; + for(var i=this.Data.DataOffset,j=0;i0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + continue; + } + + if (firstPoint) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.Line.X, item.Line.Y); + firstPoint=false; + } + else + { + this.Canvas.lineTo(item.Line.X, item.Line.Y); + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;ivalue.Value2?value.Value:value.Value2; + var minData = value.Value minData) range.Min = minData; + } + + return range; + } +} + +// 线段围成的面积图 支持横屏 +function ChartFillRGN() +{ + this.newMethod=ChartLineArea; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = true; //面积图在K线前面画,否则回挡住K线的 + this.IsHScreen=false; + + this.ClassName="ChartFillRGN"; + + this.DrawRGB=function(aryPoint) + { + var firstPoint=true; + var pointCount=0; + var aryLine2=[]; + var color=null; + for(var i in aryPoint) + { + var item=aryPoint[i]; + if (!item || (color && item.Color!=color) ) + { + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + color=null; + } + + if (!item) continue; + + if (firstPoint) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.Line.X, item.Line.Y); + firstPoint=false; + color=item.Color; + } + else + { + this.Canvas.lineTo(item.Line.X, item.Line.Y); + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + } + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var x = 0, y = 0, y2 = 0; + var aryPoint=[]; //点坐标 + for(var i=this.Data.DataOffset,j=0;iOPEN,RGB(255,0,0),1,RGB(0,255,0)) 表示沿收盘价填充宽度为成交量的区域,区域最大宽度为15像素,阳线时用红色,阴线时用绿色。 +function ChartFLOATRGN() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = false; //面积图在K线前面画,否则回挡住K线的 + + this.ClassName="ChartFLOATRGN"; + this.Color='rgb(56,67,99)'; + this.IsHScreen=false; + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + + var x = 0, y = 0, y2 = 0; + var aryPoint=[]; //点坐标 + for(var i=this.Data.DataOffset,j=0;i0) + { + var lastItem=aryLine2[aryLine2.length-1]; + var firstItem=aryLine2[0]; + if (lastItem.RightLine) + { + this.Canvas.lineTo(lastItem.RightLine.X, lastItem.RightLine.Y); + this.Canvas.lineTo(lastItem.RightLine2.X, lastItem.RightLine2.Y); + } + + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + + if (firstItem.LeftLine2) + { + this.Canvas.lineTo(firstItem.LeftLine2.X, firstItem.LeftLine2.Y); + } + + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + color=null; + } + + if (!item) continue; + + if (firstPoint) + { + this.Canvas.beginPath(); + if (item.LeftLine) + { + this.Canvas.moveTo(item.LeftLine.X, item.LeftLine.Y); + this.Canvas.lineTo(item.Line.X, item.Line.Y); + } + else + { + this.Canvas.moveTo(item.Line.X, item.Line.Y); + } + firstPoint=false; + color=item.Color; + } + else + { + this.Canvas.lineTo(item.Line.X, item.Line.Y); + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + var lastItem=aryLine2[aryLine2.length-1]; + var firstItem=aryLine2[0]; + if (lastItem.RightLine) + { + this.Canvas.lineTo(lastItem.RightLine.X, lastItem.RightLine.Y); + this.Canvas.lineTo(lastItem.RightLine2.X, lastItem.RightLine2.Y); + } + + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y); + } + + if (firstItem.LeftLine2) + { + this.Canvas.lineTo(firstItem.LeftLine2.X, firstItem.LeftLine2.Y); + } + + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + + this.Canvas.restore(); + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;i value) range.Min = value; + } + + return range; + } +} + +//线段围城的顶部或底部面积图 TODO:支持横屏 +function ChartFillBGRGN() +{ + this.newMethod=ChartFillRGN; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = true; //面积图在K线前面画,否则回挡住K线的 + this.IsHScreen=false; + + this.ClassName="ChartFillBGRGN"; + + + this.DrawVerticalRGN=function() + { + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + + var y=top, y2=bottom; + + var aryPoint=[]; //点坐标 + for(var i=this.Data.DataOffset,j=0;i0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + color=null; + } + + if (!item) continue; + + if (firstPoint) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + firstPoint=false; + color=item.Color; + } + else + { + this.Canvas.lineTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + this.Canvas.closePath(); + this.Canvas.fillStyle = color; + this.Canvas.fill(); + } + } + + this.GetMaxMin=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var range={}; + range.Min=null; + range.Max=null; + for(var i=this.Data.DataOffset,j=0;i value) range.Min = value; + } + + return range; + } +} + +// 通道面积图 支持横屏 +function ChartChannel() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + this.IsDrawFirst = true; + + this.ClassName="ChartChannel"; + this.IsHScreen=false; //是否是横屏 + this.PointCount=0; + this.DataWidth=0; + this.DistanceWidth=0; + this.ChartRight=0; //可以绘制的最右边 + this.LineColor='RGB(255,0,0)'; + this.LineDotted=[3,3]; + this.AreaColor='RGB(255,222,173)'; + this.LineWidth=1; + + this.CalculateData=function() //把数据通过nul值分割开, 并计算坐标 + { + var data=[]; + var lineData=[]; + for(var i=this.Data.DataOffset,j=0;i0) + { + data.push(lineData); + lineData=[]; //创建新的一组数据 + } + + continue; + } + + var x=this.ChartFrame.GetXFromIndex(j); + if (x>this.ChartRight) break; + var y=this.ChartFrame.GetYFromData(item.Value); + var y2=this.ChartFrame.GetYFromData(item.Value2); + + lineData.push({X:x, Y:y,Y2:y2}); + } + + if (lineData.length>0) data.push(lineData); + return data; + } + + this.DrawArea=function(lineData) + { + if (lineData.length<=0) return; + this.Canvas.beginPath(); + + var drawCount=0; + var firstItem=lineData[0]; + if (this.IsHScreen) this.Canvas.moveTo(firstItem.Y,firstItem.X); + else this.Canvas.moveTo(firstItem.X,firstItem.Y); + for(var i=1;i=0;--i) + { + var item=lineData[i]; + if (this.IsHScreen) this.Canvas.lineTo(item.Y2,item.X); + else this.Canvas.lineTo(item.X,item.Y2); + ++drawCount; + } + + this.Canvas.closePath(); + this.Canvas.fillStyle = this.AreaColor; + this.Canvas.fill(); + } + + this.DrawLine=function(lineData) + { + this.Canvas.strokeStyle=this.LineColor; + for(var k=0;k<2;++k) + { + var bFirstPoint=true; + var drawCount=0; + for(var i=0;i0) this.Canvas.stroke(); + } + } + + this.Draw=function() + { + if (this.ChartFrame.IsMinSize) return; + + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + this.PointCount=this.ChartFrame.XPointCount; + this.DataWidth=this.ChartFrame.DataWidth; + this.DistanceWidth=this.ChartFrame.DistanceWidth; + if (this.IsHScreen) this.ChartRight=this.ChartBorder.GetBottom(); + else this.ChartRight=this.ChartBorder.GetRight(); + + var drawData=this.CalculateData(); + if (!drawData || drawData.length<=0) return; + + this.Canvas.save(); + this.Canvas.lineWidth=this.LineWidth*GetDevicePixelRatio(); + this.Canvas.setLineDash(this.LineDotted); //虚线 + for(var i=0;ivalue.Value2?value.Value:value.Value2; + var minData = value.Value minData) + range.Min = minData; + } + + return range; + } +} + +//填充背景 支持横屏 +function ChartBackground() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartBackground"; + this.Color=null; + this.ColorAngle=0; //0 竖向 1 横向 + this.IsDrawFirst = true; //面积图在K线前面画,否则回挡住K线的 + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Color) return; + if (this.Color.length<=0) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + + if (this.Color.length==2) + { + if (this.IsHScreen) + { + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + } + else + { + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + } + } + + let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y); + gradient.addColorStop(0, this.Color[0]); + gradient.addColorStop(1, this.Color[1]); + this.Canvas.fillStyle=gradient; + } + else if (this.Color.length==1) + { + this.Canvas.fillStyle=this.Color[0]; + } + else + { + return; + } + + if (this.Name=="DRAWGBK2" || this.Name=="KLINE_BG") + { + this.DrawRegion(); + return; + } + + if (this.IsHScreen) + { + var left=this.ChartBorder.GetLeftEx(); + var top=this.ChartBorder.GetTop(); + var width=this.ChartBorder.GetWidthEx(); + var height=this.ChartBorder.GetHeight(); + } + else + { + var left=this.ChartBorder.GetLeft(); + var top=this.ChartBorder.GetTopEx(); + var width=this.ChartBorder.GetWidth(); + var height=this.ChartBorder.GetHeightEx(); + } + this.Canvas.fillRect(left, top,width, height); + } + + this.DrawRegion=function() + { + var xPointCount=this.ChartFrame.XPointCount; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + if (this.IsHScreen) + { + top=this.ChartBorder.GetRightEx(); + bottom=this.ChartBorder.GetLeftEx(); + } + + var aryPoint=[]; //点坐标 + for(var i=this.Data.DataOffset,j=0;i0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + if (this.IsHScreen) + { + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y+halfWidth); + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y-halfWidth); + } + else + { + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + + firstPoint=true; + pointCount=0; + aryLine2=[]; + color=null; + } + + if (!item) continue; + + if (firstPoint) + { + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(item.Line.X, item.Line.Y-halfWidth); + this.Canvas.lineTo(item.Line.X, item.Line.Y+halfWidth); + } + else + { + this.Canvas.moveTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + } + firstPoint=false; + color=item.Color; + } + else + { + if (this.IsHScreen) + { + this.Canvas.lineTo(item.Line.X, item.Line.Y-halfWidth); + this.Canvas.lineTo(item.Line.X, item.Line.Y+halfWidth); + } + else + { + this.Canvas.lineTo(item.Line.X-halfWidth, item.Line.Y); + this.Canvas.lineTo(item.Line.X+halfWidth, item.Line.Y); + } + } + + aryLine2.push(item); + ++pointCount; + } + + if (pointCount>0) + { + for(var j=aryLine2.length-1; j>=0; --j) + { + var item2=aryLine2[j]; + if (this.IsHScreen) + { + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y+halfWidth); + this.Canvas.lineTo(item2.Line2.X, item2.Line2.Y-halfWidth); + } + else + { + this.Canvas.lineTo(item2.Line2.X+halfWidth, item2.Line2.Y); + this.Canvas.lineTo(item2.Line2.X-halfWidth, item2.Line2.Y); + } + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + } + + this.GetMaxMin=function() + { + return { Min:null, Max:null }; + } +} + +//画矩形 +function ChartRectangle() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartRectangle"; + + this.Color=[]; + this.Rect; + this.BorderColor=g_JSChartResource.FrameBorderPen; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Color || !this.Rect) return; + if (this.Color.length<=0) return; + + this.Canvas.strokeStyle=this.BorderColor; + var bFill=false; + if (this.Color.length==2) + { + /* TODO 渐变下次做吧 + if (this.ColorAngle==0) + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() }; + } + else + { + var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() }; + var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() }; + } + + let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y); + gradient.addColorStop(0, this.Color[0]); + gradient.addColorStop(1, this.Color[1]); + this.Canvas.fillStyle=gradient; + */ + + this.Canvas.fillStyle=this.Color[0]; + bFill=true; + } + else if (this.Color.length==1) + { + if (this.Color[0]) + { + this.Canvas.fillStyle=this.Color[0]; + bFill=true; + } + } + else + { + return; + } + + var chartWidth=this.ChartBorder.GetWidth(); + var chartHeight=this.ChartBorder.GetHeightEx(); + var left=this.Rect.Left/1000*chartWidth; + var top=this.Rect.Top/1000*chartHeight; + var right=this.Rect.Right/1000*chartWidth; + var bottom=this.Rect.Bottom/1000*chartHeight; + + left=this.ChartBorder.GetLeft()+left + top=this.ChartBorder.GetTopEx()+top; + right=this.ChartBorder.GetLeft()+right; + bottom=this.ChartBorder.GetTopEx()+bottom; + var width=Math.abs(left-right); + var height=Math.abs(top-bottom); + if (bFill) this.Canvas.fillRect(left, top,width, height); + this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(top),ToFixedRect(width), ToFixedRect(height)); + this.Canvas.stroke(); + } +} + +// 文字+线段输出 +function ChartTextLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartTextLine"; + + this.Text; //Text=内容 Color + this.Line; //Type=线段类型 0=不画 1=直线 2=虚线, Color + this.Price; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Text || !this.Line || !IFrameSplitOperator.IsNumber(this.Price)) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var bottom=this.ChartBorder.GetBottomEx(); + var top=this.ChartBorder.GetTopEx(); + var y=this.ChartFrame.GetYFromData(this.Price); + var textWidth=0; + if (this.Text.Title) + { + var x=left+2*GetDevicePixelRatio(); + var yText=y; + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + var offsetY=8*GetDevicePixelRatio(); + if (y-offsetYbottom) + { + this.Canvas.textBaseline='bottom'; + yText=bottom; + } + + this.Canvas.fillStyle = this.Text.Color; + this.Canvas.font = this.Text.Font; + this.Canvas.fillText(this.Text.Title, x, yText); + textWidth=this.Canvas.measureText(this.Text.Title).width+4*GetDevicePixelRatio(); + } + + if (this.Line.Type>0) + { + if (this.Line.Type==2) //虚线 + { + this.Canvas.save(); + this.Canvas.setLineDash([3,5]); //虚线 + } + + var x=left+textWidth; + this.Canvas.strokeStyle=this.Line.Color; + this.Canvas.beginPath(); + this.Canvas.moveTo(x,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + this.Canvas.stroke(); + + if (this.Line.Type==2) + { + this.Canvas.restore(); + } + } + + } + + this.GetMaxMin=function() + { + var range={Min:null, Max:null}; + if (IFrameSplitOperator.IsNumber(this.Price)) + { + range.Min=this.Price; + range.Max=this.Price; + } + + return range; + } +} + +// 柱子集合 支持横屏 +function ChartMultiBar() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiBar"; + this.Bars=[]; // [ {Point:[ {Index, Value, Value2 }, ], Color:, Width: , Type: 0 实心 1 空心 }, ] + this.IsHScreen=false; + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + var dataWidth=this.ChartFrame.DataWidth; + var pixelRatio=GetDevicePixelRatio(); + + var drawBars=[]; + for(var i in this.Bars) + { + var item=this.Bars[i]; + var drawPoints={ Point:[], Color:item.Color, Width:dataWidth, Type:0 }; + if (item.Type>0) drawPoints.Type=item.Type; + if (item.Width>0) + { + drawPoints.Width=item.Width*pixelRatio; + if (drawPoints.Width>dataWidth) drawPoints.Width=dataWidth; + } + else + { + if(drawPoints.Width<4) drawPoints.Width=1*pixelRatio; + } + + for(var j in item.Point) + { + var point=item.Point[j]; + if (!IFrameSplitOperator.IsNumber(point.Index)) continue; + + var index=point.Index-offset; + if (index>=0 && index0) drawBars.push(drawPoints) + } + + for(var i in drawBars) + { + var item=drawBars[i]; + if (item.Width>=4) + { + if (item.Type==1) this.DrawHollowBar(item); + else this.DrawFillBar(item); + } + else + { + this.DrawLineBar(item); + } + } + } + + this.DrawLineBar=function(bar) + { + this.Canvas.strokeStyle=bar.Color; + var backupLineWidth=this.Canvas.lineWidth; + this.Canvas.lineWidth=bar.Width; + for(var i in bar.Point) + { + var item=bar.Point[i]; + + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(ToFixedPoint(item.Y),ToFixedPoint(item.X)); + this.Canvas.lineTo(ToFixedPoint(item.Y2),ToFixedPoint(item.X)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(item.X),ToFixedPoint(item.Y)); + this.Canvas.lineTo(ToFixedPoint(item.X),ToFixedPoint(item.Y2)); + } + + this.Canvas.stroke(); + } + + this.Canvas.lineWidth=backupLineWidth; + } + + this.DrawFillBar=function(bar) + { + this.Canvas.fillStyle=bar.Color; + for(var i in bar.Point) + { + var item=bar.Point[i]; + var x=item.X-(bar.Width/2); + var y=Math.min(item.Y,item.Y2); + var barWidth=bar.Width; + var barHeight=Math.abs(item.Y-item.Y2); + if (this.IsHScreen) + this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(x),ToFixedRect(barHeight),ToFixedRect(barWidth)); + else + this.Canvas.fillRect(ToFixedRect(x),ToFixedRect(y),ToFixedRect(barWidth),ToFixedRect(barHeight)); + } + } + + this.DrawHollowBar=function(bar) //空心柱子 + { + this.Canvas.strokeStyle=bar.Color; + var backupLineWidth=1; + for(var i in bar.Point) + { + var item=bar.Point[i]; + var x=item.X-(bar.Width/2); + var y=Math.min(item.Y,item.Y2); + var barWidth=bar.Width; + var barHeight=Math.abs(item.Y-item.Y2); + this.Canvas.beginPath(); + if (this.IsHScreen) + this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(x),ToFixedRect(barHeight),ToFixedRect(barWidth)); + else + this.Canvas.rect(ToFixedPoint(x),ToFixedPoint(y),ToFixedRect(barWidth),ToFixedRect(barHeight)); + + this.Canvas.stroke(); + } + + this.Canvas.lineWidth=backupLineWidth; + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null }; + var xPointCount=this.ChartFrame.XPointCount; + var start=this.Data.DataOffset; + var end=start+xPointCount; + for(var i in this.Bars) + { + var item=this.Bars[i]; + for(var j in item.Point) + { + var point=item.Point[j]; + if (point.Index>=start && point.IndexminValue) range.Min=minValue; + } + } + } + + return range; + } +} + +// 线段集合 支持横屏 +function ChartMultiLine() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiLine"; + this.Lines=[]; // [ {Point:[ {Index, Value }, ], Color: }, ] + this.LineDash; + this.LineWidth=1; + this.IsHScreen=false; + + //箭头配置 + this.ArrawAngle=35; //三角斜边一直线夹角 + this.ArrawLength=10; //三角斜边长度 + this.ArrawLineWidth=5; //箭头粗细 + this.Arrow={ Start:false, End:false }; //Start=是否绘制开始箭头 <- End=是否绘制结束箭头 -> + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + + var drawLines=[]; + var arrowLines=[]; + for(var i in this.Lines) + { + var line=this.Lines[i]; + var drawPoints={ Point:[], Color:line.Color }; + var drawArrowPoints={ Start:[], End:[] }; + if (line.BGColor) drawPoints.BGColor=line.BGColor; + for(var j in line.Point) + { + var point=line.Point[j]; + if (!IFrameSplitOperator.IsNumber(point.Index)) continue; + + var index=point.Index-offset; + if (index>=0 && index0) drawPoints.Point[drawPoints.Point.length-1].End=true; //点断开 + } + } + + if (drawPoints.Point.length>=2) + { + drawLines.push(drawPoints); + arrowLines.push(drawArrowPoints); + } + } + + var pixelRatio=GetDevicePixelRatio(); + this.Canvas.save(); + for(var i in drawLines) + { + if (this.LineDash) this.Canvas.setLineDash(this.LineDash); + if (IFrameSplitOperator.IsPlusNumber(this.LineWidth)) this.Canvas.lineWidth=this.LineWidth*pixelRatio; + else this.Canvas.lineWidth=1*pixelRatio; + var item=drawLines[i]; + this.DrawLine(item, arrowLines[i]); + } + this.Canvas.restore(); + } + + this.DrawLine=function(line, arrow) + { + if (line.BGColor) //背景色 + { + this.Canvas.fillStyle=line.BGColor; + for(var i in line.Point) + { + var item=line.Point[i]; + if (i==0) + { + this.Canvas.beginPath(); + if (this.IsHScreen) this.Canvas.moveTo(item.Y,item.X); + else this.Canvas.moveTo(item.X,item.Y); + } + else + { + if (this.IsHScreen) this.Canvas.lineTo(item.Y,item.X); + else this.Canvas.lineTo(item.X,item.Y); + } + } + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.Canvas.strokeStyle=line.Color; + var drawCount=0; + for(var i in line.Point) + { + var item=line.Point[i]; + if (drawCount==0) + { + this.Canvas.beginPath(); + if (this.IsHScreen) this.Canvas.moveTo(item.Y,item.X); + else this.Canvas.moveTo(item.X,item.Y); + ++drawCount; + } + else + { + if (this.IsHScreen) this.Canvas.lineTo(item.Y,item.X); + else this.Canvas.lineTo(item.X,item.Y); + ++drawCount; + } + + if (item.End==true) //点断了 要重新画 + { + if (drawCount>0) this.Canvas.stroke(); + drawCount=0; + } + } + + if (drawCount>0) this.Canvas.stroke(); + + //绘制箭头 + if (arrow.End.length==2 && this.Arrow.End==true) + this.DrawArrow(arrow.End[0],arrow.End[1]); + + if (arrow.Start.length==2 && this.Arrow.Start==true) + this.DrawArrow(arrow.Start[1],arrow.Start[0]); + + } + + this.DrawArrow=function(ptStart,ptEnd) + { + //计算箭头 + var theta=this.ArrawAngle; //三角斜边一直线夹角 + var headlen=this.ArrawLength; //三角斜边长度 + var angle = Math.atan2(ptStart.Y - ptEnd.Y, ptStart.X - ptEnd.X) * 180 / Math.PI, + angle1 = (angle + theta) * Math.PI / 180, + angle2 = (angle - theta) * Math.PI / 180, + topX = headlen * Math.cos(angle1), + topY = headlen * Math.sin(angle1), + botX = headlen * Math.cos(angle2), + botY = headlen * Math.sin(angle2); + + this.Canvas.beginPath(); + var arrowX = ptEnd.X + topX; + var arrowY = ptEnd.Y + topY; + this.Canvas.moveTo(arrowX,arrowY); + + this.Canvas.lineTo(ptEnd.X, ptEnd.Y); + + arrowX = ptEnd.X + botX; + arrowY = ptEnd.Y + botY; + this.Canvas.lineTo(arrowX,arrowY); + + this.Canvas.setLineDash([]); + this.Canvas.lineWidth=this.ArrawLineWidth*GetDevicePixelRatio(); + this.Canvas.stroke(); + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null }; + var xPointCount=this.ChartFrame.XPointCount; + var start=this.Data.DataOffset; + var end=start+xPointCount; + + for(var i in this.Lines) + { + var line=this.Lines[i]; + for(var j in line.Point) + { + var point=line.Point[j]; + if (point.Index>=start && point.Indexpoint.Value) range.Min=point.Value; + } + } + } + + return range; + } +} + +// 多文本集合 支持横屏 +function ChartMultiText() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiText"; + this.Texts=[]; //[ {Index:, Value:, Text:, Color:, Font: , Baseline:, Line:{ Color:, Dash:[虚线点], KData:"H/L", Offset:[5,10], Width:线粗细 }} ] + this.Font=g_JSChartResource.DefaultTextFont; + this.Color=g_JSChartResource.DefaultTextColor; + this.IsHScreen=false; //是否横屏 + + this.Draw=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + if (!this.Texts) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + + if (this.IsHScreen) + { + left=this.ChartBorder.GetTop(); + right=this.ChartBorder.GetBottom(); + } + + for(var i in this.Texts) + { + var item=this.Texts[i]; + + if (!item.Text) continue; + if (!IFrameSplitOperator.IsNumber(item.Index)) continue; + + var index=item.Index-offset; + if (index>=0 && index=right) + { + this.Canvas.textAlign='right'; + x=right; + } + else if (x-textWidth/2yPrice) //文字在下方 + { + yText-=item.Line.Offset[1]; + yPrice+=item.Line.Offset[0] + } + else if (yText0) this.Canvas.lineWidth=item.Line.Width; //线宽 + this.Canvas.strokeStyle = item.Line.Color; + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(yText, ToFixedPoint(x)); + this.Canvas.lineTo(yPrice,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yText); + this.Canvas.lineTo(ToFixedPoint(x),yPrice); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + } + } + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null }; + if (!this.Texts) return range; + + var xPointCount=this.ChartFrame.XPointCount; + var start=this.Data.DataOffset; + var end=start+xPointCount; + + for(var i in this.Texts) + { + var item=this.Texts[i]; + if (item.Index>=start && item.Indexitem.Value) range.Min=item.Value; + } + } + + return range; + } +} + +// 图标集合 支持横屏 +function ChartMultiSVGIcon() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiSVGIcon"; + this.Icon; //[ {Index:, Value:, Symbol:, Color:, Baseline:, Line:{ Color:, Dash:[虚线点], KData:"H/L", Offset:[5,10], Width:线粗细 } } ] + this.IconSize={ Max: g_JSChartResource.DRAWICON.Icon.MaxSize, Min:g_JSChartResource.DRAWICON.Icon.MinSize , //图标的最大最小值 + Zoom:{ Type:g_JSChartResource.DRAWICON.Icon.Zoom.Type , Value:g_JSChartResource.DRAWICON.Icon.Zoom.Value } //放大倍数 + }; + this.Family; + this.Color=g_JSChartResource.DefaultTextColor; + this.IsHScreen=false; + this.IconRect=[]; //0=序号,1=区域 + + this.Draw=function() + { + this.IconRect=[]; + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + if (!this.Family || !this.Icon) return; + if (!IFrameSplitOperator.IsNonEmptyArray(this.Icon)) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + this.DataWidth=this.ChartFrame.DataWidth; + this.DistanceWidth=this.ChartFrame.DistanceWidth; + + var border=this.GetBorder(); + if (this.IsHScreen) + { + var left=border.TopEx; + var right=border.BottomEx; + } + else + { + var left=border.LeftEx; + var right=border.RightEx; + } + + var fontSize=this.GetDynamicIconSize(this.DataWidth,this.DistanceWidth,this.IconSize.Max,this.IconSize.Min,this.IconSize.Zoom); + this.Canvas.font=fontSize+'px '+this.Family; + + for(var i=0; i=0 && index=right) + { + this.Canvas.textAlign='right'; + x+=this.DataWidth/2; + rtIcon.X=x-fontSize; + } + else if (x-textWidth/2yPrice) //文字在下方 + { + yText-=item.Line.Offset[1]; + yPrice+=item.Line.Offset[0] + } + else if (yText0) this.Canvas.lineWidth=item.Line.Width; //线宽 + this.Canvas.strokeStyle = item.Line.Color; + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(yText, ToFixedPoint(x)); + this.Canvas.lineTo(yPrice,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),yText); + this.Canvas.lineTo(ToFixedPoint(x),yPrice); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + } + } + } + + this.GetTooltipData=function(x,y,tooltip) + { + if (!IFrameSplitOperator.IsNonEmptyArray(this.IconRect)) return false; + for(var i=0; i=start && item.Indexitem.Value) range.Min=item.Value; + } + } + + return range; + } +} + +// 多dom节点 +function ChartMultiHtmlDom() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartMultiHtmlDom"; + this.Texts=[]; //[ {Index:, Value:, Text: ] Text=dom内容 + this.IsHScreen=false; //是否横屏 + this.DrawCallback; //function(op, obj) op:1=开始 2=结束 3=绘制单个数据 + this.DrawItem=[]; + this.EnableDraw=true; //是否允许绘制 + this.HQChart; + + this.Draw=function() + { + if (!this.EnableDraw) return; + + this.DrawItem=[]; + if (this.DrawCallback) this.DrawCallback(1, {Self:this} ); + + this.DrawDom(); + + if (this.DrawCallback) this.DrawCallback(2, { Self:this, DrawItem:this.DrawItem } ); + } + + this.DrawDom=function() + { + if (!this.IsShow || this.ChartFrame.IsMinSize) return; + if (!this.Data || this.Data.length<=0) return; + + this.IsHScreen=(this.ChartFrame.IsHScreen===true); + var xPointCount=this.ChartFrame.XPointCount; + var offset=this.Data.DataOffset; + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + var pixelTatio = GetDevicePixelRatio(); + if (this.HQChart) + { + var elementLeft=this.HQChart.UIElement.getBoundingClientRect().left; + var elementTop=this.HQChart.UIElement.getBoundingClientRect().top; + } + else + { + var elementLeft=null,elementTop=null; + } + + + for(var i in this.Texts) + { + var item=this.Texts[i]; + + if (!item.Text) continue; + if (!IFrameSplitOperator.IsNumber(item.Index)) continue; + + var isMinuteFrame=this.IsMinuteFrame(); + + var index=item.Index-offset; + var kItem=this.Data.Data[item.Index]; //K线数据 + var obj={ KData:kItem, Item:item, IsShow:false, Self:this }; + if (index>=0 && indexyPrice) //文字在下方 + { + yText-=item.Line.Offset[1]; + yPrice+=item.Line.Offset[0] + } + else if (yText0) lineWidth=item.Line.Width*pixelRatio; + this.Canvas.lineWidth=lineWidth; //线宽 + this.Canvas.strokeStyle = item.Line.Color; + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(yText, ToFixedPoint(x)); + this.Canvas.lineTo(yPrice,ToFixedPoint(x)); + } + else + { + this.Canvas.moveTo(ToFixedPoint2(lineWidth,x),yText); + this.Canvas.lineTo(ToFixedPoint2(lineWidth,x),yPrice); + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + } + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null }; + var xPointCount=this.ChartFrame.XPointCount; + var start=this.Data.DataOffset; + var end=start+xPointCount; + + for(var i in this.Texts) + { + var item=this.Texts[i]; + if (!IFrameSplitOperator.IsNumber(item.Index)) continue; + if (item.Index>=start && item.Indexitem.Value) range.Min=item.Value; + } + } + + return range; + } +} + +//锁 支持横屏 +function ChartLock() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartLock"; + this.WidthDiv = 0.2; // 框子宽度占比 + this.LockCount = 20; // 锁最新的几个数据 + this.BGColor = g_JSChartResource.LockBGColor; + this.TextColor = g_JSChartResource.LockTextColor; + this.Font = g_JSChartResource.DefaultTextFont; + this.Title = '🔒开通权限'; + this.LockRect=null; //上锁区域 + this.LockID; //锁ID + this.Callback; //回调 + this.IndexName; //指标名字 + this.MinWidth=null; //最小宽度 + + this.Draw=function(isDraw) + { + this.LockRect=null; + if (this.NotSupportMessage) + { + this.DrawNotSupportmessage(); + return; + } + + if (this.ChartFrame.IsHScreen===true) + { + this.HScreenDraw(isDraw); + return; + } + + var xOffset = this.ChartBorder.GetRight(); + var lOffsetWidth = 0; + if (this.ChartFrame.Data != null) + { + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0; + var chartright=this.ChartBorder.GetRight(); + var xPointCount=this.ChartFrame.XPointCount; + for(var i=this.ChartFrame.Data.DataOffset,j=0;ichartright) break; + } + lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount; + } + if (lOffsetWidth == 0) + { + lOffsetWidth = (xOffset - this.ChartBorder.GetLeft()) * this.WidthDiv; + } + var lLeft = xOffset - lOffsetWidth; + if (lLeft < this.ChartBorder.GetLeft()) + lLeft = this.ChartBorder.GetLeft(); + var lHeight = this.ChartBorder.GetBottom() - this.ChartBorder.GetTop(); + var lWidth = this.ChartBorder.GetRight() - lLeft; + + if (this.MinWidth>10 && lWidthchartright) break; + } + lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount; + } + if (lOffsetWidth == 0) + { + lOffsetWidth = (xOffset - this.ChartBorder.GetTop()) * this.WidthDiv; + } + + var lLeft = xOffset - lOffsetWidth; + if (lLeft < this.ChartBorder.GetTop()) lLeft = this.ChartBorder.GetTop(); + var lHeight = this.ChartBorder.GetRight()-this.ChartBorder.GetLeft(); + var lWidth = this.ChartBorder.GetBottom() - lLeft; + this.Canvas.fillStyle = this.BGColor; + this.Canvas.fillRect(this.ChartBorder.GetLeft(), lLeft,lHeight,lWidth); + + var xCenter = this.ChartBorder.GetLeft() + lHeight / 2; + var yCenter = lLeft + lWidth / 2; + this.Canvas.save(); + this.Canvas.translate(xCenter, yCenter); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'middle'; + this.Canvas.fillStyle = this.TextColor; + this.Canvas.font = this.Font; + this.Canvas.fillText(this.Title, 0, 0); + this.Canvas.restore(); + + this.LockRect={Left:this.ChartBorder.GetLeft(),Top:lLeft,Width:lHeight,Heigh:lWidth}; //保存上锁区域 + } + + //x,y是否在上锁区域 + this.GetTooltipData=function(x,y,tooltip) + { + if (this.LockRect==null) return false; + + this.Canvas.beginPath(); + this.Canvas.rect(this.LockRect.Left,this.LockRect.Top,this.LockRect.Width,this.LockRect.Heigh); + if (this.Canvas.isPointInPath(x,y)) + { + tooltip.Data={ ID:this.LockID, Callback:this.Callback, IndexName:this.IndexName }; + tooltip.ChartPaint=this; + return true; + } + + return false; + } +} + +//买卖盘 +function ChartBuySell() +{ + this.newMethod=ChartSingleText; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartBuySell"; + this.TextFont=g_JSChartResource.KLineTrain.Font; //"bold 14px arial"; //买卖信息字体 + this.LastDataIcon=g_JSChartResource.KLineTrain.LastDataIcon; //{Color:'rgb(0,0,205)',Text:'↓'}; + this.BuyIcon=g_JSChartResource.KLineTrain.BuyIcon; //{Color:'rgb(0,0,205)',Text:'B'}; + this.SellIcon=g_JSChartResource.KLineTrain.SellIcon; //{Color:'rgb(0,0,205)',Text:'S'}; + this.BuySellData=new Map(); //Key=数据索引index Value:Data:[ { Op: 买/卖 0=buy 1=sell, Date:, Time, Price: Vol:}, ] + this.IconFont=g_JSChartResource.KLineTrain.IconFont; + + this.AddTradeItem=function(tradeItem) + { + if (this.BuySellData.has(tradeItem.Key)) + { + var Trade=this.BuySellData.get(tradeItem.Key); + Trade.Data.push(tradeItem); + } + else + { + this.BuySellData.set(tradeItem.Key, { Data:[tradeItem] }); + } + } + + this.ClearTradeData=function() + { + this.BuySellData=new Map(); + } + + this.Draw=function() + { + if (!this.Data || !this.Data.Data) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var dataWidth=this.ChartFrame.DataWidth; + var distanceWidth=this.ChartFrame.DistanceWidth; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen===true) chartright=this.ChartBorder.GetBottom(); + var xPointCount=this.ChartFrame.XPointCount; + + if (this.IconFont) + { + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + var iconSize=dataWidth+distanceWidth; + var minIconSize=18*pixelTatio; + if (iconSizechartright) break; + + if (i==this.Data.Data.length-1) + { + //最后一个位置 画一个箭头 + var x=this.ChartFrame.GetXFromIndex(j); + var yHigh=this.ChartFrame.GetYFromData(value.High); + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='bottom'; + if (this.IconFont) + { + this.Canvas.fillStyle=this.IconFont.Last.Color + this.DrawText(this.IconFont.Last.Text,x,yHigh,isHScreen); + } + else + { + this.Canvas.fillStyle=this.LastDataIcon.Color; + this.Canvas.font=this.TextFont; + this.DrawText(this.LastDataIcon.Text,x,yHigh,isHScreen); + } + } + + var key=i; + if (!this.BuySellData.has(key)) continue; + + var trade=this.BuySellData.get(key); + var yHigh=this.ChartFrame.GetYFromData(value.High); + var yLow=this.ChartFrame.GetYFromData(value.Low); + var drawInfo=[false, false]; //0=buy 1=sell + for(var k in trade.Data) + { + if (drawInfo[0]==true && drawInfo[1]==true) break; //买卖图标只画一次 + + var bsItem=trade.Data[k]; + if (bsItem.Op==0 && drawInfo[0]==false) //买 标识在最低价上 + { + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='top'; + if (this.IconFont) + { + this.Canvas.fillStyle=this.IconFont.Buy.Color + this.DrawText(this.IconFont.Buy.Text,x,yLow,isHScreen); + } + else + { + this.Canvas.fillStyle=this.BuyIcon.Color; + this.DrawText(this.BuyIcon.Text,x,yLow,isHScreen); + } + + drawInfo[0]=true; + } + else if (bsItem.Op==1 && drawInfo[1]==false) //卖 标识在最高价上 + { + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='bottom'; + if (this.IconFont) + { + this.Canvas.fillStyle=this.IconFont.Sell.Color + this.DrawText(this.IconFont.Sell.Text,x,yHigh,isHScreen); + } + else + { + this.Canvas.fillStyle=this.SellIcon.Color; + this.DrawText(this.SellIcon.Text,x,yHigh,isHScreen); + } + + drawInfo[1]=true; + } + } + } + } +} + +//深度图 +function ChartOrderbookDepth() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName="ChartOrderbookDepth"; + this.Data=null; + + this.AskColor={ Line:g_JSChartResource.DepthChart.AskColor.Line, Area:g_JSChartResource.DepthChart.AskColor.Area } //卖 + this.BidColor={ Line:g_JSChartResource.DepthChart.BidColor.Line, Area:g_JSChartResource.DepthChart.BidColor.Area } //买 + this.LineWidth=g_JSChartResource.DepthChart.LineWidth; + + this.Draw=function() + { + if (!this.Data) return; + + var lineWidthBackup=this.Canvas.lineWidth; + this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); + this.DrawArea(this.Data.Bids, this.BidColor.Line, this.BidColor.Area, true); + this.DrawArea(this.Data.Asks, this.AskColor.Line, this.AskColor.Area, false); + this.Canvas.lineWidth=lineWidthBackup; + } + + this.DrawArea=function(aryData, colorLine, colorArea, isLeft) + { + var xRange=this.ChartFrame.VerticalRange; + var aryPoint=[]; + for(var i in aryData) + { + var item=aryData[i]; + if (isLeft) + { + if (item.PricexRange.Max) break; + } + + var x=this.ChartFrame.GetXFromIndex(item.Price); + var y=this.ChartFrame.GetYFromData(item.Vol); + aryPoint.push({X:x,Y:y}); + } + if (aryPoint.length<=1) return; + + var left=this.ChartBorder.GetLeft(); + var bottom=this.ChartBorder.GetBottom(); + var right=this.ChartBorder.GetRight(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, bottom); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.lineTo(item.X,item.Y); + } + + this.Canvas.lineTo(isLeft?left:right,aryPoint[aryPoint.length-1].Y); + this.Canvas.lineTo(isLeft?left:right,bottom); + this.Canvas.lineTo(aryPoint[0].X,bottom); + this.Canvas.closePath(); + this.Canvas.fillStyle = colorArea; + this.Canvas.fill(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, bottom); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.lineTo(item.X,item.Y); + } + this.Canvas.lineTo(isLeft?left:right,aryPoint[aryPoint.length-1].Y); + this.Canvas.strokeStyle=colorLine; + this.Canvas.stroke(); + } + + this.GetMaxMin=function() + { + var range={ Min:null, Max:null, XMin:null, XMax:null }; + var xRange=this.ChartFrame.VerticalRange; + + for(var i in this.Data.Asks) + { + var item=this.Data.Asks[i]; + if (item.Price>xRange.Max) break; + + if (range.XMin==null || range.XMin>item.Price) range.XMin=item.Price; + if (range.XMax==null || range.XMaxitem.Vol) range.Min=item.Vol; + if (range.Max==null || range.Maxitem.Price) range.XMin=item.Price; + if (range.XMax==null || range.XMaxitem.Vol) range.Min=item.Vol; + if (range.Max==null || range.Max0)) return this.DrawEmptyData(); + + let left=this.ChartBorder.GetLeft(); + let right=this.ChartBorder.GetRight(); + let top=this.ChartBorder.GetTop(); + let bottom=this.ChartBorder.GetBottom(); + let width=this.ChartBorder.GetWidth(); + let height=this.ChartBorder.GetHeight(); + + if(isNaN(this.Radius)){ + let str = this.Radius.replace("%",""); + str = str/100; + if(width >= height){ + this.Radius = str*height; + } + if(width < height) this.Radius = str*width; + } + + + this.Canvas.save(); + this.Canvas.translate(width/2,height/2); + + let totalValue=0; //求和 + for(let i in this.Data.Data) + { + totalValue += this.Data.Data[i].Value; + } + let start = 0; + let end = 0; + //画饼图 + for(let i in this.Data.Data) + { + let item =this.Data.Data[i]; + let rate=(item.Value/totalValue).toFixed(2); //占比 + //JSConsole.Chart.Log('[ChartPie::Draw]', i, rate, item); + + // 绘制扇形 + this.Canvas.beginPath(); + this.Canvas.moveTo(0,0); + + end += rate*2*Math.PI;//终止角度 + this.Canvas.strokeStyle = "white"; + this.Canvas.fillStyle = item.Color; + this.Canvas.arc(0,0,this.Radius,start,end); + this.Canvas.fill(); + this.Canvas.closePath(); + this.Canvas.stroke(); + + // 绘制直线 + this.Canvas.beginPath(); + this.Canvas.strokeStyle = item.Color; + this.Canvas.moveTo(0,0); + let x = (this.Radius + this.Distance)*Math.cos(end- (end-start)/2); + let y = (this.Radius + this.Distance)*Math.sin(end - (end-start)/2); + this.Canvas.lineTo(x,y); + // JSConsole.Chart.Log(x,y,"xy") + + // 绘制横线 + let txtLine = this.txtLine; + let paddingX = this.paddingX; + this.Canvas.textAlign = 'left'; + if( end - (end-start)/2 < 1.5*Math.PI && end - (end-start)/2 > 0.5*Math.PI ){ + + txtLine = - this.txtLine; + paddingX = - this.paddingX; + this.Canvas.textAlign = 'right'; + } + this.Canvas.lineTo( x + txtLine, y ); + this.Canvas.stroke(); + + // 写文字 + if(item.Text){ + this.Canvas.fillText( item.Text, x + txtLine + paddingX, y ); + }else{ + let text = `${item.Name}:${item.Value}`; + this.Canvas.fillText( text, x + txtLine + paddingX, y ); + } + + + start += rate*2*Math.PI;//起始角度 + } + + this.Canvas.restore(); + } + + //空数据 + this.DrawEmptyData=function() + { + JSConsole.Chart.Log('[ChartPie::DrawEmptyData]') + } +} + + +/* + 雷达图 +*/ +function ChartRadar() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.BorderPoint=[]; //边框点 + this.DataPoint=[]; //数据点 + this.CenterPoint={}; + this.StartAngle=0; + this.Color='rgb(198,198,198)'; + this.AreaColor='rgba(242,154,118,0.4)'; //面积图颜色 + this.AreaLineColor='rgb(242,154,118)'; + this.TitleFont=24*GetDevicePixelRatio()+'px 微软雅黑'; + this.TitleColor='rgb(102,102,102)'; + this.BGColor = ['rgb(255,255,255)', 'rgb(224,224,224)']//背景色 + + this.DrawBorder=function() //画边框 + { + if (this.BorderPoint.length<=0) return; + + this.Canvas.font=this.TitleFont; + this.Canvas.strokeStyle = this.Color; + const aryBorder=[1,0.8,0.6,0.4,0.2]; + for (let j in aryBorder) + { + var rate = aryBorder[j]; + var isFirstDraw=true; + for(let i in this.BorderPoint) + { + var item=this.BorderPoint[i]; + item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180) * rate; + item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180) * rate; + if (isFirstDraw) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X,item.Y); + isFirstDraw=false; + } + else + { + this.Canvas.lineTo(item.X,item.Y); + } + } + + this.Canvas.closePath(); + this.Canvas.stroke(); + this.Canvas.fillStyle = this.BGColor[j%2==0?0:1]; + this.Canvas.fill(); + } + + this.Canvas.beginPath(); + for(let i in this.BorderPoint) + { + var item=this.BorderPoint[i]; + item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180); + item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180); + this.Canvas.moveTo(this.CenterPoint.X,this.CenterPoint.Y); + this.Canvas.lineTo(item.X,item.Y); + this.DrawText(item); + } + this.Canvas.stroke(); + } + + this.DrawArea=function() + { + if (!this.DataPoint || this.DataPoint.length<=0) return; + + this.Canvas.fillStyle = this.AreaColor; + this.Canvas.strokeStyle = this.AreaLineColor; + this.Canvas.beginPath(); + var isFirstDraw=true; + for(let i in this.DataPoint) + { + var item=this.DataPoint[i]; + if (isFirstDraw) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X,item.Y); + isFirstDraw=false; + } + else + { + this.Canvas.lineTo(item.X,item.Y); + } + } + + this.Canvas.closePath(); + this.Canvas.fill(); + this.Canvas.stroke(); + } + + this.DrawText=function(item) + { + if (!item.Text) return; + + //JSConsole.Chart.Log(item.Text, item.Angle); + this.Canvas.fillStyle = this.TitleColor; + var xText = item.X, yText = item.Y; + + //显示每个角度的位置 + if (item.Angle > 0 && item.Angle < 45) { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + xText += 2; + } + else if (item.Angle >= 0 && item.Angle < 90) { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'top'; + xText += 2; + } + else if (item.Angle >= 90 && item.Angle < 135) { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'top'; + xText -= 2; + } + else if (item.Angle >= 135 && item.Angle < 180) { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'top'; + xText -= 2; + } + else if (item.Angle >= 180 && item.Angle < 225) { + this.Canvas.textAlign = 'right'; + this.Canvas.textBaseline = 'middle'; + xText -= 2; + } + else if (item.Angle >= 225 && item.Angle <= 270) { + this.Canvas.textAlign = 'center'; + this.Canvas.textBaseline = 'bottom'; + } + else if (item.Angle > 270 && item.Angle < 315) { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'bottom'; + xText += 2; + } + else { + this.Canvas.textAlign = 'left'; + this.Canvas.textBaseline = 'middle'; + xText += 2; + } + + this.Canvas.fillText(item.Text, xText, yText); + } + + this.Draw=function() + { + this.BorderPoint=[]; + this.DataPoint=[]; + this.CenterPoint={}; + if (!this.Data || !this.Data.Data || !(this.Data.Data.length>0)) + this.CalculatePoints(null); + else + this.CalculatePoints(this.Data.Data); + + this.DrawBorder(); + this.DrawArea(); + } + + this.CalculatePoints=function(data) + { + let left=this.ChartBorder.GetLeft(); + let right=this.ChartBorder.GetRight(); + let top=this.ChartBorder.GetTop(); + let bottom=this.ChartBorder.GetBottom(); + let width=this.ChartBorder.GetWidth(); + let height=this.ChartBorder.GetHeight(); + + let ptCenter={X:left+width/2, Y:top+height/2}; //中心点 + let radius=Math.min(width/2,height/2)-2 //半径 + let count=Math.max(5,data?data.length:0); + let averageAngle=360/count; + for(let i=0;i=1) value=1; + var dataRadius=radius*value; + ptData.X=ptCenter.X+dataRadius*Math.cos(angle*Math.PI/180); + ptData.Y=ptCenter.Y+dataRadius*Math.sin(angle*Math.PI/180); + } + + this.DataPoint.push(ptData); + } + + this.BorderPoint.push(ptBorder); + } + + this.CenterPoint=ptCenter; + } + + //空数据 + this.DrawEmptyData=function() + { + JSConsole.Chart.Log('[ChartPie::DrawEmptyData]') + } +} + +/* + 中国地图 +*/ + + +function ChartChinaMap() +{ + this.newMethod=IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ImageData=null; + this.Left; + this.Top; + this.Width; + this.Height; + this.ImageWidth; + this.ImageHeight; + + this.DefaultColor=[217,222,239]; + + this.Color= + [ + {Name:'海南', Color:'rgb(217,222,223)'}, + {Name:'内蒙古', Color:'rgb(217,222,225)'}, + {Name:'新疆', Color:'rgb(217,222,226)'}, + {Name:'青海', Color:'rgb(217,222,227)'}, + {Name:'西藏', Color:'rgb(217,222,228)'}, + {Name:'云南', Color:'rgb(217,222,229)'}, + {Name:'黑龙江', Color:'rgb(217,222,230)'}, + {Name:'吉林', Color:'rgb(217,222,231)'}, + {Name:'辽宁', Color:'rgb(217,222,232)'}, + {Name:'河北', Color:'rgb(217,222,233)'}, + {Name:'山东', Color:'rgb(217,222,234)'}, + {Name:'江苏', Color:'rgb(217,222,235)'}, + {Name:'浙江', Color:'rgb(217,222,236)'}, + {Name:'福建', Color:'rgb(217,222,237)'}, + {Name:'广东', Color:'rgb(217,222,238)'}, + {Name:'广西', Color:'rgb(217,222,239)'}, + {Name:'贵州', Color:'rgb(217,222,240)'}, + {Name:'湖南', Color:'rgb(217,222,241)'}, + {Name:'江西', Color:'rgb(217,222,242)'}, + {Name:'安徽', Color:'rgb(217,222,243)'}, + {Name:'湖北', Color:'rgb(217,222,244)'}, + {Name:'重庆', Color:'rgb(217,222,245)'}, + {Name:'四川', Color:'rgb(217,222,246)'}, + {Name:'甘肃', Color:'rgb(217,222,247)'}, + {Name:'陕西', Color:'rgb(217,222,248)'}, + {Name:'山西', Color:'rgb(217,222,249)'}, + {Name:'河南', Color:'rgb(217,222,250)'} + ]; + + this.Draw=function() + { + let left=this.ChartBorder.GetLeft()+1; + let right=this.ChartBorder.GetRight()-1; + let top=this.ChartBorder.GetTop()+1; + let bottom=this.ChartBorder.GetBottom()-1; + let width=this.ChartBorder.GetWidth()-2; + let height=this.ChartBorder.GetHeight()-2; + + let imageWidth=CHINA_MAP_IMAGE.width; + let imageHeight=CHINA_MAP_IMAGE.height; + + let drawImageWidth=imageWidth; + let drawImageHeight=imageHeight; + + if (height0) + { + for(let i in this.Data) + { + let item=this.Data[i]; + nameMap.set(item.Name,item.Color) + } + } + + JSConsole.Chart.Log(this.Data); + for(let i in this.Color) + { + let item=this.Color[i]; + if (nameMap.has(item.Name)) + { + colorMap.set(item.Color,nameMap.get(item.Name)); + } + else + { + defaultColorSet.add(item.Color); + } + } + + var color; + for (let i=0;i0) ++lineCount; + if (MARKET_SUFFIX_NAME.IsFutures(upperSymbol) && IFrameSplitOperator.IsNumber(klineData.Position)) ++lineCount; //持仓 + } + + //this.TitleColor=this.KLineTitlePaint.UnchagneColor; + this.IsHScreen=this.ChartFrame.IsHScreen===true; + this.Canvas.font=this.Font[0]; + var defaultfloatPrecision=GetfloatPrecision(this.HQChart.Symbol);//价格小数位数 + var maxText=' 擎: 9999.99亿 '; + if (defaultfloatPrecision>=5) maxText=` 擎: ${99.99.toFixed(defaultfloatPrecision)} `; //小数位数太多了 + this.Width=this.Canvas.measureText(maxText).width; + var lineHeight=this.GetFontHeight(); + if (lineHeight>this.LineHeight) this.LineHeight=lineHeight; + this.Height=this.LineHeight*lineCount+2*GetDevicePixelRatio()*2; + if (klineData && klineData.High>0) //最大值显示宽度 + { + maxText=` 擎: ${klineData.High.toFixed(defaultfloatPrecision)} `; + var textWidth=this.Canvas.measureText(maxText).width; + if (textWidth>this.Width) this.Width=textWidth; + } + + this.CalculateShowPosition(); + this.DrawBG(); + this.DrawTooltipData(klineData); + this.DrawBorder(); + } + + //判断显示位置 + this.CalculateShowPosition=function() + { + this.ShowPosition=0; + if (!this.LatestPoint) return; + + if(this.IsHScreen) + { + var top=this.ChartBorder.GetTop(); + var height=this.ChartBorder.GetHeight(); + var yCenter=top+height/2; + if (this.LatestPoint.Y0 && MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) + { + var value=(item.Close-item.YFClose)/item.YFClose*100; + var color = this.KLineTitlePaint.GetColor(value, 0); + var text = value.toFixed(2)+'%'; + } + else if (item.YClose>0) + { + var value=(item.Close-item.YClose)/item.YClose*100; + var color = this.KLineTitlePaint.GetColor(value, 0); + var text = value.toFixed(2)+'%'; + } + else + { + var text='--.--'; + var color=this.KLineTitlePaint.GetColor(0, 0); + } + this.Canvas.fillStyle=color; + this.Canvas.fillText(text,left+labelWidth,top); + + + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Vol',this.LanguageID); + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(text, left,top); + var vol=item.Vol; + if (upperSymbol && MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) vol/=100; //A股统一转成手 + var text=IFrameSplitOperator.FromatIntegerString(vol,2,this.LanguageID); + this.Canvas.fillStyle=this.VolColor; + this.Canvas.fillText(text,left+labelWidth,top); + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID); + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(text, left,top); + var text=IFrameSplitOperator.FormatValueString(item.Amount,2,this.LanguageID); + this.Canvas.fillStyle=this.AmountColor; + this.Canvas.fillText(text,left+labelWidth,top); + } + + //换手率 + if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.HQChart.Symbol) && item.FlowCapital>0) + { + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Exchange',this.LanguageID); + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(text, left,top); + var value=item.Vol/item.FlowCapital*100; + var text=value.toFixed(2)+'%'; + this.Canvas.fillText(text,left+labelWidth,top); + } + + //持仓量 + var upperSymbol=this.HQChart.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) + { + this.Canvas.fillStyle=this.TitleColor; + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Position',this.LanguageID); + this.Canvas.fillText(text, left,top); + var text=IFrameSplitOperator.FromatIntegerString(item.Position,2,this.LanguageID); + this.Canvas.fillText(text,left+labelWidth,top); + } + + if (this.IsHScreen) this.Canvas.restore(); + } + + this.DrawBorder=function() + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var left=this.GetLeft(); + var top=this.GetTop(); + this.Canvas.strokeStyle=this.BorderColor; + if (isHScreen) this.Canvas.strokeRect(ToFixedPoint(left),ToFixedPoint(top),this.Height,this.Width); + else this.Canvas.strokeRect(ToFixedPoint(left),ToFixedPoint(top),ToFixedRect(this.Width),ToFixedRect(this.Height)); + } + + this.DrawBG=function() + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var left=this.GetLeft(); + var top=this.GetTop(); + this.Canvas.fillStyle=this.BGColor; + if (isHScreen) this.Canvas.fillRect(left,top,this.Height,this.Width); + else this.Canvas.fillRect(left,top,this.Width,this.Height); + } + + //设置参数接口 + this.SetOption=function(option) + { + if (option.LineHeight>0) this.LineHeight=option.LineHeight*GetDevicePixelRatio(); + if (option.BGColor) this.BGColor=option.BGColor; + if (option.LanguageID>0) this.LanguageID=option.LanguageID; + } +} + +function MinuteTooltipPaint() +{ + this.newMethod=KLineTooltipPaint; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='MinuteTooltipPaint'; + this.Left=1*GetDevicePixelRatio(); + this.Top=1*GetDevicePixelRatio(); + this.YClose; + this.IsShowAveragePrice=true; //是否显示均价 + + this.GetTop=function() + { + if (this.IsHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + if (this.ShowPosition==0) + return border.TopEx+this.Left; + else + return border.BottomEx-this.Width-this.Left; + } + else + { + var border=this.ChartBorder.GetBorder(); + return border.Top+this.Top; + } + } + + this.GetLeft=function() + { + if (this.IsHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + return border.Right-this.Height-this.Top; + } + else + { + var border=this.ChartBorder.GetBorder(); + if (this.ShowPosition==0) + return border.LeftEx+this.Left; + else + return border.RightEx-this.Width-this.Left; + } + } + + this.DrawTooltipData=function(item) + { + //JSConsole.Chart.Log('[KLineTooltipPaint::DrawKLineData] ', item); + if (!this.HQChart.Symbol) return; + + var defaultfloatPrecision=GetfloatPrecision(this.HQChart.Symbol);//价格小数位数 + var left=this.GetLeft()+2*GetDevicePixelRatio(); + var top=this.GetTop()+3*GetDevicePixelRatio(); + this.YClose=this.KLineTitlePaint.YClose; + var upperSymbol=this.HQChart.Symbol.toUpperCase(); + + if (this.IsHScreen) + { + this.Canvas.save(); + var x=this.GetLeft()+this.Height, y=this.GetTop(); + + this.Canvas.translate(x, y); + this.Canvas.rotate(90 * Math.PI / 180); + + //x, y 作为原点 + left=2*GetDevicePixelRatio(); + top=3*GetDevicePixelRatio(); + } + + this.Canvas.textBaseline="top"; + this.Canvas.textAlign="left"; + this.Canvas.font=this.Font[0]; + var labelWidth=this.Canvas.measureText('擎: ').width; + + var aryDateTime=item.DateTime.split(' '); + if (aryDateTime && aryDateTime.length==2) + { + var text=IFrameSplitOperator.FormatDateString(aryDateTime[0]); + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(text, left,top); + + top+=this.LineHeight; + text=IFrameSplitOperator.FormatTimeString(aryDateTime[1]); + this.Canvas.fillText(text, left,top); + } + + var close=item.Close; + var increase=item.Increase; + var vol=item.Vol; + var amount=item.Amount; + if (item.Before) //读取盘前数据 + { + close=item.Before.Close; + increase=item.Before.Increase; + vol=item.Before.Vol; + amount=item.Before.Amount; + } + + //最新价格 + top+=this.LineHeight; + this.Canvas.fillStyle=this.TitleColor; + text=g_JSChartLocalization.GetText('Tooltip-Price',this.LanguageID); + this.Canvas.fillText(text, left,top); + var color=this.KLineTitlePaint.GetColor(close,this.YClose); + var text=close.toFixed(defaultfloatPrecision); + this.Canvas.fillStyle=color; + this.Canvas.fillText(text,left+labelWidth,top); + + var isShowAvPrice=true; + if (item.Before) isShowAvPrice=false; //集合竞价均价 + else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) isShowAvPrice=false; //外汇没有均价 + else if (MARKET_SUFFIX_NAME.IsET(upperSymbol) && !MARKET_SUFFIX_NAME.IsETShowAvPrice(upperSymbol)) isShowAvPrice=false; + + //均价 + if (isShowAvPrice && IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice) + { + top+=this.LineHeight; + this.Canvas.fillStyle=this.TitleColor; + text=g_JSChartLocalization.GetText('Tooltip-AvPrice',this.LanguageID); + this.Canvas.fillText(text, left,top); + var color=this.KLineTitlePaint.GetColor(item.AvPrice,this.YClose); + var text=item.AvPrice.toFixed(defaultfloatPrecision); + this.Canvas.fillStyle=color; + this.Canvas.fillText(text,left+labelWidth,top); + } + + //涨幅 + top+=this.LineHeight; + this.Canvas.fillStyle=this.TitleColor; + text=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID); + this.Canvas.fillText(text, left,top); + var value=(close-this.YClose)/this.YClose*100; + var color = this.KLineTitlePaint.GetColor(value, 0); + var text = value.toFixed(2)+'%'; + this.Canvas.fillStyle=color; + this.Canvas.fillText(text,left+labelWidth,top); + + //成交量 + if (IFrameSplitOperator.IsNumber(vol)) + { + this.Canvas.fillStyle=this.TitleColor; + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Vol',this.LanguageID); + this.Canvas.fillText(text, left,top); + var text=IFrameSplitOperator.FromatIntegerString(vol,2,this.LanguageID); + this.Canvas.fillText(text,left+labelWidth,top); + } + + //成交金额 + if (IFrameSplitOperator.IsNumber(amount)) + { + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID); + this.Canvas.fillText(text, left,top); + var text=IFrameSplitOperator.FormatValueString(amount,2,this.LanguageID); + this.Canvas.fillText(text,left+labelWidth,top); + } + + if (IFrameSplitOperator.IsNumber(item.Position)) + { + top+=this.LineHeight; + text=g_JSChartLocalization.GetText('Tooltip-Position',this.LanguageID); + this.Canvas.fillText(text, left,top); + var text=IFrameSplitOperator.FormatValueString(item.Position,2,this.LanguageID); + this.Canvas.fillText(text,left+labelWidth,top); + } + + if (this.IsHScreen) this.Canvas.restore(); + } +} + +//股票信息 +function StockInfoExtendChartPaint() +{ + this.newMethod=IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Left=80; + this.Right=1; + this.Top=1; + this.Bottom=1; + + this.BorderColor=g_JSChartResource.FrameBorderPen; + + this.Symbol; + this.Name; + + this.TitleFont=["14px 微软雅黑"]; + + this.Draw=function() + { + var left=this.ChartBorder.GetRight()+this.Left; + var right=this.ChartBorder.GetChartWidth()-this.Right; + var y=this.Top+18; + var middle=left+(right-left)/2; + + if (this.Symbol && this.Name) + { + this.Canvas.font=this.TitleFont[0]; + + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="bottom"; + this.Canvas.fillText(this.Symbol,middle-2,y); + + this.Canvas.textAlign="left"; + this.Canvas.fillText(this.Name,middle+2,y); + } + ; + this.Canvas.strokeStyle=this.BorderColor; + this.Canvas.moveTo(left,y); + this.Canvas.lineTo(right,y); + this.Canvas.stroke(); + + y+=30; + + this.DrawBorder(); + } + + this.DrawBorder=function() + { + var left=this.ChartBorder.GetRight()+this.Left; + var right=this.ChartBorder.GetChartWidth()-this.Right; + var top=this.Top; + var bottom=this.ChartBorder.GetChartHeight()-this.Bottom; + + this.Canvas.strokeStyle=this.BorderColor; + this.Canvas.strokeRect(left,top,(right-left),(bottom-top)); + } +} + +//筹码分布 +function StockChip() +{ + this.newMethod=IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Name='筹码分布'; + this.ClassName='StockChip'; + + this.HQChart; + this.PenBorder=g_JSChartResource.FrameBorderPen; //边框 + this.ColorProfit='rgb(255,0,0)'; //盈利的线段 + this.ColorNoProfit='rgb(90,141,248)'; //非盈利 + this.ColorAveragePrice='rgb(0,139,0)'; //平均价线 + this.ColorBG='rgb(190,190,190)'; //筹码背景线段颜色 + + this.PixelRatio=GetDevicePixelRatio(); + this.ShowType=0; //0=所有筹码 1=周期前 2=周期内 + this.IsDynamic=true; + this.ClientRect={}; + this.Font=g_JSChartResource.TitleFont; + this.InfoColor=g_JSChartResource.StockChip.InfoColor; + this.DayInfoColor=g_JSChartResource.StockChip.DayInfoColor; + this.LineHeight=16; + this.Left=50*this.PixelRatio; //左边间距 + this.IsShowX=false; //是否显示X刻度 成交量 + this.ShowXCount=3; + this.Width=150*this.PixelRatio; //筹码图宽度 + this.CalculateType=0; //0=平均分布 1=三角分布 + this.PriceZoom=100; //价格放大倍数 + + this.ButtonID=Guid(); //工具条Div id + this.NotSupportMessage="不支持筹码图"; + + this.DAY_COLOR= + [ + ['rgb(255,0,0)','rgb(255,128,128)','rgb(255,0,128)','rgb(255,100,0)','rgb(192,128,0)','rgb(255,192,0)'], + ['rgb(120,80,225)','rgb(160,160,225)','rgb(80,80,255)','rgb(120,120,255)','rgb(32,64,192)','rgb(0,64,128)'], + ]; + + this.SetOption=function(option) + { + if (!option) return; + if (option.ShowType>0) this.ShowType=option.ShowType; + if (option.IsShowX) this.IsShowX=option.IsShowX; + if (option.ShowXCount>0) this.ShowXCount=option.ShowXCount; + if (option.Width>100) this.Width=option.Width*GetDevicePixelRatio(); + if (option.CalculateType>0) this.CalculateType=option.CalculateType; + if (IFrameSplitOperator.IsNumber(option.PriceZoom)) this.PriceZoom=option.PriceZoom; + } + + this.ReloadResource=function(resource) + { + this.PenBorder=g_JSChartResource.FrameBorderPen; + this.Font=g_JSChartResource.TitleFont; + this.InfoColor=g_JSChartResource.StockChip.InfoColor; + this.DayInfoColor=g_JSChartResource.StockChip.DayInfoColor; + } + + this.Draw=function() + { + this.PixelRatio=GetDevicePixelRatio(); + var left=ToFixedPoint(this.ChartBorder.GetRight()+this.Left); + var top=ToFixedPoint(this.ChartBorder.GetTop()); + var right=ToFixedPoint(left+this.Width-1*this.PixelRatio); + var bottom=ToFixedPoint(this.ChartBorder.GetBottom()); + var width=right-left; + var height=bottom-top; + this.ClientRect={Left:left,Top:top,Width:width,Height:height}; + + if (ChartData.IsTickPeriod(this.HQChart.Period)) //分笔图没有筹码 + { + this.Canvas.font=this.Font; + this.Canvas.fillStyle=this.InfoColor; + + var x=left+width/2; + var y=top+height/2; + + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillText(this.NotSupportMessage,x,y); + + this.DrawBorder(); + return; + } + + if (this.CalculateChip()) + { + this.DrawFrame(); + this.DrawAllChip(); + if (this.ShowType==1|| this.ShowType==2) this.DrawDayChip(); + + this.CalculateCast(); //计算成本 + this.DrawChipInfo(); + } + else + { + JSConsole.Chart.Log('[StockChip::Draw] no data'); + } + + this.DrawBorder(); + if (this.SizeChange==true) this.DrawButton(); + + this.SizeChange=false; + } + + this.DrawChipInfo=function() + { + var bottom=this.ClientRect.Top+this.ClientRect.Height-1; + var left=this.ClientRect.Left+2; + var right=this.ClientRect.Left+this.ClientRect.Width; + + this.Canvas.font=this.Font; + this.Canvas.fillStyle=this.InfoColor; + this.Canvas.textBaseline='bottom'; + this.Canvas.textAlign='left'; + + var lineHeight=this.LineHeight*GetDevicePixelRatio(); + var text='70%成本价'+ this.Data.Cast[1].MinPrice.toFixed(2)+'-'+this.Data.Cast[1].MaxPrice.toFixed(2)+'集中'+this.Data.Cast[1].Rate.toFixed(2)+'%'; + this.Canvas.fillText(text,left,bottom); + bottom-=lineHeight; + + text='90%成本价'+ this.Data.Cast[0].MinPrice.toFixed(2)+'-'+this.Data.Cast[0].MaxPrice.toFixed(2)+'集中'+this.Data.Cast[0].Rate.toFixed(2)+'%';; + this.Canvas.fillText(text,left,bottom); + bottom-=lineHeight; + + text='平均成本:'+this.Data.ChipInfo.AveragePrice.toFixed(2)+'元'; + this.Canvas.fillText(text,left,bottom); + bottom-=lineHeight; + + text=+this.Data.YPrice.toFixed(2)+'处获利盘:'+this.Data.ChipInfo.YProfitRate.toFixed(2)+'%'; + this.Canvas.fillText(text,left,bottom); + bottom-=lineHeight; + + text='获利比例:'; + this.Canvas.fillText(text,left,bottom); + var textWidth=this.Canvas.measureText(text).width+2; + var barLeft=left+textWidth; + var barWidth=(right-5-barLeft); + this.Canvas.strokeStyle=this.ColorNoProfit; + this.Canvas.strokeRect(barLeft,bottom-lineHeight,barWidth,lineHeight); + this.Canvas.strokeStyle=this.ColorProfit; + this.Canvas.strokeRect(barLeft,bottom-lineHeight,barWidth*(this.Data.ChipInfo.ProfitRate/100),lineHeight); + text=this.Data.ChipInfo.ProfitRate.toFixed(2)+'%'; + this.Canvas.textAlign='center'; + this.Canvas.fillText(text,barLeft+barWidth/2,bottom); + bottom-=lineHeight; + + this.Canvas.textAlign='left'; + text='成本分布,日期:'+IFrameSplitOperator.FormatDateString(this.Data.SelectData.Date); + if (this.Data.SelectData.Time) text+=' '+IFrameSplitOperator.FormatTimeString(this.Data.SelectData.Time); + this.Canvas.fillText(text,left,bottom); + bottom-=lineHeight; + + if (this.ShowType!=1 && this.ShowType!=2) return; + + var right=this.ClientRect.Left+this.ClientRect.Width-1; + this.Canvas.textAlign='right'; + var textWidth=50; + this.Data.DayChip.sort(function(a,b){return b.Day-a.Day;}) + for(var i in this.Data.DayChip) + { + var item=this.Data.DayChip[i]; + var rate=0; + if (this.Data.ChipInfo && this.Data.ChipInfo.Vol>0) rate=item.Vol/this.Data.ChipInfo.Vol*100; + text=item.Day+'周期'+(this.ShowType==1?'前':'内')+'成本'+(IFrameSplitOperator.IsNumber(rate)? (rate.toFixed(2)+'%'):"--.--%"); + if (i==0) textWidth=this.Canvas.measureText(text).width+8; + this.Canvas.fillStyle=item.Color; + this.Canvas.fillRect(right-textWidth,bottom-lineHeight,textWidth,lineHeight); + + this.Canvas.fillStyle=this.DayInfoColor; + this.Canvas.fillText(text,right,bottom); + bottom-=lineHeight; + } + } + + this.DrawDayChip=function() + { + var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame; + for(var i in this.Data.DayChip) + { + var aryPoint=[]; + var chipData=this.Data.DayChip[i].Chip; + if (!chipData) continue; + var totalVol=0; + for(var j=0;j0) spanHtml+='  '; + spanHtml+=spanItem; + } + + divButton.style.right = Math.floor((chartRight-right+2)/pixelTatio) + "px"; + divButton.style.top = top + "px"; + //divButton.style.width=toolbarWidth+"px"; + divButton.style.height=toolbarHeight+'px'; + divButton.innerHTML=spanHtml; + + var self = this; + for(var i in ICON_LIST) + { + var item=ICON_LIST[i]; + $("#"+this.ButtonID+" ."+item.ID).click( + { + IconID:item.ID, + ShowType:i + }, + function (event) + { + var id=event.data.IconID; + var showType=event.data.ShowType; + self.ShowType=showType; + if (self.HQChart) self.HQChart.Draw(); + + for(var i in ICON_LIST) + { + var item=ICON_LIST[i]; + var style=$("#"+self.ButtonID+" ."+item.ID)[0].style; + style['color']=item.Color[i==showType?1:0]; + } + } + ) + } + } + + this.DrawAllChip=function() + { + var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame; + var selectPrice=this.Data.SelectData.Close; + var aryProfitPoint=[]; + var aryNoProfitPoint=[]; + var totalVol=0,totalAmount=0,totalProfitVol=0, totalYProfitVol=0; //总的成交量, 总的成交金额, 总的盈利的成交量 + var yPrice=this.Data.YPrice; + + var maxPrice=KLineFrame.HorizontalMax; + var minPrice=KLineFrame.HorizontalMin; + + var MaxVol=1; + for(var i=0;i=minPrice) + { + if (MaxVolmaxPrice || price0?totalProfitVol/totalVol*100:0, + YProfitRate:totalVol>0?totalYProfitVol/totalVol*100:0 + }; + + if (this.ShowType==0) + { + this.DrawLines(aryProfitPoint,this.ColorProfit); + this.DrawLines(aryNoProfitPoint,this.ColorNoProfit); + var averagePrice=this.Data.ChipInfo.AveragePrice; + if (averagePrice>0 && averagePrice<=maxPrice && averagePrice>=minPrice) + { + averagePrice=averagePrice.toFixed(2); + this.DrawAveragePriceLine(aryProfitPoint,aryNoProfitPoint,KLineFrame.GetYFromData(averagePrice),this.ColorAveragePrice); + } + } + else //在火焰山模式下, 筹码用一个颜色 + { + this.DrawLines(aryProfitPoint,this.ColorBG); + this.DrawLines(aryNoProfitPoint,this.ColorBG); + } + } + + this.CalculateCast=function() //计算 90% 70%的成本价 + { + if (!this.Data.ChipInfo || !this.Data.ChipInfo.Vol) return; + + var aryCast= + [ + {Start:0.05,End:0.95, MaxPrice:0, MinPrice:0, Rate:0}, + {Start:0.15,End:0.85, MaxPrice:0, MinPrice:0, Rate:0} + ]; + + var averagePrice=this.Data.ChipInfo.AveragePrice; + var totalProfitVol=this.Data.ChipInfo.ProfitVol; + var tempVol=0; + for(var i=0, castCount=0;iitemCast.Start) + { + itemCast.MinPrice=price; + ++castCount; + } + + if (itemCast.MaxPrice<=0 && rate>itemCast.End) + { + itemCast.MaxPrice=price; + ++castCount; + } + } + } + + for(var i in aryCast) + { + var item=aryCast[i]; + var addPrice=item.MaxPrice+item.MinPrice; + if (addPrice) item.Rate=Math.abs(item.MaxPrice-item.MinPrice)/addPrice*100; + } + + this.Data.Cast=aryCast; + } + + this.DrawArea=function(aryPoint,color) + { + if (aryPoint.length<=0) return; + + this.Canvas.fillStyle=color; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.ClientRect.Left,aryPoint[0].Y); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.lineTo(item.X,item.Y); + } + this.Canvas.lineTo(this.ClientRect.Left,aryPoint[aryPoint.length-1].Y); + this.Canvas.fill(); + } + + this.DrawLines=function(aryPoint,color) + { + if (aryPoint.length<=0) return; + this.Canvas.strokeStyle=color; + this.Canvas.beginPath(); + for(var i in aryPoint) + { + var item=aryPoint[i]; + this.Canvas.moveTo(this.ClientRect.Left,item.Y); + this.Canvas.lineTo(item.X,item.Y); + } + this.Canvas.stroke(); + } + + this.DrawAveragePriceLine=function(aryProfitPoint,aryNoProfitPoint,y,color) + { + for(var i in aryProfitPoint) + { + var item=aryProfitPoint[i]; + if (item.Y==y) + { + this.Canvas.strokeStyle=color; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.ClientRect.Left,item.Y); + this.Canvas.lineTo(item.X,item.Y); + this.Canvas.stroke(); + return; + } + } + + for(var i in aryNoProfitPoint) + { + var item=aryNoProfitPoint[i]; + if (item.Y==y) + { + this.Canvas.strokeStyle=color; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.ClientRect.Left,item.Y); + this.Canvas.lineTo(item.X,item.Y); + this.Canvas.stroke(); + return; + } + } + } + + this.DrawBorder=function() + { + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.strokeRect(this.ClientRect.Left,this.ClientRect.Top,this.ClientRect.Width,this.ClientRect.Height); + } + + this.EvenlyDistribute=function(aryChip, data) //平均分布 data={Low, High, Vol, MaxVol, MaxPrice, MinPrice } + { + var low=data.Low, high=data.High, maxPrice=data.MaxPrice, minPrice=data.MinPrice, maxVol=1; + if ( (high-low)== 0) return; + var averageVol=data.Vol/(high-low); + + for(var j=low;j<=high && j<=maxPrice;++j) + { + var index=j-minPrice; + aryChip[index]+=averageVol; + if (maxVolmiddlePrice;--i,++j) + { + var y = Math.tan(ANGLE* PI / 180)*j; + + totalValue+=y + aryVol.push({Index:i-minPrice, Value:y}); + } + + if (totalValue>0) + { + for(var i=0;i=4) return false; //分钟K线不支持, 没时间做,以后再做吧 + var count=bindData.DataOffset+parseInt(this.HQChart.CursorIndex); + if (count>=bindData.Data.length) count=bindData.Data.length-1; + var selData=bindData.Data[count]; + var yPrice=selData.Close; + + var mouseY=this.HQChart.LastPoint.Y; + if (mouseY) yPrice=this.HQChart.Frame.SubFrame[0].Frame.GetYData(mouseY); + + //JSConsole.Chart.Log("[StockChip::CalculateChip]",count,this.HQChart.CursorIndex,selData); + const rate=1; + var aryVol=[]; + var seed=1,vol,maxPrice,minPrice; + for(let i=count;i>=0;--i) + { + var item=bindData.Data[i]; + var changeRate=1; //换手率 + if (item.FlowCapital>0) changeRate=item.Vol/item.FlowCapital; + if (i==count) vol=item.Vol*changeRate; + else vol=item.Vol*seed; + var dataItem={Vol:vol,High:item.High,Low:item.Low}; + aryVol.push(dataItem); + seed*=(1-changeRate*rate); + + if (!maxPrice || maxPriceitem.Low) minPrice=item.Low; + } + + //JSConsole.Chart.Log("[StockChip::CalculateChip]",maxPrice,minPrice); + if (!maxPrice || !minPrice) return true; + + var priceZoom=this.PriceZoom; + + maxPrice=parseInt(maxPrice*priceZoom); + minPrice=parseInt(minPrice*priceZoom); + + var dataCount=maxPrice-minPrice; + var aryChip=new Array() + for(let i=0;i<=dataCount;++i) + { + aryChip.push(0); + } + + var dayChip=[]; + var distributeData; + if (this.ShowType==2) + { + var dayChip= + [ + {Day:100, Color:this.DAY_COLOR[1][5]}, {Day:60, Color:this.DAY_COLOR[1][4]}, {Day:30, Color:this.DAY_COLOR[1][3]}, + {Day:20, Color:this.DAY_COLOR[1][2]}, {Day:10, Color:this.DAY_COLOR[1][1]}, {Day:5, Color:this.DAY_COLOR[1][0]} + ]; + for(let i in aryVol) + { + var item=aryVol[i]; + var high=parseInt(item.High*priceZoom); + var low=parseInt(item.Low*priceZoom); + var averageVol=item.Vol; + if (high-low>0) averageVol=item.Vol/(high-low); + if (averageVol<=0.000000001) continue; + + for(var k=0;k=0;--i) + { + var item=aryVol[i]; + var high=parseInt(item.High*priceZoom); + var low=parseInt(item.Low*priceZoom); + var averageVol=item.Vol; + if (high-low>0) averageVol=item.Vol/(high-low); + if (averageVol<=0.000000001) continue; + + for(var k=0;k0) averageVol=item.Vol/(high-low); + if (averageVol<=0.000000001) continue; + + distributeData={Low:low, High:high, Vol:item.Vol, MaxPrice:maxPrice, MinPrice:minPrice}; + this.CalculateDistribute(aryChip, distributeData); + } + } + + this.Data={AllChip:aryChip, MaxVol:distributeData.MaxVol, MaxPrice:maxPrice, MinPrice:minPrice,SelectData:selData, DayChip:dayChip, YPrice:yPrice}; + return true; + } + + this.DrawFrame=function() //X轴成交量坐标 + { + if (this.IsShowX==false) return; + if (this.Data.MaxVol<=0) return; + + var isDrawXFrame=this.HQChart.Frame.SubFrame.length===1 ? false:true; //是否画X轴,如果只有1个窗口就不画 + var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame; + var chartBorder=KLineFrame.ChartBorder; + var bottom=ToFixedPoint(chartBorder.GetBottomEx()+1); + if (!isDrawXFrame) bottom=this.ClientRect.Top+this.ClientRect.Height; + var left=this.ClientRect.Left; + var right=left+this.ClientRect.Width; + + this.Canvas.strokeStyle=this.PenBorder; + this.Canvas.beginPath(); + if (isDrawXFrame) + { + this.Canvas.moveTo(left,bottom); + this.Canvas.lineTo(right,bottom); + } + + var showCount=this.ShowXCount; + var maxValue=this.Data.MaxVol; + var perValue=Math.floor(maxValue/showCount); + this.Canvas.font=this.Font; + this.Canvas.textBaseline='top'; + this.Canvas.fillStyle=this.InfoColor; + var xOffset=10*GetDevicePixelRatio(); + for(var i=1;i<=showCount;++i) + { + var vol=perValue*i; + var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left; + x=ToFixedPoint(x); + if (i==showCount) //最后一个刻度不要画线了 + { + this.Canvas.textAlign='right'; + var text=IFrameSplitOperator.FormatValueString(maxValue, 1); + this.Canvas.fillText(text,x,bottom+2); + } + else + { + this.Canvas.moveTo(x,this.ClientRect.Top); + this.Canvas.lineTo(x,bottom); + + this.Canvas.textAlign='center'; + + var text=IFrameSplitOperator.FormatValueString(vol, 1); + var textWidth=this.Canvas.measureText(text).width; + this.Canvas.fillText(text,Math.floor(x-textWidth*0.25),bottom+2); + } + } + + this.Canvas.stroke(); + } +} + +//画图工具条 +function DrawToolsButton() +{ + this.newMethod=IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='DrawToolsButton'; + this.HQChart; + this.ID=Guid(); + this.ToolsDiv; + // this.Color='rgb(105,105,105)'; //颜色 + this.Color = "#696969"; //input type="color"不支持rgb的颜色格式 + + //this.Left=5; + this.Top=5*GetDevicePixelRatio(); + this.Width=45*GetDevicePixelRatio(); //宽度 + + this.SetOption=function(option) + { + var pixelRatio=GetDevicePixelRatio(); + if (!option) return; + if (option.Width>10) this.Width=option.Width*pixelRatio; + } + + this.Clear=function() + { + if (this.ToolsDiv) this.ChartBorder.UIElement.parentNode.removeChild(this.ToolsDiv); + } + + this.Draw = function () { + if (this.SizeChange == false) return; + + //工具列表 + const TOOL_LIST = + [ + [ + { HTML: { Title: '线段', IClass: 'iconfont icon-draw_line', ID: 'icon-segment' }, Name: '线段' }, + { HTML: { Title: '尺子', IClass: 'iconfont icon-ruler', ID: 'icon_ruler' }, Name: '尺子' }, + { HTML: { Title: '射线', IClass: 'iconfont icon-draw_rays', ID: 'icon-beam' }, Name: '射线' }, + { HTML: { Title: '标价线', IClass: 'iconfont icon-price_line', ID: 'icon-price-line' }, Name: '标价线' }, + { HTML: { Title: '垂直线', IClass: 'iconfont icon-vertical_line', ID: 'icon-vertical-line' }, Name: '垂直线' }, + { HTML: { Title: '箭头', IClass: 'iconfont icon-draw_rays', ID: 'icon-beam2' }, Name: '箭头' }, + { HTML: { Title: '趋势线', IClass: 'iconfont icon-draw_trendline', ID: 'icon-trendline' }, Name: '趋势线' }, + { HTML: { Title: '水平线', IClass: 'iconfont icon-draw_hline', ID: 'icon-hline' }, Name: '水平线' }, + { HTML: { Title: '水平线段', IClass: 'iconfont icon-draw_hlinesegment', ID: 'icon-hlineseg' }, Name: '水平线段' }, + { HTML: { Title: '平行射线', IClass: 'iconfont icon-draw_p_rays_lines', ID: 'icon-rayslineseg' }, Name: '平行射线' }, + { HTML: { Title: '平行线', IClass: 'iconfont icon-draw_parallel_lines', ID: 'icon-parallellines' }, Name: '平行线' }, + { HTML: { Title: '平行通道', IClass: 'iconfont icon-draw_parallelchannel', ID: 'icon-parallelchannel' }, Name: '平行通道' }, + { HTML: { Title: '价格通道线', IClass: 'iconfont icon-draw_pricechannel', ID: 'icon-pricechannel' }, Name: '价格通道线' }, + { HTML: { Title: 'M头W底', IClass: 'iconfont icon-draw_wavemw', ID: 'icon-wavemw' }, Name: 'M头W底' }, + { HTML: { Title: '头肩型', IClass: 'iconfont icon-draw_head_shoulders_bt', ID: 'icon-Head-Shoulders' }, Name: '头肩型' }, + { HTML: { Title: '波浪尺', IClass: 'iconfont icon-waveruler', ID: 'icon-wave-ruler' }, Name: '波浪尺' }, + { HTML: { Title: 'AB波浪尺', IClass: 'iconfont icon-waveruler', ID: 'icon-wave-ruler' }, Name: 'AB波浪尺' }, + { HTML: { Title: '箱型线', IClass: 'iconfont icon-draw_box', ID: 'icon-drawbox' }, Name: '箱型线' }, + ], + [ + { HTML: { Title: '圆弧', IClass: 'iconfont icon-draw_arc', ID: 'icon-arc' }, Name: '圆弧线' }, + { HTML: { Title: '矩形', IClass: 'iconfont icon-rectangle', ID: 'icon-rect' }, Name: '矩形' }, + { HTML: { Title: '平行四边形', IClass: 'iconfont icon-draw_quadrangle', ID: 'icon-quad' }, Name: '平行四边形' }, + { HTML: { Title: '三角形', IClass: 'iconfont icon-draw_triangle', ID: 'icon-triangle' }, Name: '三角形' }, + { HTML: { Title: '圆', IClass: 'iconfont icon-draw_circle', ID: 'icon-circle' }, Name: '圆' }, + { HTML: { Title: '对称角度', IClass: 'iconfont icon-draw_symangle', ID: 'icon-symangle' }, Name: '对称角度' }, + ], + [ + { HTML: { Title: '文本', IClass: 'iconfont icon-draw_text', ID: 'icon-text' }, Name: '文本' }, + { HTML: { Title: '向上箭头', IClass: 'iconfont icon-arrow_up', ID: 'icon-arrowup' }, Name: 'icon-arrow_up' }, + { HTML: { Title: '向下箭头', IClass: 'iconfont icon-arrow_down', ID: 'icon-arrowdown' }, Name: 'icon-arrow_down' }, + { HTML: { Title: '向左箭头', IClass: 'iconfont icon-arrow_left', ID: 'icon-arrowleft' }, Name: 'icon-arrow_left' }, + { HTML: { Title: '向右箭头', IClass: 'iconfont icon-arrow_right', ID: 'icon-arrowright' }, Name: 'icon-arrow_right' }, + ], + [ + { HTML: { Title: '江恩角度线', IClass: 'iconfont icon-draw_gannfan', ID: 'icon-gannfan' }, Name: '江恩角度线' }, + { HTML: { Title: '斐波那契周期线', IClass: 'iconfont icon-draw_fibonacci', ID: 'icon-fibonacci' }, Name: '斐波那契周期线' }, + { HTML: { Title: '阻速线', IClass: 'iconfont icon-draw_resline', ID: 'icon-resline' }, Name: '阻速线' }, + { HTML: { Title: '黄金分割', IClass: 'iconfont icon-draw_goldensection', ID: 'icon-goldensection' }, Name: '黄金分割' }, + { HTML: { Title: '百分比线', IClass: 'iconfont icon-draw_percentage', ID: 'icon-percentage' }, Name: '百分比线' }, + { HTML: { Title: '波段线', IClass: 'iconfont icon-draw_waveband', ID: 'icon-waveband' }, Name: '波段线' }, + { HTML: { Title: '线形回归线', IClass: 'iconfont icon-linear_3', ID: 'icon-waveband2' }, Name: '线形回归线' }, + { HTML: { Title: '线形回归带', IClass: 'iconfont icon-linear_1', ID: 'icon-waveband3' }, Name: '线形回归带' }, + { HTML: { Title: '延长线形回归带', IClass: 'iconfont icon-linear_2', ID: 'icon-waveband5' }, Name: '延长线形回归带' }, + ], + [{ HTML: { Title: '全部删除', IClass: 'iconfont icon-recycle_bin', ID: 'icon-delete' }, Name: '全部删除' }] + ]; + + var hqChart = this.HQChart; + + if (!this.ToolsDiv) { + var div = document.createElement("div"); + div.className = 'drawtools'; + div.id = this.ID; + + var spanList = ""; //一层菜单 + var menuTwoList = ""; //二层菜单 + var menuOne = new Array(); + TOOL_LIST.forEach(function(item,index){ + menuOne.push(item[0]); + }); + for (var i = 0; i < TOOL_LIST.length; i++) { + var itemOut = menuOne[i]; + var itemIn = TOOL_LIST[i]; + var menuTwoStr = ""; + var contentArrow = ""; + for (var j = 0; j < itemIn.length; j++) { + var currentItem = itemIn[j]; + var menuTwoName = currentItem.Name; + if(menuTwoName.indexOf('up') > -1){ + menuTwoName = "向上箭头"; + }else if(menuTwoName.indexOf('down') > -1){ + menuTwoName = "向下箭头"; + }else if(menuTwoName.indexOf('left') > -1){ + menuTwoName = "向左箭头"; + }else if(menuTwoName.indexOf('right') > -1){ + menuTwoName = "向右箭头"; + } + menuTwoStr += ''; + } + if (i !== TOOL_LIST.length - 1) { //不是“全部删除”项 + menuTwoList = ''; + contentArrow = ''; + } else { + menuTwoList = ""; + contentArrow = ""; + } + + var spanNode = '
' + menuTwoList + contentArrow +'
'; + spanList += spanNode; + } + this.ChartBorder.UIElement.parentNode.appendChild(div); + + div.innerHTML = spanList; + this.ToolsDiv = div; + + for (var i in TOOL_LIST) { + var item = TOOL_LIST[i][0]; + $('#' + this.ID + " .first-" + item.HTML.ID).hover(function(){ //箭头的旋转过渡 + $(".drawtools").find(".contentArrow").hide(); + $(this).find(".contentArrow").removeClass("trans").show(); + }); + $('#' + this.ID + " .first-" + item.HTML.ID+" .contentArrow").click(function(event){ //点击三角展示二级菜单 + event.stopPropagation(); + $(".drawtools").find(".menuTwo").hide(); + $(this).siblings('.menuTwo').show(); + }); + $('#' + this.ID + " .first-" + item.HTML.ID+" .trans").click(function(){ //点击三角隐藏二级菜单 + event.stopPropagation(); + $(this).siblings('.menuTwo').hide(); + }); + + + if (item.Name == '全部删除') { + $('#' + this.ID + " .first-icon-delete").click(function () { + $(".drawtools").find(".menuTwo").hide(); + $(this).siblings().removeClass('active'); + $(this).addClass('active'); + hqChart.ClearChartDrawPicture(); + $(".subTolls").css("display", "none"); + }); + } + else { + $('#' + this.ID + " .first-" + menuOne[i].HTML.ID).click( //一层菜单类名是:“first-”+item.HTML.ID + { + // DrawName: menuOne[i].Name, //把画法名字传进去 + CurrentIndex:i + }, + function (event) { + $(".drawtools").find(".menuTwo").hide(); + $(this).siblings().removeClass('active'); + $(this).addClass('active'); + hqChart.CreateChartDrawPicture(menuOne[event.data.CurrentIndex].Name); + } + ); + for (var j in TOOL_LIST[i]) { + var itemTwo = TOOL_LIST[i][j]; + let classname = itemTwo.HTML.IClass; //闭包问题 + $('#' + this.ID + ' .' + itemTwo.HTML.ID).hover(function(event){ + event.stopPropagation(); + $(this).closest('.icon-image').find(".contentArrow").addClass("trans"); + }); + $('#' + this.ID + ' .' + itemTwo.HTML.ID).click(//二层菜单 + { + DrawName: itemTwo.Name, //把画法名字传进去 + CurrentIndex:i, + CurrentData:itemTwo + }, + function (event) { + event.stopPropagation(); + $(this).closest('.icon-image').find(".contentArrow").hide(); + $(this).siblings().removeClass("current"); + $(this).addClass("current"); + $(this).closest('.icon-image').children('i').eq(0).removeClass().addClass(classname,"active").attr('title',event.data.CurrentData.HTML.Title); + menuOne.splice(event.data.CurrentIndex,1,event.data.CurrentData); + $(this).parent().hide(); + hqChart.CreateChartDrawPicture(event.data.DrawName); + } + ); + } + } + } + + } + var curID = this.ID; + $(document).click(function(event){ + if(!($("#"+curID).is(event.target)) && ($("#"+curID).has(event.target).length === 0)){ + $("#"+curID+" .menuTwo").hide(); + $("#"+curID+" .contentArrow").hide(); + } + }); + var scrollPos = GetScrollPosition(); + var pixelRatio=GetDevicePixelRatio(); + // var left=this.ChartBorder.GetChartWidth()-this.Right-this.ToolsWidth; + var left=ToFixedPoint(this.ChartBorder.GetRight()+this.Left); + // var top = this.Top+this.ChartBorder.UIElement.getBoundingClientRect().top+scrollPos.Top; + var top = this.ChartBorder.GetTop(); + this.ToolsDiv.style.left = left/pixelRatio + "px"; + this.ToolsDiv.style.top = top/pixelRatio + "px"; + this.ToolsDiv.style.width = (this.Width-5)/pixelRatio + "px"; + this.ToolsDiv.style.height = 'auto'; + this.ToolsDiv.style.position = "absolute"; + this.ToolsDiv.style.display = "block"; + // this.ToolsDiv.style.paddingLeft = "10px"; + + this.SizeChange == true; + } +} + + +//窗口分割 +function FrameSplitPaint() +{ + this.newMethod=IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='FrameSplitPaint'; + this.LineColor='rgb(255,0,0)'; + this.TextColor="rgb(255,0,0)"; + this.LineWidth=2; + this.Font='bold '+18*GetDevicePixelRatio() +"px 微软雅黑"; + this.TextTopOffset=10*GetDevicePixelRatio(); + + //设置参数接口 + this.SetOption=function(option) + { + if (option) + { + if (option.LineColor) this.LineColor=option.LineColor; + if (option.TextColor) this.TextColor=option.TextColor; + if (option.Font) this.Font=option.Font; + if (IFrameSplitOperator.IsNumber(option.LineWidth)) this.LineWidth=option.LineWidth; + if (IFrameSplitOperator.IsNumber(option.TextTopOffset)) this.TextTopOffset=option.TextTopOffset; + } + } + + this.Draw=function() + { + if (!this.HQChart) return; + if (this.HQChart.Period!=0 && this.HQChart.Period!=1) return; + + if (!this.HQChart.ChartPaint[0]) return; + var data=this.HQChart.ChartPaint[0].Data; + if (!data) return; + this.Data=data; + if (!this.ChartFrame || !this.ChartFrame.SubFrame) return; + var subFrame=this.ChartFrame.SubFrame[0].Frame; + if (!subFrame) return; + + var isHScreen=(subFrame.IsHScreen===true); + var dataWidth=subFrame.DataWidth; + var distanceWidth=subFrame.DistanceWidth; + var xPointCount=subFrame.XPointCount; + + var lineWidth=this.LineWidth * GetDevicePixelRatio(); + + if (isHScreen) + { + var border=this.ChartBorder.GetHScreenBorder(); + var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.BottomEx; + } + else + { + var border=this.ChartBorder.GetBorder(); + var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=border.RightEx; + var drawHeight=border.ChartHeight-border.TopTitle-5*GetDevicePixelRatio(); + } + + this.Canvas.save(); + this.Canvas.lineWidth=lineWidth; + this.Canvas.font=this.Font; + this.Canvas.textAlign='center'; + this.Canvas.textBaseline='top'; + var preQuarter={ Left:border.LeftEx, Top:ToFixedPoint2(lineWidth,border.TopTitle), Height:drawHeight, LineWidth:lineWidth }; + for(var i=this.Data.DataOffset,j=0;ichartright) break; + var x=left+(right-left)/2; + + + if (!preQuarter.DateInfo) + { + preQuarter.DateInfo=dateInfo; + preQuarter.Right=x; + } + else + { + var preDateInfo=preQuarter.DateInfo; + if (preDateInfo.Year!=dateInfo.Year || preDateInfo.Quarter!=dateInfo.Quarter) + { + preQuarter.Right=x; + this.DrawQuarter(preQuarter); + + preQuarter.Left=x; + preQuarter.Right=null; + preQuarter.DateInfo=dateInfo; + } + else + { + preQuarter.Right=x; + } + } + } + + this.DrawQuarter(preQuarter); + + this.Canvas.restore(); + } + + this.DrawQuarter=function(quarterInfo) + { + if (!IFrameSplitOperator.IsNumber(quarterInfo.Left) || !IFrameSplitOperator.IsNumber(quarterInfo.Right)) return; + + var left=ToFixedPoint2(quarterInfo.LineWidth,quarterInfo.Left); + var right=ToFixedPoint2(quarterInfo.LineWidth,quarterInfo.Right); + var drawWidth=right-left; + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.strokeRect(left, quarterInfo.Top, drawWidth, quarterInfo.Height); + + var date=quarterInfo.DateInfo; + var shortYear=date.Year%100; + if (shortYear<10) var strYear='0'+shortYear; + else var strYear=shortYear.toString(); + var text=`${strYear}年${date.Quarter}季度`; + var textWidth = this.Canvas.measureText(text).width; + if (textWidth<(drawWidth+5)) + { + var x=left+drawWidth/2; + var y=quarterInfo.Top+this.TextTopOffset; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,x,y); + } + } + + this.GetQuarter=function(date) + { + if (!IFrameSplitOperator.IsNumber(date)) return null; + + var year=parseInt(date/10000); + var month=parseInt((date%10000)/100); + + switch(month) + { + case 1: + case 2: + case 3: + var quarter=1; + break; + case 4: + case 5: + case 6: + var quarter=2; + break; + case 7: + case 8: + case 9: + var quarter=3; + break; + case 10: + case 11: + case 12: + var quarter=4; + break; + default: + return null; + } + + return { Year:year, Month:month, Quarter:quarter }; + } +} + +function RectSelectPaint() +{ + this.newMethod=IExtendChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='RectSelectPaint'; + this.LineColor=g_JSChartResource.RectSelect.LineColor; //竖线 + this.LineWidth=g_JSChartResource.RectSelect.LineWidth; + this.LineDotted=g_JSChartResource.RectSelect.LineDotted; + this.AreaColor=g_JSChartResource.RectSelect.AreaColor; //面积 + + this.FirstPoint; + this.SecondPoint; + this.PreventClose=false; //如果外面有div,可以设置成true, 有外面控制 + + this.StartDate; //{ Date:, Index: } + this.EndDate; //{ Date:, Index: } + + this.HQChart; + this.DragRect; //拖拽边框区域 + + //设置参数接口 + this.SetOption=function(option) + { + if (option) + { + if (option.LineColor) this.LineColor=option.LineColor; + if (option.AreaColor) this.AreaColor=option.AreaColor; + } + } + + this.ReloadResource=function(resource) + { + this.LineColor=g_JSChartResource.RectSelect.LineColor; //竖线 + this.LineWidth=g_JSChartResource.RectSelect.LineWidth; + this.LineDotted=g_JSChartResource.RectSelect.LineDotted; + this.AreaColor=g_JSChartResource.RectSelect.AreaColor; //面积 + } + + this.IsMinuteChart=function() + { + var className=this.HQChart.ClassName; + var isMinuteChart=(className=="MinuteChartContainer" || className=="MinuteChartHScreenContainer") ? true:false; + return isMinuteChart; + } + + this.GetKData=function() + { + if (!this.HQChart) return null; + var isMinuteChart=this.IsMinuteChart(); + + var data=null; + if (isMinuteChart) + { + if (!this.HQChart.SourceData) return null; + data=this.HQChart.SourceData; + } + else + { + if (!this.HQChart.ChartPaint[0]) return null; + data=this.HQChart.ChartPaint[0].Data; + if (!data) return null; + } + + return data; + } + + this.GetPointCount=function() + { + var count=0; + if (this.FirstPoint) ++count; + if (this.SecondPoint) ++count; + return count; + } + + this.DateToNumber=function(value, isMinuteChart) + { + if (isMinuteChart) + { + if (IFrameSplitOperator.IsNumber(value.Date)) return value.Date*10000+value.Time; + else return value.Time; + } + else + { + var period=this.HQChart.Period; + if (ChartData.IsDayPeriod(period, true)) return value.Date; //YYYYMMDD + else if (ChartData.IsMinutePeriod(period, true)) return value.Date*10000+value.Time; //YYYYMMDDHHMM + else if (ChartData.IsSecondPeriod(period) || ChartData.IsTickPeriod(period)) return value.Date*1000000+value.Time; //YYYYMMDDHHMMSS + } + + return null; + } + + this.GetSelectRectData=function() + { + if (!this.FirstPoint && !this.SecondPoint) return null; + + var data=this.GetKData(); + if (!data) return null; + var isMinuteChart=this.IsMinuteChart(); + var firstDate=this.DateToNumber(this.FirstPoint,isMinuteChart); + var secondDate=this.DateToNumber(this.SecondPoint,isMinuteChart); + var selectData={ Start:null, End:null, Data:data }; + for(var i=0;iselectData.End) + { + var temp=selectData.Start; + selectData.Start=selectData.End; + selectData.End=temp; + } + return selectData; + } + } + + return null; + } + + this.SortPoint=function() + { + var isMinuteChart=this.IsMinuteChart(); + if (this.FirstPoint && !this.SecondPoint) + { + this.StartDate={ Date:this.DateToNumber(this.FirstPoint,isMinuteChart), Index:0 }; + } + else if (this.FirstPoint && this.SecondPoint) + { + var firstValue=this.DateToNumber(this.FirstPoint,isMinuteChart); + var secondValue=this.DateToNumber(this.SecondPoint,isMinuteChart); + if (firstValuethis.StartDate.Date && startEnd.Date0) this.FrameID=option.FrameID; + if (option.Width>0) this.Width=option.Width; + if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID=option.ID; + if (option.IsShowCorssCursor) this.IsShowCorssCursor=option.IsShowCorssCursor; + } + + this.Draw=function() + { + if (!this.Data) return; + if (!this.ChartFrame || !this.ChartFrame.SubFrame || this.ChartFrame.SubFrame.length<=this.FrameID) return; + + var isHScreen=(this.ChartFrame.IsHScreen===true); + var frame=this.ChartFrame.SubFrame[this.FrameID].Frame; + var chartBorder=frame.ChartBorder; + + var height=chartBorder.GetHeightEx(); + var left=chartBorder.GetRight(); + var top=chartBorder.GetTopEx(); + var bottom=chartBorder.GetBottomEx(); + var width=this.Width; + if (width>this.ChartBorder.Right) width=this.ChartBorder.Right; + + if (isHScreen) + { + left=chartBorder.GetBottom(); + top=chartBorder.GetRight(); + bottom=chartBorder.GetLeft(); + var width=this.Width; + if (width>this.ChartBorder.Bottom) width=this.ChartBorder.Bottom; + } + + var rtClient={ Left:left, Top:top, Right:left+width, Bottom:bottom, Height:height, Width:width }; + this.YRange={ Max:null, Min:0 }; + for(var i in this.Data) + { + var item=this.Data[i]; + this.CalculateYRange(item, this.YRange); + } + + for(var i in this.Data) + { + var item=this.Data[i]; + if (item.Height>0 && item.Height<=1) rtClient.Height=height*item.Height; + else rtClient.Height=height; + + if (item.Type==1) + { + rtClient.Top=top; + rtClient.Bottom=top+rtClient.Height; + this.DrawUpArea(item,rtClient); + } + else if (item.Type==2) + { + rtClient.Bottom=bottom; + rtClient.Top=rtClient.Bottom-rtClient.Height; + this.DrawDownArea(item,rtClient); + } + else + { + rtClient.Top=top; + rtClient.Bottom=bottom; + this.DrawDefaultArea(item,rtClient); + } + } + } + + this.CalculateYRange=function(drawData, yRange) + { + for(var i in drawData.Data) + { + var item=drawData.Data[i]; + if (yRange.Max==null || yRange.Maxitem.Y) yRange.Min=item.Y; + } + } + + + this.GetXFromData=function(value, rtClient, minMax) //获取X轴坐标 + { + var width = rtClient.Width * (value - minMax.Min) / (minMax.Max - minMax.Min); + return rtClient.Left + width; + } + + this.GetYFromData=function(value, rtClient, minMax, isASC) + { + if (isASC) + { + var height = rtClient.Height * (value - minMax.Min) / (minMax.Max - minMax.Min); + return rtClient.Bottom - height; + } + else + { + var height = rtClient.Height * (value - minMax.Min) / (minMax.Max - minMax.Min); + return rtClient.Top + height; + } + } + + this.DrawDefaultArea=function(drawData, rtClient) + { + var frame=this.ChartFrame.SubFrame[this.FrameID].Frame; + var yRange=(drawData.Range && drawData.Range.Y) ? drawData.Range.Y : this.YRange; + + var aryPoint=[]; + for(var i in drawData.Data) + { + var item=drawData.Data[i]; + var x=this.GetXFromData(item.Y,rtClient,yRange); + if (item.X>frame.HorizontalMax || item.X0) this.DrawLine(aryPoint,lineColor, areaColor,rtClient); + } + + this.DrawUpArea=function(drawData,rtClient) + { + var aryPoint=[]; + for(var i in drawData.Data) + { + var item=drawData.Data[i]; + var x=this.GetXFromData(item.Y,rtClient,drawData.Range.Y); + var y=this.GetYFromData(item.X,rtClient,drawData.Range.X,drawData.IsASC); + + aryPoint.push({X:x,Y:y}); + } + + var lineColor=drawData.LineColor? drawData.LineColor: this.LineColor; + var areaColor=drawData.AreaColor? drawData.AreaColor: this.AreaColor; + if (aryPoint.length>0) this.DrawLine(aryPoint,lineColor, areaColor,rtClient); + } + + this.DrawDownArea=function(drawData,rtClient) + { + var aryPoint=[]; + for(var i in drawData.Data) + { + var item=drawData.Data[i]; + var x=this.GetXFromData(item.Y,rtClient,drawData.Range.Y); + var y=this.GetYFromData(item.X,rtClient,drawData.Range.X,drawData.IsASC); + + aryPoint.push({X:x,Y:y}); + } + + var lineColor=drawData.LineColor? drawData.LineColor: this.LineColor; + var areaColor=drawData.AreaColor? drawData.AreaColor: this.AreaColor; + if (aryPoint.length>0) this.DrawLine(aryPoint,lineColor, areaColor,rtClient); + } + + this.DrawLine=function(aryPoint,lineColor, areaColor,rtClient) + { + var isHScreen=(this.ChartFrame.IsHScreen===true); + var bFirstPoint=true; + var drawCount=0; + this.Canvas.strokeStyle=lineColor; + + for(var i in aryPoint) + { + var item=aryPoint[i]; + if (bFirstPoint) + { + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(item.Y,item.X); + else this.Canvas.moveTo(item.X,item.Y); + bFirstPoint=false; + } + else + { + if (isHScreen) this.Canvas.lineTo(item.Y,item.X); + else this.Canvas.lineTo(item.X,item.Y); + } + + ++drawCount; + } + + if (drawCount>0) this.Canvas.stroke(); + + //面积 + var firstPoint=aryPoint[0]; + var lastPoint=aryPoint[aryPoint.length-1]; + if (isHScreen) + { + this.Canvas.lineTo(lastPoint.Y,rtClient.Left); + this.Canvas.lineTo(firstPoint.Y,rtClient.Left); + } + else + { + this.Canvas.lineTo(rtClient.Left,lastPoint.Y); + this.Canvas.lineTo(rtClient.Left,firstPoint.Y); + } + this.Canvas.closePath(); + + if (Array.isArray(areaColor)) + { + if (isHScreen) + { + let gradient = this.Canvas.createLinearGradient(this.ChartBorder.GetRightEx(),this.ChartBorder.GetTop(), this.ChartBorder.GetLeft(),this.ChartBorder.GetTop()); + gradient.addColorStop(0, areaColor[0]); + gradient.addColorStop(1, areaColor[1]); + this.Canvas.fillStyle=gradient; + } + else + { + let gradient = this.Canvas.createLinearGradient(rtClient.Right,rtClient.Top, rtClient.Left,rtClient.Top); + var offset=1/areaColor.length; + for(var i in areaColor) + { + gradient.addColorStop(i*offset, areaColor[i]); + } + this.Canvas.fillStyle=gradient; + } + } + else + { + this.Canvas.fillStyle=areaColor; + } + + this.Canvas.fill(); + + } + + this.GetYValueByXValue=function(x,floatPrecision) + { + var aryXValue=[]; + var value=0; + var zoom=ZOOM_VALUE[floatPrecision]; + for(var i in this.Data) + { + var item=this.Data[i]; + if (item.Type!=0 || !item.Data || item.Data.length<0 || item.IsShowCorssCursor==false) continue; + + var bFind=false; + for(var j in item.Data) + { + var dataItem=item.Data[j]; + value=parseInt(dataItem.X*zoom); + if (value==x) + { + bFind=true; + var findItem= + { + X:dataItem.X, Y:dataItem.Y, + TextColor:item.TextColor? item.TextColor: this.TextColor, + TextBGColor:item.TextBGColor? item.TextBGColor: this.TextBGColor + }; + if (dataItem.YText) findItem.YText=dataItem.YText; + + aryXValue.push(findItem); + break; + } + } + + if (!bFind) + { + var first=item.Data[0]; + var last=item.Data[item.Data.length-1]; + if (x>first.X*zoom && x0) this.FrameID=option.FrameID; + if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID=option.ID; + } + + this.Draw=function() + { + if (!this.Data || !this.HQChart) return; + if (!this.ChartFrame || !this.ChartFrame.SubFrame || this.ChartFrame.SubFrame.length<=this.FrameID) return; + var klineChart=this.HQChart.ChartPaint[0]; + if (!klineChart || !klineChart.Data) return; + + this.ChartSubFrame=this.ChartFrame.SubFrame[this.FrameID].Frame; + this.ChartBorder=this.ChartSubFrame.ChartBorder; + this.KData=klineChart.Data; + this.Period=this.HQChart.Period; + if (!this.KData || this.KData.Data.length<=0) return; + + var isHScreen=(this.ChartSubFrame.IsHScreen===true); + this.XPointCount=this.ChartSubFrame.XPointCount; + var xPointCount=this.ChartSubFrame.XPointCount; + + var firstKItem=this.KData.Data[this.KData.DataOffset]; + var endIndex=this.KData.DataOffset+xPointCount-1; + if (endIndex>=this.KData.Data.length) endIndex=this.KData.Data.length-1; + var endKItem=this.KData.Data[endIndex]; + var showData=this.GetShowData(firstKItem,endKItem); + if (!showData || showData.length<=0) return; + + var kLineMap=this.BuildKLineMap(); + var bottom=this.ChartBorder.GetBottomEx(); + var top=this.ChartBorder.GetTopEx(); + var height=this.ChartBorder.GetHeightEx(); + if (isHScreen) + { + top=this.ChartBorder.GetRightEx(); + bottom=this.ChartBorder.GetLeftEx(); + height=this.ChartBorder.GetWidthEx(); + } + + for(var i in showData) + { + var item=showData[i]; + var rt=this.GetBGCoordinate(item,kLineMap); + if (!rt) continue; + + if (Array.isArray(item.Color)) + { + var gradient; + if (isHScreen) gradient = this.Canvas.createLinearGradient(bottom,rt.Left, top, rt.Left); + else gradient = this.Canvas.createLinearGradient(rt.Left,top, rt.Left,bottom); + var offset=1/item.Color.length; + for(var i in item.Color) + { + gradient.addColorStop(i*offset, item.Color[i]); + } + this.Canvas.fillStyle=gradient; + } + else + { + this.Canvas.fillStyle=item.Color; + } + + if (isHScreen) this.Canvas.fillRect(ToFixedRect(bottom),ToFixedRect(rt.Left),ToFixedRect(height),ToFixedRect(rt.Width)); + else this.Canvas.fillRect(ToFixedRect(rt.Left),ToFixedRect(top),ToFixedRect(rt.Width),ToFixedRect(height)); + } + } + + this.GetShowData=function(first, end) + { + var aryData=[]; + for(var i in this.Data) + { + var item=this.Data[i]; + var showItem={ }; + if (item.Start.Date>=first.Date && item.Start.Date<=end.Date) showItem.Start=item.Start; + if (item.End.Date>=first.Date && item.End.Date<=end.Date) showItem.End=item.End; + if (showItem.Start || showItem.End) + { + showItem.Color=item.Color; + aryData.push(showItem); + } + } + + //JSConsole.Chart.Log('[BackgroundPaint::GetShowData] aryData ', aryData); + return aryData; + } + + this.BuildKLineMap=function() + { + var isHScreen=(this.ChartSubFrame.IsHScreen===true); + var dataWidth=this.ChartSubFrame.DataWidth; + var distanceWidth=this.ChartSubFrame.DistanceWidth; + var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin; + var chartright=this.ChartBorder.GetRight(); + if (isHScreen) chartright=this.ChartBorder.GetBottom(); + + var mapKLine={ Data:new Map() ,Start:null, End:null } ; //Key: date / date time, Value:索引 + for(var i=this.KData.DataOffset,j=0; ichartright) break; + var x=left+(right-left)/2; + + if (j==0) mapKLine.XLeft=left; + mapKLine.XRight=right; + + var value={ Index:i, ShowIndex:j , X:x, Right:right, Left:left, Date:kItem.Date }; + if (ChartData.IsMinutePeriod(this.Period,true)) + { + var key=`Date:${kItem.Date} Time:${kItem.Time}`; + value.Time=kItem.Time; + } + else + { + var key=`Date:${kItem.Date}`; + } + + mapKLine.Data.set(key,value); + + //保存下起始和结束位置 + if (j==0) mapKLine.Start=value; + mapKLine.End=value; + } + + return mapKLine; + } + + this.GetBGCoordinate=function(item,kLineMap) + { + var xLeft=null, xRight=null; + var isMinutePeriod=ChartData.IsMinutePeriod(this.Period,true); + var bSingleDate=false; + //JSConsole.Chart.Log('[BackgroundPaint::GetBGCoordinate] item ', item); + if (isMinutePeriod) + { + if (item.Start && item.End && item.Start.Date==item.End.Date && item.Start.Time==item.End.Time) bSingleDate=true; + } + else + { + if (item.Start && item.End && item.Start.Date==item.End.Date) bSingleDate=true; + } + + if (bSingleDate) + { + if (isMinutePeriod) + var key=`Date:${item.Start.Date} Time:${item.Start.Time}`; + else + var key=`Date:${item.Start.Date}`; + + if (!kLineMap.Data.has(key)) return null; + var findItem=kLineMap.Data.get(key); + xLeft=findItem.Left; + xRight=findItem.Right; + return { Left:xLeft, Right:xRight, Width:xRight-xLeft }; + } + + if (item.Start) + { + if (isMinutePeriod) + var key=`Date:${item.Start.Date} Time:${item.Start.Time}`; + else + var key=`Date:${item.Start.Date}`; + + if (kLineMap.Data.has(key)) + { + var findItem=kLineMap.Data.get(key); + xLeft=findItem.Left; + } + else + { + if (isMinutePeriod) + { + if (item.Start.Dateitem.Start.Date || (value.Date==item.Start.Date && value.Time>item.Start.Time)) + { + xLeft=value.Left; + break; + } + } + } + } + else + { + if (item.Start.Date<=kLineMap.Start.Date) + { + xLeft=kLineMap.Start.Left; + } + else + { + for(var kItem of kLineMap.Data) + { + var value=kItem[1]; + if (value.Date>item.Start.Date) + { + xLeft=value.Left; + break; + } + } + } + } + } + } + else + { + xLeft=kLineMap.XLeft; + } + + if (item.End) + { + if (isMinutePeriod) + var key=`Date:${item.End.Date} Time:${item.End.Time}`; + else + var key=`Date:${item.End.Date}`; + + if (kLineMap.Data.has(key)) + { + var findItem=kLineMap.Data.get(key); + xRight=findItem.Right; + } + else + { + if (isMinutePeriod) + { + if (item.End.Date=kLineMap.End.Time) ) + { + xRight=kLineMap.End.Right; + } + else + { + var previousX=null; + for(var kItem of kLineMap.Data) + { + var value=kItem[1]; + if (value.Date>item.End.Date || (value.Date==item.End.Date && value.Time>item.End.Time) ) + { + xRight=previousX; + break; + } + previousX=value.Right; + } + } + } + else + { + if (item.End.Date<=kLineMap.End.Date) + { + xRight=kLineMap.End.Right; + } + else + { + var previousX=null; + for(var kItem of kLineMap.Data) + { + var value=kItem[1]; + if (value.Date>item.End.Date) + { + xRight=previousX; + break; + } + previousX=value.Right; + } + } + } + } + } + else + { + xRight=kLineMap.XRight; + } + + if (xLeft==null || xRight==null) return null; + + return { Left:xLeft, Right:xRight, Width:xRight-xLeft }; + } +} + + +//弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 } +function BarrageList() +{ + this.PlayList=[]; //正在播放队列 + this.Cache=[]; //没有播放的弹幕数据 + this.MinLineHeight=40*GetDevicePixelRatio(); + this.Height; //高度 + this.Step=1; + + //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 } + this.GetPlayList=function(obj) + { + var canves=obj.Canves; + var right=obj.Right; + var left=obj.Left; + var width=right-left; + var isMoveStep=obj.IsMoveStep; + + var list=[]; + var yOffset=0; + for(var i=0;ithis.MinLineHeight) lineHeight=ary.Height; + + var bAddNewItem=true; //是否需要加入新弹幕 + var bRemoveFirst=false; //是否删除第1个数据 + for(var j=0;j0) //最后一个数据了 判断是否需要增加弹幕 + { + bAddNewItem=false; + if (!item.TextWidth) + { + if (item.Font && item.Font.Name) canves.font=item.Font.Name; + else canves.font=obj.Font; + item.TextWidth=canves.measureText(playItem.Text+'擎擎').width; + } + + if (item.X>=item.TextWidth) + bAddNewItem=true; + } + else if (j==0) + { + bRemoveFirst=false; + if (!item.TextWidth) + { + if (item.Font && item.Font.Name) canves.font=item.Font.Name; + else canves.font=obj.Font; + item.TextWidth=canves.measureText(playItem.Text+'擎擎').width; + } + + if (item.X>width+item.TextWidth) bRemoveFirst=true; + } + + item.X+=this.Step; + } + + if(isMoveStep && bAddNewItem && this.Cache.length>0) //最后一个数据了 判断是否需要增加弹幕 + { + var cacheItem=this.Cache.shift(); + var newItem={ X:0, Text:cacheItem.Text, Color:cacheItem.Color , Font:cacheItem.Font, Info:cacheItem.Info }; + ary.Data.push(newItem); + } + + if (isMoveStep && bRemoveFirst && ary.Data.length>0) + { + var removeItem=ary.Data.shift(); + this.OnItemPlayEnd(obj.HQChart,removeItem); + } + + yOffset+=lineHeight; + } + + return list; + } + + //根据高度计算播放队列个数 + this.CacluatePlayLine=function(height) + { + this.Height=height; + var lineCount=parseInt(height/this.MinLineHeight); + if (this.PlayList.lengthlineCount) + { + var removeCount=this.PlayList.length-lineCount; + for(var i=0;i0) this.BarrageList.Step=option.Step; + if (option.MinLineHeight) this.Barrage.MinLineHeight=option.MinLineHeight; + } + } + + this.DrawHScreen=function() + { + var height=this.ChartBorder.GetWidth(); + var left=this.ChartBorder.GetTop(); + var right=this.ChartBorder.GetBottom(); + var top=this.ChartBorder.GetRightEx(); + var wdith=this.ChartBorder.GetChartWidth(); + + if (height!=this.BarrageList.Height) + this.BarrageList.CacluatePlayLine(height); + + this.Canvas.textBaseline="middle"; + this.Canvas.textAlign="left"; + + var play=this.BarrageList.GetPlayList({Canves:this.Canvas, Right:right, Left:left, Font:this.Font, IsMoveStep:this.IsMoveStep, HQChart:this.HQChart}); + this.IsMoveStep=false; + if (!play) return; + + this.Canvas.save(); + this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0); + this.Canvas.rotate(90 * Math.PI / 180); + + for(var i=0;i0) fontHeight=item.Font.Height; + var yOffset=item.Y+parseInt((item.Height-fontHeight)/2); + + this.Canvas.fillText(item.Text, right-item.X,top+yOffset); + } + + this.Canvas.restore(); + } + + this.Draw=function() + { + if (this.ChartFrame.IsHScreen) + { + this.DrawHScreen(); + return; + } + + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var top=this.ChartBorder.GetTopEx(); + var height=this.ChartBorder.GetHeight(); + + if (height!=this.BarrageList.Height) + this.BarrageList.CacluatePlayLine(height); + + this.Canvas.textBaseline="middle"; + this.Canvas.textAlign="left"; + + var play=this.BarrageList.GetPlayList({Canves:this.Canvas, Right:right, Left:left, Font:this.Font, IsMoveStep:this.IsMoveStep, HQChart:this.HQChart}); + this.IsMoveStep=false; + if (!play) return; + + for(var i=0;i0) fontHeight=item.Font.Height; + var yOffset=item.Y+parseInt((item.Height-fontHeight)/2); + + this.Canvas.fillText(item.Text, right-item.X,top+yOffset); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +//坐标分割 +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +function IFrameSplitOperator() +{ + this.ChartBorder; //边框信息 + this.Frame; //框架信息 + this.FrameSplitData; //坐标轴分割方法 + this.SplitCount=5; //刻度个数 + this.StringFormat=0; //刻度字符串格式 + this.IsShowLeftText=true; //显示左边刻度 + this.IsShowRightText=true; //显示右边刻度 + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.GetEventCallback; //事件回调 + + ////////////////////// + // data.Min data.Max data.Interval data.Count + // + this.IntegerCoordinateSplit=function(data) + { + var splitItem=this.FrameSplitData.Find(data.Interval); + if (!splitItem) return false; + + if (data.Interval==splitItem.FixInterval) return true; + + var fixMax=data.Max, fixMin=data.Min; + + var maxValue=data.Max/splitItem.FixInterval; + var minValue=data.Min/splitItem.FixInterval; + //调整到整数倍数,不能整除的 +1 + if (IFrameSplitOperator.IsFloat(maxValue)) fixMax=parseInt((maxValue+0.5).toFixed(0))*splitItem.FixInterval; + if (IFrameSplitOperator.IsFloat(minValue)) fixMin=parseInt((minValue-0.5).toFixed(0))*splitItem.FixInterval; + + if (data.Min==0) fixMin=0; //最小值是0 不用调整了. + if (fixMin<0 && data.Min>0) fixMin=0; //都是正数的, 最小值最小调整为0 + + var count=0; + for(var i=fixMin;(i-fixMax)<0.00000001;i+=splitItem.FixInterval) + { + ++count; + } + + data.Interval=splitItem.FixInterval; + data.Max=fixMax; + data.Min=fixMin; + data.Count=count; + + return true; + } + + this.Filter = function (aryInfo,keepZero) + { + if (this.SplitCount <= 0 || aryInfo.length <= 0 || aryInfo.length <= this.SplitCount) return aryInfo; + + //分割线比预设的多, 过掉一些 + var filter = parseInt(aryInfo.length / this.SplitCount); + if (filter <= 1) filter = 2; + var data = []; + for (var i = 0; i < aryInfo.length; i += filter) + { + if (i + filter >= aryInfo.length && i != aryInfo.length - 1) //最后一个数据放进去 + { + data.push(aryInfo[aryInfo.length - 1]); + } + else + { + data.push(aryInfo[i]); + } + } + + if (this.SplitCount == 2 && data.length>2) //之显示第1个和最后一个刻度 + { + for(var i=1;i= '1' && char <= '9') return false; + } + + return true; + } + +} + +//字符串格式化 千分位分割 +IFrameSplitOperator.FormatValueThousandsString=function(value,floatPrecision) +{ + if (value==null || isNaN(value)) + { + if (floatPrecision>0) + { + var nullText='-.'; + for(var i=0;i0){ + var numFloat = num.split('.')[1]; + var numM = num.split('.')[0]; + while (numM.length > 3) + { + result = ',' + numM.slice(-3) + result; + numM = numM.slice(0, numM.length - 3); + } + if (numM) { result = numM + result + '.' + numFloat; } + }else{ + while (num.length > 3) + { + result = ',' + num.slice(-3) + result; + num = num.slice(0, num.length - 3); + } + if (num) { result = num + result; } + } + + return result; +} + +//数据输出格式化 floatPrecision=小数位数 +IFrameSplitOperator.FormatValueString=function(value, floatPrecision,languageID) +{ + if (value==null || isNaN(value)) + { + if (floatPrecision>0) + { + var nullText='-.'; + for(var i=0;i-0.00000000001) + { + return "0"; + } + + var absValue = Math.abs(value); + if (languageID===JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID) + { + if (absValue < 10000) + return value.toFixed(floatPrecision); + else if (absValue < 1000000) + return (value/1000).toFixed(floatPrecision)+"K"; + else if (absValue < 1000000000) + return (value/1000000).toFixed(floatPrecision)+"M"; + else if (absValue < 1000000000000) + return (value/1000000000).toFixed(floatPrecision)+"B"; + else + return (value/1000000000000).toFixed(floatPrecision)+"T"; + } + else + { + if (absValue < 10000) + return value.toFixed(floatPrecision); + else if (absValue < 100000000) + return (value/10000).toFixed(floatPrecision)+"万"; + else if (absValue < 1000000000000) + return (value/100000000).toFixed(floatPrecision)+"亿"; + else + return (value/1000000000000).toFixed(floatPrecision)+"万亿"; + } + + return ''; +} + +//整形输出格式化 floatPrecision=小数位数 +IFrameSplitOperator.FromatIntegerString=function(value, floatPrecision,languageID) +{ + if (value<10000 && IFrameSplitOperator.IsInteger(value)) floatPrecision=0; //<10000的整形 去掉小数位数 + return IFrameSplitOperator.FormatValueString(value, floatPrecision,languageID); +} + +IFrameSplitOperator.NumberToString=function(value) +{ + if (value<10) return '0'+value.toString(); + return value.toString(); +} + +IFrameSplitOperator.FormatDateString=function(value,format, languageID) +{ + var year=parseInt(value/10000); + var month=parseInt(value/100)%100; + var day=value%100; + + switch(format) + { + case 'MM-DD': + return IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day); + case "YYYY/MM/DD": + return year.toString() + '/' + IFrameSplitOperator.NumberToString(month) + '/' + IFrameSplitOperator.NumberToString(day); + case "YYYY/MM/DD/W": + { + var date=new Date(year,month-1,day); + var week=g_JSChartLocalization.GetText(WEEK_NAME[date.getDay()],languageID); + return year.toString() + '/' + IFrameSplitOperator.NumberToString(month) + '/' + IFrameSplitOperator.NumberToString(day)+"/"+ week.toString(); + } + case "DD/MM/YYYY": + return IFrameSplitOperator.NumberToString(day) + '/' + IFrameSplitOperator.NumberToString(month) + '/' + year.toString(); + default: + return year.toString() + '-' + IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day); + } +} + +IFrameSplitOperator.FormatTimeString=function(value, format) //format hh:mm:ss +{ + if (format=='HH:MM:SS') + { + var hour=parseInt(value/10000); + var minute=parseInt((value%10000)/100); + var second=value%100; + return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second); + } + else if (format=='HH:MM') + { + var hour=parseInt(value/100); + var minute=value%100; + return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute); + } + else + { + if (value<10000) + { + var hour=parseInt(value/100); + var minute=value%100; + return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute); + } + else + { + var hour=parseInt(value/10000); + var minute=parseInt((value%10000)/100); + var second=value%100; + return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second); + } + } +} + +//报告格式化 +IFrameSplitOperator.FormatReportDateString=function(value) +{ + var year=parseInt(value/10000); + var month=parseInt(value/100)%100; + var monthText; + switch(month) + { + case 3: + monthText="一季度报"; + break; + case 6: + monthText="半年报"; + break; + case 9: + monthText="三季度报"; + break; + case 12: + monthText="年报"; + break; + } + + return year.toString()+ monthText; +} + +IFrameSplitOperator.FormatDateTimeString=function(value,isShowDate,isShowTime) +{ + var aryValue=value.split(' '); + if (aryValue.length<2) return ""; + var result=""; + + if (isShowDate) + { + var date=parseInt(aryValue[0]); + var year=parseInt(date/10000); + var month=parseInt(date%10000/100); + var day=date%100; + var text=year.toString() +'-'+ (month<10? ('0'+month.toString()) :month.toString()) +'-'+ (day<10? ('0'+day.toString()):day.toString()); + result+=text; + } + + if (isShowTime) + { + var time=parseInt(aryValue[1]); + var minute=time%100; + var hour=parseInt(time/100); + var text=(hour<10? ('0'+hour.toString()):hour.toString()) + ':' + (minute<10?('0'+minute.toString()):minute.toString()); + if (result.length>0) result+=" "; + result+=text; + } + + return result; +} + +//字段颜色格式化 +IFrameSplitOperator.FormatValueColor = function (value, value2) +{ + if (value != null && value2 == null) //只传一个值的 就判断value正负 + { + if (value == 0) return 'PriceNull'; + else if (value > 0) return 'PriceUp'; + else return 'PriceDown'; + } + + //2个数值对比 返回颜色 + if (value == null || value2 == null) return 'PriceNull'; + if (value == value2) return 'PriceNull'; + else if (value > value2) return 'PriceUp'; + else return 'PriceDown'; +} + +IFrameSplitOperator.IsNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return true; +} + +//判断是否是正数 +IFrameSplitOperator.IsPlusNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return value>0; +} + +//是否是整形 +IFrameSplitOperator.IsInteger=function(x) +{ + return (typeof x === 'number') && (x % 1 === 0); +} + +//判断字段是否存在 +IFrameSplitOperator.IsObjectExist=function(obj) +{ + if (obj===undefined) return false; + if (obj==null) return false; + + return true; +} + +//是否时bool +IFrameSplitOperator.IsBool=function(value) +{ + if (value===true || value===false) return true; + return false; +} + +IFrameSplitOperator.IsString=function(value) +{ + var type=typeof(value); + if (type=='string') return true; + return false; +} + +//是否是非空的数组 +IFrameSplitOperator.IsNonEmptyArray=function(ary) +{ + if (!ary) return; + if (!Array.isArray(ary)) return; + + return ary.length>0; +} + +IFrameSplitOperator.IsFloat=function(value) +{ + if (value===undefined) return false; + if (value==null) return false; + if (isNaN(value)) return false; + + return value!=parseInt(value); +} + +IFrameSplitOperator.RemoveZero=function(strValue) +{ + while(strValue.length>0) + { + var index=strValue.length-1; + var ch=strValue[index]; + if (ch=="0") + { + strValue=strValue.substr(0,index); + } + else if (ch==".") + { + strValue=strValue.substr(0,index); + break; + } + else + { + break; + } + } + + return strValue; +} + +function FrameSplitKLinePriceY() +{ + this.newMethod=IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + this.CoordinateType=0; //坐标类型 0=普通坐标 1=百分比坐标 (右边坐标刻度) 2=对数对标 3=等比坐标 4=等分坐标 5=黄金分割 + this.Symbol; + this.Data; //K线数据 (计算百分比坐标) + this.FrameSplitData2; //坐标轴分割方法(计算百分比刻度) + this.FloatPrecision=null; //小数位数 (设置了就使用这个位数,否则使用品种对应的小数位数) + + this.Period; //周期 + this.KLineChart; + this.Custom=[]; //[{Type:0}]; 定制刻度 0=显示最后的价格刻度 + this.SplitType=0; //0=自动分割 1=固定分割 2=分笔图价格分割 + + this.DefaultYMaxMin; //{ Max:null, Min:null }; //指定最大,最小, Y轴范围必须比最大值大, 比最小值小 + this.FixedYMaxMin; //{ Max, Min} 固定Y轴最大最小值 + this.EnableZoomUpDown=false; //上下左右拖拽 + this.LastMaxMin; //当前显示的最高最低范围 + + this.PercentageTextFormat=0; //0=显示第1行 1=显示2行 2=单行格式: 价格/百分比 + + this.IsEnableDragY=function() + { + return this.CoordinateType==0 || this.CoordinateType==1; + } + + this.Operator=function() + { + var splitData={}; + splitData.Max=this.Frame.HorizontalMax; + splitData.Min=this.Frame.HorizontalMin; + splitData.Count=this.SplitCount; + + if (splitData.Max==splitData.Min) //如果一样上下扩大下 + { + splitData.Max+=splitData.Max*0.01; + splitData.Min-=splitData.Min*0.01 + } + + var isFixedMaxMin=(this.FixedYMaxMin && IFrameSplitOperator.IsNumber(this.FixedYMaxMin.Max) && IFrameSplitOperator.IsNumber(this.FixedYMaxMin.Min)); + if (isFixedMaxMin) + { + splitData.Max=this.FixedYMaxMin.Max; + splitData.Min=this.FixedYMaxMin.Min; + //JSConsole.Chart.Log(`[FrameSplitKLinePriceY::Operator] FixedYMaxMin.Max=${this.FixedYMaxMin.Max} FixedYMaxMin.Min=${this.FixedYMaxMin.Min} `); + } + else if (this.DefaultYMaxMin) //指定最小的Y轴范围 + { + var range=this.DefaultYMaxMin; + if (IFrameSplitOperator.IsNumber(range.Max)) + { + if (splitData.Min>range.Max) splitData.Min=range.Max; + else if (splitData.Maxrange.Min) splitData.Min=range.Min; + } + } + + splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1); + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + var width=this.Frame.ChartBorder.GetChartWidth(); //画布的宽度 + var isPhoneModel=width<450*pixelTatio; + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + if (isPhoneModel && MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol)) defaultfloatPrecision = 0; //手机端指数不显示小数位数,太长了 + if (this.FloatPrecision!=null) defaultfloatPrecision=this.FloatPrecision; + JSConsole.Chart.Log(`[FrameSplitKLinePriceY::Operator] Max=${splitData.Max} Min=${splitData.Min} Count=${splitData.Count} isPhoneModel=${isPhoneModel} defaultfloatPrecision=${defaultfloatPrecision} `); + + this.Frame.Logarithmic=null; + this.Frame.MultiTextFormat=0; + var bFilter=true; //是否需要通过高度过滤刻度 + splitData.IsFixedMaxMin=isFixedMaxMin; + splitData.IsFilter=bFilter; + if (ChartData.IsTickPeriod(this.Period)) + { + this.SplitTickData(splitData,defaultfloatPrecision); + bFilter=splitData.IsFilter; + } + else if (FrameSplitKLinePriceY.SplitCustom) + { + FrameSplitKLinePriceY.SplitCustom(this,splitData,defaultfloatPrecision); //自定义分割 + bFilter=false; + } + else + { + switch(this.CoordinateType) + { + case 1: + if (!this.SplitPercentage(splitData,defaultfloatPrecision,isFixedMaxMin)) + { + this.SplitDefault(splitData,defaultfloatPrecision,isFixedMaxMin); + } + else + { + this.Frame.MultiTextFormat=this.PercentageTextFormat; + bFilter=false; + } + break; + case 3: //等比坐标 +10%/-10% 涨幅分割 + if (!this.SplitIncrease(splitData,defaultfloatPrecision)) + this.SplitDefault(splitData,defaultfloatPrecision); + else + bFilter=false; + break; + case 4: //等分坐标 + this.SplitAverage(splitData,defaultfloatPrecision); + bFilter=false; + break; + case 5: //黄金分割 + this.SplitGoldenSection(splitData,defaultfloatPrecision); + bFilter=false; + break; + case 2: //对数坐标 + if (this.SplitLogarithmic(splitData,defaultfloatPrecision)) + { + bFilter=false; + } + else + { + this.SplitDefault(splitData,defaultfloatPrecision); + } + break; + default: + if (this.SplitType==1) + { + this.SplitFixed(splitData,defaultfloatPrecision); + bFilter=false; + } + else + { + this.SplitDefault(splitData,defaultfloatPrecision,isFixedMaxMin); + } + break; + } + } + + this.CustomCoordinate(defaultfloatPrecision); + if (bFilter) this.Frame.HorizontalInfo = this.Filter(this.Frame.HorizontalInfo,false); + this.Frame.HorizontalMax=splitData.Max; + this.Frame.HorizontalMin=splitData.Min; + + if (this.EnableZoomUpDown==true && !this.FixedYMaxMin) + this.FixedYMaxMin={ Max:splitData.Max, Min:splitData.Min }; + + /* + for(var i=0;i=splitData.Min;value-=splitData.Interval) + { + var price=(value+1)*firstOpenPrice; + if (pricemaxValue) continue; + + var item=new CoordinateInfo(); + item.Value=price; + if (this.IsShowLeftText) item.Message[0]=price.toFixed(floatPrecision); //左边价格坐标 + if (this.IsShowRightText) + { + var strPrice=price.toFixed(floatPrecision); + var text=(value*100).toFixed(2); //右边百分比 + text=IFrameSplitOperator.RemoveZero(text); + text+='%'; + item.Message[1]=[text,strPrice]; + item.ExtendData=textColor; + } + aryHorizontal.push(item); + } + + aryHorizontal.sort((left, right)=> + { + return left.Value - right.Value; + }); + + this.Frame.HorizontalInfo=aryHorizontal; + + splitData.Min=minValue; //最大最小值调整 + splitData.Max=maxValue; + return true; + } + + //等比坐标 当前屏最后第2根K线的收盘加为基准, 上下涨幅10%分割 + this.SplitIncrease=function(splitData,floatPrecision) + { + var basePrice=this.GetLast2ndClose(); + if (!IFrameSplitOperator.IsNumber(basePrice)) return false; + this.IntegerCoordinateSplit(splitData); + this.Frame.HorizontalInfo=[]; + + var increase=g_JSChartResource.FrameSplitIncrease.Increase; + var aryHorizontal=[]; + for(var price=basePrice; pricesplitData.Min; price=price*(1-increase)) + { + var item= new CoordinateInfo(); + item.Value=price; + var text=price.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=text; + if (this.IsShowRightText) item.Message[1]=text; + aryHorizontal.push(item); + } + + this.Frame.HorizontalInfo=aryHorizontal; + return true; + } + + //等分坐标:以画面显示的最高价、最低价为基准,对这个区域N等分,显示分割的数值线 + this.SplitAverage=function(splitData,floatPrecision) + { + var max=splitData.Max; + var min=splitData.Min; + //this.IntegerCoordinateSplit(splitData); + this.Frame.HorizontalInfo=[]; + var count=g_JSChartResource.FrameSplitAverage.Count; + var interval=(max-min)/count; + + for(var i=0;i<=count;++i) + { + var item=new CoordinateInfo(); + item.Value=min+interval*i; + var text=item.Value.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=text; + if (this.IsShowRightText) item.Message[1]=text; + + this.Frame.HorizontalInfo[i]=item; + } + } + + this.SplitGoldenSection=function(splitData,floatPrecision) + { + var max=splitData.Max; + var min=splitData.Min; + //this.IntegerCoordinateSplit(splitData); + this.Frame.HorizontalInfo=[]; + + var aryHorizontal=[]; + var GOLDEN_ARRAY=g_JSChartResource.FrameGoldenSection.Golden; + for(var i in GOLDEN_ARRAY) + { + var value=(max-min)*GOLDEN_ARRAY[i]+min; + item=new CoordinateInfo(); + item.Value=value; + var text=value.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=text; + if (this.IsShowRightText) item.Message[1]=text; + aryHorizontal.push(item); + } + + this.Frame.HorizontalInfo=aryHorizontal; + } + + this.SplitLogarithmic=function(splitData,floatPrecision) //对数坐标 + { + var minInterval=g_JSChartResource.FrameLogarithmic.MinInterval; //最小间距 + var firstOpenPrice=this.GetFirstOpenPrice(); //获取当前屏第1个K线的开盘价 + if (!IFrameSplitOperator.IsNumber(firstOpenPrice)) return false; + var height=this.ChartBorder.GetHeightEx(); + + var ARRAY_INCREASE=[0.01, 0.02, 0.04, 0.08, 0.1, 0.2]; + var increase=ARRAY_INCREASE[ARRAY_INCREASE.length-1]; + for(var i=0; iminInterval) + { + increase=value; + break; + } + } + + var aryUp=[]; + var price=firstOpenPrice; + var i=increase; + do + { + var item={ Start:price }; + price=firstOpenPrice*(1+i); + item.End=price; + aryUp.push(item); + + i+=increase; + } while(pricesplitData.Min); + var min=price; + + splitData.Max=max; + splitData.Min=min; + + JSConsole.Chart.Log("[FrameSplitKLinePriceY::SplitLogarithmic] up, down", aryUp, aryDown); + + this.Frame.HorizontalInfo=[]; + var item=new CoordinateInfo(); + item.Value=firstOpenPrice; + item.Font=g_JSChartResource.FrameLogarithmic.OpenPriceFont; + var strText=item.Value.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=strText; //左边价格坐标 + if (this.IsShowRightText) item.Message[1]=strText; //右边价格坐标 + this.Frame.HorizontalInfo.push(item); + + for(var i in aryUp) + { + var item=new CoordinateInfo(); + item.Value=aryUp[i].End; + var strText=item.Value.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=strText; //左边价格坐标 + if (this.IsShowRightText) item.Message[1]=strText; //右边价格坐标 + this.Frame.HorizontalInfo.push(item); + } + + for(var i in aryDown) + { + var item=new CoordinateInfo(); + item.Value=aryDown[i].End; + var strText=item.Value.toFixed(floatPrecision); + if (this.IsShowLeftText) item.Message[0]=strText; //左边价格坐标 + if (this.IsShowRightText) item.Message[1]=strText; //右边价格坐标 + this.Frame.HorizontalInfo.splice(0,0,item); + } + + this.Frame.Logarithmic={ Up:aryUp, Down:aryDown, OpenPrice:firstOpenPrice }; + return true; + } + + this.SplitDefault=function(splitData,floatPrecision,isFixedMaxMin) //默认坐标 + { + //固定最大最小值 不自动调整范围 + if (!isFixedMaxMin) this.IntegerCoordinateSplit(splitData); + + this.Frame.HorizontalInfo=[]; + for(var i=0,value=splitData.Min;i=0) dec=item.FloatPrecision; + var latestItem=this.GetLatestPrice(dec,item); + if (latestItem) this.Frame.CustomHorizontalInfo.push(latestItem); + } + else if (item.Type==1) + { + this.CustomFixedCoordinate(item); + } + } + } + + this.GetLatestPrice=function(floatPrecision,option) + { + if (!this.Data || !this.Data.Data) return null; + if (this.Data.Data.length<=0) return null; + var latestItem=this.Data.Data[this.Data.Data.length-1]; + var info=new CoordinateInfo(); + info.Type=0; + info.Value=latestItem.Close; + info.TextColor=g_JSChartResource.FrameLatestPrice.TextColor; + info.LineType=2; //虚线 + var strPrice=latestItem.Close.toFixed(floatPrecision); + if (option.DateTime=='HH:MM' && ChartData.IsMinutePeriod(this.Period,true)) + { + var strTime=IFrameSplitOperator.FormatTimeString(latestItem.Time,option.DateTime); + var aryText=[{Text:strPrice}, { Text:strTime} ]; + if (option.Position=='left') info.Message[0]=aryText; + else info.Message[1]=aryText; + } + else + { + if (option.Position=='left') info.Message[0]=strPrice + else info.Message[1]=strPrice; + } + + if (latestItem.Close>latestItem.Open) info.LineColor=g_JSChartResource.FrameLatestPrice.UpBarColor; + else if (latestItem.Close=this.Data.Data.length) endIndex=this.Data.Data.length-1; + var price=null; + for(var i=endIndex, count=0; i>=0 && i=2) break; + } + + return price; + } + + this.CustomFixedCoordinate=function(option) //固定坐标刻度 + { + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + for(var i in option.Data) + { + var item=option.Data[i]; + var info=new CoordinateInfo(); + info.Type=1; + info.TextColor=item.TextColor; + info.LineColor=item.Color; + info.LineType=2; //虚线 + if (IFrameSplitOperator.IsNumber(option.LineType)) info.LineType=option.LineType; + if (option.IsShowLine==false) info.LineType=-1; + + info.Value=item.Value; + var text; + if (item.Text) text=item.Text; + else text=info.Value.toFixed(defaultfloatPrecision); + if (option.Position=='left') info.Message[0]=text; + else info.Message[1]=text; + + this.Frame.CustomHorizontalInfo.push(info); + } + } + + ////////////////////// + // data.Min data.Max data.Interval data.Count + // + this.IntegerCoordinateSplit2=function(data) + { + var splitItem=this.FrameSplitData2.Find(data.Interval); + if (!splitItem) return false; + + if (data.Interval==splitItem.FixInterval) return true; + + //调整到整数倍数,不能整除的 +1 + var fixMax=parseInt((data.Max/(splitItem.FixInterval)+0.5).toFixed(0))*splitItem.FixInterval; + var fixMin=parseInt((data.Min/(splitItem.FixInterval)-0.5).toFixed(0))*splitItem.FixInterval; + if (data.Min==0) fixMin=0; //最小值是0 不用调整了. + if (fixMin<0 && data.Min>0) fixMin=0; //都是正数的, 最小值最小调整为0 + + var count=0; + for(var i=fixMin;(i-fixMax)<0.00000001;i+=splitItem.FixInterval) + { + ++count; + } + + data.Interval=splitItem.FixInterval; + data.Max=fixMax; + data.Min=fixMin; + data.Count=count; + + return true; + } +} + +function FrameSplitY() +{ + this.newMethod=IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.SplitCount=3; //刻度个数 + this.FloatPrecision = 2; //坐标小数位数(默认2) + this.FLOATPRECISION_RANGE=[1,0.1,0.01,0.001,0.0001]; + this.SplitType=0; //0=自动分割 1=固定分割 + this.Custom=[]; //[{Type:0}]; 定制刻度 + this.DefaultYMaxMin; //{ Max:null, Min:null }; //指定最大,最小, Y轴范围必须比最大值大, 比最小值小 + this.EnableRemoveZero=true; + this.LineType=null; //线段样式 + this.IgnoreYValue = null; //在这个数组里的数字不显示在刻度上 + this.FixedYMaxMin; //{ Max, Min} 固定Y轴最大最小值 + this.EnableZoomUpDown=false; + + this.IsBeforeData=false; + this.BeforeOpenData; + + this.IsAfterData=false; + this.AfterCloseData; + + this.MultiDayBeforeOpenData; + this.MultiDayAfterCloseData; + + this.IsEnableDragY=function() + { + return true; + } + + this.GetFloatPrecision=function(value,floatPrecision) + { + if (value>this.FLOATPRECISION_RANGE[0]) return floatPrecision; + if (floatPrecision<0) return 2; + for(;floatPrecisionthis.FLOATPRECISION_RANGE[floatPrecision]) break; + } + + return floatPrecision; + } + + this.Operator=function() + { + var splitData={}; + splitData.Max=this.Frame.HorizontalMax; + splitData.Min=this.Frame.HorizontalMin; + + if (splitData.Max==splitData.Min) //如果一样上下扩大下 + { + if (splitData.Max==0) + { + splitData.Max=1; + splitData.Min=-1; + } + else + { + splitData.Max+=splitData.Max*0.01; + splitData.Min-=splitData.Min*0.01; + } + } + + var isFixedMaxMin=false; + if (this.FixedYMaxMin && IFrameSplitOperator.IsNumber(this.FixedYMaxMin.Max) && IFrameSplitOperator.IsNumber(this.FixedYMaxMin.Min)) isFixedMaxMin=true; + if (isFixedMaxMin) + { + splitData.Max=this.FixedYMaxMin.Max; + splitData.Min=this.FixedYMaxMin.Min; + } + else if (this.DefaultYMaxMin) //指定最小的Y轴范围 + { + var range=this.DefaultYMaxMin; + if (IFrameSplitOperator.IsNumber(range.Max)) + { + if (splitData.Min>range.Max) splitData.Min=range.Max; + else if (splitData.Maxrange.Min) splitData.Min=range.Min; + } + } + + if(this.Frame.YSpecificMaxMin) + { + splitData.Count=this.Frame.YSpecificMaxMin.Count; + splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1); + } + else if (this.SplitType==1 || isFixedMaxMin) + { + splitData.Count=this.SplitCount; + splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1); + } + else + { + splitData.Count=this.SplitCount; + splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1); + this.IntegerCoordinateSplit(splitData); + } + + this.Frame.HorizontalInfo=[]; + + if (this.Frame.YSplitScale) //固定坐标 + { + for(var i in this.Frame.YSplitScale) + { + var value=this.Frame.YSplitScale[i]; + var coordinate=new CoordinateInfo(); + coordinate.Value=value; + if (IFrameSplitOperator.IsNumber(this.LineType)) coordinate.LineType=this.LineType; + + var absValue=Math.abs(value); + if (absValue<0.0000000001) + { + coordinate.Message[1]=0; + } + else if (absValue 1000) floatPrecision=0; + this.Frame.HorizontalInfo[i].Message[1]=IFrameSplitOperator.FormatValueString(value,floatPrecision,this.LanguageID); + } + else + { + var absValue=Math.abs(value); + if (absValue<0.0000000001) + { + this.Frame.HorizontalInfo[i].Message[1]=0; + } + else if (absValue0 && splitData.Min<0)); + if (this.EnableRemoveZero) this.RemoveZero(this.Frame.HorizontalInfo); + this.Frame.HorizontalMax=splitData.Max; + this.Frame.HorizontalMin=splitData.Min; + + if (this.EnableZoomUpDown==true && !this.FixedYMaxMin) + this.FixedYMaxMin={ Max:splitData.Max, Min:splitData.Min }; + + this.RightFrameSplitY(); + this.CallAcutionSplitY(this.SplitCount,splitData); + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SPLIT_YCOORDINATE); + if (event && event.Callback) + { + var data={ID:this.Frame.Identify, Frame:this.Frame }; + event.Callback(event,data,this); + } + } + } + + this.FilterIgnoreYValue = function () + { + if (!this.IgnoreYValue || this.IgnoreYValue.length <= 0) return; + + var setValue = new Set(this.IgnoreYValue); + this.Frame.HorizontalInfo = this.Frame.HorizontalInfo.filter(item => !setValue.has(item.Value)); + } + + this.CallAcutionSplitY=function(count,splitData) + { + if (this.Frame.Identify!=1) return null; + var aryCallAcution=this.GetCallAcutionSplitY(count,splitData); + if (!aryCallAcution) return; + + for(var i in this.Frame.HorizontalInfo) //把显示的数据迁移到 Message[2] Message[3] + { + var item=this.Frame.HorizontalInfo[i]; + if (aryCallAcution.IsBeforeData) + { + item.Message[2]=item.Message[0]; + item.Message[0]=null; + } + + if (aryCallAcution.IsAfterData) + { + item.Message[3]=item.Message[1]; + item.Message[1]=null; + } + } + + //集合竞价的坐标插入最后 + for(var i in aryCallAcution.HorizontalInfo) + { + var item=aryCallAcution.HorizontalInfo[i]; + this.Frame.HorizontalInfo.push(item); + } + } + + this.GetCallAcutionSplitY=function(count,splitData) + { + if (this.Frame.Identify!=1) return null; + + var isBeforeData=(this.IsBeforeData==true && this.BeforeOpenData && (this.BeforeOpenData.Ver==2.0 || this.BeforeOpenData.Ver==3.0)); + var isAfterData=(this.IsAfterData==true && this.AfterCloseData && (this.AfterCloseData.Ver==2.0 || this.AfterCloseData.Ver==3.0)); + + if (isBeforeData || isAfterData) + { + var intervalY=(splitData.Max-splitData.Min)/(count-1); + if (isBeforeData) var intervalLeft=(this.BeforeOpenData.VolMax-this.BeforeOpenData.VolMin)/(count-1); + if (isAfterData) var intervalRight=(this.AfterCloseData.VolMax-this.AfterCloseData.VolMin)/(count-1); + + var aryHorizontalInfo=[]; + for(var i=0;ithis.MinTextDistance && barDistance>=this.MinBarDistance) + { + var time=IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time); + infoData={Value:index-xOffset, Text:time}; + } + + if (infoData) + { + var info= new CoordinateInfo(); + info.Value=infoData.Value; + if (this.ShowText) info.Message[0]=infoData.Text; + if (info.Value==0) info.LineType=-1; //第1个分割线不画 + this.Frame.VerticalInfo.push(info); + textDistance=0; + barDistance=0; + if (i==0) textDistance=-(this.MinTextDistance/2); + } + } + } + + this.SplitSecond=function() //根据时间分割 + { + this.Frame.VerticalInfo=[]; + var itemWidth=this.Frame.DistanceWidth+this.Frame.DataWidth; + var xOffset=this.Frame.Data.DataOffset; + var xPointCount=this.Frame.XPointCount; + var textDistance=0; + var barDistance=0; + + for(var i=0, index=xOffset; ithis.MinTextDistance && barDistance>=this.MinBarDistance) + { + var time=IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time,"HH:MM:SS"); + infoData={Value:index-xOffset, Text:time}; + } + + if (infoData) + { + var info= new CoordinateInfo(); + info.Value=infoData.Value; + if (this.ShowText) info.Message[0]=infoData.Text; + this.Frame.VerticalInfo.push(info); + textDistance=0; + barDistance=0; + if (i==0) textDistance=-(this.MinTextDistance/2); + } + } + } + + this.SplitDate=function() //根据日期分割 + { + this.Frame.VerticalInfo=[]; + var xOffset=this.Frame.Data.DataOffset; + var xPointCount=this.Frame.XPointCount; + var lastYear=null, lastMonth=null; + var minDistance=12; + var monthCount=0; + for(var i=0, index=xOffset, distance=minDistance;ithis.YClose) info.LineColor=g_JSChartResource.FrameLatestPrice.UpBarColor; + else if (priceitem.Price) range.Min=item.Price; + } + + //集合竞价均线统计 + if (callAuctionData.Ver==3.0 && IFrameSplitOperator.IsNumber(item.AvPrice)) + { + if (range.Max==null) range.Max=item.AvPrice; + if (range.Min==null) range.Min=item.AvPrice; + + if (range.Maxitem.AvPrice) range.Min=item.AvPrice; + } + } + } + + this.GetMaxMin=function() //计算图中所有的数据的最大最小值 + { + var max=this.YClose; + var min=this.YClose; + var data=this.Data; + var isBerforeData=false; + if (this.SourceData) + { + data=this.SourceData; + isBerforeData=true; + } + + for(var i in data.Data) + { + var value=null; + if (isBerforeData) + { + var item=data.Data[i]; + if (item.Before) value=item.Before.Close; + else value=item.Close; + } + else + { + value=data.Data[i]; + } + if (value==null) continue; + if (maxvalue) min=value; + } + + if (this.AverageData) + { + for(var i in this.AverageData.Data) + { + if (this.AverageData.Data[i]==null) continue; + if (maxthis.AverageData.Data[i]) min=this.AverageData.Data[i]; + } + } + + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + //if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue; + var range=item.GetMaxMin(); + if (range.Max && range.Max>max) max=range.Max; + if (range.Min && range.Minthis.LimitPrice.Min) min=this.LimitPrice.Min; + } + + if (IFrameSplitOperator.IsNumber(this.High) && IFrameSplitOperator.IsNumber(this.Low)) + { + if (maxthis.Low) min=this.Low; + } + + var range={ Max:null, Min:null }; + if (this.IsAfterData && this.AfterCloseData) + { + this.GetCallAuctionMaxMin(this.AfterCloseData,range); + } + + if (this.IsBeforeData && this.BeforeOpenData) + { + this.GetCallAuctionMaxMin(this.BeforeOpenData,range); + } + + if (this.MultiDayBeforeOpenData && this.ChartBorder.MultiDayMinute.Count>1 && this.ChartBorder.MultiDayMinute.Left>0) + { + for(var i in this.MultiDayBeforeOpenData) + { + var dayItem=this.MultiDayBeforeOpenData[i]; + this.GetCallAuctionMaxMin(dayItem,range); + } + } + + if (this.MultiDayAfterCloseData && this.ChartBorder.MultiDayMinute.Count>1 && this.ChartBorder.MultiDayMinute.Right>0) + { + for(var i in this.MultiDayAfterCloseData) + { + var dayItem=this.MultiDayAfterCloseData[i]; + this.GetCallAuctionMaxMin(dayItem,range); + } + } + + if (IFrameSplitOperator.IsNumber(range.Max) && maxrange.Min) min=range.Min; + + return { Max:max, Min:min }; + } + + this.USASplit=function(range) + { + var max=range.Max; + var min=range.Min; + + if (max==min) + { + max=max+max*0.1; + min=min-min*0.1; + } + else + { + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + var height=this.Frame.ChartBorder.GetHeight(); //画布的高度 + var spacePrice=5*pixelTatio*(max-min)/height; + max+=spacePrice; + min-=spacePrice; + if (min<0) min=range.Min; + } + + var showCount=this.SplitCount; + var distance=(max-min)/(showCount-1); + const minDistance=[1, 0.1, 0.01, 0.001, 0.0001]; + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + if (distance0) coordinate.TextColor=g_JSChartResource.UpTextColor; + else if (per<0) coordinate.TextColor=g_JSChartResource.DownTextColor; + if (this.IsShowRightText) + { + if (this.RightTextFormat==1) coordinate.Message[1]=strPrice; + else coordinate.Message[1]=IFrameSplitOperator.FormatValueString(per,2)+'%'; //百分比 + } + } + + this.Frame.HorizontalInfo.push(coordinate); + } + + if (this.YClose>min && this.YClose0? xcoordinateData.MiddleCount: parseInt(minuteCount/2); + var xcoordinate = xcoordinateData.Data; + + this.Frame.XPointCount=minuteCount*this.DayCount; + this.Frame.MinuteCount=minuteCount; + this.Frame.VerticalInfo=[]; + + if (this.DayCount<=1) + { + for(var i in xcoordinate) + { + var info=new CoordinateInfo(); + if (g_JSChartResource.Minute.FrameSplitTextColor) info.TextColor=g_JSChartResource.Minute.FrameSplitTextColor; + info.Value=xcoordinate[i][0]; + if (this.ShowText) + info.Message[0]=xcoordinate[i][3]; + this.Frame.VerticalInfo[i]=info; + } + } + else + { + for(var i=this.DayData.length-1,j=0;i>=0;--i,++j) + { + var info=new CoordinateInfo(); + info.Value=j*minuteCount+minuteMiddleCount; + info.LineType=-1; //线段不画 + if (this.ShowText) info.Message[0]=IFrameSplitOperator.FormatDateString(this.DayData[i].Date, this.ShowFormate==0?'YYYY-MM-DD':'MM-DD'); + this.Frame.VerticalInfo.push(info); + + var info=new CoordinateInfo(); + info.Value=(j+1)*minuteCount; + this.Frame.VerticalInfo.push(info); + } + } + + if (this.GetEventCallback) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SPLIT_XCOORDINATE); + if (event && event.Callback) + { + var data={ID:this.Frame.Identify, Frame:this.Frame }; + event.Callback(event,data,this); + } + } + } +} + +function FrameSplitXData() +{ + this.newMethod=IFrameSplitOperator; //派生 + this.newMethod(); + delete this.newMethod; + + this.ShowText=true; //是否显示坐标信息 + this.Symbol; //股票代码 + this.MinTextDistance=50*GetDevicePixelRatio(); + this.MinBarDistance=5; //刻度间最小的K线间距 + + this.Operator=function() + { + if (this.Frame.Data==null || this.Frame.XData==null) return; + this.Frame.VerticalInfo=[]; + var xOffset=this.Frame.Data.DataOffset; + var xPointCount=this.Frame.XPointCount; + + for(var i=0, index=xOffset; i=200 && this.ClientPos<=299) + { + return this.GetMultiDayBeforeOpenXIndex(); + } + else if (this.ClientPos>=300 && this.ClientPos<=399) + { + return this.GetMultiDayAfterCloseXIndex(); + } + + return false; + } + + this.GetBeforeOpenXIndex=function() + { + if (!IFrameSplitOperator.IsNumber(this.Value)) return false; + if (!this.BeforeOpenData || !this.BeforeOpenData.Data) return false; + + var index=this.Frame.GetLeftExtendXData(this.Value, this.BeforeOpenData); + index=parseInt(index.toFixed(0)); + + if (index>=0 && index=this.BeforeOpenData.Data.length) index=this.BeforeOpenData.Data.length-1; + + var findIndex=-1; + for(var i=index; i>=0; --i) + { + var item=this.BeforeOpenData.Data[i]; + if (IFrameSplitOperator.IsNumber(item.Price)) + { + findIndex=i; + break; + } + } + + if (findIndex<0) + { + for(var i=index+1; i=this.MultiDayBeforeOpenData.length) return; + + var dayData=this.MultiDayBeforeOpenData[dayIndex]; + var indexData=this.Frame.GetLeftExtendXData(this.Value, this.MultiDayBeforeOpenData); + var index=parseInt(indexData.DataIndex.toFixed(0)); + var dayIndex=indexData.DayIndex; + + if (index>=0 && index=dayData.Data.length) index=dayData.Data.length-1; + var findIndex=-1; + for(var i=index; i>=0; --i) + { + var item=dayData.Data[i]; + if (IFrameSplitOperator.IsNumber(item.Price)) + { + findIndex=i; + break; + } + } + + if (findIndex<0) return false; + + this.Item=item; + this.DataIndex=findIndex; + this.DayIndex=dayIndex; + this.X=this.Frame.GetLeftExtendXFromIndex(findIndex, dayData); //调整X轴坐标 + return true; + } + + this.GetAfterCloseXIndex=function() + { + if (!IFrameSplitOperator.IsNumber(this.Value)) return false; + if (!this.AfterCloseData || !this.AfterCloseData.Data) return false; + + var index=this.Frame.GetRightExtendXData(this.Value, this.AfterCloseData); + index=parseInt(index.toFixed(0)); + + if (index>=0 && index=this.AfterCloseData.Data.length) index=this.AfterCloseData.Data.length-1; + var findIndex=-1; + for(var i=index; i>=0; --i) + { + var item=this.AfterCloseData.Data[i]; + if (IFrameSplitOperator.IsNumber(item.Price)) + { + findIndex=i; + break; + } + } + + if (findIndex<0) + { + for(var i=index+1; i=this.MultiDayAfterCloseData.length) return; + + var dayData=this.MultiDayAfterCloseData[dayIndex]; + var indexData=this.Frame.GetRightExtendXData(this.Value, this.MultiDayAfterCloseData); + var index=parseInt(indexData.DataIndex.toFixed(0)); + var dayIndex=indexData.DayIndex; + + if (index>=0 && index=dayData.Data.length) index=dayData.Data.length-1; + var findIndex=-1; + for(var i=index; i>=0; --i) + { + var item=dayData.Data[i]; + if (IFrameSplitOperator.IsNumber(item.Price)) + { + findIndex=i; + break; + } + } + + if (findIndex<0) return false; + + this.Item=item; + this.DataIndex=findIndex; + this.DayIndex=dayIndex; + this.X=this.Frame.GetRightExtendXFromIndex(findIndex, dayData); //调整X轴坐标 + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//十字光标 +function ChartCorssCursor() +{ + this.Frame; + this.Canvas; //画布 + + this.HPenColor=g_JSChartResource.CorssCursorHPenColor; //水平线颜色 + this.HPenType=0; //水平线样式 0=虚线 1=实线 + + this.VPenColor=g_JSChartResource.CorssCursorVPenColor; //垂直线颜色 + this.VPenType=0; //垂直线颜色 0=虚线 1=实线 2=K线宽度 + this.VLineType=0; //垂直线 0=默认指标标题栏不画, 1=指标标题栏也画 + + this.Font=g_JSChartResource.CorssCursorTextFont; //字体 + this.TextColor=g_JSChartResource.CorssCursorTextColor; //文本颜色 + this.TextBGColor=g_JSChartResource.CorssCursorBGColor; //文本背景色 + this.BorderColor=g_JSChartResource.CorssCursorBorderColor; //边框颜色 + this.XRangeBGColor=g_JSChartResource.CorssCursorXRangeBGColor; + this.TextHeight=20; //文本字体高度 + this.LastPoint; + this.CursorIndex; + this.IsOnlyDrawKLine=false; //是否只能画在K线上 (手机端) + this.IsOnlyDrawMinute=false; //是否只能画在走势图价格线上 + this.IsFixXLastTime=false; //是否修正X轴,超出当前时间的,X轴调整到当前最后的时间. + this.IsDrawXRangeBG=false; //是否绘制十字光标背景 + + this.PointX; + this.PointY; + + this.StringFormatX; + this.StringFormatY; + + this.ShowTextMode={ Left:1, Right:1, Bottom:1 }; //0=不显示 1=显示在框架外 2=显示在框架内 + this.TextFormat= { Right:0 }; //0=默认 1=价格显示(分时图才有用) + this.IsShowCorss=true; //是否显示十字光标 + this.IsShow=true; + this.IsShowClose=false; //Y轴始终显示收盘价 + this.ClientPos=-1; + this.CallAcutionXOperator; + + //内部使用 + this.Close=null; //收盘价格 + this.Status=0; //当前状态 0=隐藏 1=显示 + + this.ReloadResource=function(resource) + { + this.Font=g_JSChartResource.CorssCursorTextFont; //字体 + + this.HPenColor=g_JSChartResource.CorssCursorHPenColor; //水平线颜色 + this.VPenColor=g_JSChartResource.CorssCursorVPenColor; //垂直线颜色 + this.TextColor=g_JSChartResource.CorssCursorTextColor; //文本颜色 + this.TextBGColor=g_JSChartResource.CorssCursorBGColor; //文本背景色 + this.BorderColor=g_JSChartResource.CorssCursorBorderColor; //边框颜色 + this.XRangeBGColor=g_JSChartResource.CorssCursorXRangeBGColor; + } + + this.GetCloseYPoint=function(index) + { + if (!this.StringFormatX.Data) return null; + var data = this.StringFormatX.Data; + if (!data.Data || data.Data.length <= 0) return null; + var dataIndex = data.DataOffset + index; + if (dataIndex >= data.Data.length) dataIndex = data.Data.length - 1; + if (dataIndex < 0) return null; + + var klineData = data.Data[dataIndex]; + if (!klineData) return null; + this.Close=klineData.Close; + var yPoint = this.Frame.GetYFromData(this.Close); + return yPoint; + } + + this.GetMinuteCloseYPoint=function(index) + { + if (!IFrameSplitOperator.IsNumber(index)) return null; + index=parseInt(index); + if (!this.StringFormatX.Data) return null; + var data = this.StringFormatX.Data; + if (!data.Data || data.Data.length <= 0) return null; + var dataIndex = data.DataOffset + index; + if (dataIndex >= data.Data.length) dataIndex = data.Data.length - 1; + if (dataIndex < 0) return null; + + var close = data.Data[dataIndex]; + if (!IFrameSplitOperator.IsNumber(index)) return null; + this.Close=close; + var yPoint = this.Frame.GetYFromData(this.Close); + return yPoint; + } + + this.GetDateTimeRange=function(index, option) + { + if (!IFrameSplitOperator.IsNumber(index)) return null; + index=parseInt(index); + var data = this.StringFormatX.Data; + if (!data.Data || data.Data.length <= 0) return null; + var dataIndex = data.DataOffset + index; + if (dataIndex>=data.Data.length || dataIndex<0) return null; + if (!this.Frame || !this.Frame.SubFrame[0] || !this.Frame.SubFrame[0].Frame) return null; + var frame=this.Frame.SubFrame[0].Frame; + + var dataWidth=frame.DataWidth; + var distanceWidth=frame.DistanceWidth; + var xPointCount=frame.XPointCount; + + var kItem = data.Data[dataIndex]; + if (!kItem) return null; + + var date=kItem.Date*1000000; + var time=parseInt(kItem.Time/100)*100; + var startTime=date+time; + var endTime=date+time+59; + + var endIndex=dataIndex; + for(var i=dataIndex; iendTime) break; + if (i-data.DataOffset>=xPointCount) break; + endIndex=i; + } + + var startIndex=dataIndex; + for(var i=dataIndex;i>=0 && i>=data.DataOffset;--i) + { + var item=data.Data[i]; + var dateTime=item.Date*1000000+item.Time; + if (dateTime10) + { + this.Canvas.beginPath(); + if (this.Frame.IsHScreen===true) + { + this.Canvas.rect(border.Left,border.Top,border.Right-border.Left,border.TopEx-border.Top); + } + else + { + this.Canvas.rect(border.Left,border.Top,border.LeftEx-border.Left,border.Bottom-border.Top); + } + + if (this.Canvas.isPointInPath(x,y)) return 2; + } + + if (this.Frame.ChartBorder.RightExtendWidth>10) + { + this.Canvas.beginPath(); + if (this.Frame.IsHScreen===true) + { + this.Canvas.rect(border.Left,border.BottomEx,border.Right-border.Left,border.Bottom-border.BottomEx); + } + else + { + this.Canvas.rect(border.RightEx,border.Top,border.Right-border.RightEx,border.Bottom-border.Top); + } + + if (this.Canvas.isPointInPath(x,y)) return 3; + } + + return -1; + } + + this.GetFontHeight=function(font) + { + return GetFontHeight(this.Canvas, font, "擎"); + } + + this.Draw=function() + { + this.Status=0; + if (!this.LastPoint) return; + + this.Close=null; + var x=this.LastPoint.X; + var y=this.LastPoint.Y; + + var clientPos=this.PtInClient(x,y); + + this.PointY=null; + this.PointY==null; + this.ClientPos=clientPos; + if (clientPos<=0) return; + + if (this.Frame.IsHScreen===true) + { + this.HScreenDraw(); + return; + } + + var border=this.Frame.ChartBorder.GetBorder(); + var left=border.Left + var right=border.Right; + var top=border.TopTitle; + var bottom=border.Bottom; + var rightWidth=this.Frame.ChartBorder.Right; + var chartRight=border.ChartWidth; + + if (this.IsOnlyDrawKLine) //手机端 十字只能画在K线上 + { + x=this.Frame.GetXFromIndex(this.CursorIndex); + if (this.IsShowClose) + { + var yPoint = this.GetCloseYPoint(this.CursorIndex); + if (yPoint != null) y=yPoint; + } + } + else if (this.IsOnlyDrawMinute) + { + var yPoint = this.GetMinuteCloseYPoint(this.CursorIndex); + if (yPoint != null) y=yPoint; + } + + if (this.CallAcutionXOperator) + { + this.CallAcutionXOperator.Value=x; + this.CallAcutionXOperator.Point={X:x, Y:y}; + this.CallAcutionXOperator.ClientPos=clientPos; + + if (this.CallAcutionXOperator.Operator()) + { + x=this.CallAcutionXOperator.X; + } + } + + if (this.IsFixXLastTime) + { + var value=this.FixMinuteLastTimeXPoint(this.CursorIndex) + if (value) + { + x=value.X; + this.CursorIndex=value.Index; + } + } + + this.PointY=[[left,y],[right,y]]; + this.PointX=[[x,top],[x,bottom]]; + + //十字线 + if (this.IsShowCorss) + { + var rangeBG=null; + if (this.IsDrawXRangeBG) + { + rangeBG=this.GetDateTimeRange(this.CursorIndex); + } + + var pixel=GetDevicePixelRatio(); + if (this.HPenType==1 || this.HPenType==0) //0=实线 1=虚线 + { + this.Canvas.strokeStyle=this.HPenColor; + if (this.HPenType==0) this.Canvas.setLineDash([3*pixel,2*pixel]); //虚线 + //this.Canvas.lineWidth=0.5 + this.Canvas.beginPath(); + this.Canvas.moveTo(left,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + this.Canvas.stroke(); + if (this.HPenType==0) this.Canvas.setLineDash([]); + } + + this.Canvas.save(); + this.Canvas.strokeStyle=this.VPenColor; + if (this.VPenType==0) + { + this.Canvas.setLineDash([3*pixel,2*pixel]); //虚线 + } + else if (this.VPenType==2) + { + let barWidth=this.Frame.SubFrame[0].Frame.DataWidth; //和K线一样宽度 + if (barWidth>2*pixel) this.Canvas.lineWidth=barWidth; + } + + this.Canvas.beginPath(); + if (this.VLineType==1) + { + if (rangeBG) + { + this.Canvas.fillStyle=this.XRangeBGColor; + this.Canvas.fillRect(rangeBG.XStart, border.Top, (rangeBG.XEnd-rangeBG.XStart),(border.Bottom-border.Top)); + } + + this.Canvas.moveTo(ToFixedPoint(x),border.Top); + this.Canvas.lineTo(ToFixedPoint(x),border.Bottom); + } + else + { + if (this.Frame.SubFrame.length>0) + { + for(var i in this.Frame.SubFrame) + { + var frame=this.Frame.SubFrame[i].Frame; + var subBorder=frame.ChartBorder.GetBorder(); + top=subBorder.TopTitle; + bottom=subBorder.Bottom; + + if (rangeBG) + { + this.Canvas.fillStyle=this.XRangeBGColor; + this.Canvas.fillRect(rangeBG.XStart, top, (rangeBG.XEnd-rangeBG.XStart),(bottom-top)); + } + + this.Canvas.moveTo(ToFixedPoint(x),top); + this.Canvas.lineTo(ToFixedPoint(x),bottom); + } + } + else + { + this.Canvas.moveTo(ToFixedPoint(x),top); + this.Canvas.lineTo(ToFixedPoint(x),bottom); + } + } + + this.Canvas.stroke(); + this.Canvas.restore(); + + } + + var xValue=this.Frame.GetXData(x); + var yValueExtend={}; + var yValue=this.Frame.GetYData(y,yValueExtend); + if ( (this.IsOnlyDrawMinute || this.IsShowClose) && this.Close != null) yValue=this.Close; + + //this.StringFormatX.Value=xValue; + this.StringFormatX.Value=this.CursorIndex; + this.StringFormatX.Point={X:x, Y:y}; + this.StringFormatX.ClientPos=clientPos; + + this.StringFormatY.Value=yValue; + this.StringFormatY.RValue=yValueExtend.RightYValue; //右侧子坐标 + this.StringFormatY.FrameID=yValueExtend.FrameID; + this.StringFormatY.Point={X:x, Y:y}; + this.StringFormatY.ClientPos=clientPos; + + this.Canvas.font=this.Font; + var textHeight=this.GetFontHeight(); + if (textHeight>this.TextHeight) this.TextHeight=textHeight; + + //Y轴 + if ( ((this.ShowTextMode.Left==1 && this.Frame.ChartBorder.Left>=30) || this.ShowTextMode.Left==2 || + (this.ShowTextMode.Right==1 && this.Frame.ChartBorder.Right>=30) || this.ShowTextMode.Right==2 ) && this.StringFormatY.Operator() ) + { + var text=this.StringFormatY.Text; + this.Canvas.font=this.Font; + var textWidth=this.Canvas.measureText(text).width+4; //前后各空2个像素 + + if (this.Frame.ChartBorder.Left>=30 && this.ShowTextMode.Left==1) + { + if (left=30 && this.ShowTextMode.Right==1) + { + var isOverlayIndex=false; //是否有叠加子坐标 + var overlayIndexInterval=null; //子坐标间距 + if (yValueExtend.FrameID>=0) + { + var frame=this.Frame.SubFrame[yValueExtend.FrameID]; + isOverlayIndex=frame.OverlayIndex.length>0; + overlayIndexInterval=frame.Interval; + } + + if (isOverlayIndex && textWidth>overlayIndexInterval) //大于子坐标宽度 + { + var drawRight=right+overlayIndexInterval; + if (drawRight>chartRight) drawRight=chartRight; + this.DrawTextBGRect(drawRight-2-textWidth,y-this.TextHeight/2,textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,drawRight-4,y,textWidth); + } + else if (rightWidth0) + { + var yOffset=0; + for(var i in this.StringFormatY.RExtendText) + { + var item=this.StringFormatY.RExtendText[i]; + var rText='--.--' + if (item.YText) rText=item.YText; + else if (IFrameSplitOperator.IsNumber(item.Y)) rText=item.Y.toFixed(0); + var rTextWidth=this.Canvas.measureText(rText).width+4; //前后各空2个像素 + + if (rightWidth=right) + { + this.DrawTextBGRect(right-textWidth,bottom+2,textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,right-2,yCenter,textWidth); + } + else + { + this.DrawTextBGRect(x-textWidth/2,bottom+2,textWidth,this.TextHeight); + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,x,yCenter,textWidth); + } + } + + //子坐标Y轴 + if (yValueExtend.FrameID>=0) + { + var frame=this.Frame.SubFrame[yValueExtend.FrameID]; + var isAutoRightBorder=false; + if (this.Frame.AutoRightBorder) isAutoRightBorder=true; + var overlayLeft=right; + if (isAutoRightBorder) overlayLeft=right+this.Frame.AuotRightWidth; + this.Canvas.font=this.Font; + for(var i in frame.OverlayIndex) + { + var item=frame.OverlayIndex[i]; + if (item.Frame.IsShow===false) continue; + + if (!isAutoRightBorder) overlayLeft+=frame.Interval; + + if (overlayLeft+30>chartRight) break; + var yValue=item.Frame.GetYData(y); + + for(var i=2;i>=0;--i) + { + var text=IFrameSplitOperator.FormatValueString(yValue,i); + var textWidth=this.Canvas.measureText(text).width+4; //前后各空2个像素 + if (textWidth2*pixel) this.Canvas.lineWidth=barWidth; + } + + this.Canvas.beginPath(); + if (this.VLineType==1) + { + this.Canvas.moveTo(left,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + } + else + { + if (this.Frame.SubFrame.length>0) + { + for(var i in this.Frame.SubFrame) + { + var frame=this.Frame.SubFrame[i].Frame; + var subBorder=frame.ChartBorder.GetHScreenBorder(); + this.Canvas.moveTo(subBorder.Left,ToFixedPoint(y)); + this.Canvas.lineTo(subBorder.RightEx,ToFixedPoint(y)); + } + } + else + { + this.Canvas.moveTo(left,ToFixedPoint(y)); + this.Canvas.lineTo(right,ToFixedPoint(y)); + } + } + + this.Canvas.stroke(); + this.Canvas.restore(); + } + + var xValue=this.Frame.GetXData(y); + var yValueExtend={}; + var yValue=this.Frame.GetYData(x,yValueExtend); + + this.StringFormatX.Value=xValue; + this.StringFormatX.Point={X:x, Y:y}; + this.StringFormatX.ClientPos=this.ClientPos; + + this.StringFormatY.Value=yValue; + this.StringFormatY.FrameID=yValueExtend.FrameID; + this.StringFormatY.Point={X:x, Y:y}; + this.StringFormatY.ClientPos=this.ClientPos; + + if ( ((this.ShowTextMode.Left==1 && this.Frame.ChartBorder.Top>=30) || this.ShowTextMode.Left==2 || + (this.ShowTextMode.Right==1 && this.Frame.ChartBorder.Bottom>=30) || this.ShowTextMode.Right==2) && this.StringFormatY.Operator() ) + { + var text=this.StringFormatY.Text; + this.Canvas.font=this.Font; + var textWidth=this.Canvas.measureText(text).width+4; //前后各空2个像素 + + if (this.Frame.ChartBorder.Top>=30 && this.ShowTextMode.Left==1) + { + var xText=x; + var yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle=this.TextBGColor; + if (top>=textWidth) + { + this.Canvas.fillRect(0,-(this.TextHeight/2),-textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,-2,0,textWidth); + } + else + { + this.Canvas.fillRect((textWidth-top),-(this.TextHeight/2),-textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,(textWidth-top)-2,0,textWidth); + } + + this.Canvas.restore(); + } + else if (this.ShowTextMode.Left==2) + { + var xText=x; + var yText=top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle=this.TextBGColor; + this.Canvas.fillRect(0,-(this.TextHeight/2),textWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,2,0,textWidth); + + this.Canvas.restore(); + } + + if (this.StringFormatY.RText) + { + text=this.StringFormatY.RText; + var textWidth=this.Canvas.measureText(text).width+4; //前后各空2个像素 + } + + if (this.Frame.ChartBorder.Bottom>=30 && this.ShowTextMode.Right==1) + { + var xText=x; + var yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle=this.TextBGColor; + if (bottomWidth>textWidth) + { + this.Canvas.fillRect(0,-(this.TextHeight/2),textWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,2,0,textWidth); + } + else + { + this.Canvas.fillRect((bottomWidth-textWidth),-(this.TextHeight/2),textWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,(bottomWidth-textWidth)+2,0,textWidth); + } + + if (this.StringFormatY.RExtendText && this.StringFormatY.RExtendText.length>0) + { + var yOffset=0; + for(var i in this.StringFormatY.RExtendText) + { + var item=this.StringFormatY.RExtendText[i]; + var rText='--.--' + if (item.YText) rText=item.YText; + else if (IFrameSplitOperator.IsNumber(item.Y)) rText=item.Y.toFixed(0); + var rTextWidth=this.Canvas.measureText(rText).width+4; //前后各空2个像素 + + this.Canvas.fillStyle=item.TextBGColor; + if (bottomWidth>rTextWidth) + { + this.Canvas.fillRect(0,yOffset+this.TextHeight/2,rTextWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=item.TextColor; + this.Canvas.fillText(rText,2,yOffset+this.TextHeight,rTextWidth); + } + else + { + var rTextLeft=bottomWidth-rTextWidth; + this.Canvas.fillRect(rTextLeft,yOffset+this.TextHeight/2,rTextWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=item.TextColor; + this.Canvas.fillText(rText,rTextLeft+2,yOffset+this.TextHeight,rTextWidth); + } + + + yOffset+=this.TextHeight; + } + } + + this.Canvas.restore(); + } + else if (this.ShowTextMode.Right==2) + { + var xText=x; + var yText=bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillStyle=this.TextBGColor; + this.Canvas.fillRect(0,-(this.TextHeight/2),-textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,-2,0,textWidth); + + this.Canvas.restore(); + } + } + + if (this.ShowTextMode.Bottom===1 && this.StringFormatX.Operator()) + { + var text=this.StringFormatX.Text; + this.Canvas.font=this.Font; + + this.Canvas.fillStyle=this.TextBGColor; + var textWidth=this.Canvas.measureText(text).width+4; //前后各空2个像素 + if (y-textWidth/2<3) //左边位置不够了, 顶着左边画 + { + var xText=left; + var yText=y; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillRect(0,0,textWidth,this.TextHeight); + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,2,this.TextHeight/2,textWidth); + + this.Canvas.restore(); + } + else if (y+textWidth/2>=bottom) + { + var xText=left; + var yText=y; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillRect(-textWidth,0,textWidth,this.TextHeight); + this.Canvas.textAlign="right"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,-2,this.TextHeight/2,textWidth); + + this.Canvas.restore(); + } + else + { + var xText=left; + var yText=y; + + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + + this.Canvas.fillRect(-textWidth/2,0,textWidth,this.TextHeight); + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="middle"; + this.Canvas.fillStyle=this.TextColor; + this.Canvas.fillText(text,0,this.TextHeight/2,textWidth); + + this.Canvas.restore(); + } + } + + this.Status=1; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +//深度图十字光标 +function DepthChartCorssCursor() +{ + this.Frame; + this.HQChart; + this.Canvas; //画布 + this.Data; + this.Symbol; + + this.HPenType=0; //水平线样式 0=虚线 1=实线 + this.VPenType=0; //垂直线颜色 0=虚线 1=实线 + this.LineDash=g_JSChartResource.DepthCorss.LineDash; + this.IsShowTooltip=true; + + this.AskColor=g_JSChartResource.DepthCorss.AskColor.Line; //卖 + this.BidColor=g_JSChartResource.DepthCorss.BidColor.Line; //买 + this.LineWidth=g_JSChartResource.DepthCorss.LineWidth; + + this.Tooltip= + { + LineHeight:g_JSChartResource.DepthCorss.Tooltip.LineHeight, + Border: + { + Top:g_JSChartResource.DepthCorss.Tooltip.Border.Top, + Left:g_JSChartResource.DepthCorss.Tooltip.Border.Left, + Bottom:g_JSChartResource.DepthCorss.Tooltip.Border.Bottom, + Center: g_JSChartResource.DepthCorss.Tooltip.Border.Center + }, + Font:g_JSChartResource.DepthCorss.Tooltip.Font, + TextColor:g_JSChartResource.DepthCorss.Tooltip.TextColor, + BGColor:g_JSChartResource.DepthCorss.Tooltip.BGColor + }; // Width: Height: + + this.Font=g_JSChartResource.CorssCursorTextFont; //字体 + this.TextColor=g_JSChartResource.CorssCursorTextColor; //文本颜色 + this.TextBGColor=g_JSChartResource.CorssCursorBGColor; //文本背景色 + this.TextHeight=20; //文本字体高度 + this.LastPoint; + + this.PointX; + this.PointY; + + this.StringFormatX; + this.StringFormatY; + + this.IsShowCorss=true; //是否显示十字光标 + this.IsShow=true; + + this.GetVol=function(price, isAsk) + { + if (!this.Data) return null; + var aryData=isAsk? this.Data.Asks:this.Data.Bids; + if (!aryData || !Array.isArray(aryData) || aryData.length<=0) return null; + + for(var i in aryData) + { + var item=aryData[i]; + if (item.Price==price) return item.Vol; + } + + return null; + } + + this.Draw=function() + { + this.Status=0; + if (!this.LastPoint) return; + if (!this.Data) return; + if (!this.IsShow) return; + + var x=this.LastPoint.X; + var y=this.LastPoint.Y; + + var isInClient=false; + this.Canvas.beginPath(); + this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight()); + isInClient=this.Canvas.isPointInPath(x,y); + + this.PointY=null; + this.PointY==null; + + if (!isInClient) return; + + if (this.Frame.IsHScreen===true) + { + return; + } + + var left=this.Frame.ChartBorder.GetLeft(); + var right=this.Frame.ChartBorder.GetRight(); + var top=this.Frame.ChartBorder.GetTopTitle(); + var bottom=this.Frame.ChartBorder.GetBottom(); + var rightWidth=this.Frame.ChartBorder.Right; + var chartRight=this.Frame.ChartBorder.GetChartWidth(); + + var xValue=this.Frame.GetXData(x); + var xInfo=this.Frame.GetXFromPrice(xValue); //调整价格到有数据的点上 + + if (!xInfo) return; + + var yVol=this.GetVol(xInfo.Price, xInfo.IsAsk); + y=this.Frame.GetYFromData(yVol); //调整Y轴, 让它在线段上 + + xInfo.Vol=yVol; + xInfo.Y=y; + + this.PointY=[[left,y],[right,y]]; + this.PointX=[[x,top],[x,bottom]]; + + if (this.IsShowCorss) + { + if (xInfo.IsAsk) this.Canvas.strokeStyle=this.AskColor; + else this.Canvas.strokeStyle=this.BidColor; + var lineWidthBackup=this.Canvas.lineWidth; + var pixel=GetDevicePixelRatio(); + this.Canvas.lineWidth=this.LineWidth*pixel; + var lineWidth=this.Canvas.lineWidth; + + if (this.HPenType==1 || this.HPenType==0) //0=实线 1=虚线 + { + if (this.HPenType==0) this.Canvas.setLineDash(this.LineDash); //虚线 + var yFix=ToFixedPoint2(lineWidth, y); + this.Canvas.beginPath(); + this.Canvas.moveTo(left,yFix); + this.Canvas.lineTo(right,yFix); + this.Canvas.stroke(); + if (this.HPenType==0) this.Canvas.setLineDash([]); + } + + if (this.VPenType==0) this.Canvas.setLineDash(this.LineDash); //虚线 + var xFix=ToFixedPoint2(lineWidth, xInfo.X); + this.Canvas.beginPath(); + this.Canvas.moveTo(xFix,top); + this.Canvas.lineTo(xFix,bottom); + this.Canvas.stroke(); + if (this.VPenType==0) this.Canvas.setLineDash([]); + + this.Canvas.lineWidth=lineWidthBackup; + } + + if (this.HQChart) + { + var event=this.HQChart.GetEventCallback(JSCHART_EVENT_ID.ON_DRAW_DEPTH_TOOLTIP); + if (event) + { + event.Callback(event,xInfo,this); + } + } + + if (this.IsShowTooltip) this.DrawTooltip(xInfo); + } + + this.DrawTooltip=function(data) + { + var price=data.Price; + var vol=data.Vol; + var pixel=GetDevicePixelRatio(); + var border=this.Tooltip.Border; + this.Canvas.font=this.Tooltip.Font; + var floatPrecision=2; + if (this.Symbol) floatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数 + var maxText='擎擎: 9999.99亿 '; + if (floatPrecision>=5) maxText=`擎擎: ${99.99.toFixed(floatPrecision)} `; //小数位数太多了 + this.Tooltip.Width=this.Canvas.measureText(maxText).width+border.Left*pixel; + this.Tooltip.Height=this.Tooltip.LineHeight*pixel*4+border.Top*pixel+border.Bottom*pixel+border.Center*pixel; + + var chartRight=this.Frame.ChartBorder.GetRight(); + var chartTop=this.Frame.ChartBorder.GetTop(); + + var left=data.X+2*pixel; + var top=data.Y-this.Tooltip.Height-2*pixel; + if (left+this.Tooltip.Width>=chartRight) left=data.X-this.Tooltip.Width-2*pixel; + if (top=this.MultiDayBeforeOpenData.length) return false; + var dayData=this.MultiDayBeforeOpenData[dayIndex]; + var range={ Max:dayData.VolMax, Min:dayData.VolMin }; + var y=this.Frame.IsHScreen? this.Point.X: this.Point.Y; + var value=item.Frame.GetLeftExtendYData(y,false,{ Range:range } ); + var defaultfloatPrecision=2; //价格小数位数 + if (IFrameSplitOperator.IsNumber(value)) + { + this.RText=this.Text; + this.Text=IFrameSplitOperator.FormatValueString(value,defaultfloatPrecision,this.LanguageID); + } + } + + this.GetMultiDayAfterClose=function() + { + if (!this.Frame) return false; + var item=this.Frame.SubFrame[this.FrameID]; + if (!item || !item.Frame) return false; + if (!this.MultiDayAfterCloseData || !IFrameSplitOperator.IsNonEmptyArray(this.MultiDayAfterCloseData)) return; + var dayIndex=this.ClientPos-300; + if (dayIndex<0 || dayIndex>=this.MultiDayAfterCloseData.length) return false; + var dayData=this.MultiDayAfterCloseData[dayIndex]; + + var range={ Max:dayData.VolMax, Min:dayData.VolMin }; + var y=this.Frame.IsHScreen? this.Point.X: this.Point.Y; + var value=item.Frame.GetRightExtendYData(y,false,{ Range:range } ); + var defaultfloatPrecision=2; //价格小数位数 + if (IFrameSplitOperator.IsNumber(value)) + { + this.RText=IFrameSplitOperator.FormatValueString(value,defaultfloatPrecision,this.LanguageID); + } + } + + this.Operator=function() + { + this.RText=null; + this.RExtendText=[]; + if (IFrameSplitOperator.IsString(this.RValue)) this.RText=this.RValue; + if (!this.Value) return false; + + this.PercentageText=null; + var defaultfloatPrecision=2; //价格小数位数 + if (this.FrameID==0) //第1个窗口显示原始价格 + { + var defaultfloatPrecision=GetfloatPrecision(this.Symbol); + this.Text=this.Value.toFixed(defaultfloatPrecision); + if (this.YClose>0) this.PercentageText=((this.Value-this.YClose)*100/this.YClose).toFixed(2); //走势图右边坐标显示百分比 + + this.GetExtendPaintData(defaultfloatPrecision); + } + else if (this.FrameID==1) + { + this.Text=IFrameSplitOperator.FormatValueString(this.Value,defaultfloatPrecision,this.LanguageID); + if (IFrameSplitOperator.IsNumber(this.RValue)) this.RText=IFrameSplitOperator.FormatValueString(this.RValue,defaultfloatPrecision,this.LanguageID); + if (this.ClientPos==2) this.GetBeforeOpen(); + else if (this.ClientPos==3) this.GetAfterClose(); + else if (this.ClientPos>=200 && this.ClientPos<=299) this.GetMultiDayBeforeOpen(); + else if (this.ClientPos>=300 && this.ClientPos<=399) this.GetMultiDayAfterClose(); + } + else + { + this.Text=IFrameSplitOperator.FormatValueString(this.Value,defaultfloatPrecision,this.LanguageID); + } + + return true; + } + + //深度图刻度 + this.GetExtendPaintData=function(floatPrecision) + { + var value=parseInt(this.Value*ZOOM_VALUE[floatPrecision]); + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + if (item.ClassName=='DepthMapPaint' && item.FrameID==this.FrameID) + { + var aryData=item.GetYValueByXValue(value,floatPrecision); + for(var j in aryData) + { + this.RExtendText.push(aryData[j]); + } + } + } + } +} + +function HQDateStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.DateFormatType=0; //0=YYYY-MM-DD 1=YYYY/MM/DD 2=YYYY/MM/DD/W 3=DD/MM/YYYY + this.LanguageID=0; + + this.Operator=function() + { + if (!IFrameSplitOperator.IsNumber(this.Value)) return false; + if (!this.Data) return false; + + var index=this.Value; + index=parseInt(index.toFixed(0)); + if (this.Data.DataOffset+index>=this.Data.Data.length) return false; + var currentData = this.Data.Data[this.Data.DataOffset+index]; + var dateFormatString="YYYY-MM-DD"; + if (this.DateFormatType==1) dateFormatString="YYYY/MM/DD"; + else if (this.DateFormatType==2) dateFormatString="YYYY/MM/DD/W"; + else if (this.DateFormatType==3) dateFormatString="DD/MM/YYYY"; + this.Text=IFrameSplitOperator.FormatDateString(currentData.Date, dateFormatString,this.LanguageID); + if (ChartData.IsMinutePeriod(this.Data.Period,true) ) // 分钟周期 + { + var time = IFrameSplitOperator.FormatTimeString(currentData.Time); + this.Text = this.Text + " " + time; + } + else if (ChartData.IsSecondPeriod(this.Data.Period)) + { + var time = IFrameSplitOperator.FormatTimeString(currentData.Time,'HH:MM:SS'); + this.Text = this.Text + " " + time; + } + else if (ChartData.IsTickPeriod(this.Data.Period)) //分笔 + { + var time = IFrameSplitOperator.FormatTimeString(currentData.Time); + this.Text = this.Text + " " + time; + } + + return true; + } +} + +function HQMinuteTimeStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Frame; + this.Symbol; + this.Point; + this.ClientPos=-1; + this.BeforeOpenData; //单日分时图 盘前数据 + this.AfterCloseData; //单日分时图 收盘数据 + + this.MultiDayBeforeOpenData; //多日分时图 盘前数据 + this.MultiDayAfterCloseData; //多日分时图 收盘数据 + + this.GetBeforeOpen=function() + { + if (!this.BeforeOpenData || !this.BeforeOpenData.Data) return false; + + var x=this.Frame.IsHScreen==true?this.Point.Y:this.Point.X; + var index=this.Frame.GetLeftExtendXData(x, this.BeforeOpenData); + index=parseInt(index.toFixed(0)); + if (index>=this.BeforeOpenData.Data.length) return false; + + var item=this.BeforeOpenData.Data[index]; + this.Text=this.FormatCallAcutionDateTime(item, this.BeforeOpenData); + + return true; + } + + this.GetAfterClose=function() + { + if (!this.AfterCloseData || !this.AfterCloseData.Data) return false; + + var x=this.Frame.IsHScreen==true?this.Point.Y:this.Point.X; + var index=this.Frame.GetRightExtendXData(x, this.AfterCloseData); + index=parseInt(index.toFixed(0)); + if (index>=this.AfterCloseData.Data.length) return false; + + var item=this.AfterCloseData.Data[index]; + this.Text=this.FormatCallAcutionDateTime(item, this.AfterCloseData); + + return true; + } + + this.GetMultiDayBeforeOpen=function() + { + if (!this.MultiDayBeforeOpenData || !this.MultiDayBeforeOpenData ) return false; + if (this.Frame.ChartBorder.MultiDayMinute.Count<=1 || this.Frame.ChartBorder.MultiDayMinute.Left<=0) return false; + + var x=this.Frame.IsHScreen==true?this.Point.Y:this.Point.X; + var index=this.Frame.GetLeftExtendXData(x, this.MultiDayBeforeOpenData); + if (!index) return false; + + if (index.DayIndex>=this.MultiDayBeforeOpenData.length) return false; + var dayItem=this.MultiDayBeforeOpenData[index.DayIndex]; + index.DataIndex=parseInt(index.DataIndex.toFixed(0)); + if (index.DataIndex>=dayItem.Data.length) return false; + + var item=dayItem.Data[index.DataIndex]; + this.Text=this.FormatCallAcutionDateTime(item, dayItem); + + return true; + } + + this.GetMultiDayAfterClose=function() + { + if (!this.MultiDayAfterCloseData) return false; + if (this.Frame.ChartBorder.MultiDayMinute.Count<=1 || this.Frame.ChartBorder.MultiDayMinute.Right<=0) return false; + + var x=this.Frame.IsHScreen==true?this.Point.Y:this.Point.X; + var index=this.Frame.GetRightExtendXData(x, this.MultiDayAfterCloseData); + if (!index) return false; + if (index.DayIndex>=this.MultiDayAfterCloseData.length) return false; + var dayItem=this.MultiDayAfterCloseData[index.DayIndex]; + index.DataIndex=parseInt(index.DataIndex.toFixed(0)); + if (index.DataIndex>=dayItem.Data.length) return false; + + var item=dayItem.Data[index.DataIndex]; + this.Text=this.FormatCallAcutionDateTime(item, dayItem); + + return true; + } + + this.FormatCallAcutionDateTime=function(item, callAcutionData) + { + var time=item.Time; + if (callAcutionData.Ver==1.0) + return IFrameSplitOperator.FormatTimeString(time,"HH:MM"); + else + return IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); + } + + this.Operator=function() + { + if (this.ClientPos==2) return this.GetBeforeOpen(); + else if (this.ClientPos==3) return this.GetAfterClose(); + else if (this.ClientPos>=200 && this.ClientPos<=299) return this.GetMultiDayBeforeOpen(); + else if (this.ClientPos>=300 && this.ClientPos<=399) return this.GetMultiDayAfterClose(); + + if (!IFrameSplitOperator.IsNumber(this.Value)) return false; + + var index=Math.abs(this.Value); + index=parseInt(index.toFixed(0)); + var showIndex=index; + if (this.Frame && this.Frame.MinuteCount) showIndex=index%this.Frame.MinuteCount; + + var timeStringData=g_MinuteTimeStringData; + var timeData=timeStringData.GetTimeData(this.Symbol); + if (!timeData) return false; + + if (showIndex<0) showIndex=0; + else if (showIndex>timeData.length) showIndex=timeData.length-1; + if (this.Frame && index>=this.Frame.XPointCount) + showIndex=timeData.length-1; + + var time=timeData[showIndex]; + this.Text=IFrameSplitOperator.FormatTimeString(time); + return true; + } +} + + +//行情tooltip提示信息格式 +var WEEK_NAME=["日","一","二","三","四","五","六"]; +function HistoryDataStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Symbol; + this.UpColor=g_JSChartResource.UpTextColor; + this.DownColor=g_JSChartResource.DownTextColor; + this.UnchagneColor=g_JSChartResource.UnchagneTextColor; + + this.VolColor=g_JSChartResource.Title.VolColor; + this.AmountColor=g_JSChartResource.Title.AmountColor; + this.TurnoverRateColor=g_JSChartResource.Title.TurnoverRateColor; + this.PositionColor=g_JSChartResource.Title.PositionColor; + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.LineCount=0; //一共几行 + this.LineHeight=g_JSChartResource.PCTooltip.LineHeight; //单行高度 + this.Width=157; //宽度 + this.Height=this.LineHeight*5; //高度 + + + this.Operator=function() + { + var data=this.Value.Data; + if (!data) return false; + + this.Width=157; + if (this.LanguageID==JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID) this.Width=180; + + var date=new Date(parseInt(data.Date/10000),(data.Date/100%100-1),data.Date%100); + var strDate=IFrameSplitOperator.FormatDateString(data.Date); + var title2=g_JSChartLocalization.GetText(WEEK_NAME[date.getDay()],this.LanguageID); + var isTickPeriod=ChartData.IsTickPeriod(this.Value.ChartPaint.Data.Period); + if (ChartData.IsMinutePeriod(this.Value.ChartPaint.Data.Period,true)) // 分钟周期 + { + title2=IFrameSplitOperator.FormatTimeString(data.Time); + } + else if (ChartData.IsSecondPeriod(this.Value.ChartPaint.Data.Period) || isTickPeriod) + { + title2=IFrameSplitOperator.FormatTimeString(data.Time,'HH:MM:SS'); + } + + var upperSymbol=this.Symbol.toUpperCase(); + var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数 + var increase=null; + if (data.YClose>0) increase=(data.Close-data.YClose)/data.YClose*100; + if (isTickPeriod) + { + var strText= + ""+strDate+"  "+title2+""+ + ""+g_JSChartLocalization.GetText('DivTooltip-Price',this.LanguageID)+""+ + ""+data.Open.toFixed(defaultfloatPrecision)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Increase',this.LanguageID)+""+ + (increase==null? ""+'--'+"
" : + ""+increase.toFixed(2)+'%'+"
"); + + this.LineCount=4; + } + else + { + var vol=data.Vol; + if (upperSymbol && MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) vol/=100; //A股统一转成手 + var strText= + ""+strDate+"  "+title2+""+ + ""+g_JSChartLocalization.GetText('DivTooltip-Open',this.LanguageID)+""+ + ""+data.Open.toFixed(defaultfloatPrecision)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-High',this.LanguageID)+""+ + ""+data.High.toFixed(defaultfloatPrecision)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Low',this.LanguageID)+""+ + ""+data.Low.toFixed(defaultfloatPrecision)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Close',this.LanguageID)+""+ + ""+data.Close.toFixed(defaultfloatPrecision)+"
"+ + //" 前收: "+IFrameSplitOperator.FormatValueString(data.YClose,2)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Vol',this.LanguageID)+""+ + ""+IFrameSplitOperator.FormatValueString(vol,2,this.LanguageID)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Amount',this.LanguageID)+""+ + ""+IFrameSplitOperator.FormatValueString(data.Amount,2,this.LanguageID)+"
"+ + ""+g_JSChartLocalization.GetText('DivTooltip-Increase',this.LanguageID)+""+ + (increase==null? ""+'--'+"
" : + ""+increase.toFixed(2)+'%'+"
"); + + this.LineCount=8; + + if(MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol) && data.FlowCapital>0) //换手率 + { + var value=data.Vol/data.FlowCapital*100; + strText+= ""+g_JSChartLocalization.GetText('DivTooltip-Exchange',this.LanguageID)+"" + + ""+value.toFixed(2)+'%'+"
"; + ++this.LineCount; + } + + if (MARKET_SUFFIX_NAME.IsFutures(upperSymbol) && IFrameSplitOperator.IsNumber(data.Position)) + { + strText+= ""+g_JSChartLocalization.GetText('DivTooltip-Position',this.LanguageID)+"" + + ""+data.Position+"
"; + ++this.LineCount; + } + + //叠加股票 + if (this.Value.ChartPaint.Name=="Overlay-KLine") + { + var title=""+this.Value.ChartPaint.Title+""; + strText=title+strText; + ++this.LineCount; + } + } + + this.Text=strText; + + this.Height=this.LineCount*this.LineHeight; + return true; + } + + this.GetColor=function(price,yclse) + { + if(price>yclse) return this.UpColor; + else if (price"; + + if(infoList.length > 8) + { + var strBox="
共"+infoList.length+"条
"; + html+=strBox; + } + + this.Text=html; + return true; + } + + this.DefaultFormat=function(item) + { + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + var strText=""+strDate+"   "+item.Title+""; + return strText; + } + + //大宗交易 + this.BlockTradingFormat=function(item) + { + var showPriceInfo = item.ExtendData; + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + var strText=""+strDate+"  成交价: "+showPriceInfo.Price.toFixed(2)+"收盘价: "+showPriceInfo.ClosePrice.toFixed(2)+ + "
溢折价率: "+ + showPriceInfo.Premium.toFixed(2)+"%成交量(万股): "+showPriceInfo.Vol.toFixed(2)+"
"; + + return strText; + } + + //龙虎榜 + this.TradeDetailFormat=function(item) + { + /*var detail= + [ + "日价格涨幅偏离值达到9.89%", + "日价格涨幅偏离值达格涨幅偏离值达格涨幅偏离值达到9.89%" + ] + */ + + var detail=item.ExtendData.Detail; + //格式:日期 上榜原因: detail[0].TypeExplain + // detail[1].TypeExplain + // 一周后涨幅: xx 四周后涨幅: xx + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + var reasons = []; + for(var i in detail) + { + reasons += ""+detail[i].TypeExplain+"
" + // reasons += detail[i] + "
" + } + + var strText= ""+strDate+"   上榜原因:  "+reasons+"
一周后涨幅: "+ item.ExtendData.FWeek.Week1.toFixed(2)+ + "%   四周后涨幅: "+ + item.ExtendData.FWeek.Week4.toFixed(2)+"%
"; + + return strText; + } + + //调研 + this.ResearchFormat=function(item) + { + var levels=item.ExtendData.Level; + var recPerson=''; + if(levels.length==0) + { + recPerson = "一般调研" + } + else + { + for(var j in levels) + { + if(levels[j]==0) recPerson+="证券代表   "; + else if(levels[j]==1) recPerson+="董秘   "; + else if(levels[j]==2) recPerson+="总经理   "; + else if(levels[j]==3) recPerson+="董事长   "; + } + + recPerson='接待:   '+recPerson; + } + + var researchType=''; + if (item.ExtendData.Type && item.ExtendData.Type!='其他') + { + researchType='   '+''+item.ExtendData.Type+''; + if (levels.length==0) recPerson=''; + } + + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + var strText=""+strDate+"   "+researchType+recPerson+""; + return strText; + } + + //业绩预测 + this.PerformanceForecastFormat=function(item) + { + var reportDate=item.ExtendData.ReportDate; + var year=parseInt(reportDate/10000); //年份 + var day=reportDate%10000; //比较 这个去掉年份的日期 + var reportType; + if(day == 1231){ + reportType = "年报" + }else if(day == 331){ + reportType = "一季度报" + }else if(day == 630){ + reportType = "半年度报" + }else if(day == 930){ + reportType = "三季度报" + } + + var weekData=""; + if (item.ExtendData.FWeek) + { + if (item.ExtendData.FWeek.Week1!=null) weekData+="一周后涨幅:"+ item.ExtendData.FWeek.Week1.toFixed(2)+"%"; + if (item.ExtendData.FWeek.Week4!=null) weekData+=" 四周后涨幅:"+ item.ExtendData.FWeek.Week4.toFixed(2)+"%"; + if (weekData.length>0) weekData="
  "+weekData+""; + } + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + var strText=""+strDate+"  "+year+reportType+item.Title+" "+weekData+""; + return strText; + } + + this.GetColor=function(price) + { + if(price>0) return this.UpColor; + else if (price<0) return this.DownColor; + else return this.UnchagneColor; + } +} + +//交易信息提示信息格式 +function KLineTradeDataStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Width=120; + this.Operator=function() + { + var data=this.Value.Data; + if (!data) return false; + + var item=data.Data; + var title=`${item.Name}${item.Param}:`; + var content; + if (item.Type==1) content=`买入`; + else content=`卖出`; + + this.Text=title+content; + return true; + } +} + +//分时图异动信息格式化 +function MinuteInfoDataStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Width=200; + this.Operator=function() + { + var data=this.Value.Data; + if (!data) return false; + + var item=data.Data.Item; + var strTime=IFrameSplitOperator.FormatTimeString(item.Time); + var time=`${strTime} `; + var content=`${item.Title}`; + if (item.Content) content=`${item.Content}`; + + this.Text=time+content; + return true; + } +} + +function IconDataStringFormat() +{ + this.newMethod=IChangeStringFormat; //派生 + this.newMethod(); + delete this.newMethod; + + this.Width=200; + this.Operator=function() + { + if (!this.Value || !this.Value.Data) return false; + var data=this.Value.Data; + if (!data.Item) return false; + if (!data.Item.Text) return false; + + this.Text=data.Item.Text; + return true; + } +} + +function DivTooltipDataForamt() +{ + this.DataMap=new Map( + [ + ["KLineTradeDataStringFormat", { Create:function() { return new KLineTradeDataStringFormat(); } }], + ["MinuteInfoDataStringFormat", { Create:function() { return new MinuteInfoDataStringFormat(); } }], + ["HistoryDataStringFormat", { Create:function() { return new HistoryDataStringFormat(); } }], + ["KLineInfoDataStringFormat", { Create:function() { return new KLineInfoDataStringFormat(); } }], + ["IconDataStringFormat", { Create:function() { return new IconDataStringFormat(); } }], + + ["CorssCursor_XStringFormat", { Create:function() { return new HQDateStringFormat(); } }], + ["CorssCursor_YStringFormat", { Create:function() { return new HQPriceStringFormat(); } }] + ] + ); + + this.Create=function(name) + { + if (!this.DataMap.has(name)) return null; + + var item=this.DataMap.get(name); + return item.Create(); + } +} + +var g_DivTooltipDataForamt=new DivTooltipDataForamt(); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 标题 +// +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +function IChartTitlePainting() +{ + this.Frame; + this.Data=new Array(); + this.Canvas; //画布 + this.IsDynamic=false; //是否是动态标题 + this.Position=0; //标题显示位置 0 框架里的标题 1 框架上面 + this.CursorIndex; //数据索引 + this.Font=g_JSChartResource.TitleFont; + this.Title; //固定标题(可以为空) + this.TitleColor=g_JSChartResource.DefaultTextColor; + this.ClassName='IChartTitlePainting'; + this.DrawStatus; + this.GetEventCallback; + + this.ReloadResource=function() + { + this.Font=g_JSChartResource.TitleFont; + this.TitleColor=g_JSChartResource.DefaultTextColor; + } +} + +//var PERIOD_NAME=["日线","周线","月线","年线","1分","5分","15分","30分","60分","季线","分笔", "2小时","4小时","双周","",""]; +var RIGHT_NAME=['不复权','前复权','后复权']; + +function DynamicKLineTitlePainting() +{ + this.newMethod=IChartTitlePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='DynamicKLineTitlePainting'; + this.IsDynamic=true; + this.IsShow=true; //是否显示 + this.Period; //周期 + + this.UpColor=g_JSChartResource.UpTextColor; + this.DownColor=g_JSChartResource.DownTextColor; + this.UnchagneColor=g_JSChartResource.UnchagneTextColor; + + this.VolColor=g_JSChartResource.Title.VolColor; + this.AmountColor=g_JSChartResource.Title.AmountColor; + this.DateTimeColor=g_JSChartResource.Title.DateTimeColor; + this.NameColor = g_JSChartResource.Title.NameColor; + this.SettingColor=g_JSChartResource.Title.SettingColor; //周期 复权 + this.TurnoverRateColor=g_JSChartResource.Title.TurnoverRateColor; //换手率 + this.PositionColor=g_JSChartResource.Title.PositionColor; //持仓 + + this.Symbol; + this.Name; + + this.SpaceWidth=2*GetDevicePixelRatio(); //获取设备的分辨率; + this.OverlayChartPaint; //叠加画法 + + this.IsShowName=true; //是否显示股票名称 + this.IsShowSettingInfo=true; //是否显示设置信息(周期 复权) + this.IsShowDateTime=true; //是否显示日期 + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.OnDrawEvent; + + this.ReloadResource=function() + { + this.Font=g_JSChartResource.TitleFont; + + this.TitleColor=g_JSChartResource.DefaultTextColor; + + this.UpColor=g_JSChartResource.UpTextColor; + this.DownColor=g_JSChartResource.DownTextColor; + this.UnchagneColor=g_JSChartResource.UnchagneTextColor; + + this.VolColor=g_JSChartResource.Title.VolColor; + this.AmountColor=g_JSChartResource.Title.AmountColor; + this.DateTimeColor=g_JSChartResource.Title.DateTimeColor; + this.NameColor = g_JSChartResource.Title.NameColor; + this.SettingColor=g_JSChartResource.Title.SettingColor; + this.TurnoverRateColor=g_JSChartResource.Title.TurnoverRateColor; //换手率 + this.PositionColor=g_JSChartResource.Title.PositionColor; //持仓 + } + + this.GetCurrentKLineData=function() //获取当天鼠标位置所在的K线数据 + { + if (this.CursorIndex==null || !this.Data) return null; + if (this.Data.length<=0) return null; + + var index=this.CursorIndex; + index=parseInt(index.toFixed(0)); + var dataIndex=this.Data.DataOffset+index; + if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1; + if (dataIndex<0) return null; + + var item=this.Data.Data[dataIndex]; + return item; + } + + this.DrawItem=function(item) + { + var isHScreen=this.Frame.IsHScreen===true; + var left=this.Frame.ChartBorder.GetLeft(); + var bottom=this.Frame.ChartBorder.GetTop()-this.Frame.ChartBorder.Top/2; + var right=this.Frame.ChartBorder.GetRight(); + var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数 + var upperSymbol=this.Symbol.toUpperCase(); + + if (isHScreen) + { + if (this.Frame.ChartBorder.Right<5) return; + var left=2; + var bottom=this.Frame.ChartBorder.Right/2; //上下居中显示 + var right=this.Frame.ChartBorder.GetHeight(); + var xText=this.Frame.ChartBorder.GetChartWidth(); + var yText=this.Frame.ChartBorder.GetTop(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + else + { + if (this.Frame.ChartBorder.Top<5*GetDevicePixelRatio()) return; + } + + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=this.Font; + var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen }; + + if (this.IsShowName) + { + if (!this.DrawText(this.Name,this.NameColor,position)) return; + } + + if (this.IsShowSettingInfo) + { + var periodName=''; + if (this.Data.Period>CUSTOM_MINUTE_PERIOD_START && this.Data.Period<=CUSTOM_MINUTE_PERIOD_END) + periodName=(this.Data.Period-CUSTOM_MINUTE_PERIOD_START)+g_JSChartLocalization.GetText('自定义分钟',this.LanguageID); + else if (this.Data.Period>CUSTOM_DAY_PERIOD_START && this.Data.Period<=CUSTOM_DAY_PERIOD_END) + periodName=(this.Data.Period-CUSTOM_DAY_PERIOD_START)+g_JSChartLocalization.GetText('自定义日线',this.LanguageID); + else if (this.Data.Period>CUSTOM_SECOND_PERIOD_START && this.Data.Period<=CUSTOM_SECOND_PERIOD_END) + periodName=(this.Data.Period-CUSTOM_SECOND_PERIOD_START)+g_JSChartLocalization.GetText('自定义秒',this.LanguageID); + else + periodName=g_JSChartLocalization.GetText(ChartData.GetPeriodName(this.Data.Period),this.LanguageID); + var rightName=g_JSChartLocalization.GetText(RIGHT_NAME[this.Data.Right],this.LanguageID); + var text="("+periodName+" "+rightName+")"; + var isStock=MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol); //是否是指数 + if(item.Time!=null || !isStock) text="("+periodName+")"; //分钟K线 指数 没有复权 + if (!this.DrawText(text,this.SettingColor,position)) return; + } + + if (this.IsShowDateTime) //是否显示日期 + { + var text=IFrameSplitOperator.FormatDateString(item.Date); + if (!this.DrawText(text,this.DateTimeColor,position)) return; + } + + var isTickPeriod=ChartData.IsTickPeriod(this.Period); + + if (ChartData.IsMinutePeriod(this.Period,true) && IFrameSplitOperator.IsNumber(item.Time)) + { + var text=IFrameSplitOperator.FormatTimeString(item.Time); + if (!this.DrawText(text,this.DateTimeColor,position)) return; + } + else if (ChartData.IsSecondPeriod(this.Period) && IFrameSplitOperator.IsNumber(item.Time)) + { + var text=IFrameSplitOperator.FormatTimeString(item.Time, "HH:MM:SS"); + if (!this.DrawText(text,this.DateTimeColor,position)) return; + } + else if (isTickPeriod) + { + var text=IFrameSplitOperator.FormatTimeString(item.Time, "HH:MM:SS"); + if (!this.DrawText(text,this.DateTimeColor,position)) return; + } + + if (isTickPeriod) + { + var color=this.GetColor(item.Open,item.YClose); + var text=g_JSChartLocalization.GetText('KTitle-Price',this.LanguageID)+item.Open.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + + if (item.YClose>0) + { + var value=(item.Close-item.YClose)/item.YClose*100; + var color = this.GetColor(value, 0); + var text = g_JSChartLocalization.GetText('KTitle-Increase',this.LanguageID) + value.toFixed(2)+'%'; + if (!this.DrawText(text,color,position)) return; + } + + return; + } + + var color=this.GetColor(item.Open,item.YClose); + var text=g_JSChartLocalization.GetText('KTitle-Open',this.LanguageID)+item.Open.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + + var color=this.GetColor(item.High,item.YClose); + var text=g_JSChartLocalization.GetText('KTitle-High',this.LanguageID)+item.High.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + + var color=this.GetColor(item.Low,item.YClose); + var text=g_JSChartLocalization.GetText('KTitle-Low',this.LanguageID)+item.Low.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + + var color=this.GetColor(item.Close,item.YClose); + var text=g_JSChartLocalization.GetText('KTitle-Close',this.LanguageID)+item.Close.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + + if (item.YFClose>0 && MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) + { + var value=(item.Close-item.YFClose)/item.YFClose*100; + var color = this.GetColor(value, 0); + var text = g_JSChartLocalization.GetText('KTitle-Increase',this.LanguageID) + value.toFixed(2)+'%'; + if (!this.DrawText(text,color,position)) return; + } + else if (item.YClose>0) + { + var value=(item.Close-item.YClose)/item.YClose*100; + var color = this.GetColor(value, 0); + var text = g_JSChartLocalization.GetText('KTitle-Increase',this.LanguageID) + value.toFixed(2)+'%'; + if (!this.DrawText(text,color,position)) return; + } + + var vol=item.Vol; + if (upperSymbol && MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) vol/=100; //A股原始单位股, 转成股 + var text=g_JSChartLocalization.GetText('KTitle-Vol',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(vol,2,this.LanguageID); + if (!this.DrawText(text,this.VolColor,position)) return; + + if (IFrameSplitOperator.IsNumber(item.Amount)) + { + var text=g_JSChartLocalization.GetText('KTitle-Amount',this.LanguageID)+IFrameSplitOperator.FormatValueString(item.Amount,2,this.LanguageID); + if (!this.DrawText(text,this.AmountColor,position)) return; + } + + if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol) && item.FlowCapital>0) //A股有换手率 + { + var value=item.Vol/item.FlowCapital*100; //成交量/流通A股*100 + var text=g_JSChartLocalization.GetText('KTitle-Exchange',this.LanguageID)+IFrameSplitOperator.FormatValueString(value,2,this.LanguageID)+'%'; + if (!this.DrawText(text,this.TurnoverRateColor,position)) return; + } + + if (MARKET_SUFFIX_NAME.IsFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) //持仓量 + { + var text=g_JSChartLocalization.GetText('KTitle-Position',this.LanguageID)+item.Position; + if (!this.DrawText(text,this.PositionColor,position)) return; + } + + //叠加股票的名字 + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (!item.Symbol || !item.Title) continue; + + var name=item.Title; + var clrText=item.Color; + var text='['+name+']'; + if (!this.DrawText(text,clrText,position)) return; + } + } + + this.Draw=function() + { + if (!this.IsShow) return; + if (this.CursorIndex==null || !this.Data || this.Data.length<=0) + { + this.OnDrawEventCallback(null); + return; + } + + this.Canvas.font=this.Font; + this.SpaceWidth = this.Canvas.measureText('0').width; + + var index=this.CursorIndex; + index=parseInt(index.toFixed(0)); + var dataIndex=this.Data.DataOffset+index; + if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1; + if (dataIndex<0) + { + this.OnDrawEventCallback(null); + return; + } + + var item=this.Data.Data[dataIndex]; + this.OnDrawEventCallback(item); + this.Canvas.save(); + this.DrawItem(item); + this.Canvas.restore(); + } + + this.OnDrawEventCallback=function(drawData) + { + if (!this.OnDrawEvent || !this.OnDrawEvent.Callback) return; + var data={ Draw: drawData, Name:this.ClassName}; + if (this.Data && this.Data.Data) + { + var index=Math.abs(this.CursorIndex); + index=parseInt(index.toFixed(0)); + var dataIndex=this.Data.DataOffset+index; + var dataCount=this.Data.Data.length; + + data.DataIndex=dataIndex; + data.DataCount=dataCount; + } + + //叠加股票 + if (IFrameSplitOperator.IsNonEmptyArray(this.OverlayChartPaint)) + { + data.OverlayStock=[]; + for(var i=0; iyclse) return this.UpColor; + else if (price right) return false; + this.Canvas.fillText(title, position.Left, position.Bottom, textWidth); + + position.Left += textWidth + this.SpaceWidth; + return true; + } + +} + +function DynamicMinuteTitlePainting() +{ + this.newMethod=DynamicKLineTitlePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='DynamicMinuteTitlePainting'; + + this.SpaceWidth=1*GetDevicePixelRatio(); + this.YClose; + this.IsShowDate=false; //标题是否显示日期 + this.IsShowTime=true; //标题是否显示时间 + this.IsShowName=true; //标题是否显示股票名字 + this.IsShowAveragePrice=true; //是否显示均线价格 + this.OverlayChartPaint; //叠加画法 + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.LastShowData; //保存最后显示的数据 给tooltip用 + this.OnDrawEvent; + this.PointInfo=null; + + this.MultiDayBeforeOpenData; //多日分时图 盘前数据 + this.MultiDayAfterCloseData; //多日分时图 收盘数据 + + this.GetCurrentKLineData=function() //获取当天鼠标位置所在的K线数据 + { + if (this.LastShowData) return this.LastShowData; + + if (this.CursorIndex==null || !this.Data) return null; + if (this.Data.length<=0) return null; + + var index=Math.abs(this.CursorIndex); + index=parseInt(index.toFixed(0)); + var dataIndex=this.Data.DataOffset+index; + if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1; + if (dataIndex<0) return null; + + var item=this.Data.Data[dataIndex]; + return item; + } + + this.GetLatestKLineData=function() //获取最新一个K线数据 + { + if (!this.Data || !this.Data.Data) return null; + var count=this.Data.Data.length; + if (count<=0) return null; + + return this.Data.Data[count-1]; + } + + this.DrawItem=function(item) + { + var isHScreen=this.Frame.IsHScreen===true; + var border=this.Frame.GetBorder(); + var left=border.Left; + var bottom=border.Top-this.Frame.ChartBorder.Top/2; + var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数 + + if (isHScreen) + { + if (this.Frame.ChartBorder.Right<5) return; + var left=2; + var bottom=this.Frame.ChartBorder.Right/2; //上下居中显示 + var xText=border.ChartWidth; + var yText=border.Top; + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + else + { + if (this.Frame.ChartBorder.Top<5) return; + } + + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=this.Font; + var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen }; + if(this.IsShowName) + { + if (!this.DrawText(this.Name,this.NameColor,position)) return; + } + + if (this.IsShowDate || this.IsShowTime) + { + var text=IFrameSplitOperator.FormatDateTimeString(item.DateTime,this.IsShowDate, this.IsShowTime); + if (!this.DrawText(text,this.DateTimeColor,position)) return; + } + + var close=item.Close; + var increase=item.Increase; + var vol=item.Vol; + var amount=item.Amount; + if (item.Before) //读取盘前数据 + { + close=item.Before.Close; + increase=item.Before.Increase; + vol=item.Before.Vol; + amount=item.Before.Amount; + } + + if (close) + { + var color=this.GetColor(close,this.YClose); + var text=g_JSChartLocalization.GetText('MTitle-Close',this.LanguageID)+close.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + } + + if (increase!=null) + { + var color=this.GetColor(increase,0); + var text=g_JSChartLocalization.GetText('MTitle-Increase',this.LanguageID)+increase.toFixed(2)+'%'; + if (!this.DrawText(text,color,position)) return; + } + + var isShowAvPrice=true; + var upperSymbol=this.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsET(upperSymbol) && !MARKET_SUFFIX_NAME.IsETShowAvPrice(upperSymbol)) isShowAvPrice=false; + + if (item.AvPrice && isShowAvPrice && this.IsShowAveragePrice) + { + var color=this.GetColor(item.AvPrice,this.YClose); + var text=g_JSChartLocalization.GetText('MTitle-AvPrice',this.LanguageID)+item.AvPrice.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + } + + var text=g_JSChartLocalization.GetText('MTitle-Vol',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(vol,2); + if (!this.DrawText(text,this.VolColor,position)) return; + + if (IFrameSplitOperator.IsNumber(amount)) + { + var text=g_JSChartLocalization.GetText('MTitle-Amount',this.LanguageID)+IFrameSplitOperator.FormatValueString(amount,2); + if (!this.DrawText(text,this.AmountColor,position)) return; + } + + if (IFrameSplitOperator.IsNumber(item.Position)) + { + var text=g_JSChartLocalization.GetText('MTitle-Position',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(item.Position,2); + if (!this.DrawText(text,this.VolColor,position)) return; + } + + //叠加股票的名字 + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (!item.Symbol || !item.Title) continue; + + var name=item.Title; + var clrText=item.Color; + var text='['+name+']'; + if (!this.DrawText(text,clrText,position)) return; + } + } + + this.DrawCallAuction=function() //集合竞价标题 + { + var isHScreen=this.Frame.IsHScreen===true; + var border=this.Frame.GetBorder(); + + var left=border.Left; + var bottom=border.Top-this.Frame.ChartBorder.Top/2; + var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数 + var bDraw=true; + if (isHScreen) + { + var left=2; + var bottom=this.Frame.ChartBorder.Right/2; //上下居中显示 + var xText=border.ChartWidth; + var yText=border.Top; + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + } + else + { + if (this.Frame.ChartBorder.Top<5) bDraw=false; + } + + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=this.Font; + var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen }; + if(bDraw && this.IsShowName) + { + if (!this.DrawText(this.Name,this.NameColor,position)) return; + } + + var strTime; + var dataVersion=1; + if (this.PointInfo.ClientPos==2) + { + if (!this.BeforeOpenData) return; + if (!this.CallAcutionXOperator) return; + + this.CallAcutionXOperator.Value=isHScreen?this.PointInfo.Point.Y:this.PointInfo.Point.X; + this.CallAcutionXOperator.Point={X:this.PointInfo.Point.X, Y:this.PointInfo.Point.Y}; + this.CallAcutionXOperator.ClientPos=this.PointInfo.ClientPos; + var callbackData={Explain:"BeforeOpen", Data:null, DataIndex:null, DataTotalCount:this.BeforeOpenData.TotalCount }; + if (!this.CallAcutionXOperator.Operator()) + { + this.OnDrawCallAuctionEventCallback(callbackData); + return false; + } + + callbackData.DataIndex=this.CallAcutionXOperator.DataIndex; + var item=this.CallAcutionXOperator.Item; + var time=item.Time; + if (this.BeforeOpenData.Ver==1.0) strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM"); + else strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); + + dataVersion=this.BeforeOpenData.Ver; + callbackData.Data=item; + } + else if (this.PointInfo.ClientPos==3) + { + if (!this.AfterCloseData) return; + if (!this.CallAcutionXOperator) return; + + this.CallAcutionXOperator.Value=isHScreen?this.PointInfo.Point.Y:this.PointInfo.Point.X; + this.CallAcutionXOperator.Point={X:this.PointInfo.Point.X, Y:this.PointInfo.Point.Y}; + this.CallAcutionXOperator.ClientPos=this.PointInfo.ClientPos; + var callbackData={Explain:"AfterClose", Data:null, DataIndex:null, DataTotalCount:this.AfterCloseData.TotalCount }; + if (!this.CallAcutionXOperator.Operator()) + { + this.OnDrawCallAuctionEventCallback(callbackData); + return false; + } + + callbackData.DataIndex=this.CallAcutionXOperator.DataIndex; + var item=this.CallAcutionXOperator.Item; + + var time=item.Time; + if (this.AfterCloseData.Ver==1.0) strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM"); + else strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); + dataVersion=this.AfterCloseData.Ver; + callbackData.Data=item; + } + else if (this.PointInfo.ClientPos>=200 && this.PointInfo.ClientPos<=299) + { + if (!this.MultiDayBeforeOpenData || !IFrameSplitOperator.IsNonEmptyArray(this.MultiDayBeforeOpenData) ) return; + + var x=this.Frame.IsHScreen==true?this.PointInfo.Point.Y:this.PointInfo.Point.X; + this.CallAcutionXOperator.Value=x; + this.CallAcutionXOperator.Point={X:this.PointInfo.Point.X, Y:this.PointInfo.Point.Y}; + this.CallAcutionXOperator.ClientPos=this.PointInfo.ClientPos; + var callbackData={Explain:"MultiDayBeforeOpen", Data:null, DataIndex:null }; + if (!this.CallAcutionXOperator.Operator()) + { + this.OnDrawCallAuctionEventCallback(callbackData); + return false; + } + + callbackData.DataIndex=this.CallAcutionXOperator.DataIndex; + callbackData.DayIndex=this.CallAcutionXOperator.DayIndex; + var dayItem=this.MultiDayBeforeOpenData[this.CallAcutionXOperator.DayIndex]; + var item=this.CallAcutionXOperator.Item; + var time=item.Time; + if (dayItem.Ver==1.0) strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM"); + else strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + + strTime=`${strDate} ${strTime}`; + + dataVersion=dayItem.Ver; + callbackData.Data=item; + } + else if (this.PointInfo.ClientPos>=300 && this.PointInfo.ClientPos<=399) + { + if (!this.MultiDayAfterCloseData || !IFrameSplitOperator.IsNonEmptyArray(this.MultiDayAfterCloseData) ) return; + + var x=this.Frame.IsHScreen==true?this.PointInfo.Point.Y:this.PointInfo.Point.X; + this.CallAcutionXOperator.Value=x; + this.CallAcutionXOperator.Point={X:this.PointInfo.Point.X, Y:this.PointInfo.Point.Y}; + this.CallAcutionXOperator.ClientPos=this.PointInfo.ClientPos; + var callbackData={Explain:"MultiDayAfterClose", Data:null, DataIndex:null }; + if (!this.CallAcutionXOperator.Operator()) + { + this.OnDrawCallAuctionEventCallback(callbackData); + return false; + } + + callbackData.DataIndex=this.CallAcutionXOperator.DataIndex; + callbackData.DayIndex=this.CallAcutionXOperator.DayIndex; + var dayItem=this.MultiDayAfterCloseData[this.CallAcutionXOperator.DayIndex]; + var item=this.CallAcutionXOperator.Item; + var time=item.Time; + if (dayItem.Ver==1.0) strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM"); + else strTime=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); + var strDate=IFrameSplitOperator.FormatDateString(item.Date); + + strTime=`${strDate} ${strTime}`; + + dataVersion=dayItem.Ver; + callbackData.Data=item; + } + else + { + return; + } + + if (bDraw && this.IsShowTime && strTime ) + { + if (!this.DrawText(strTime,this.DateTimeColor,position)) return; + } + + //匹配价 + if (bDraw && item && IFrameSplitOperator.IsNumber(item.Price)) + { + var color=this.GetColor(item.Price,this.YClose); + var filedName='MTitle-AC-Price'; + if (this.BeforeOpenData && this.BeforeOpenData.Ver==1.0) filedName="MTitle-Close"; + var text=g_JSChartLocalization.GetText(filedName,this.LanguageID)+item.Price.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + } + + //竞价涨幅 + if (bDraw && item && IFrameSplitOperator.IsPlusNumber(this.YClose) && IFrameSplitOperator.IsNumber(item.Price)) + { + var value=(item.Price-this.YClose)/this.YClose*100; + var color=this.GetColor(value,0); + var filedName='MTitle-AC-Increase'; + if (this.BeforeOpenData && this.BeforeOpenData.Ver==1.0) filedName="MTitle-Increase"; + var text=g_JSChartLocalization.GetText(filedName,this.LanguageID)+value.toFixed(2)+"%"; + if (!this.DrawText(text,color,position)) return; + } + + if (dataVersion==3.0) + { + if (bDraw && item && IFrameSplitOperator.IsNumber(item.AvPrice)) + { + var color=this.GetColor(item.Price,this.YClose); + var text=g_JSChartLocalization.GetText('MTitle-AC-AvPrice',this.LanguageID)+item.AvPrice.toFixed(defaultfloatPrecision); + if (!this.DrawText(text,color,position)) return; + } + } + + //匹配量 + if (bDraw && IFrameSplitOperator.IsNumber(item.Vol[0])) + { + var filedName='MTitle-AC-Vol'; + if (this.BeforeOpenData && this.BeforeOpenData.Ver==1.0) filedName="MTitle-Vol"; + var text=g_JSChartLocalization.GetText(filedName,this.LanguageID)+IFrameSplitOperator.FromatIntegerString(item.Vol[0],2); + if (!this.DrawText(text,this.VolColor,position)) return; + } + + //未匹配量 + if (bDraw && IFrameSplitOperator.IsNumber(item.Vol[1])) + { + var text=g_JSChartLocalization.GetText('MTitle-AC-NotMatchVol',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(item.Vol[1],2); + if (!this.DrawText(text,this.VolColor,position)) return; + } + + this.OnDrawCallAuctionEventCallback(callbackData); + } + + this.OnDrawCallAuctionEventCallback=function(drawData) + { + if (!this.OnDrawEvent || !this.OnDrawEvent.Callback) return; + var data={ Draw: drawData, Name:this.ClassName }; + this.OnDrawEvent.Callback(this.OnDrawEvent,data,this); + } + + this.Draw=function() + { + this.LastShowData=null; + if (!this.IsShow) return; + if (this.PointInfo && + ( this.PointInfo.ClientPos==2 || this.PointInfo.ClientPos==3 || + (this.PointInfo.ClientPos>=200&& this.PointInfo.ClientPos<=299) || + (this.PointInfo.ClientPos>=300&& this.PointInfo.ClientPos<=399) ) ) + { //集合竞价区域 + this.Canvas.save(); + this.DrawCallAuction(); + this.Canvas.restore(); + return; + } + + if (this.CursorIndex==null || !this.Data || !this.Data.Data || this.Data.Data.length<=0) + { + this.OnDrawEventCallback(null); + return; + } + + this.Canvas.font=this.Font; + this.SpaceWidth = this.Canvas.measureText('0').width; + + var isShowLastData=false; + if (this.DrawStatus && this.DrawStatus.IsTitleShowLatestData) + { + var status=this.DrawStatus; + if (!IFrameSplitOperator.IsNumber(status.FrameID) || status.FrameID<0) + isShowLastData=true; + else if (status.CorssCursorTouchEnd && status.IsOnTouch==false) + isShowLastData=true; + } + + if (isShowLastData) + { + var item=this.GetLatestKLineData(); + } + else + { + //var index=Math.abs(this.CursorIndex-0.5); + var index=this.CursorIndex; + index=parseInt(index.toFixed(0)); + var dataIndex=index+this.Data.DataOffset; + if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1; + var item=this.Data.Data[dataIndex]; + } + + this.LastShowData=item; + + this.Canvas.save(); + this.OnDrawEventCallback(item); + this.DrawItem(item); + this.Canvas.restore(); + } +} + +//字符串输出格式 +var STRING_FORMAT_TYPE = +{ + DEFAULT: 1, //默认 2位小数 单位自动转化 (万 亿) + ORIGINAL:2, //原始数据 + INTEGER:3, //整形数据输出 如果不是整形使用 DEFAULT + THOUSANDS:21, //千分位分割 +}; + +function DynamicTitleData(data,name,color) +{ + this.Data=data; + this.Name=name; + this.Color=color; //字体颜色 + this.DataType; //数据类型 + this.StringFormat=STRING_FORMAT_TYPE.DEFAULT; //字符串格式 + this.FloatPrecision=2; //小数位数 + this.IsShow=true; //是否显示 +} + +function DynamicChartTitlePainting() +{ + this.newMethod=IChartTitlePainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='DynamicChartTitlePainting'; + + this.IsDynamic=true; + this.Data=[]; + this.Explain; + + this.ColorIndex; //五彩K线名字 {Name:'名字'} + this.IsShowColorIndexTitle=true; + this.IsShowUpDownArrow=true; //指标数据是否显示 上涨下跌箭头 + this.IsShowIndexName=true; //是否显示指标名字 + this.IsShowIndexTitle=true; //是否显示指标标题信息 + + this.TradeIndex; //专家系统名字{Name:'名字', Param:'参数'} + this.IsShowTradeIndexTitle=true; + + this.OverlayIndex=new Map(); //叠加指标 key=Identify value={ Data:数据, Title:标题, Identify:标识} + this.IsShowOverlayIndexName=true; //是否显示叠加指标名字 + this.OverlayIndexType={ Position:0, LineSpace:5, BGColor:g_JSChartResource.OverlayIndexTitleBGColor }; //Position 0=主图指标后面显示 1=叠加指标单行显示 + //LineSpace 行间距 + + this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + this.TitleRect; //指标名字显示区域 + this.IsDrawTitleBG=false; //是否绘制指标名字背景色 + this.BGColor=g_JSChartResource.IndexTitleBGColor; + this.OnDrawEvent; + this.ParamSpace=2; //参数显示的间距 + + this.IsKLineFrame=false; //是否是K线框架标题 + + + //动态标题 + this.OutName=null; + this.OutValue=null; + + this.ReloadResource=function() + { + this.Font=g_JSChartResource.TitleFont; + this.TitleColor=g_JSChartResource.DefaultTextColor; + this.OverlayIndexType.BGColor=g_JSChartResource.OverlayIndexTitleBGColor; + } + + this.SetDynamicOutName=function(outName, args) + { + if (!this.OutName) + { + this.OutName=new Map(); + this.OutValue=new Map(); + } + else + { + this.OutName.clear(); + this.OutValue.clear(); + } + + var mapArgs=new Map(); + for(var i in args) + { + var item=args[i]; + mapArgs.set(`{${item.Name}}`, item); + } + + for(var i in outName) + { + var item=outName[i]; + if (item.DynamicName) + { + var aryFond = item.DynamicName.match(/{\w*}/i); + if (!aryFond || aryFond.length<=0) + { + this.OutName.set(item.Name, item.DynamicName); + } + else + { + var dyName=item.DynamicName; + var bFind=true; + for(var j=0;jthis.TitleRect.Left && xthis.TitleRect.Top && y0) text+=','; + + text+=year.toString(); + switch(quarter) + { + case 1: + text+='一季报 '; + break; + case 2: + text+='半年报 '; + break; + case 3: + text+='三季报 '; + break; + case 4: + text+='年报 '; + break; + } + + text+=this.FormatValue(value,format); + } + + return text; + } + + this.IsShowLastData=function() + { + var isShowLastData=false; + if (this.DrawStatus && this.DrawStatus.IsTitleShowLatestData) + { + var status=this.DrawStatus; + if (!IFrameSplitOperator.IsNumber(status.FrameID) || status.FrameID<0) + isShowLastData=true; + else if (status.CorssCursorTouchEnd && status.IsOnTouch==false) + isShowLastData=true; + } + + return isShowLastData; + } + + this.OnDrawTitleEvent=function() + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_INDEXTITLE_DRAW); + if (!event) return; + + var data={ Index:null, Data:this.Data ,Title:this.Title, FrameID:this.Frame.Identify, OverlayIndex:this.OverlayIndex }; + if (IFrameSplitOperator.IsNumber(this.CursorIndex)) + { + var index=Math.abs(this.CursorIndex); + index=parseInt(index.toFixed(0)); + data.Index=index; //当前屏数据索引 + } + + var pixelTatio = GetDevicePixelRatio(); + var border=this.Frame.GetBorder(); + data.Left=border.LeftEx/pixelTatio; + data.Top=border.Top/pixelTatio; + data.Right=border.RightEx/pixelTatio; + + event.Callback(event,data,this); + + } + + this.Draw=function() + { + if (this.Frame.IsMinSize) return; + + this.IsKLineFrame= this.Frame.ClassName=='KLineFrame' || this.Frame.ClassName=='KLineHScreenFrame'; + this.IsDrawTitleBG=this.Frame.IsDrawTitleBG; + this.IsShowUpDownArrow=this.Frame.IsShowTitleArraw; + this.IsShowIndexName=this.Frame.IsShowIndexName; + this.IsShowOverlayIndexName=this.Frame.IsShowOverlayIndexName; + this.OverlayIndexType.Position=this.Frame.OverlayIndexType.Position; + this.OverlayIndexType.LineSpace=this.Frame.OverlayIndexType.LineSpace; + this.ParamSpace=this.Frame.IndexParamSpace; + this.TitleRect=null; + + this.OnDrawTitleEvent(); + + if (this.Frame.IsShowIndexTitle==false) return; + if (this.CursorIndex==null ) return; + if (!this.Data) return; + if (this.Frame.ChartBorder.TitleHeight<5) return; + + if (this.Frame.IsHScreen===true) + { + this.Canvas.save(); + this.HScreenDraw(); + this.Canvas.restore(); + return; + } + + var left=this.Frame.ChartBorder.GetLeft()+1; + var bottom=this.Frame.ChartBorder.GetTop()+this.Frame.ChartBorder.TitleHeight/2; //上下居中显示 + var right=this.Frame.ChartBorder.GetRight(); + + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=this.Font; + + if (this.Title && this.IsShowIndexName) + { + var textWidth=this.Canvas.measureText(this.Title).width+2; + if (this.IsDrawTitleBG) + { + var spaceSize=GetDevicePixelRatio()*2; + this.Canvas.fillStyle=this.BGColor; + this.TitleRect={Left:left, Top:this.Frame.ChartBorder.GetTop()+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)}; //保存下标题的坐标 + this.Canvas.fillRect(this.TitleRect.Left,this.TitleRect.Top,this.TitleRect.Width,this.TitleRect.Height); + } + + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(this.Title,left,bottom,textWidth); + left+=textWidth; + } + + var isShowLastData=this.IsShowLastData(); + var lockRect=this.Frame.GetLockRect(); + if (lockRect) //指标上锁区域不显示动态标题 + { + var index=Math.abs(this.CursorIndex); + if (this.IsKLineFrame) index=this.CursorIndex; + var x=this.Frame.GetXFromIndex(index.toFixed(0)); + if (x>=lockRect.Left) return; + if (isShowLastData) return; + } + + for(var i in this.Data) + { + var item=this.Data[i]; + if (!item || !item.Data || !item.Data.Data) continue; + + if (item.Data.Data.length<=0) continue; + if (item.IsShow===false) continue; + + var value=null; + var valueText=null; + if (item.DataType=="StraightLine") //直线只有1个数据 + { + value=item.Data.Data[0]; + valueText=this.FormatValue(value,item); + } + else + { + if (isShowLastData) + { + var dataIndex=item.Data.Data.length-1; //显示最后一个数据 + } + else + { + var index=Math.abs(this.CursorIndex); + index=parseInt(index.toFixed(0)); + if (this.IsKLineFrame) index=this.CursorIndex; + var dataIndex=item.Data.DataOffset+index; + if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1; + if (dataIndex<0) continue; + //if (item.Data.DataOffset+index>=item.Data.Data.length) continue; + } + + value=item.Data.Data[dataIndex]; + if (value==null) continue; + + if (item.DataType=="HistoryData-Vol") + { + value=value.Vol; + valueText=this.FormatValue(value,item); + } + else if (item.DataType=="MultiReport") + { + valueText=this.FormatMultiReport(value,item); + } + else + { + var arrow=null; + if (this.IsShowUpDownArrow) + { + var proValue=null; + if (dataIndex-1>=0) proValue=item.Data.Data[dataIndex-1]; + if (IFrameSplitOperator.IsNumber(proValue)) + { + if (proValue>value) arrow='↓'; + else if (proValueright) break; + + this.Canvas.fillText(text,left,bottom,textWidth); + left+=textWidth; + } + + if (this.Explain) //说明信息 + { + this.Canvas.fillStyle=this.TitleColor; + var text="说明:"+this.Explain; + var textWidth=this.Canvas.measureText(text).width+2; + if (left+textWidth=item.Data.Data.length) dataIndex=item.Data.Data.length-1; + if (dataIndex<0) continue; + + value=item.Data.Data[dataIndex]; + if (value==null) continue; + + if (item.DataType=="HistoryData-Vol") + { + value=value.Vol; + valueText=this.FormatValue(value,item); + } + else if (item.DataType=="MultiReport") + { + valueText=this.FormatMultiReport(value,item); + } + else + { + valueText=this.FormatValue(value,item); + } + } + + var text=item.Name+":"+valueText; + var textWidth=this.Canvas.measureText(text).width+2; //后空2个像素 + if ((x+textWidth)>right) break; + if (this.OverlayIndexType.BGColor) + { + this.Canvas.fillStyle=this.OverlayIndexType.BGColor; + var rtBG={Left:x, Top:y, Width:textWidth, Height: fontHeight+lineSpace }; //保存下标题的坐标 + this.Canvas.fillRect(rtBG.Left,rtBG.Top,rtBG.Width,rtBG.Height); + } + + this.Canvas.fillStyle=item.Color; + this.Canvas.fillText(text,x,y,textWidth); + x+=textWidth; + } + + y+=fontHeight+lineSpace; + if ((y+fontHeight+lineSpace)>=bottom) break; + } + } + + this.DrawOverlayIndex=function(left) //叠加指标标题 + { + var bottom=this.Frame.ChartBorder.GetTop()+this.Frame.ChartBorder.TitleHeight/2; //上下居中显示 + var right=this.Frame.ChartBorder.GetRight(); + if (this.Frame.IsHScreen===true) + { + bottom=-this.Frame.ChartBorder.TitleHeight/2; + right=this.Frame.ChartBorder.GetHeight(); + } + if (left>right) return; + + var spaceWidth=5*GetDevicePixelRatio(); + var drawLeft=left; + for(item of this.OverlayIndex) + { + left+=spaceWidth; + var overlayItem=item[1]; + if (overlayItem.Title && this.IsShowOverlayIndexName) + { + this.Canvas.fillStyle=this.TitleColor; + var textWidth=this.Canvas.measureText(overlayItem.Title).width+2; + drawLeft=left; + left+=textWidth; + if (left>right) break; + this.Canvas.fillText(overlayItem.Title,drawLeft,bottom,textWidth); + } + + for(var i in overlayItem.Data) + { + var item=overlayItem.Data[i]; + if (!item || !item.Data || !item.Data.Data || !item.Name) continue; + if (item.Data.Data.length<=0) continue; + + var value=null; + var valueText=null; + if (item.DataType=="StraightLine") //直线只有1个数据 + { + value=item.Data.Data[0]; + valueText=this.FormatValue(value,item); + } + else + { + var index=Math.abs(this.CursorIndex-0.5); + index=parseInt(index.toFixed(0)); + if (this.IsKLineFrame) index=this.CursorIndex; + var dataIndex=item.Data.DataOffset+index; + if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1; + if (dataIndex<0) continue; + + value=item.Data.Data[dataIndex]; + if (value==null) continue; + + if (item.DataType=="HistoryData-Vol") + { + value=value.Vol; + valueText=this.FormatValue(value,item); + } + else if (item.DataType=="MultiReport") + { + valueText=this.FormatMultiReport(value,item); + } + else + { + valueText=this.FormatValue(value,item); + } + } + + this.Canvas.fillStyle=item.Color; + + var text=item.Name+":"+valueText; + var textWidth=this.Canvas.measureText(text).width+2; //后空2个像素 + drawLeft=left; + left+=textWidth; + if (left>right) break; + this.Canvas.fillText(text,drawLeft,bottom,textWidth); + } + + if (left>right) break; + } + } + + this.HScreenDraw=function() + { + var border=this.Frame.ChartBorder.GetHScreenBorder(); + var xText=this.Frame.ChartBorder.GetRightTitle(); + var yText=border.TopEx; + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + + var left=1; + var bottom=-this.Frame.ChartBorder.TitleHeight/2; //上下居中显示 + var right=this.Frame.ChartBorder.GetHeight(); + + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=this.Font; + + if (this.Title && this.IsShowIndexName) + { + var textWidth=this.Canvas.measureText(this.Title).width+2; + + if (this.IsDrawTitleBG) + { + var spaceSize=GetDevicePixelRatio()*2; + this.Canvas.fillStyle=this.BGColor; + this.TitleRect= {Left:this.Frame.ChartBorder.GetRightTitle(),Top:border.TopEx,Width:this.Frame.ChartBorder.TitleHeight ,Height:textWidth}; //保存下标题的坐标 + let drawRect={Left:left, Top:-this.Frame.ChartBorder.TitleHeight+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)}; + this.Canvas.fillRect(drawRect.Left,drawRect.Top,drawRect.Width,drawRect.Height); + } + + this.Canvas.fillStyle=this.TitleColor; + this.Canvas.fillText(this.Title,left,bottom,textWidth); + left+=textWidth; + } + + var lockRect=this.Frame.GetLockRect(); + if (lockRect) //指标上锁区域不显示动态标题 + { + var index=Math.abs(this.CursorIndex-0.5); + if (this.IsKLineFrame) index=this.CursorIndex; + var x=this.Frame.GetXFromIndex(index.toFixed(0)); + if (x>=lockRect.Top) return; + } + + for(var i in this.Data) + { + var item=this.Data[i]; + if (!item || !item.Data || !item.Data.Data || !item.Name) continue; + + if (item.Data.Data.length<=0) continue; + + var value=null; + var valueText=null; + if (item.DataType=="StraightLine") //直线只有1个数据 + { + value=item.Data.Data[0]; + valueText=this.FormatValue(value,item); + } + else + { + var index=Math.abs(this.CursorIndex-0.5); + index=parseInt(index.toFixed(0)); + if (this.IsKLineFrame) index=this.CursorIndex; + var dataIndex=item.Data.DataOffset+index; + if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1; + if (dataIndex<0) continue; + + value=item.Data.Data[dataIndex]; + if (value==null) continue; + + if (item.DataType=="HistoryData-Vol") + { + value=value.Vol; + valueText=this.FormatValue(value,item); + } + else if (item.DataType=="MultiReport") + { + valueText=this.FormatMultiReport(value,item); + } + else + { + valueText=this.FormatValue(value,item); + } + } + + this.Canvas.fillStyle=item.Color; + + var text=item.Name+":"+valueText; + var textWidth=this.Canvas.measureText(text).width+2; //后空2个像素 + if ((left+textWidth)>right) break; + + this.Canvas.fillText(text,left,bottom,textWidth); + left+=textWidth; + } + + if (this.Explain) //说明信息 + { + this.Canvas.fillStyle=this.TitleColor; + var text="说明:"+this.Explain; + var textWidth=this.Canvas.measureText(text).width+2; + if (left+textWidth0) this.LineWidth=option.LineWidth; + if (option.AreaColor) this.AreaColor=option.AreaColor; + if (option.PointColor) this.PointColor=option.PointColor; + } + + this.SetLineWidth=function() + { + this.BackupLineWidth=null; + if (this.LineWidth>0) + { + this.BackupLineWidth=this.Canvas.lineWidth; + this.Canvas.lineWidth=this.LineWidth*GetDevicePixelRatio(); + } + } + + this.RestoreLineWidth=function() + { + if (this.BackupLineWidth!=null) + { + this.Canvas.lineWidth=this.BackupLineWidth; + } + } + + //Point => Value + this.PointToValue=function() + { + if (!this.Frame) return false; + + if (this.Frame.ClassName=="MinuteFrame" || this.Frame.Class=="MinuteHScreenFrame") + { + return this.PointToValue_Minute(); + } + else + { + return this.PointToValue_KLine(); + } + } + + this.PointToKLine=function(aryPoint) + { + if (!this.Frame) return null; + var data=this.Frame.Data; + if (!data) return null; + + var kLine=[]; + var isHScreen=this.Frame.IsHScreen; + if (isHScreen) + { + for(var i in aryPoint) + { + var item=aryPoint[i]; + var xValue=parseInt(this.Frame.GetXData(item.Y))+data.DataOffset; + var yValue=this.Frame.GetYData(item.X); + + var valueItem={ XValue:xValue, YValue:yValue }; + var kline=data.Data[xValue]; + valueItem.DateTime={ Date:kline.Date }; + if (IFrameSplitOperator.IsNumber(kline.Time)) valueItem.DateTime.Time=kline.Time; + + kLine[i]=valueItem; + } + } + else + { + for(var i in aryPoint) + { + var item=aryPoint[i]; + var index=parseInt(this.Frame.GetXData(item.X,false)); + var xValue=index+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) + { + xValue=data.Data.length-1; + index=xValue-data.DataOffset; + } + var yValue=this.Frame.GetYData(item.Y,false); + + var valueItem={ XValue:xValue, YValue:yValue, XIndex:index }; + var kline=data.Data[xValue]; + valueItem.DateTime={ Date:kline.Date }; + if (IFrameSplitOperator.IsNumber(kline.Time)) valueItem.DateTime.Time=kline.Time; + + kLine[i]=valueItem; + } + } + + return kLine; + } + + this.PointToValue_KLine=function() + { + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + + var isHScreen=this.Frame.IsHScreen; + if (isHScreen) + { + for(var i in this.Point) + { + var item=this.Point[i]; + var xValue=parseInt(this.Frame.GetXData(item.Y))+data.DataOffset; + var yValue=this.Frame.GetYData(item.X); + + var valueItem={ XValue:xValue, YValue:yValue }; + var kline=data.Data[xValue]; + valueItem.DateTime={ Date:kline.Date }; + if (IFrameSplitOperator.IsNumber(kline.Time)) valueItem.DateTime.Time=kline.Time; + + this.Value[i]=valueItem; + } + } + else + { + for(var i in this.Point) + { + var item=this.Point[i]; + var xValue=parseInt(this.Frame.GetXData(item.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var yValue=this.Frame.GetYData(item.Y,false); + + var valueItem={ XValue:xValue, YValue:yValue }; + var kline=data.Data[xValue]; + valueItem.DateTime={ Date:kline.Date }; + if (IFrameSplitOperator.IsNumber(kline.Time)) valueItem.DateTime.Time=kline.Time; + + this.Value[i]=valueItem; + } + } + + return true; + } + + this.PointToValue_Minute=function() + { + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + + var isHScreen=this.Frame.IsHScreen; + if (isHScreen) + { + for(var i in this.Point) + { + var item=this.Point[i]; + var xValue=parseInt(this.Frame.GetXData(item.Y)); + var yValue=this.Frame.GetYData(item.X); + + var valueItem={ XValue:xValue, YValue:yValue }; + var minuteItem=data.Data[xValue]; + valueItem.DateTime={ Date:minuteItem.Date ,Time:minuteItem.Time}; + this.Value[i]=valueItem; + } + } + else + { + for(var i in this.Point) + { + var item=this.Point[i]; + var xValue=parseInt(this.Frame.GetXData(item.X)); + var yValue=this.Frame.GetYData(item.Y); + + var valueItem={ XValue:xValue, YValue:yValue }; + var minuteItem=data.Data[xValue]; + valueItem.DateTime={ Date:minuteItem.Date, Time:minuteItem.Time }; + this.Value[i]=valueItem; + } + } + + return true; + } + + this.IsPointIn=function(x,y) + { + return false; + } + + //Value => Point + this.ValueToPoint=function() + { + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + + //this.UpdateXValue(); + var isHScreen=this.Frame.IsHScreen; + this.Point=[]; + for(var i in this.Value) + { + var item=this.Value[i]; + var pt=new Point(); + if (isHScreen) + { + pt.Y=this.Frame.GetXFromIndex(item.XValue-data.DataOffset); + pt.X=this.Frame.GetYFromData(item.YValue); + } + else + { + pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset, false); + pt.Y=this.Frame.GetYFromData(item.YValue,false); + } + this.Point[i]=pt; + } + } + + this.UpdateXValue=function() //通过datetime更新x的索引 + { + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + + var aryDateTime=[]; + for(var i in this.Value) + { + var item=this.Value[i]; + var dateTime={ Date:item.DateTime.Date }; + if (IFrameSplitOperator.IsNumber(item.DateTime.Time)) dateTime.Time=item.DateTime.Time; + aryDateTime[i]=dateTime; + } + + data.FindDataIndexByDateTime(aryDateTime); + for(var i in aryDateTime) + { + var findItem=aryDateTime[i]; + var valueItem=this.Value[i]; + if (findItem.Index>=0) valueItem.XValue=findItem.Index; + } + } + + //xStep,yStep 移动的偏移量 + this.Move=function(xStep,yStep) + { + if (this.Status!=20) return fasle; + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + if (this.MovePointIndex==null) return false; + + var index=parseInt(this.MovePointIndex); + if (index===100) //整体移动 + { + for(var i in this.Point) + { + this.Point[i].X+=xStep; + this.Point[i].Y+=yStep; + } + } + else if (index===0 || index===1 || index===2 || index===3 || index===4 || index===5) + { + if (index=showCount) ++invaildX; + + var pt=new Point(); + if (isHScreen) //横屏X,Y对调 + { + pt.Y=this.Frame.GetXFromIndex(item.XValue-data.DataOffset,false); + pt.X=this.Frame.GetYFromData(item.YValue,false); + } + else + { + pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset,false); + pt.Y=this.Frame.GetYFromData(item.YValue,false); + } + drawPoint.push(pt); + } + + if (option && option.IsCheckX===true) + { + if (invaildX==this.Value.length) return null; + } + } + else + { + drawPoint=this.Point; + } + + return drawPoint; + } + + this.IsYValueInFrame=function(yValue) + { + if (!this.Frame) return false; + + if (yValue>this.Frame.HorizontalMax || yValue0) + { + var b2=bottom-ptStart.Y; + var a2=a*b2/b; + + var pt=new Point(); + pt.X=ptStart.X+a2; + pt.Y=bottom; + result.End=pt; + + + var b2=ptEnd.Y-top; + var a2=a*b2/b; + var pt2=new Point(); + pt2.Y=top; + pt2.X=ptEnd.X-a2; + result.Start=pt2; + } + else + { + var b2=bottom-ptStart.Y; + var a2=Math.abs(a)*b2/b; + + var pt=new Point(); + pt.X=ptStart.X-a2;; + pt.Y=bottom; + result.End=pt; + + var b2=ptEnd.Y-top; + var a2=Math.abs(a)*b2/b; + var pt2=new Point(); + pt2.Y=top; + pt2.X=ptEnd.X+a2; + result.Start=pt2; + } + + return result; + } + + //计算2个点线的,点0->点1->延长线的点 + this.CalculateExtendLineEndPoint=function(aryPoint) + { + var left=this.Frame.ChartBorder.GetLeft(); + var right=this.Frame.ChartBorder.GetRight(); + + var a=aryPoint[1].X-aryPoint[0].X; + var b=aryPoint[1].Y-aryPoint[0].Y; + + if (a>0) + { + var a1=right-aryPoint[0].X; + var b1=a1*b/a; + var y=b1+aryPoint[0].Y; + + var pt=new Point(); + pt.X=right; + pt.Y=y; + return pt; + } + else + { + var a1=aryPoint[0].X-left; + var b1=a1*b/Math.abs(a); + var y=b1+aryPoint[0].Y; + + var pt=new Point(); + pt.X=left; + pt.Y=y; + return pt; + } + } + + //坐标是否在点上 返回在第几个点上 + this.IsPointInXYValue=function(x,y) + { + if (!this.Frame) return -1; + + var data=this.Frame.Data; + if (!data) return -1; + if (!this.Value) return -1; + + var radius=5; + if (this.Option) radius+=this.Option.Zoom; + var isHScreen=this.Frame.IsHScreen; + radius*=GetDevicePixelRatio(); + for(var i=0;i=0) return value; + + value=this.IsPointInLine(x,y); + if (value>=0) return 100; + + return -1; + } + + this.DrawLine=function(ptStart,ptEnd,isDottedline) + { + if (isDottedline) this.Canvas.setLineDash([5,10]); + + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + + if (isDottedline) this.Canvas.setLineDash([]); + } + + this.CreateLineData=function(ptStart,ptEnd) + { + var line={Start:new Point(), End:new Point()}; + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=ptEnd.Y; + line.End.X=ptEnd.X; + + return line; + } + + //导出成存储格式 + this.ExportStorageData=function() + { + var storageData= + { + ClassName:this.ClassName, + Symbol:this.Symbol, Guid:this.Guid, Period:this.Period,Value:[] , + FrameID:this.Frame.Identify, LineColor:this.LineColor, AreaColor:this.AreaColor, + LineWidth:this.LineWidth, Right:this.Right + }; + for(var i in this.Value) + { + var item=this.Value[i]; + storageData.Value.push({ XValue:item.XValue, YValue:item.YValue, DateTime:item.DateTime }); + } + + if (this.Text) storageData.Text=this.Text; //如果有文本, 也导出 + if (this.FontOption) storageData.FontOption=this.FontOption; //字体也导出 + + return storageData; + } + + this.IsFrameMinSize=function() //框架是否是最小化模式 + { + return this.Frame && this.Frame.IsMinSize; + } + +} + +IChartDrawPicture.ColorToRGBA=function(color,opacity) +{ + var reg = /^(rgb|RGB)/; + if (reg.test(color)) + { + var strHex = "#"; + var colorArr = color.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(","); // 把RGB的3个数值变成数组 + // 转成16进制 + for (var i = 0; i < colorArr.length; i++) + { + var hex = Number(colorArr[i]).toString(16); + if (hex === "0") hex += hex; + strHex += hex; + } + + color=strHex; + } + + return "rgba(" + parseInt("0x" + color.slice(1, 3)) + "," + parseInt("0x" + color.slice(3, 5)) + "," + parseInt("0x" + color.slice(5, 7)) + "," + opacity + ")"; +} + +IChartDrawPicture.ArrayDrawPricture= +[ + { Name:"线段", ClassName:'ChartDrawPictureLine', Create:function() { return new ChartDrawPictureLine(); } }, + { Name:"射线", ClassName:'ChartDrawPictureHaflLine', Create:function() { return new ChartDrawPictureHaflLine(); } }, + { Name:"箭头", ClassName:"ChartDrawArrowLine", Create:function() { return new ChartDrawArrowLine(); } }, + { Name:"水平线", ClassName:'ChartDrawPictureHorizontalLine', Create:function() { return new ChartDrawPictureHorizontalLine(); }}, + { Name:"趋势线", ClassName:'ChartDrawPictureTrendLine', Create:function() { return new ChartDrawPictureTrendLine(); }}, + { Name:"矩形", ClassName:'ChartDrawPictureRect', Create:function() { return new ChartDrawPictureRect(); }}, + { Name:"圆弧线", ClassName:'ChartDrawPictureArc', Create:function() { return new ChartDrawPictureArc(); }}, + { Name:"M头W底", ClassName:'ChartDrawPictureWaveMW', Create:function() { return new ChartDrawPictureWaveMW(); }}, + { Name:"头肩型", ClassName:"ChartDrawHeadShouldersBT", Create:function() { return new ChartDrawHeadShouldersBT(); }}, + { Name:"平行线", ClassName:'ChartDrawPictureParallelLines', Create:function() { return new ChartDrawPictureParallelLines(); }}, + { Name:"平行通道", ClassName:'ChartDrawPictureParallelChannel', Create:function() { return new ChartDrawPictureParallelChannel(); }}, + { Name:"价格通道线", ClassName:'ChartDrawPicturePriceChannel', Create:function() { return new ChartDrawPicturePriceChannel(); }}, + { Name:"文本", ClassName:'ChartDrawPictureText', Create:function() { return new ChartDrawPictureText(); }}, + { Name:"江恩角度线", ClassName:'ChartDrawPictureGannFan', Create:function() { return new ChartDrawPictureGannFan(); }}, + { Name:"阻速线", ClassName:'ChartDrawPictureResistanceLine', Create:function() { return new ChartDrawPictureResistanceLine(); }}, + { Name:"黄金分割", ClassName:'ChartDrawPictureGoldenSection', Create:function() { return new ChartDrawPictureGoldenSection(); }}, + { Name:"百分比线", ClassName:'ChartDrawPicturePercentage', Create:function() { return new ChartDrawPicturePercentage(); }}, + { Name:"波段线", ClassName:'ChartDrawPictureWaveBand', Create:function() { return new ChartDrawPictureWaveBand(); }}, + { Name:"三角形", ClassName:'ChartDrawPictureTriangle', Create:function() { return new ChartDrawPictureTriangle(); }}, + { Name:"对称角度", ClassName:'ChartDrawPictureSymmetryAngle', Create:function() { return new ChartDrawPictureSymmetryAngle(); }}, + { Name:"圆", ClassName:'ChartDrawPictureCircle', Create:function() { return new ChartDrawPictureCircle(); }}, + { Name:"平行四边形", ClassName:'ChartDrawPictureQuadrangle', Create:function() { return new ChartDrawPictureQuadrangle(); }}, + { Name:"斐波那契周期线", ClassName:'ChartDrawPictureFibonacci', Create:function() { return new ChartDrawPictureFibonacci(); }}, + { Name:"线形回归线", ClassName:"ChartDrawLinearRegression", Create:function() { return new ChartDrawLinearRegression(); } }, + { Name:"线形回归带", ClassName:"ChartDrawLinearRegression", Create:function() { return new ChartDrawLinearRegression({ IsShowMaxMinLine:true }); } }, + { Name:"延长线形回归带", ClassName:"ChartDrawLinearRegression", Create:function() { return new ChartDrawLinearRegression({ IsShowMaxMinLine:true, IsShowExtendLine:true }); } }, + { Name:"尺子", ClassName:"ChartDrawRuler", Create:function() { return new ChartDrawRuler(); } }, + { Name:"标价线", ClassName:"ChartDrawPriceLine", Create:function() { return new ChartDrawPriceLine(); } }, + { Name:"垂直线", ClassName:"ChartDrawVerticalLine", Create:function() { return new ChartDrawVerticalLine(); } }, + { Name:"波浪尺", ClassName:"ChartDrawWaveRuler", Create:function() { return new ChartDrawWaveRuler(); } }, + { Name:"AB波浪尺", ClassName:"ChartDrawWaveRuler2Point", Create:function() { return new ChartDrawWaveRuler2Point(); }}, + { Name:"箱型线", ClassName:"ChartDrawBox", Create:function() { return new ChartDrawBox(); } }, + { Name:"2点画图例子", ClassName:"ChartDrawTwoPointDemo", Create:function() { return new ChartDrawTwoPointDemo(); } }, + { Name:"3点画图例子", ClassName:"ChartDrawThreePointDemo", Create:function() { return new ChartDrawThreePointDemo(); } }, + { Name:"水平线段", ClassName:"ChartDrawHLineSegment", Create:function() { return new ChartDrawHLineSegment();} }, + { Name:"平行射线", ClassName:"ChartDrawParallelRaysLines", Create:function() { return new ChartDrawParallelRaysLines();}}, + { ClassName:'ChartDrawPictureIconFont', Create:function() { return new ChartDrawPictureIconFont(); }} + +]; + +IChartDrawPicture.MapIonFont=new Map( +[ + ["icon-arrow_up", { Text:'\ue683', Color:'#318757', Family:"iconfont"}], + ["icon-arrow_down", { Text:'\ue681', Color:'#db563e', Family:"iconfont"}], + ["icon-arrow_right", { Text:'\ue682', Color:'#318757', Family:"iconfont"}], + ["icon-arrow_left", { Text:'\ue680',Color:'#318757', Family:"iconfont"}] +]); + +IChartDrawPicture.GetDrawPictureByName=function(value) +{ + for(var i in IChartDrawPicture.ArrayDrawPricture) + { + var item=IChartDrawPicture.ArrayDrawPricture[i]; + if (item.Name==value) return item; + } + + return null; +} + +IChartDrawPicture.GetDrawPictureByClassName=function(value) +{ + for(var i in IChartDrawPicture.ArrayDrawPricture) + { + var item=IChartDrawPicture.ArrayDrawPricture[i]; + if (item.ClassName==value) return item; + } + + return null; +} + +//注册一个新的画图工具 {Name:中文名字, ClassName:类名, Create:function()} +IChartDrawPicture.RegisterDrawPicture=function(obj) +{ + if (!obj.Name || !obj.ClassName || !obj.Create) return false; + + var item={ Name:obj.Name, ClassName:obj.ClassName, Create:obj.Create }; + IChartDrawPicture.ArrayDrawPricture.push(item); + + JSConsole.Chart.Log('[IChartDrawPicture.RegisterDrawPicture] registered new draw picture class. item=',item); + return true; +} + +//注册一个新图标 {Name:, Text: , Color:, Family:} +IChartDrawPicture.RegisterIonFont=function(obj) +{ + if (!obj.Name || !obj.Text || !obj.Family) return false; + + var isOverwirte=IChartDrawPicture.MapIonFont.has(obj.Name); + IChartDrawPicture.MapIonFont.set(obj.Name, obj); + + JSConsole.Chart.Log('[IChartDrawPicture.RegisterIonFont] registered new icon font, obj=, isOverwirte=',obj, isOverwirte); + return true; +} + +IChartDrawPicture.CreateChartDrawPicture=function(obj) //创建画图工具 +{ + var item=IChartDrawPicture.GetDrawPictureByClassName(obj.ClassName); + if (!item) return null; + + var chartDraw=item.Create(); + + if (obj.Period>=0) chartDraw.Period=obj.Period; + if (obj.Right>=0) chartDraw.Right=obj.Right; + if (obj.Guid) chartDraw.Guid=obj.Guid; + if (obj.Symbol) chartDraw.Symbol=obj.Symbol; + if (obj.Value) chartDraw.Value=obj.Value; + if (obj.Text) chartDraw.Text=obj.Text; + if (obj.LineColor) chartDraw.LineColor=obj.LineColor; + if (obj.AreaColor) chartDraw.AreaColor=obj.AreaColor; + if (obj.FontOption) chartDraw.FontOption=obj.FontOption; + if (obj.Label) chartDraw.Label=obj.Label; + if (obj.LineWidth>0) chartDraw.LineWidth=obj.LineWidth; + if (obj.EnableMove===false) chartDraw.EnableMove=obj.EnableMove; + + return chartDraw; +} + + + + + + +//画图工具-线段 +function ChartDrawPictureLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint( {IsCheckX:true, IsCheckY:true} ); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + + this.ClipFrame(); + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + /* + if (this.IsSelected) + { + this.Canvas.strokeStyle='rgba(255,0,0,0.5)'; + this.Canvas.lineWidth=20 * GetDevicePixelRatio(); + this.Canvas.stroke(); + } + */ + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } +} + +//画图工具-箭头线 +function ChartDrawArrowLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawArrowLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.ArrawLineWidth=5; + this.ArrawLength=15; //三角斜边长度 + this.ArrawAngle=35; //三角斜边一直线夹角 + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint( {IsCheckX:true, IsCheckY:true} ); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + + this.ClipFrame(); + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + //计算箭头 + var theta=this.ArrawAngle; //三角斜边一直线夹角 + var headlen=this.ArrawLength; //三角斜边长度 + var angle = Math.atan2(ptStart.Y - ptEnd.Y, ptStart.X - ptEnd.X) * 180 / Math.PI, + angle1 = (angle + theta) * Math.PI / 180, + angle2 = (angle - theta) * Math.PI / 180, + topX = headlen * Math.cos(angle1), + topY = headlen * Math.sin(angle1), + botX = headlen * Math.cos(angle2), + botY = headlen * Math.sin(angle2); + + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + + this.Canvas.beginPath(); + + var arrowX = ptEnd.X + topX; + var arrowY = ptEnd.Y + topY; + this.Canvas.moveTo(arrowX,arrowY); + + this.Canvas.lineTo(ptEnd.X, ptEnd.Y); + + arrowX = ptEnd.X + botX; + arrowY = ptEnd.Y + botY; + this.Canvas.lineTo(arrowX,arrowY); + + this.Canvas.lineWidth=this.ArrawLineWidth*GetDevicePixelRatio(); + this.Canvas.stroke(); + + this.RestoreLineWidth(); + + /* + if (this.IsSelected) + { + this.Canvas.strokeStyle='rgba(255,0,0,0.5)'; + this.Canvas.lineWidth=20 * GetDevicePixelRatio(); + this.Canvas.stroke(); + } + */ + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint([drawPoint[0]]); //画点 + this.Canvas.restore(); + } +} + +//画图工具-射线 +function ChartDrawPictureHaflLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureHaflLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint || drawPoint.length!=2) return; + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + this.ClipFrame(); + + this.Canvas.strokeStyle=this.LineColor; + this.SetLineWidth(); + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X,drawPoint[0].Y); + this.Canvas.lineTo(drawPoint[1].X,drawPoint[1].Y); + var endPoint=this.CalculateExtendLineEndPoint(drawPoint); + this.Canvas.lineTo(endPoint.X,endPoint.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } +} + +// 画图工具-水平线 支持横屏 +function ChartDrawPictureHorizontalLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.Super_SetOption=this.SetOption; //父类函数 + this.Super_ExportStorageData=this.ExportStorageData; + + this.Label; //{Text:文本, Position: 0=左, 1=右 } + + this.SetOption=function(option) + { + if (this.Super_SetOption) this.Super_SetOption(option); + if (option) + { + if (option.Label) this.Label=option.Label; + } + } + + this.ExportStorageData=function() + { + var storageData; + if (this.Super_ExportStorageData) + { + storageData=this.Super_ExportStorageData(); + if (this.Label) storageData.Label=this.Label; + } + + return storageData; + } + + this.PointCount=1; + this.ClassName='ChartDrawPictureHorizontalLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint(); + if (!drawPoint || drawPoint.length!=1) return; + if (!this.Frame) return; + if (this.Value.length!=1) return; + if (!this.IsYValueInFrame(this.Value[0].YValue)) return null; + + var isHScreen=this.Frame.IsHScreen; + var left=this.Frame.ChartBorder.GetLeft(); + var right=this.Frame.ChartBorder.GetRight(); + if (isHScreen) + { + left=this.Frame.ChartBorder.GetTop(); + right=this.Frame.ChartBorder.GetBottom(); + } + this.ClipFrame(); + + this.Canvas.strokeStyle=this.LineColor; + this.SetLineWidth(); + this.Canvas.beginPath(); + if (isHScreen) + { + this.Canvas.moveTo(drawPoint[0].X,left); + this.Canvas.lineTo(drawPoint[0].X,right); + } + else + { + this.Canvas.moveTo(left,drawPoint[0].Y); + this.Canvas.lineTo(right,drawPoint[0].Y); + } + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:new Point(), End:new Point()}; + if (isHScreen) + { + line.Start.X=drawPoint[0].X; + line.Start.Y=left; + line.End.X=drawPoint[0].X; + line.End.Y=right; + } + else + { + line.Start.X=left; + line.Start.Y=drawPoint[0].Y; + line.End.X=right; + line.End.Y=drawPoint[0].Y; + } + this.LinePoint.push(line); + + //画点 + this.DrawPoint(drawPoint); + + //显示价格 + + this.Canvas.fillStyle=this.LineColor; + this.Canvas.font=this.Font; + if (isHScreen) + { + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="bottom"; + var xText=drawPoint[0].X; + var yText=left; + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度 + var yValue=this.Frame.GetYData(drawPoint[0].X); + var text=yValue.toFixed(2); + if (this.Label) + { + if (this.Label.Position==0) text=this.Label.Text+yValue.toFixed(2); + else if (this.Label.Position==1) text=yValue.toFixed(2)+this.Label.Text; + } + this.Canvas.fillText(text,0,0); + } + else + { + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="bottom"; + var yValue=this.Frame.GetYData(drawPoint[0].Y); + var text=yValue.toFixed(2); + if (this.Label) + { + if (this.Label.Position==0) text=this.Label.Text+yValue.toFixed(2); + else if (this.Label.Position==1) text=yValue.toFixed(2)+this.Label.Text; + } + this.Canvas.fillText(text,left,drawPoint[0].Y); + } + + this.Canvas.restore(); + } +} + +//趋势线 +function ChartDrawPictureTrendLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureTrendLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint || drawPoint.length!=2) return; + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd); + + this.ClipFrame(); + + this.Canvas.strokeStyle=this.LineColor; + this.SetLineWidth(); + this.Canvas.beginPath(); + this.Canvas.moveTo(extendLine.Start.X,extendLine.Start.Y); + this.Canvas.lineTo(extendLine.End.X,extendLine.End.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } +} + + +//画图工具-矩形 +function ChartDrawPictureRect() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureRect'; + + this.Draw=function() + { + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint || drawPoint.length!=2) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + this.ClipFrame(); + + this.Canvas.strokeStyle=this.LineColor; + this.SetLineWidth(); + this.Canvas.beginPath(); + this.Canvas.rect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + //透明背景 + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.fillRect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn=function(x,y) + { + if (this.IsFrameMinSize()) return -1; + if (!this.Frame || this.Status!=10) return -1; + + var data=this.Frame.Data; + if (!data) return -1; + + var nIndex=this.IsPointInXYValue(x,y); + if (nIndex>=0) return nIndex; + + var aryPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!aryPoint || aryPoint.length!=2) return -1; + + //是否在矩形边框上 + var linePoint=[ {X:aryPoint[0].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[0].Y}]; + if (this.IsPointInLine(linePoint,x,y)) + return 100; + + linePoint=[ {X:aryPoint[1].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[1].Y}]; + if (this.IsPointInLine2(linePoint,x,y)) + return 100; + + linePoint=[ {X:aryPoint[1].X,Y:aryPoint[1].Y},{X:aryPoint[0].X,Y:aryPoint[1].Y}]; + if (this.IsPointInLine(linePoint,x,y)) + return 100; + + linePoint=[ {X:aryPoint[0].X,Y:aryPoint[1].Y},{X:aryPoint[0].X,Y:aryPoint[0].Y}]; + if (this.IsPointInLine2(linePoint,x,y)) + return 100; + + return -1; + } + + //点是否在线段上 水平线段 + this.IsPointInLine=function(aryPoint,x,y) + { + var radius=5; + if (this.Option) radius+=this.Option.Zoom; + var pixel=GetDevicePixelRatio(); + radius*=pixel; + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X,aryPoint[0].Y+radius); + this.Canvas.lineTo(aryPoint[0].X,aryPoint[0].Y-radius); + this.Canvas.lineTo(aryPoint[1].X,aryPoint[1].Y-radius); + this.Canvas.lineTo(aryPoint[1].X,aryPoint[1].Y+radius); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x,y)) + return true; + } + + //垂直线段 + this.IsPointInLine2=function(aryPoint,x,y) + { + var radius=5; + if (this.Option) radius+=this.Option.Zoom; + var pixel=GetDevicePixelRatio(); + radius*=pixel; + + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X-radius,aryPoint[0].Y); + this.Canvas.lineTo(aryPoint[0].X+radius,aryPoint[0].Y); + this.Canvas.lineTo(aryPoint[1].X+radius,aryPoint[1].Y); + this.Canvas.lineTo(aryPoint[1].X-radius,aryPoint[1].Y); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x,y)) + return true; + } +} + + +//画图工具-弧形 +function ChartDrawPictureArc() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureArc'; + + this.Draw=function() + { + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint(); + if (!drawPoint || drawPoint.length!=2) return; + + this.ClipFrame(); + + //this.Canvas.beginPath(); + //this.Canvas.rect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y); + this.SetLineWidth(); + if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第一象限 + { + var a = drawPoint[1].X - drawPoint[0].X; + var b = drawPoint[0].Y - drawPoint[1].Y; + var step = (a > b) ? 1/a : 1 / b; + var xcenter = drawPoint[0].X; + var ycenter = drawPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 1.5*Math.PI; i < 2*Math.PI; i+=step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter+b*Math.sin(i)*-1); + } + for (var j = 0; j <= 0.5*Math.PI; j += step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(j), ycenter+b*Math.sin(j)*-1); + } + } + else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第二象限 + { + var a = drawPoint[0].X - drawPoint[1].X; + var b = drawPoint[0].Y - drawPoint[1].Y; + var step = (a > b) ? 1/a:1/b; + var xcenter = drawPoint[1].X; + var ycenter = drawPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 0; i <= Math.PI; i += step) + { + this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + } + else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第三象限 + { + var a = drawPoint[0].X - drawPoint[1].X; + var b = drawPoint[1].Y - drawPoint[0].Y; + var step = (a > b) ? 1/a:1/b; + var xcenter = drawPoint[0].X; + var ycenter = drawPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 0.5*Math.PI; i <= 1.5*Math.PI; i += step) + { + this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + } + else if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第四象限 + { + var a = drawPoint[1].X - drawPoint[0].X; + var b = drawPoint[1].Y - drawPoint[0].Y; + var step = (a > b) ? 1/a : 1/b; + var xcenter = drawPoint[1].X; + var ycenter = drawPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = Math.PI; i <= 2*Math.PI; i += step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + } + + + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.stroke(); + this.RestoreLineWidth(); + //this.Canvas.closePath(); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn=function(x,y) + { + if (this.IsFrameMinSize()) return -1; + if (!this.Frame || this.Status!=10) return -1; + + var data=this.Frame.Data; + if (!data) return -1; + + //是否在点上 + var nIndex=this.IsPointInXYValue(x,y); + if (nIndex>=0) return nIndex; + + var aryPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!aryPoint || aryPoint.length!=2) return -1; + + //是否在弧线上 + var ArcPoint=[ {X:aryPoint[0].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[1].Y}]; + if (this.IsPointInArc(ArcPoint, x, y)) + return 100; + + return -1; + } + this.IsPointInArc=function(aryPoint,x,y) + { + if (aryPoint.length != 2) + return false; + if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第一象限 + { + var a = aryPoint[1].X - aryPoint[0].X; + var b = aryPoint[0].Y - aryPoint[1].Y; + var step = (a > b) ? 1/a : 1 / b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1/ainer : 1/biner; + var xcenter = aryPoint[0].X; + var ycenter = aryPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 1.5*Math.PI; i < 2*Math.PI; i+=step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter+b*Math.sin(i)*-1); + } + for (var j = 0; j <= 0.5*Math.PI; j += step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(j), ycenter+b*Math.sin(j)*-1); + } + for (var k = 0.5*Math.PI; k >= 0; k -= stepiner) + { + this.Canvas.lineTo(xcenter+ainer*Math.cos(k), ycenter + biner*Math.sin(j)*-1); + } + for (var l = 2*Math.PI; l >= 1.5*Math.PI; l -= stepiner) + { + this.Canvas.lineTo(xcenter + ainer*Math.cos(l), ycenter + biner*Math.sin(l)*-1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第二象限 + { + var a = aryPoint[0].X - aryPoint[1].X; + var b = aryPoint[0].Y - aryPoint[1].Y; + var step = (a > b) ? 1/a:1/b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner; + var xcenter = aryPoint[1].X; + var ycenter = aryPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 0; i <= Math.PI; i += step) + { + this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + for (var j = Math.PI; j >= 0; j -= stepiner) + { + this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner*Math.sin(j)*-1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第三象限 + { + var a = aryPoint[0].X - aryPoint[1].X; + var b = aryPoint[1].Y - aryPoint[0].Y; + var step = (a > b) ? 1/a:1/b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1/ainer : 1/biner; + var xcenter = aryPoint[0].X; + var ycenter = aryPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 0.5*Math.PI; i <= 1.5*Math.PI; i += step) + { + this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + for (var j = 1.5*Math.PI; j >= 0.5*Math.PI; j -= stepiner) + { + this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner*Math.sin(j)*-1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第四象限 + { + var a = aryPoint[1].X - aryPoint[0].X; + var b = aryPoint[1].Y - aryPoint[0].Y; + var step = (a > b) ? 1/a : 1/b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1/ainer : 1/biner; + var xcenter = aryPoint[1].X; + var ycenter = aryPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = Math.PI; i <= 2*Math.PI; i += step) + { + this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter + b*Math.sin(i)*-1); + } + for (var j = 2*Math.PI; j >= Math.PI; j -= stepiner) + { + this.Canvas.lineTo(xcenter + ainer*Math.cos(j), ycenter + biner*Math.sin(j)*-1); + } + this.Canvas.closePath(); + } + if (this.Canvas.isPointInPath(x,y)) + return true; + else + return false; + + } + +} + +//M头W底 支持横屏 +function ChartDrawPictureWaveMW() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureWaveMW'; + this.PointCount=5; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.IsHScreen=false; + + this.Draw=function() + { + this.IsHScreen=this.Frame.IsHScreen; + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + //var points=drawPoint.slice(0); + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + this.ClipFrame(); + + this.CalculateLines(drawPoint); + this.SetLineWidth(); + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + this.RestoreLineWidth(); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.CalculateLines=function(points) + { + if (!this.Frame) return; + if (points.length<2) return; + + if (this.Status==2) + { + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[0].Y; + line.Start.X=points[0].X; + line.End.Y=points[1].Y; + line.End.X=points[1].X; + this.LinePoint.push(line); + + var xMove=points[1].X-points[0].X; + var yMove=points[1].Y-points[0].Y; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=points[1].Y; + line.Start.X=points[1].X; + line.End.Y=points[1].Y+yMove; + line.End.X=points[0].X; + } + else + { + line.Start.Y=points[1].Y; + line.Start.X=points[1].X; + line.End.Y=points[0].Y; + line.End.X=points[1].X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[2]=newPt; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=ptStart.Y+yMove; + line.End.X=points[1].X; + } + else + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=points[1].Y; + line.End.X=ptStart.X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[3]=newPt; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=ptStart.Y+yMove; + line.End.X=points[0].X; + } + else + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=points[0].Y; + line.End.X=ptStart.X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[4]=newPt; + + this.PointCount=this.Point.length; + } + else if (points.length==5) + { + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[0].Y; + line.Start.X=points[0].X; + line.End.Y=points[1].Y; + line.End.X=points[1].X; + this.LinePoint.push(line); + + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[1].Y; + line.Start.X=points[1].X; + line.End.Y=points[2].Y; + line.End.X=points[2].X; + this.LinePoint.push(line); + + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[2].Y; + line.Start.X=points[2].X; + line.End.Y=points[3].Y; + line.End.X=points[3].X; + this.LinePoint.push(line); + + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[3].Y; + line.Start.X=points[3].X; + line.End.Y=points[4].Y; + line.End.X=points[4].X; + this.LinePoint.push(line); + } + } +} + +//头肩 头肩顶(Head and shoulders top)以及头肩底(Head and shoulders bottom)两种形态 +//支持横屏 +function ChartDrawHeadShouldersBT() +{ + this.newMethod=ChartDrawPictureWaveMW; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawHeadShouldersBT'; + + this.CalculateLines_Backup=this.CalculateLines; + this.CalculateLines=function(points) + { + if (!this.Frame) return; + if (points.length<2) return; + + if (this.Status==2) + { + var line={Start:new Point(), End:new Point()}; + line.Start.Y=points[0].Y; + line.Start.X=points[0].X; + line.End.Y=points[1].Y; + line.End.X=points[1].X; + this.LinePoint.push(line); + + var xMove=points[1].X-points[0].X; + var yMove=points[1].Y-points[0].Y; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=points[1].Y; + line.Start.X=points[1].X; + + line.End.Y=points[1].Y+yMove; + line.End.X=points[0].X-xMove; + } + else + { + line.Start.Y=points[1].Y; + line.Start.X=points[1].X; + line.End.Y=points[0].Y-yMove; + line.End.X=points[1].X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[2]=newPt; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=ptStart.Y+yMove; + line.End.X=points[1].X; + } + else + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=points[1].Y; + line.End.X=ptStart.X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[3]=newPt; + line={Start:new Point(), End:new Point()}; + if (this.IsHScreen) + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=ptStart.Y+yMove; + line.End.X=points[0].X; + } + else + { + line.Start.Y=ptStart.Y; + line.Start.X=ptStart.X; + line.End.Y=points[0].Y; + line.End.X=ptStart.X+xMove; + } + this.LinePoint.push(line); + + var ptStart=line.End; + var newPt=new Point(); + newPt.X=ptStart.X; + newPt.Y=ptStart.Y; + this.Point[4]=newPt; + + this.PointCount=this.Point.length; + } + else + { + return this.CalculateLines_Backup(points); + } + + } +} + +//平行线 +function ChartDrawPictureParallelLines() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureParallelLines'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.PointCount=3; + this.LastPoint; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + var points=drawPoint.slice(0); + this.CalculateLines(points); + + this.ClipFrame(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + + this.DrawArea(); + this.DrawPoint(points); //画点 + this.Canvas.restore(); + } + + this.SetLastPoint=function(obj) + { + this.LastPoint={X:obj.X,Y:obj.Y}; + } + + this.DrawArea=function() + { + if (this.LinePoint.length!=2) return; + + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y); + this.Canvas.lineTo(this.LinePoint[0].End.X,this.LinePoint[0].End.Y); + this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y); + this.Canvas.lineTo(this.LinePoint[1].Start.X,this.LinePoint[1].Start.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.CalculateLines=function(points) + { + if (this.PointStatus==2 && this.LastPoint) + { + var pt=new Point(); + pt.X=this.LastPoint.X; + pt.Y=this.LastPoint.Y; + points[2]=pt; + } + + if (points.length==2) + { + var linePoint=this.CalculateExtendLinePoint(points[0],points[1]); + this.LinePoint.push(linePoint); + } + else if (points.length==3) + { + var linePoint=this.CalculateExtendLinePoint(points[0],points[1]); + this.LinePoint.push(linePoint); + + //计算平行线 + var xMove=points[2].X-points[1].X; + var yMove=points[2].Y-points[1].Y; + + var ptStart=new Point(); + var ptEnd=new Point(); + ptStart.X=points[0].X+xMove; + ptStart.Y=points[0].Y+yMove; + ptEnd.X=points[1].X+xMove; + ptEnd.Y=points[1].Y+yMove; + linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd); + this.LinePoint.push(linePoint); + } + } +} + +//平行射线 +function ChartDrawParallelRaysLines() +{ + this.newMethod=ChartDrawPictureParallelLines; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawParallelRaysLines'; + + this.DrawArea=function() + { + } + + this.CalculateLines=function(points) + { + if (this.PointStatus==2 && this.LastPoint) + { + var pt=new Point(); + pt.X=this.LastPoint.X; + pt.Y=this.LastPoint.Y; + points[2]=pt; + } + + if (points.length==2) + { + var endPoint=this.CalculateExtendLineEndPoint([points[0],points[1]]); + this.LinePoint.push({ Start:points[0], End:endPoint }); + } + else if (points.length==3) + { + var endPoint=this.CalculateExtendLineEndPoint([points[0],points[1]]); + this.LinePoint.push({ Start:points[0], End:endPoint }); + + //计算平行线 + var xMove=points[2].X-points[1].X; + var yMove=points[2].Y-points[1].Y; + + var ptStart=new Point(); + var ptEnd=new Point(); + ptStart.X=points[0].X+xMove; + ptStart.Y=points[0].Y+yMove; + ptEnd.X=points[1].X+xMove; + ptEnd.Y=points[1].Y+yMove; + endPoint=this.CalculateExtendLineEndPoint([ptStart,ptEnd]); + this.LinePoint.push({ Start:points[2], End:endPoint }); + } + } +} + +//价格通道线 +function ChartDrawPicturePriceChannel() +{ + this.newMethod=ChartDrawPictureParallelLines; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPicturePriceChannel'; + + this.CalculateLines=function(points) + { + if (this.PointStatus==2 && this.LastPoint) + { + var pt=new Point(); + pt.X=this.LastPoint.X; + pt.Y=this.LastPoint.Y; + points[2]=pt; + } + + if (points.length==2) + { + var linePoint=this.CalculateExtendLinePoint(points[0],points[1]); + this.LinePoint.push(linePoint); + } + else if (points.length==3) + { + var linePoint=this.CalculateExtendLinePoint(points[0],points[1]); + this.LinePoint.push(linePoint); + + //计算平行线 + var xMove=points[2].X-points[1].X; + var yMove=points[2].Y-points[1].Y; + + var ptStart=new Point(); + var ptEnd=new Point(); + ptStart.X=points[0].X+xMove; + ptStart.Y=points[0].Y+yMove; + ptEnd.X=points[1].X+xMove; + ptEnd.Y=points[1].Y+yMove; + linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd); + this.LinePoint.push(linePoint); + + var ptStart=new Point(); + var ptEnd=new Point(); + ptStart.X=points[0].X-xMove; + ptStart.Y=points[0].Y-yMove; + ptEnd.X=points[1].X-xMove; + ptEnd.Y=points[1].Y-yMove; + linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd); + this.LinePoint.push(linePoint); + } + } + + this.DrawArea=function() + { + if (this.LinePoint.length!=3) return; + + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.LinePoint[1].Start.X,this.LinePoint[1].Start.Y); + this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y); + this.Canvas.lineTo(this.LinePoint[2].End.X,this.LinePoint[2].End.Y); + this.Canvas.lineTo(this.LinePoint[2].Start.X,this.LinePoint[2].Start.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } +} + +//平行通道 +function ChartDrawPictureParallelChannel() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureParallelChannel'; + this.ChannelWidth=50; + this.AreaColor='rgba(25,25,25,0.4)'; + this.LinePoint=[]; + + //计算需要画的点的坐标 + this.CalculateDrawPoint=function() + { + if (this.Status<2) return null; + if(!this.Point.length || !this.Frame) return null; + var data=this.Frame.Data; + if (!data) return null; + + var drawPoint=[]; + if (this.Status==10) //完成 + { + for(var i=0; i<2; ++i) + { + var item=this.Value[i]; + var pt=new Point(); + pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset); + pt.Y=this.Frame.GetYFromData(item.YValue); + drawPoint.push(pt); + } + } + else + { + for(var i=0; i=2) + { + var linePoint={Start:new Point(), End:new Point() }; + linePoint.Start.X=drawPoint[0].X; + linePoint.Start.Y=drawPoint[0].Y; + linePoint.End.X=drawPoint[1].X; + linePoint.End.Y=drawPoint[1].Y; + this.LinePoint.push(linePoint); + + if (drawPoint.length==3 || this.Status==10) + { + var x=linePoint.End.X-linePoint.Start.X; + var y=linePoint.End.Y-linePoint.Start.Y; + var angle=Math.atan(Math.abs(x/y)); + var yMove=this.ChannelWidth/Math.sin(angle); + + //JSConsole.Chart.Log('[ChartDrawPictureParallelChannel::CalculateDrawPoint]',xMove); + + linePoint={Start:new Point(), End:new Point() }; + linePoint.Start.X=drawPoint[0].X; + linePoint.Start.Y=drawPoint[0].Y-yMove; + linePoint.End.X=drawPoint[1].X; + linePoint.End.Y=drawPoint[1].Y-yMove; + this.LinePoint.push(linePoint); + + var ptCenter=new Point(); + ptCenter.X=linePoint.Start.X+(linePoint.End.X-linePoint.Start.X)/2; + ptCenter.Y=linePoint.Start.Y+(linePoint.End.Y-linePoint.Start.Y)/2; + drawPoint[3]=ptCenter; + + this.Point[2]=ptCenter; + var xValue=parseInt(this.Frame.GetXData(ptCenter.X))+data.DataOffset; + var yValue=this.Frame.GetYData(ptCenter.Y); + this.Value[2]={XValue:xValue,YValue:yValue}; + this.PointCount=this.Point.length; //完成以后是3个点 + } + } + + return drawPoint; + } + + this.DrawLine=function(ptStart,ptEnd) + { + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + } + + this.DrawArea=function(pt,pt2,pt3,pt4) + { + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(pt.X,pt.Y); + this.Canvas.lineTo(pt2.X,pt2.Y); + this.Canvas.lineTo(pt3.X,pt3.Y); + this.Canvas.lineTo(pt4.X,pt4.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint(); + if (!drawPoint) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + this.ClipFrame(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + + if (this.LinePoint.length==2) + { + this.DrawArea(this.LinePoint[0].Start,this.LinePoint[0].End,this.LinePoint[1].End,this.LinePoint[1].Start); + } + + this.Canvas.restore(); + + this.DrawPoint(drawPoint); //画点 + } + + //xStep,yStep 移动的偏移量 + this.Move=function(xStep,yStep) + { + if (this.Status!=20) return fasle; + if (!this.Frame) return false; + var data=this.Frame.Data; + if (!data) return false; + + if (this.MovePointIndex==100) //整体移动 + { + for(var i in this.Point) + { + this.Point[i].X+=xStep; + this.Point[i].Y+=yStep; + } + } + else if (this.MovePointIndex==0 || this.MovePointIndex==1) + { + if (this.MovePointIndex=this.Point.length) continue; + pt.X=this.Point[i].X; + pt.Y=this.Point[i].Y; + } + + this.Canvas.beginPath(); + this.Canvas.arc(pt.X,pt.Y,5,0,360); + if (this.Canvas.isPointInPath(x,y)) return i; + } + + //是否在线段上 + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + var ptStart=item.Start; + var ptEnd=item.End; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y+5); + this.Canvas.lineTo(ptStart.X,ptStart.Y-5); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y-5); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y+5); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x,y)) + return 100; + } + + return -1; + } +} + +//文本 +function ChartDrawPictureText() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureText'; + this.Text='文本'; + this.PointCount=1; + this.FontOption={ Family:'微软雅黑', Size:20, Weight:null, Style:null }; //Weight(bold 粗体), Style(italic) + //矢量图片 + //this.Text="\ue606"; + //this.FontOption={ Family:'iconfont', Size:20, Weight:null, Style:null }; //Weight(bold 粗体), Style(italic) + this.TextRect=null; //文字区域 + this.IsInitialized=false; //是否初始化了 + this.SettingMenu; + this.HQChart; + + this.SetOption=function(option) + { + if (!option) return; + + if (option.LineColor) this.LineColor=option.LineColor; + if (option.Text) this.Text=option.Text; + if (option.FontOption) this.FontOption=option.FontOption; + } + + this.Draw=function(textFont) + { + this.TextRect=null; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint || drawPoint.length!=1) return; + + this.ClipFrame(); + + this.Canvas.fillStyle=this.LineColor; + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="bottom"; + this.Canvas.font=this.GetTextFont(); + this.Canvas.fillText(this.Text,drawPoint[0].X,drawPoint[0].Y); + var textWidth=this.Canvas.measureText(this.Text).width; + + var textHeight=this.FontOption.Size*GetDevicePixelRatio(); + this.TextRect={}; + this.TextRect.Left=drawPoint[0].X-textWidth/2; + this.TextRect.Top=drawPoint[0].Y-textHeight; + this.TextRect.Width=textWidth; + this.TextRect.Height=textHeight + //this.Canvas.strokeRect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height); + this.Canvas.restore(); + + if (this.IsInitialized===false) + { + this.SetTextOption(); + this.IsInitialized=true; + } + } + + //根据设置动态生成字体 + this.GetTextFont=function() + { + const defaultFont=16*GetDevicePixelRatio() +"px 微软雅黑"; + if (!this.FontOption || !this.FontOption.Family || this.FontOption.Size<=0) return defaultFont; + + var font=''; + if (this.FontOption.Color) font+=this.FontOption.Color+' '; + if (this.FontOption.Style) font+=this.FontOption.Style+' '; + if (this.FontOption.Weight) font+=this.FontOption.Weight+' '; + if (this.FontOption.Size>=0) font+=this.FontOption.Size*GetDevicePixelRatio()+'px '; + font+=this.FontOption.Family; + + return font; + } + + this.SetTextOption=function() + { + JSConsole.Chart.Log('[ChartDrawPictureText::SetTextOption]'); + //创建div设置窗口 + if (!this.SettingMenu) this.SettingMenu=new ChartPictureTextSettingMenu(this.Frame.ChartBorder.UIElement.parentNode); + + this.SettingMenu.ChartPicture=this; + this.SettingMenu.HQChart=this.HQChart; + this.SettingMenu.Position={Left:this.TextRect.Left+this.TextRect.Width,Top:this.TextRect.Top}; + this.SettingMenu.DoModal(); + } + + this.IsPointIn=function(x,y) + { + if (!this.Frame || this.Status!=10) return -1; + + var data=this.Frame.Data; + if (!data) return -1; + if (!this.TextRect) return -1; + + this.Canvas.beginPath(); + this.Canvas.rect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height); + if (this.Canvas.isPointInPath(x,y)) return 100; + + return -1; + } +} + +//iconfont 图片 +function ChartDrawPictureIconFont() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureIconFont'; + this.PointCount=1; + //矢量图片 + this.Text="\ue606"; + this.FontOption={ Family:'iconfont', Size:24}; //Weight(bold 粗体), Style(italic) + this.TextRect=null; //文字区域 + this.SettingMenu; + this.Angle=0; //旋转角度 + + + this.SetOption=function(option) + { + if (!option) return; + + if (option.LineColor) this.LineColor=option.LineColor; + if (option.FontOption && option.FontOption.Size>0) this.FontOption.Size=option.FontOption.Size; + if (IFrameSplitOperator.IsNumber(option.Angle)) this.Angle=option.Angle; + } + + this.Draw=function() + { + this.TextRect=null; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint || drawPoint.length!=1) return; + var font=this.GetTextFont(); + if (!font) return; + + this.ClipFrame(); + + var isHScreen=this.Frame.IsHScreen; + var pixel=GetDevicePixelRatio(); + + this.Canvas.fillStyle=this.LineColor; + this.Canvas.textAlign="center"; + this.Canvas.textBaseline="middle"; + this.Canvas.font=font; + + if (isHScreen) + { + this.Canvas.translate(drawPoint[0].X, drawPoint[0].Y); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.Text,0,0); + } + else + { + if (this.Angle!=0) + { + this.Canvas.translate(drawPoint[0].X, drawPoint[0].Y); + this.Canvas.rotate(this.Angle*(Math.PI / 180)); + this.Canvas.fillText(this.Text,0,0); + } + else + { + this.Canvas.fillText(this.Text,drawPoint[0].X,drawPoint[0].Y); + } + } + + var textWidth=this.Canvas.measureText(this.Text).width; + this.TextRect={}; + this.TextRect.Left=drawPoint[0].X-textWidth/2; + this.TextRect.Top=drawPoint[0].Y-this.FontOption.Size*pixel; + this.TextRect.Width=textWidth; + this.TextRect.Height=this.FontOption.Size*pixel; + + //this.Canvas.strokeRect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height); + this.Canvas.restore(); + } + + //根据设置动态生成字体 + this.GetTextFont=function() + { + if (!this.FontOption || !this.FontOption.Family || this.FontOption.Size<=0) return null; + + var font=''; + if (this.FontOption.Size>=0) font+=this.FontOption.Size*GetDevicePixelRatio()+'px '; + font+=this.FontOption.Family; + + return font; + } + + this.IsPointIn=function(x,y) + { + if (!this.Frame || this.Status!=10) return -1; + + var data=this.Frame.Data; + if (!data) return -1; + if (!this.TextRect) return -1; + + var offset=0; + if (this.Option && this.Option.Zoom>=1) + { + offset=this.Option.Zoom*GetDevicePixelRatio(); + } + + this.Canvas.beginPath(); + this.Canvas.rect(this.TextRect.Left-offset,this.TextRect.Top-offset,this.TextRect.Width+offset*2,this.TextRect.Height+offset*2); + if (this.Canvas.isPointInPath(x,y)) return 100; + + return -1; + } +} + +//江恩角度线(Gann Fan),亦又称作甘氏线的 +function ChartDrawPictureGannFan() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureGannFan'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.LinePoint=[]; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + this.ClipFrame(); + var quadrant=this.GetQuadrant(drawPoint[0],drawPoint[1]); + + this.SetLineWidth(); + if (quadrant===1 || quadrant===4) + { + this.CalculateLines(drawPoint[0],drawPoint[1],quadrant); + this.DrawArea(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End,item.IsDottedLine); + } + + for(var i in this.LinePoint) + { + var item =this.LinePoint[i]; + if (item.Text && item.PtEnd) this.DrawTitle(item.PtEnd,item.Text); + } + } + else + { + this.DrawLine(drawPoint[0],drawPoint[1],false); + } + this.RestoreLineWidth(); + + this.Canvas.restore(); + this.DrawPoint(drawPoint); //画点 + } + + //获取在第几象限 + this.GetQuadrant=function(ptStart,ptEnd) + { + if (ptStart.XptEnd.Y) return 1; + else if (ptStart.XptEnd.Y) return 2; + else if (ptStart.X < ptEnd.X && ptStart.Y< ptEnd.Y) return 4; + else return 3; + } + + //isDotline 是否是虚线 + this.DrawLine=function(ptStart,ptEnd,isDottedline) + { + if (isDottedline) this.Canvas.setLineDash([5,10]); + + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + + if (isDottedline) this.Canvas.setLineDash([]); + } + + this.DrawTitle=function(pt,text) + { + this.Canvas.fillStyle=this.LineColor; + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="bottom"; + this.Canvas.font=this.Font; + this.Canvas.fillText(text,pt.X,pt.Y); + } + + this.DrawArea=function() + { + var lineStart=null,lineEnd=null; + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + if (item.Text=='1:8') lineStart=this.LinePoint[i]; + else if (item.Text=='8:1') lineEnd=this.LinePoint[i]; + } + + if (!lineStart || !lineEnd) return; + + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(lineStart.End.X,lineStart.End.Y); + this.Canvas.lineTo(lineStart.Start.X,lineStart.Start.Y); + this.Canvas.lineTo(lineEnd.End.X,lineEnd.End.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + //计算线段 + this.CalculateLines=function(ptStart,ptEnd,quadrant) + { + if (!this.Frame) return false; + var top=this.Frame.ChartBorder.GetTopEx(); + var right=this.Frame.ChartBorder.GetRight(); + var bottom=this.Frame.ChartBorder.GetBottom(); + + const SPLIT_LINE_VALUE=[0.5, 1.0/3, 0.25, 0.125, 2.0/3]; + const SPLIT_LINE_X_TITLE=["1:2","1:3","1:4","1:8","2:3"]; + const SPLIT_LINE_Y_TITLE=["2:1","3:1","4:1","8:1","3:2"]; + var ptLineStart=new Point(); + var ptLineEnd=new Point(); + ptLineStart.X=ptStart.X; + ptLineStart.Y=ptStart.Y; + ptLineEnd.X=ptEnd.X; + ptLineEnd.Y=ptEnd.Y; + var lineWidth=Math.abs(ptStart.X-ptEnd.X); + var lineHeight=Math.abs(ptStart.Y-ptEnd.Y); + if (quadrant===1) + { + /* + var line={Start:ptLineStart, End:new Point(), IsDottedLine:false}; + line.End.X=ptStart.X; + line.End.Y=top; + this.LinePoint.push(line); + + line={Start:ptLineStart, End:new Point(), IsDottedLine:false}; + line.End.X=right; + line.End.Y=ptStart.Y; + this.LinePoint.push(line); + */ + + var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd); + var line={Start:ptLineStart, End:extendLine.Start, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'}; + this.LinePoint.push(line); + + for(var i in SPLIT_LINE_VALUE) + { + if (lineWidth>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(),Text:SPLIT_LINE_X_TITLE[i]}; + line.PtEnd.Y=ptEnd.Y; + line.PtEnd.X=ptStart.X+lineWidth*SPLIT_LINE_VALUE[i]; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.Start; + this.LinePoint.push(line); + } + if (lineHeight>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]}; + line.PtEnd.Y=ptStart.Y-lineHeight*SPLIT_LINE_VALUE[i]; + line.PtEnd.X=ptEnd.X; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.Start; + this.LinePoint.push(line); + } + } + + } + else if (quadrant==4) + { + /* + var line={Start:ptLineStart, End:new Point(), IsDottedLine:false}; + line.End.X=ptStart.X; + line.End.Y=bottom; + this.LinePoint.push(line); + + line={Start:ptLineStart, End:new Point(), IsDottedLine:false}; + line.End.X=right; + line.End.Y=ptStart.Y; + this.LinePoint.push(line); + */ + + var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd); + var line={Start:ptLineStart, End:extendLine.End, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'}; + this.LinePoint.push(line); + + for(var i in SPLIT_LINE_VALUE) + { + if (lineWidth>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(),Text:SPLIT_LINE_X_TITLE[i]}; + line.PtEnd.Y=ptEnd.Y; + line.PtEnd.X=ptStart.X+lineWidth*SPLIT_LINE_VALUE[i]; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.End; + this.LinePoint.push(line); + } + if (lineHeight>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]}; + line.PtEnd.Y=ptStart.Y+lineHeight*SPLIT_LINE_VALUE[i]; + line.PtEnd.X=ptEnd.X; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.End; + this.LinePoint.push(line); + } + } + } + else return false; + + return true; + } +} + +//阻速线 (高 3等份) +function ChartDrawPictureResistanceLine() +{ + this.newMethod=ChartDrawPictureGannFan; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureResistanceLine'; + + //计算线段 + this.CalculateLines=function(ptStart,ptEnd,quadrant) + { + if (!this.Frame) return false; + var top=this.Frame.ChartBorder.GetTopEx(); + var right=this.Frame.ChartBorder.GetRight(); + var bottom=this.Frame.ChartBorder.GetBottom(); + + const SPLIT_LINE_VALUE=[1.0/3, 2.0/3]; + const SPLIT_LINE_Y_TITLE=["3:1","3:2"]; + var ptLineStart=new Point(); + var ptLineEnd=new Point(); + ptLineStart.X=ptStart.X; + ptLineStart.Y=ptStart.Y; + ptLineEnd.X=ptEnd.X; + ptLineEnd.Y=ptEnd.Y; + var lineWidth=Math.abs(ptStart.X-ptEnd.X); + var lineHeight=Math.abs(ptStart.Y-ptEnd.Y); + if (quadrant===1) + { + var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd); + var line={Start:ptLineStart, End:extendLine.Start, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'}; + this.LinePoint.push(line); + + for(var i in SPLIT_LINE_VALUE) + { + if (lineHeight>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]}; + line.PtEnd.Y=ptStart.Y-lineHeight*SPLIT_LINE_VALUE[i]; + line.PtEnd.X=ptEnd.X; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.Start; + this.LinePoint.push(line); + } + } + + } + else if (quadrant==4) + { + var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd); + var line={Start:ptLineStart, End:extendLine.End, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'}; + this.LinePoint.push(line); + + for(var i in SPLIT_LINE_VALUE) + { + if (lineHeight>5) + { + line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]}; + line.PtEnd.Y=ptStart.Y+lineHeight*SPLIT_LINE_VALUE[i]; + line.PtEnd.X=ptEnd.X; + var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd); + line.End=extendLine.End; + this.LinePoint.push(line); + } + } + } + else return false; + + return true; + } + + this.DrawArea=function() + { + var lineStart=null,lineEnd=null; + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + if (item.Text=='1:1') lineStart=this.LinePoint[i]; + else if (item.Text=='3:1') lineEnd=this.LinePoint[i]; + } + + if (!lineStart || !lineEnd) return; + + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(lineStart.End.X,lineStart.End.Y); + this.Canvas.lineTo(lineStart.Start.X,lineStart.Start.Y); + this.Canvas.lineTo(lineEnd.End.X,lineEnd.End.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } +} + +//黄金分割线 +function ChartDrawPictureGoldenSection() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureGoldenSectionLine'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.Font=14*GetDevicePixelRatio() +"px 微软雅黑"; + + this.GetSectionData=function() + { + const GOLDEN_SECTION_DATA= [0,0.236,0.382,0.5,0.618,0.80,1,1.236,1.382,1.5,1.618,1.8,2]; + return GOLDEN_SECTION_DATA; + } + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint(); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + + this.CalculateLines(drawPoint[0],drawPoint[1]); + this.ClipFrame(); + + this.SetLineWidth(); + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End,item.IsDottedLine); + } + this.RestoreLineWidth(); + + for(var i in this.LinePoint) + { + var item =this.LinePoint[i]; + if (item.Text) this.DrawTitle(item.Start,item.Text); + } + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + + } + + this.CalculateHSCreenLines=function(ptStart,ptEnd) + { + var sectionData=this.GetSectionData(); + var left=this.Frame.ChartBorder.GetTop(); + var right=this.Frame.ChartBorder.GetBottom(); + var lineHeight=ptStart.X-ptEnd.X; + + for(var i=0;i=0) return value; + + if (this.CircleData && this.CircleData.R>8) + { + var triangleX=this.CircleData.X-x; + var triangleY=this.CircleData.Y-y; + var r=Math.sqrt(triangleX*triangleX+triangleY*triangleY); //计算直径 + if (rthis.CircleData.R-8) return 100; + } + + return -1; + } +} + +//四边形 +function ChartDrawPictureQuadrangle() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureQuadrangle'; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.PointCount=3; + this.LastPoint; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3); + var points=drawPoint.slice(0); + this.CalculateLines(points); + + this.ClipFrame(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + + this.DrawArea(); + this.DrawPoint(points); //画点 + this.Canvas.restore(); + } + + this.SetLastPoint=function(obj) + { + this.LastPoint={X:obj.X,Y:obj.Y}; + } + + this.DrawArea=function() + { + if (this.LinePoint.length!=4) return; + + this.Canvas.fillStyle=this.AreaColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y); + this.Canvas.lineTo(this.LinePoint[0].End.X,this.LinePoint[0].End.Y); + this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y); + this.Canvas.lineTo(this.LinePoint[2].End.X,this.LinePoint[2].End.Y); + this.Canvas.closePath(); + this.Canvas.fill(); + } + + this.CalculateLines=function(points) + { + if (this.PointStatus==2 && this.LastPoint) + { + var pt=new Point(); + pt.X=this.LastPoint.X; + pt.Y=this.LastPoint.Y; + points[2]=pt; + } + + if (points.length==2) + { + var linePoint=this.CreateLineData(points[0],points[1]); + this.LinePoint.push(linePoint); + } + else if (points.length==3) + { + var linePoint=this.CreateLineData(points[0],points[1]); + this.LinePoint.push(linePoint); + + var linePoint=this.CreateLineData(points[1],points[2]); + this.LinePoint.push(linePoint); + + //计算平行线 + var xMove=points[2].X-points[1].X; + var yMove=points[2].Y-points[1].Y; + + var pt4=new Point(); //第4个点的坐标 + pt4.X=points[0].X+xMove; + pt4.Y=points[0].Y+yMove; + + var linePoint=this.CreateLineData(points[2],pt4); + this.LinePoint.push(linePoint); + + var linePoint=this.CreateLineData(pt4,points[0]); + this.LinePoint.push(linePoint); + } + } +} + +//斐波那契周期线 +function ChartDrawPictureFibonacci() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawPictureFibonacci'; + this.PointCount=1; + this.Font=14*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint(); + if (!drawPoint) return; + + this.CalculateLines(); + if (this.LinePoint.length<=0) return; + + this.ClipFrame(); + + this.SetLineWidth(); + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + this.DrawTitle(item.Start,item.Title); + } + this.RestoreLineWidth(); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.DrawTitle=function(pt,text) + { + this.Canvas.fillStyle=this.LineColor; + this.Canvas.textAlign="left"; + this.Canvas.textBaseline="top"; + this.Canvas.font=this.Font; + if (this.Frame.IsHScreen) + { + this.Canvas.save(); + this.Canvas.translate(pt.X,pt.Y); + this.Canvas.rotate(90 * Math.PI / 180); + + this.Canvas.fillText(text,2,10); + this.Canvas.restore(); + } + else + { + this.Canvas.fillText(text,pt.X+2,pt.Y+10); + } + } + + this.CalculateHSCreenLines=function() + { + var data=this.Frame.Data; + if (!data) return; + + var xStart=null; + if (this.Status==10) + { + if (this.Value.length!=1) return; + xStart=this.Value[0].XValue; + } + else + { + if (this.Point.length!=1) return; + xStart=parseInt(this.Frame.GetXData(this.Point[0].Y))+data.DataOffset; + } + + var top=this.Frame.ChartBorder.GetRightEx(); + var bottom=this.Frame.ChartBorder.GetLeftEx(); + var showCount=this.Frame.XPointCount; + const LINE_DATA=[1,2,3,5,8,13,21,34,55,89,144,233]; + for(var i=0;i=showCount) continue; + + var x=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + var line={Start:new Point(), End:new Point(), Title:LINE_DATA[i]}; + line.Start.X=top; + line.Start.Y=x; + line.End.X=bottom; + line.End.Y=x; + this.LinePoint.push(line); + } + } + + this.CalculateLines=function() + { + if (this.Status<2) return; + if (!this.Frame) return; + var data=this.Frame.Data; + if (!data) return; + + if (this.Frame.IsHScreen) + { + this.CalculateHSCreenLines(); + return; + } + + var xStart=null; + if (this.Status==10) + { + if (this.Value.length!=1) return; + xStart=this.Value[0].XValue; + } + else + { + if (this.Point.length!=1) return; + xStart=parseInt(this.Frame.GetXData(this.Point[0].X))+data.DataOffset; + } + + var top=this.Frame.ChartBorder.GetTopEx(); + var bottom=this.Frame.ChartBorder.GetBottom(); + var showCount=this.Frame.XPointCount; + const LINE_DATA=[1,2,3,5,8,13,21,34,55,89,144,233]; + for(var i=0;i=showCount) continue; + + var x=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + var line={Start:new Point(), End:new Point(), Title:LINE_DATA[i]}; + line.Start.Y=top; + line.Start.X=x; + line.End.Y=bottom; + line.End.X=x; + this.LinePoint.push(line); + } + + } +} + +//线性回归 +function ChartDrawLinearRegression(option) +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawLinearRegression'; + this.PointCount=2; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.ChartBorder; + this.Lines=[]; //回归线 { XValue:, YValue: } + this.IsShowMaxMinLine=false; //是否显示最高 最低价通道 + this.IsShowExtendLine=false; //是否显示延长线 + this.ExtendLineDash=[5,5]; //延长线虚线 + this.MaxPoint; + this.MinPoint; + + if (option) + { + if (option.IsShowMaxMinLine==true) this.IsShowMaxMinLine=true; + if (option.IsShowExtendLine==true) this.IsShowExtendLine=true; + } + + //导入 + this.ImportStorageData=function(storageData) + { + if (storageData.IsShowMaxMinLine==true) this.IsShowMaxMinLine=true; + if (storageData.IsShowExtendLine==true) this.IsShowExtendLine=true; + if (storageData.Lines) this.Lines=storageData.Lines; + if (storageData.MaxPoint && storageData.MinPoint) + { + this.MaxPoint=storageData.MaxPoint; + this.MinPoint=storageData.MinPoint; + } + } + + //导出 + this.Super_ExportStorageData=this.ExportStorageData; + this.ExportStorageData=function() + { + var storageData; + if (this.Super_ExportStorageData) + { + storageData=this.Super_ExportStorageData(); + storageData.IsShowMaxMinLine=this.IsShowMaxMinLine; + storageData.IsShowExtendLine=this.IsShowExtendLine; + storageData.Lines=this.Lines; + if (this.IsShowMaxMinLine) + { + storageData.MaxPoint=this.MaxPoint; + storageData.MinPoint=this.MinPoint; + } + } + + return storageData; + } + + + this.PointToValue_Backup=this.PointToValue; + + this.PointToValue=function() + { + //拖拽完成 把点移动到线段头尾 + this.Point[0]={X:this.Lines[0].X, Y:this.Lines[0].Y}; + this.Point[1]={X:this.Lines[this.Lines.length-1].X, Y:this.Lines[this.Lines.length-1].Y}; + this.PointToValue_Backup(); + } + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint( { IsCheckX:true, IsCheckY:true} ); + if (!drawPoint || drawPoint.length!=2) + { + if (this.Status==10) + { + this.ClipFrame(); + this.ChartBorder=this.Frame.ChartBorder; + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.DrawLinearLines(); + this.RestoreLineWidth(); + this.Canvas.restore(); + } + return; + } + + this.ClipFrame(); + this.ChartBorder=this.Frame.ChartBorder; + + //0=开始画 1=完成第1个点 2=完成第2个点 3=完成第3个点 10=完成 20=移动) + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + if (this.Status==10) + { + this.DrawLinearLines(); + } + else + { + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + + var kPoint=this.PointToKLine([ptStart,ptEnd]); + //JSConsole.Chart.Log('[ChartDrawLinearRegression::Draw] kPoint', kPoint); + var linear=this.Calculate(kPoint); + //JSConsole.Chart.Log('[ChartDrawLinearRegression::Draw] linear', linear); + + this.Lines=linear.Points; + this.MaxPoint=linear.Max; + this.MinPoint=linear.Min; + + this.DrawLinearLines(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,top); + this.Canvas.lineTo(ptStart.X,bottom); + + this.Canvas.moveTo(ptEnd.X,top); + this.Canvas.lineTo(ptEnd.X,bottom); + this.Canvas.stroke(); + + } + + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.Calculate=function(kPoint) + { + var startPoint=kPoint[0]; + var endPoint=kPoint[1]; + if (startPoint.XValue>endPoint.XValue) + { + startPoint=kPoint[1]; + endPoint=kPoint[0]; + } + + var num=(endPoint.XValue-startPoint.XValue)+1; + var data=this.Frame.Data.Data; + var i=endPoint.XValue; + var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope; + var i, j,x, k; + for(j = 0, x=num; j < num && j <= i; ++j, --x) + { + Ex += x; + Ey += data[i - j].Close; + } + Ex /= num; + Ey /= num; + for(j = 0, x=num; j < num && j <= i; ++j,--x) + { + Sxy += (x-Ex)*(data[i-j].Close-Ey); + Sxx += (x-Ex)*(x-Ex); + } + Slope = Sxy / Sxx; //斜率 + Const = Ey - Ex*Slope; + //value=Slope * num + Const; //Y轴值 线性回归公式 + + var points=[]; + var max=null, min=null; + for(j = 0, k=num; j < num && j <= i; ++j,--k) + { + var xIndex=endPoint.XIndex-j; + var xValue=endPoint.XValue-j; + var x=this.Frame.GetXFromIndex(xIndex); + + var yValue=Slope * k + Const; + var y=this.Frame.GetYFromData(yValue,false); + + var item={ X:x, Y:y, XValue:xValue, YValue:yValue }; + points.push(item); + + if (max==null || max.Highdata[xValue].Low) + { + min={X:x,Y:y,XValue:xValue, YValue:yValue, Low:data[xValue].Low }; + } + } + + return { Points:points, Slope:Slope, Const:Const , Max:max, Min:min }; + } + + this.DrawLinearLines=function() + { + if (!this.Frame) return null; + var data=this.Frame.Data; + if (!data) return null; + + var showCount=this.Frame.XPointCount; + var dataOffset=data.DataOffset; + var isHScreen=this.Frame.IsHScreen; + var drawLines=[]; + for(var i in this.Lines) + { + var item=this.Lines[i]; + var dataIndex=item.XValue-dataOffset; + if (dataIndex<0 || dataIndex>=showCount) continue; + var pt={}; + if (isHScreen) //横屏X,Y对调 + { + + } + else + { + pt.X=this.Frame.GetXFromIndex(dataIndex,false); + pt.Y=this.Frame.GetYFromData(item.YValue,false); + pt.YValue=item.YValue; + } + + drawLines.push(pt); + } + + if (drawLines.length>1) + { + for(var i in drawLines) + { + var item=drawLines[i]; + + if (i==0) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X,item.Y); + } + else + { + this.Canvas.lineTo(item.X,item.Y); + if (i==drawLines.length-1) this.Canvas.stroke(); + } + } + + this.DrawExtendLine(drawLines); + } + + //最大 最小通道 + if (this.IsShowMaxMinLine && drawLines.length>1 && this.MaxPoint && this.MinPoint) + { + var highOffset=this.MaxPoint.High-this.MaxPoint.YValue; + for(var i in drawLines) + { + var item=drawLines[i]; + item.Y=this.Frame.GetYFromData((item.YValue+highOffset),false); + + if (i==0) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X,item.Y); + } + else + { + this.Canvas.lineTo(item.X,item.Y); + if (i==drawLines.length-1) this.Canvas.stroke(); + } + } + this.DrawExtendLine(drawLines); + + var lowOffset=this.MinPoint.Low-this.MinPoint.YValue; + for(var i in drawLines) + { + var item=drawLines[i]; + item.Y=this.Frame.GetYFromData((item.YValue+lowOffset),false); + + if (i==0) + { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X,item.Y); + } + else + { + this.Canvas.lineTo(item.X,item.Y); + if (i==drawLines.length-1) this.Canvas.stroke(); + } + } + this.DrawExtendLine(drawLines); + } + } + + //画延长线 + this.DrawExtendLine=function(lines) + { + if (!this.IsShowExtendLine) return; + + var ptStart=lines[0]; + var ptEnd=lines[lines.length-1]; + var aryPoint; + if (ptEnd.X>ptStart.X) aryPoint=[ptStart, ptEnd]; + else aryPoint=[ptEnd, ptStart]; + + var ptExtend=this.CalculateExtendLineEndPoint(aryPoint); + this.Canvas.beginPath(); + this.Canvas.moveTo(ptEnd.X,ptEnd.Y); + this.Canvas.lineTo(ptExtend.X,ptExtend.Y); + this.Canvas.setLineDash(this.ExtendLineDash); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + +} + +//尺子 +function ChartDrawRuler() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawRuler'; + this.PointCount=2; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.TitleColor=g_JSChartResource.ChartDrawRuler.TitleColor; + this.IsHScreen=false; + + this.Draw=function() + { + this.LinePoint=[]; + var drawPoint=this.CalculateDrawPoint( {IsCheckX:true, IsCheckY:true} ); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + this.IsHScreen=this.Frame.IsHScreen; + + this.ClipFrame(); + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + var kPoint=this.PointToKLine([ptStart,ptEnd]); + //JSConsole.Chart.Log('[ChartDrawLinearRegression::ChartDrawRuler] kPoint', kPoint); + var kDataInfo=this.Calculate(kPoint); + //JSConsole.Chart.Log('[ChartDrawLinearRegression::ChartDrawRuler] kDataInfo', kDataInfo); + + var increase=IFrameSplitOperator.IsNumber(kDataInfo.Increase)? kDataInfo.Increase.toFixed(2)+'%': "--"; + var risefall=IFrameSplitOperator.IsNumber(kDataInfo.Risefall)? kDataInfo.Risefall.toFixed(2): "--"; + var title=`间距:${kDataInfo.Count} 涨跌:${risefall} 涨幅:${increase}`; + //JSConsole.Chart.Log('[ChartDrawLinearRegression::ChartDrawRuler] title', title); + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + + //绘制信息 + this.Canvas.textBaseline='bottom'; + this.Canvas.textAlign='left'; + this.Canvas.font=this.Font; + if (this.TitleColor) + { + this.Canvas.fillStyle=this.TitleColor; + } + else if (IFrameSplitOperator.IsNumber(kDataInfo.Increase)) + { + if (kDataInfo.Increase>0) this.Canvas.fillStyle=g_JSChartResource.UpTextColor; + else if (kDataInfo.Increase<0) this.Canvas.fillStyle=g_JSChartResource.DownTextColor; + else this.Canvas.fillStyle=g_JSChartResource.UnchagneTextColor; + } + else + { + this.Canvas.fillStyle=g_JSChartResource.UnchagneTextColor; + } + + var offset=3*GetDevicePixelRatio(); + var xText=ptStart.X + var yText=ptStart.Y + if (this.IsHScreen) + { + this.Canvas.translate(xText+offset,yText+offset); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(title,0,0); + } + else + { + this.Canvas.fillText(title,xText+offset,yText-offset); + } + + + this.Canvas.restore(); + } + + this.Calculate=function(kPoint) + { + var startPoint=kPoint[0]; + var endPoint=kPoint[1]; + if (startPoint.XValue>endPoint.XValue) + { + startPoint=kPoint[1]; + endPoint=kPoint[0]; + } + + var data=this.Frame.Data.Data; + var open, high, low, yClose, close; + var count=0; + for(var i=startPoint.XValue;i<=endPoint.XValue;++i, ++count) + { + var item=data[i]; + if (count==0) + { + yClose=item.YClose; + open=item.Open; + close=item.Close; + low=item.Low; + high=item.High; + } + else + { + close=item.Close; + if (low>item.Low) low=item.Low; + if (high=0 && indexright) return; + + var bottom=chartBorder.GetBottom(); + var ptEnd={X:ptStart.X, Y:bottom}; + var price=this.Frame.GetYData(ptStart.X, false); + } + else + { + var bottom=chartBorder.GetBottomEx(); + var top=chartBorder.GetTopEx(); + if (ptStart.Ybottom) return; + + var right=chartBorder.GetRight(); + var ptEnd={X:right, Y:ptStart.Y}; + var price=this.Frame.GetYData(ptStart.Y, false); + } + + + + this.ClipFrame(); + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + + this.Canvas.textBaseline='bottom'; + this.Canvas.textAlign='left'; + this.Canvas.fillStyle=this.LineColor; + this.Canvas.font=this.Font; + var offset=2*GetDevicePixelRatio(); + var xText=ptStart.X; + var yText=ptStart.Y; + if (this.IsHScreen) + { + this.Canvas.translate(xText+offset,yText+offset); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(price.toFixed(2),0,0); + } + else + { + this.Canvas.fillText(price.toFixed(2),xText+offset,yText-offset); + } + + this.Canvas.restore(); + } +} + +//画图工具-竖线 支持横屏 +function ChartDrawVerticalLine() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawVerticalLine'; + this.PointCount=1; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.IsHScreen=false; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + if (!this.Frame || !this.Frame.Data) return; + var data=this.Frame.Data; + var drawPoint=this.CalculateDrawPoint( { IsCheckX:true, IsCheckY:true } ); + if (!drawPoint) return; + if (drawPoint.length!=1) return; + + this.IsHScreen=this.Frame.IsHScreen; + var pt=drawPoint[0]; + + var chartBorder=this.Frame.ChartBorder; + if (this.IsHScreen) + { + var xValue=Math.round(this.Frame.GetXData(pt.Y,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var yLine=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + yLine=ToFixedPoint2(this.LineWidth,yLine); + var left=chartBorder.GetLeftEx(); + var right=chartBorder.GetRightEx(); + var ptStart={ X:left, Y:yLine }; + var ptEnd={ X:right, Y:yLine }; + } + else + { + var xValue=Math.round(this.Frame.GetXData(pt.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var xLine=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + xLine=ToFixedPoint2(this.LineWidth,xLine); + var top=chartBorder.GetTopEx(); + var bottom=chartBorder.GetBottomEx(); + var ptStart={ X:xLine, Y:top }; + var ptEnd={ X:xLine, Y:bottom }; + } + + this.ClipFrame(); + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,ptStart.Y); + this.Canvas.lineTo(ptEnd.X,ptEnd.Y); + this.Canvas.stroke(); + this.RestoreLineWidth(); + + var line={Start:ptStart, End:ptEnd}; + this.LinePoint.push(line); + + if (this.Status==10) this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } +} + +//画图工具-波浪尺 +function ChartDrawWaveRuler() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawWaveRuler'; + this.PointCount=3; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.LastPoint; + this.LinePoint; + this.ScaleRuler=g_JSChartResource.ChartDrawWaveRuler.ScaleRuler; + this.RulerWidth=g_JSChartResource.ChartDrawWaveRuler.RulerWidth;; //刻度尺长度 + this.RulerLineWidth=g_JSChartResource.ChartDrawWaveRuler.RulerLineWidth; + this.MaxScaleRuler=g_JSChartResource.ChartDrawWaveRuler.MaxScaleRuler; //尺子最大的高度比 + this.Font=g_JSChartResource.ChartDrawWaveRuler.MaxScaleRuler; + this.IsHScreen=false; + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + this.IsHScreen=this.Frame.IsHScreen; + this.CalculateLines(drawPoint); + + this.ClipFrame(); + this.SetLineWidth(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + + //绘制波浪信息 + if (drawPoint.length==3) this.DrawWaveRuler(drawPoint); + + this.RestoreLineWidth(); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.SetLastPoint=function(obj) + { + this.LastPoint={X:obj.X,Y:obj.Y}; + } + + this.CalculateLines=function(points) + { + if (this.PointStatus==2 && this.LastPoint) + { + var pt=new Point(); + pt.X=this.LastPoint.X; + pt.Y=this.LastPoint.Y; + points[2]=pt; + } + + if (points.length==2) + { + var linePoint= + { + Start:{X:points[0].X,Y:points[0].Y}, + End:{X:points[1].X,Y:points[1].Y} + }; + this.LinePoint.push(linePoint); + } + else if (points.length==3) + { + var linePoint= + { + Start:{X:points[0].X,Y:points[0].Y}, + End:{X:points[1].X,Y:points[1].Y} + }; + this.LinePoint.push(linePoint); + + linePoint= + { + Start:{X:points[1].X,Y:points[1].Y}, + End:{X:points[2].X,Y:points[2].Y} + }; + + this.LinePoint.push(linePoint); + } + } + + this.DrawWaveRuler=function(points) + { + var ptBottom=points[1]; + var ptStart=points[0]; + var ptEnd=points[2]; + + var lineWidth=this.RulerLineWidth*GetDevicePixelRatio(); + this.Canvas.lineWidth=lineWidth; + this.Canvas.textBaseline='middle'; + this.Canvas.textAlign='left'; + this.Canvas.fillStyle=this.LineColor; + this.Canvas.font=this.Font; + var rulerWidth=this.RulerWidth*GetDevicePixelRatio();//刻度线长度 + + if (this.IsHScreen) + { + var rulerHeight=ptStart.X-ptBottom.X; + var ptExtendBottom={ X:ptEnd.X-this.MaxScaleRuler*rulerHeight, Y:ptEnd.Y}; + this.DrawLine(ptEnd,ptExtendBottom); + this.LinePoint.push({Start:ptEnd, End:ptExtendBottom}); + var y=ptEnd.Y-rulerWidth/2, y2=ptEnd.Y+rulerWidth/2; + } + else + { + var rulerHeight=ptStart.Y-ptBottom.Y; + var ptExtendBottom={ X:ToFixedPoint2(lineWidth,ptEnd.X), Y:ptEnd.Y-this.MaxScaleRuler*rulerHeight }; + this.DrawLine({X:ToFixedPoint2(lineWidth,ptEnd.X), Y:ptEnd.Y},ptExtendBottom); + this.LinePoint.push({Start:ptEnd, End:ptExtendBottom}); + var x=ptEnd.X-rulerWidth/2, x2=ptEnd.X+rulerWidth/2; + } + + var textOffset=4*GetDevicePixelRatio(); + for(var i in this.ScaleRuler) + { + var item=this.ScaleRuler[i]; + if (this.IsHScreen) + { + var x=ptEnd.X - item.Value*rulerHeight; + var price=this.Frame.GetYData(x, false); + this.DrawLine({X:x, Y:y}, {X:x, Y:y2}); + var text=`${price.toFixed(2)} ${item.Text? item.Text: item.Value.toFixed(3)}`; + this.Canvas.save(); + this.Canvas.translate(x,ptEnd.Y); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(text,textOffset,0); + this.Canvas.restore(); + } + else + { + var y=ptEnd.Y - item.Value*rulerHeight; + var price=this.Frame.GetYData(y, false); + this.DrawLine({X:x, Y:ToFixedPoint2(lineWidth,y)}, {X:x2, Y:ToFixedPoint2(lineWidth,y)}); + var text=`${price.toFixed(2)} ${item.Text? item.Text: item.Value.toFixed(3)}`; + this.Canvas.fillText(text,x2+textOffset,y); + } + } + } +} + +//画图工具-波浪尺 2个点的 +function ChartDrawWaveRuler2Point() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawWaveRuler2Point'; + this.PointCount=2; + this.Font=14*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.PointRate=[ 510, 517, 511.4]; + this.LinePoint; + this.CPoint; + this.ScaleRuler=g_JSChartResource.ChartDrawWaveRuler2Point.ScaleRuler; + this.RulerWidth=g_JSChartResource.ChartDrawWaveRuler2Point.RulerWidth;; //刻度尺长度 + this.RulerLineWidth=g_JSChartResource.ChartDrawWaveRuler2Point.RulerLineWidth; + this.MaxScaleRuler=g_JSChartResource.ChartDrawWaveRuler2Point.MaxScaleRuler; //尺子最大的高度比 + this.Font=g_JSChartResource.ChartDrawWaveRuler2Point.MaxScaleRuler; + this.IsHScreen=false; + this.Super_SetOption=this.SetOption; //父类函数 + + this.SetOption=function(option) + { + if (this.Super_SetOption) this.Super_SetOption(option); + if (option) + { + if (option.PointRate) this.PointRate=option.PointRate; + } + } + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + this.IsHScreen=this.Frame.IsHScreen; + this.CalculateLines(drawPoint); + + this.ClipFrame(); + this.SetLineWidth(); + + for(var i in this.LinePoint) + { + var item=this.LinePoint[i]; + this.DrawLine(item.Start,item.End); + } + + var points=[drawPoint[0],drawPoint[1],this.CPoint]; + this.DrawWaveRuler(points); + + this.RestoreLineWidth(); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.CalculateLines=function(points) + { + var firstLine= + { + Start:{ X:points[0].X,Y:points[0].Y }, + End:{ X:points[1].X,Y:points[1].Y } + }; + this.LinePoint.push(firstLine); + + var a=points[0].Y; + var b=points[1].Y; + + var width=this.PointRate[1]-this.PointRate[0]; + var offset=this.PointRate[2]-this.PointRate[0]; + + var c=a-((a-b)*offset)/width; + + this.CPoint={ X:points[1].X, Y:c }; + } + + this.DrawWaveRuler=function(points) + { + var ptBottom=points[1]; + var ptStart=points[0]; + var ptEnd=points[2]; + + var lineWidth=this.RulerLineWidth*GetDevicePixelRatio(); + this.Canvas.lineWidth=lineWidth; + this.Canvas.textBaseline='middle'; + this.Canvas.textAlign='left'; + this.Canvas.fillStyle=this.LineColor; + this.Canvas.font=this.Font; + var rulerWidth=this.RulerWidth*GetDevicePixelRatio();//刻度线长度 + + if (this.IsHScreen) + { + var rulerHeight=ptStart.X-ptBottom.X; + var ptExtendBottom={ X:ptEnd.X-this.MaxScaleRuler*rulerHeight, Y:ptEnd.Y}; + this.DrawLine(ptEnd,ptExtendBottom); + this.LinePoint.push({Start:ptEnd, End:ptExtendBottom}); + var y=ptEnd.Y-rulerWidth/2, y2=ptEnd.Y+rulerWidth/2; + } + else + { + var rulerHeight=ptStart.Y-ptBottom.Y; + var ptExtendBottom={ X:ToFixedPoint2(lineWidth,ptEnd.X), Y:ptEnd.Y-this.MaxScaleRuler*rulerHeight }; + this.DrawLine({X:ToFixedPoint2(lineWidth,ptEnd.X), Y:ptEnd.Y},ptExtendBottom); + this.LinePoint.push({Start:ptEnd, End:ptExtendBottom}); + var x=ptEnd.X, x2=ptEnd.X+rulerWidth; + } + + var textOffset=4*GetDevicePixelRatio(); + for(var i in this.ScaleRuler) + { + var item=this.ScaleRuler[i]; + if (this.IsHScreen) + { + var x=ptEnd.X - item.Value*rulerHeight; + var price=this.Frame.GetYData(x, false); + this.DrawLine({X:x, Y:y}, {X:x, Y:y2}); + var text=`${price.toFixed(2)} ${item.Text? item.Text: item.Value.toFixed(3)}`; + this.Canvas.save(); + this.Canvas.translate(x,ptEnd.Y); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(text,textOffset,0); + this.Canvas.restore(); + } + else + { + var y=ptEnd.Y - item.Value*rulerHeight; + var price=this.Frame.GetYData(y, false); + this.DrawLine({X:x, Y:ToFixedPoint2(lineWidth,y)}, {X:x2, Y:ToFixedPoint2(lineWidth,y)}); + var text=`${price.toFixed(2)} ${item.Text? item.Text: item.Value.toFixed(3)}`; + this.Canvas.fillText(text,x2+textOffset,y); + } + } + } +} + +//画图工具-箱型线 支持横屏 +function ChartDrawBox() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawBox'; + this.PointCount=2; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.IsHScreen=false; + this.AvPriceLineDash=[5*GetDevicePixelRatio(),5*GetDevicePixelRatio()]; + this.KLineBorder; + + this.PointToValue_Backup=this.PointToValue; + this.PointToValue=function() + { + //拖拽完成 把点移动到线段头尾 + this.Point[0]={X:this.KLineBorder.Left, Y:this.KLineBorder.Top}; + this.Point[1]={X:this.KLineBorder.Right, Y:this.KLineBorder.Bottom}; + this.PointToValue_Backup(); + } + + this.Draw=function() + { + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint( {IsCheckX:true, IsCheckY:true} ); + if (!drawPoint) return; + if (drawPoint.length!=2) return; + this.IsHScreen=this.Frame.IsHScreen; + + this.ClipFrame(); + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + var kPoint=this.PointToKLine([ptStart,ptEnd]); + JSConsole.Chart.Log('[ChartDrawBox::Draw] kPoint', kPoint); + var kDataInfo=this.Calculate(kPoint); + JSConsole.Chart.Log('[ChartDrawBox::Draw] kDataInfo', kDataInfo); + var klineBorder=this.GetKLineBorder(drawPoint, kDataInfo); + this.KLineBorder=klineBorder; + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + this.Canvas.strokeRect(ToFixedRect(klineBorder.Left), ToFixedRect(klineBorder.Top), ToFixedRect(klineBorder.Right-klineBorder.Left), ToFixedRect(klineBorder.Bottom-klineBorder.Top)); + + //均价 + var avPriceText; + if (kDataInfo.Amount>0 && kDataInfo.Vol>0) + { + var price=kDataInfo.Amount/kDataInfo.Vol; + avPriceText=`均价${price.toFixed(2)}`; + + this.Canvas.beginPath(); + if (this.IsHScreen) + { + var x=this.Frame.GetYFromData(price,false); + this.Canvas.moveTo(ToFixedPoint2(this.Canvas.lineWidth,x),klineBorder.Top); + this.Canvas.lineTo(ToFixedPoint2(this.Canvas.lineWidth,x),klineBorder.Bottom); + } + else + { + var y=this.Frame.GetYFromData(price,false); + this.Canvas.moveTo(klineBorder.Left,ToFixedPoint2(this.Canvas.lineWidth,y)); + this.Canvas.lineTo(klineBorder.Right,ToFixedPoint2(this.Canvas.lineWidth,y)); + } + + this.Canvas.setLineDash(this.AvPriceLineDash); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + this.RestoreLineWidth(); + + this.DrawPoint(drawPoint); //画点 + + //绘制信息 + this.Canvas.textBaseline='top'; + this.Canvas.textAlign='left'; + this.Canvas.font=this.Font; + this.Canvas.fillStyle=this.LineColor; + var textOffset=2*GetDevicePixelRatio(); + var text=`K线数${kDataInfo.Count}`; + if (this.IsHScreen) this.DrawText(text,klineBorder.Right,klineBorder.Top, textOffset, textOffset); + else this.DrawText(text,klineBorder.Left,klineBorder.Top, textOffset, textOffset); + + if (avPriceText) + { + if (this.IsHScreen) this.DrawText(avPriceText,x,klineBorder.Top, textOffset, textOffset); //均价 + else this.DrawText(avPriceText,klineBorder.Left,y, textOffset, textOffset); //均价 + } + + var yClose=IFrameSplitOperator.IsNumber(kDataInfo.YClose)? kDataInfo.YClose:kDataInfo.Open; + var value=(kDataInfo.Close-yClose)/yClose*100; + text=`${value.toFixed(2)}%`; + if (this.IsHScreen) this.DrawText(text,klineBorder.Right,klineBorder.Bottom, textOffset, textOffset); + else this.DrawText(text,klineBorder.Right,klineBorder.Top, textOffset, textOffset); + + value=kDataInfo.Close-yClose; + text=`${value.toFixed(2)}`; + this.Canvas.textBaseline='bottom'; + if (this.IsHScreen) this.DrawText(text,klineBorder.Left,klineBorder.Bottom, textOffset, 0); + else this.DrawText(text,klineBorder.Right,klineBorder.Bottom, textOffset, 0); + + this.Canvas.restore(); + } + + this.Calculate=function(kPoint) + { + var startPoint=kPoint[0]; + var endPoint=kPoint[1]; + if (startPoint.XValue>endPoint.XValue) + { + startPoint=kPoint[1]; + endPoint=kPoint[0]; + } + + var data=this.Frame.Data.Data; + var open, high, low, yClose, close, vol=0, amount=0; + var count=0; + for(var i=startPoint.XValue;i<=endPoint.XValue;++i, ++count) + { + var item=data[i]; + if (count==0) + { + yClose=item.YClose; + open=item.Open; + close=item.Close; + low=item.Low; + high=item.High; + } + else + { + close=item.Close; + if (low>item.Low) low=item.Low; + if (high=data.Data.length) xValue=data.Data.length-1; + var yStart=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + xValue=Math.round(this.Frame.GetXData(ptEnd.Y,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var yEnd=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + var xHigh=this.Frame.GetYFromData(kInfo.High,false); + var xLow=this.Frame.GetYFromData(kInfo.Low,false); + + var result= { Left:xLow, Top:yStart, Right:xHigh, Bottom:yEnd }; + + this.LinePoint.push({Start:{X:xLow, Y:yStart}, End:{X:yEnd, Y:yStart}}); + this.LinePoint.push({Start:{X:xLow, Y:yEnd}, End:{X:yEnd, Y:yEnd}}); + this.LinePoint.push({Start:{X:xLow, Y:yStart}, End:{X:xLow, Y:yEnd}}); + this.LinePoint.push({Start:{X:xHigh, Y:yStart}, End:{X:xHigh, Y:yEnd}}); + + return result; + } + else + { + var xValue=Math.round(this.Frame.GetXData(ptStart.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var xStart=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + xValue=Math.round(this.Frame.GetXData(ptEnd.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var xEnd=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + var yHigh=this.Frame.GetYFromData(kInfo.High,false); + var yLow=this.Frame.GetYFromData(kInfo.Low,false); + var result= { Left:xStart, Top:yHigh, Right:xEnd, Bottom:yLow }; + + this.LinePoint.push({Start:{X:xStart, Y:yHigh}, End:{X:xEnd, Y:yHigh}}); + this.LinePoint.push({Start:{X:xStart, Y:yLow}, End:{X:xEnd, Y:yLow}}); + this.LinePoint.push({Start:{X:xStart, Y:yHigh}, End:{X:xStart, Y:yLow}}); + this.LinePoint.push({Start:{X:xEnd, Y:yHigh}, End:{X:xEnd, Y:yLow}}); + + return result; + } + } + + this.DrawText=function(text, x, y, xOffset, yOffset) + { + if (this.IsHScreen) + { + this.Canvas.save(); + this.Canvas.translate(x,y); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(text,xOffset,yOffset); + this.Canvas.restore(); + } + else + { + this.Canvas.fillText(text,x+xOffset,y+yOffset); + } + } + +} + +function ChartDrawTwoPointDemo() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawTwoPointDemo'; + this.PointCount=2; + this.Font=16*GetDevicePixelRatio() +"px 微软雅黑"; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.IsHScreen=false; + this.PointInfo=[]; //保存点及点对应K线的信息 + + this.PointToValue_Backup=this.PointToValue; + this.PointToValue=function() + { + this.OnFinish(); + this.PointToValue_Backup(); + } + + //移动或创建完成 + this.OnFinish=function() + { + for(var i in this.PointInfo) + { + var item=this.PointInfo[i]; + if (!item || !item.Point || !IFrameSplitOperator.IsNumber(item.Point.Y2)) continue; + this.Point[i].Y=this.PointInfo[i].Point.Y2; //点Y轴坐标调整 + } + } + + this.Draw=function() + { + this.LinePoint=[]; + this.PointInfo=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true}); + if (!drawPoint) return; + + this.IsHScreen=this.Frame.IsHScreen; + //this.CalculateLines(drawPoint); + + this.ClipFrame(); + this.SetLineWidth(); + + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1] + this.CalculatePointInfo(drawPoint); + if (this.Status==10) //绘制完成 + { + this.DrawEx(); + } + else //绘制中只画连线 + { + for(var i=1;i0;--i) + { + var price=endInfo.KItem.YClose*(0.03*i)+endInfo.KItem.High; //%2涨幅一档画线 + var y=this.Frame.GetYFromData(price,false); + y=this.ToFixedPoint(y); + + //线段 + this.DrawLine({X:ptEnd.X, Y:y}, {X:ptEnd.X+100, Y:y}); + + //文字 + this.Canvas.fillText(price.toFixed(2), ptEnd.X-5, y); + } + this.Canvas.setLineDash([]); + } + + //防止竖线或横线模糊调整坐标 + this.ToFixedPoint=function(value,width) + { + if (!IFrameSplitOperator.IsNumber(width)) width=this.LineWidth; + return ToFixedPoint2(width, value) + } +} + +function ChartDrawThreePointDemo() +{ + this.newMethod=ChartDrawTwoPointDemo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawThreePointDemo'; + this.PointCount=3; + + this.SetLastPoint=function(obj) + { + this.LastPoint={X:obj.X,Y:obj.Y}; + } + + //计算需要调整的Y轴坐标 + this.CalculateYPoint=function() + { + var y; + for(var i in this.PointInfo) + { + var item=this.PointInfo[i]; + if (i==0 || i==2) y=this.Frame.GetYFromData(item.KItem.Low,false); + else y=this.Frame.GetYFromData(item.KItem.High,false); + item.Point.Y2=y; + } + } + + this.DrawEx=function() + { + //起始点 结束点信息 + var startInfo=this.PointInfo[0]; + var secondInfo=this.PointInfo[1]; + var endInfo=this.PointInfo[2]; + + //K线边框信息 + var chartBorder=this.Frame.ChartBorder; + var frameRight=chartBorder.GetRight(); //右边 + var frameLeft=chartBorder.GetLeft(); //左边 + + var ptStart={ X:startInfo.Point.X, Y:startInfo.Point.Y2 }; + var ptSecond={ X:secondInfo.Point.X, Y:secondInfo.Point.Y2}; + var ptEnd={ X:endInfo.Point.X, Y:endInfo.Point.Y2 }; + + this.DrawLine(ptStart,ptSecond); + this.DrawLine(ptSecond,ptEnd); + this.LinePoint.push({Start:ptStart, End:ptSecond}); + this.LinePoint.push({Start:ptSecond, End:ptEnd}); + + this.Canvas.setLineDash([5,5]); + //this.Canvas.lineWidth=2; + var topPrice=endInfo.KItem.YClose*0.09+endInfo.KItem.High; //结束点最高价+涨幅6% 画竖线 + var y=this.Frame.GetYFromData(topPrice,false); + this.DrawLine({X:this.ToFixedPoint(ptEnd.X), Y:ptEnd.Y}, {X:this.ToFixedPoint(ptEnd.X), Y:y}); + + this.Canvas.textAlign="right"; + this.Canvas.textBaseline = 'center'; + this.Canvas.fillStyle='rgb(33,45,100)'; + + for(var i=3;i>0;--i) + { + var price=endInfo.KItem.YClose*(0.03*i)+endInfo.KItem.High; //%2涨幅一档画线 + var y=this.Frame.GetYFromData(price,false); + y=this.ToFixedPoint(y); + + //线段 + this.DrawLine({X:ptEnd.X, Y:y}, {X:ptEnd.X+100, Y:y}); + + //文字 + this.Canvas.fillText(price.toFixed(2), ptEnd.X-5, y); + } + this.Canvas.setLineDash([]); + } +} + +//画图工具-水平线段 +function ChartDrawHLineSegment() +{ + this.newMethod=IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ChartDrawHLineSegment'; + this.PointCount=2; + this.IsPointIn=this.IsPointIn_XYValue_Line; + this.ChartBorder; + + this.PointToValue_Backup=this.PointToValue; + this.PointToValue=function() + { + //拖拽完成 把点移动到线段头尾 + if (this.Frame.IsHScreen) + { + this.Point[1].X=this.Point[0].X; + } + else + { + this.Point[1].Y=this.Point[0].Y; + } + + this.PointToValue_Backup(); + } + + this.Draw=function() + { + this.IsHScreen=this.Frame.IsHScreen; + this.LinePoint=[]; + if (this.IsFrameMinSize()) return; + + var drawPoint=this.CalculateDrawPoint( { IsCheckX:false, IsCheckY:true} ); + if (!drawPoint || drawPoint.length!=2) return; + + this.ClipFrame(); + this.ChartBorder=this.Frame.ChartBorder; + + this.SetLineWidth(); + this.Canvas.strokeStyle=this.LineColor; + var ptStart=drawPoint[0]; + var ptEnd=drawPoint[1]; + + if (this.Status==10) //0=开始画 1=完成第1个点 2=完成第2个点 3=完成第3个点 10=完成 20=移动) + { + this.DrawLine(ptStart,ptEnd,false); + } + else + { + //var kPoint=this.PointToKLine([ptStart,ptEnd]); + this.DrawVerticalLine(ptStart,ptEnd); + } + + this.RestoreLineWidth(); + + var line={ Start:ptStart, End:ptEnd }; + this.LinePoint.push(line); + + this.DrawPoint(drawPoint); //画点 + this.Canvas.restore(); + } + + this.DrawVerticalLine=function(ptStart, ptEnd) + { + var data=this.Frame.Data; + if (this.IsHScreen) + { + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var xValue=Math.round(this.Frame.GetXData(ptStart.Y,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var yStart=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + xValue=Math.round(this.Frame.GetXData(ptEnd.Y,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var yEnd=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + this.Canvas.beginPath(); + this.Canvas.moveTo(left,yStart); + this.Canvas.lineTo(right,yStart); + + this.Canvas.moveTo(left,yEnd); + this.Canvas.lineTo(right,yEnd); + this.Canvas.stroke(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(ptStart.X,yStart); + this.Canvas.lineTo(ptStart.X,yEnd); + this.Canvas.stroke(); + } + else + { + var top=this.ChartBorder.GetTopEx(); + var bottom=this.ChartBorder.GetBottomEx(); + + var xValue=Math.round(this.Frame.GetXData(ptStart.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var xStart=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + xValue=Math.round(this.Frame.GetXData(ptEnd.X,false))+data.DataOffset; + if (xValue<0) xValue=0; + else if (xValue>=data.Data.length) xValue=data.Data.length-1; + var xEnd=this.Frame.GetXFromIndex(xValue-data.DataOffset,false); + + this.Canvas.beginPath(); + this.Canvas.moveTo(xStart,top); + this.Canvas.lineTo(xStart,bottom); + + this.Canvas.moveTo(xEnd,top); + this.Canvas.lineTo(xEnd,bottom); + this.Canvas.stroke(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(xStart,ptStart.Y); + this.Canvas.lineTo(xEnd,ptStart.Y); + this.Canvas.stroke(); + } + } +} + +function ChartDrawStorage() +{ + this.DrawData=new Map(); //画图工具数据 key=symbol-Period, value=Map() Key:Guid, Value:{Guid, Symbol, Period, ClassName, Value} + this.StorageKey; + + this.Load=function(key) //从本地读取画图工具 + { + if (!key) return; + this.StorageKey=key; + var cacheValue = localStorage[this.StorageKey]; + JSConsole.Chart.Log(`[ChartDrawStorage::Save] Load to localStorage, key=${this.StorageKey}, cache=${cacheValue}`); + + if (!cacheValue) return; + if (typeof(cacheValue) != "string") return; + + var saveData=JSON.parse(cacheValue); + for(var i in saveData) + { + var item=saveData[i]; + var drawMap=new Map(); + + for(var j in item.Value) + { + var drawItem=item.Value[j]; + drawMap.set(drawItem.Key, drawItem.Value); + } + + this.DrawData.set(item.Key,drawMap); + } + } + + this.Save=function() //把数据保存到本地 + { + if (!this.StorageKey) return; + + var saveData=[]; + for(var stock of this.DrawData) + { + var key=stock[0]; + var value=stock[1]; + var itemData={ Key:key, Value:[]}; + + for(var drawItem of value) + { + itemData.Value.push({Key:drawItem[0], Value:drawItem[1]}); + } + + saveData.push(itemData) + } + + JSConsole.Chart.Log(`[ChartDrawStorage::Save] save to localStorage, key=${this.StorageKey}`); + var strData=JSON.stringify(saveData); + localStorage[this.StorageKey]=strData; + } + + this.SaveDrawData=function(drawPicture) //保存一个画图工具 + { + var strKey=drawPicture.Symbol+'-'+drawPicture.Period; + var data=drawPicture.ExportStorageData(); + if (!data) return; + + if (this.DrawData.has(strKey)) //更新 + { + JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] Upate: key, drawPicture, data', strKey, drawPicture,data); + this.DrawData.get(strKey).set(data.Guid, data); + } + else //新增 + { + JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] Insert: key, drawPicture, data', strKey, drawPicture,data); + var newData=new Map(); + newData.set(data.Guid, data); + this.DrawData.set(strKey,newData); + } + + JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] All draw data: ', this.DrawData); + + this.Save(); + } + + this.DeleteDrawData=function(drawPicture) //删除一个画图工具 + { + var strKey=drawPicture.Symbol+'-'+drawPicture.Period; + if (!this.DrawData.has(strKey)) return; + + var mapDraw=this.DrawData.get(strKey); + if (!mapDraw.has(drawPicture.Guid)) return; + + mapDraw.delete(drawPicture.Guid); + this.Save(); + } + + this.Clear=function() + { + this.DrawData=new Map(); + this.Save(); + } + + this.GetDrawData=function(obj) //获取已有的画图工具数据{Symbol: , Period:, } + { + var data=[]; + var strKey=obj.Symbol+'-'+obj.Period; + if (!this.DrawData.has(strKey)) return data; + + var stockData=this.DrawData.get(strKey); + for(var item of stockData) + { + data.push(item[1]); + } + + return data; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// 数据分割 +// [0]=Start起始 [1]=End结束 [2]=FixInterval修正的间隔 [3]=Increase +// +function SplitData() +{ + this.Data=[ + [0.000001, 0.000002, 0.000001, 0.0000001], + [0.000002, 0.000004, 0.000002, 0.0000002], + [0.000004, 0.000005, 0.000004, 0.0000001], + [0.000005, 0.00001, 0.000005, 0.0000005], + + [0.00001, 0.00002, 0.00001, 0.000001], + [0.00002, 0.00004, 0.00002, 0.000002], + [0.00004, 0.00005, 0.00004, 0.000001], + [0.00005, 0.0001, 0.00005, 0.000005], + + [0.0001, 0.0002, 0.0001, 0.00001], + [0.0002, 0.0004, 0.0002, 0.00002], + [0.0004, 0.0005, 0.0004, 0.00001], + [0.0005, 0.001, 0.0005, 0.00005], + + [0.001, 0.002, 0.001, 0.0001], + [0.002, 0.004, 0.002, 0.0002], + [0.004, 0.005, 0.004, 0.0001], + [0.005, 0.01, 0.005, 0.0005], + + [0.01, 0.02, 0.01, 0.001], + [0.02, 0.04, 0.02, 0.002], + [0.04, 0.05, 0.04, 0.001], + [0.05, 0.1, 0.05, 0.005], + + [0.1, 0.2, 0.1, 0.01], + [0.2, 0.4, 0.2, 0.02], + [0.4, 0.5, 0.4, 0.01], + [0.5, 1, 0.5, 0.05], + + [1, 2, 0.5, 0.05], + [2, 4, 0.5, 0.05], + [4, 5, 0.5, 0.05], + [5, 10, 0.5, 0.05], + + [10, 20, 10, 2], + [20, 40, 20, 5], + [40, 50, 40, 2], + [50, 100, 50, 10], + + [100, 200, 100, 10], + [200, 400, 200, 20], + [400, 500, 400, 10], + [500, 1000, 500, 50], + + [1000, 2000, 1000, 50], + [2000, 4000, 2000, 50], + [4000, 5000, 4000, 50], + [5000, 10000, 5000, 100], + + [10000, 20000, 10000, 1000], + [20000, 40000, 10000, 1000], + [40000, 50000, 10000, 1000], + [50000, 100000, 10000, 1000], + + [100000, 200000, 100000, 10000], + [200000, 400000, 100000, 10000], + [400000, 500000, 100000, 10000], + [500000, 1000000, 100000, 10000], + + [1000000, 2000000, 1000000, 100000], + [2000000, 4000000, 1000000, 100000], + [4000000, 5000000, 1000000, 100000], + [5000000, 10000000, 1000000, 100000], + + [10000000, 20000000, 10000000, 1000000], + [20000000, 40000000, 10000000, 1000000], + [40000000, 50000000, 10000000, 1000000], + [50000000, 100000000, 10000000, 1000000], + + [100000000, 200000000, 100000000, 10000000], + [200000000, 400000000, 100000000, 10000000], + [400000000, 500000000, 100000000, 10000000], + [500000000, 1000000000, 100000000, 10000000], + + [1000000000, 2000000000, 1000000000, 100000000], + [2000000000, 4000000000, 1000000000, 100000000], + [4000000000, 5000000000, 1000000000, 100000000], + [5000000000, 10000000000, 1000000000, 100000000] + ]; + + this.Find=function(interval) + { + for(var i in this.Data) + { + var item =this.Data[i]; + if (interval>item[0] && interval<=item[1]) + { + var result={}; + result.FixInterval=item[2]; + result.Increase=item[3]; + return result; + } + } + + return null; + } +} + +function PriceSplitData() +{ + this.newMethod=SplitData; //派生 + this.newMethod(); + delete this.newMethod; + + this.Data=[ + [0.000001, 0.000002, 0.000001, 0.0000001], + [0.000002, 0.000004, 0.000002, 0.0000002], + [0.000004, 0.000005, 0.000004, 0.0000001], + [0.000005, 0.00001, 0.000005, 0.0000005], + + [0.00001, 0.00002, 0.00001, 0.000001], + [0.00002, 0.00004, 0.00002, 0.000002], + [0.00004, 0.00005, 0.00004, 0.000001], + [0.00005, 0.0001, 0.00005, 0.000005], + + [0.0001, 0.0002, 0.0001, 0.00001], + [0.0002, 0.0004, 0.0002, 0.00002], + [0.0004, 0.0005, 0.0004, 0.00001], + [0.0005, 0.001, 0.0005, 0.00005], + + [0.001, 0.002, 0.001, 0.0001], + [0.002, 0.004, 0.002, 0.0001], + [0.004, 0.005, 0.004, 0.0001], + [0.005, 0.01, 0.005, 0.0005], + + [0.01, 0.02, 0.01, 0.001], + [0.02, 0.04, 0.02, 0.001], + [0.04, 0.05, 0.04, 0.001], + [0.05, 0.1, 0.05, 0.001], + + [0.1, 0.2, 0.1, 0.01], + [0.2, 0.4, 0.2, 0.01], + [0.4, 0.5, 0.2, 0.01], + [0.5, 0.8, 0.2, 0.01], + [0.8, 1, 0.5, 0.01], + + [1, 2, 0.5, 0.05], + [2, 4, 0.5, 0.05], + [4, 5, 0.5, 0.05], + [5, 10, 0.5, 0.05], + + [10, 12, 10, 2], + [20, 40, 20, 5], + [40, 50, 40, 2], + [50, 100, 50, 10], + + [100, 200, 100, 10], + [200, 400, 200, 20], + [400, 500, 400, 10], + [500, 1000, 500, 50], + + [1000, 2000, 1000, 50], + [2000, 4000, 2000, 50], + [4000, 5000, 4000, 50], + [5000, 10000, 5000, 100], + + [10000, 20000, 10000, 1000], + [20000, 40000, 20000, 2000], + [40000, 50000, 40000, 1000], + [50000, 100000, 50000, 5000], + + [100000, 200000, 100000, 10000], + [200000, 400000, 200000, 20000], + [400000, 500000, 400000, 10000], + [500000, 1000000, 500000, 50000], + + [1000000, 2000000, 1000000, 100000], + [2000000, 4000000, 2000000, 200000], + [4000000, 5000000, 4000000, 100000], + [5000000, 10000000, 5000000, 500000], + + [10000000, 20000000, 10000000, 1000000], + [20000000, 40000000, 20000000, 2000000], + [40000000, 50000000, 40000000, 1000000], + [50000000, 100000000, 50000000, 5000000], + + [100000000, 200000000, 100000000, 10000000], + [200000000, 400000000, 200000000, 20000000], + [400000000, 500000000, 400000000, 10000000], + [500000000, 1000000000, 500000000, 50000000], + + [1000000000, 2000000000, 1000000000, 100000000], + [2000000000, 4000000000, 2000000000, 200000000], + [4000000000, 5000000000, 4000000000, 100000000], + [5000000000, 10000000000, 5000000000, 500000000] + ]; +} + +///////////////////////////////////////////////////////////////////////////// +// 全局配置颜色 +// +// +function JSChartResource() +{ + this.TooltipBGColor="rgb(255, 255, 255)"; //背景色 + this.TooltipAlpha=0.92; //透明度 + + this.SelectRectBGColor="rgba(1,130,212,0.06)"; //背景色 + // this.SelectRectAlpha=0.06; //透明度 + + this.UpBarColor="rgb(238,21,21)"; //上涨柱子颜色 + this.DownBarColor="rgb(25,158,0)"; //下跌柱子颜色 + this.UnchagneBarColor="rgb(0,0,0)"; //平盘柱子颜色 + this.EmptyBarBGColor="rgb(255,255,255)"; //空心柱子背景色 + + this.Minute={}; + this.Minute.VolBarColor=null; //分时图成交量柱子颜色 默认不用, 设置了柱子就不是红绿了 + this.Minute.VolTitleColor="rgb(105,105,105)"; + this.Minute.PriceColor="rgb(50,171,205)"; //分时图价格线颜色 + this.Minute.AreaPriceColor='rgba(50,171,205,0.1)'; //价格的面积图 + this.Minute.AvPriceColor="rgb(238,127,9)"; //分时图均价线颜色 + this.Minute.PositionColor='rgb(218,165,32)'; //持仓量线段颜色 + this.Minute.FrameSplitTextColor=null; //刻度文字颜色 (缺省使用 this.FrameSplitTextColor) + + this.Minute.Before= + { + BGColor:'rgba(240,240,240,0.7)', //分钟 集合竞价背景 + LineColor:"rgb(50,171,205)", //集合竞价线段颜色 + VolColor:["rgb(192,192,0)"], //成交量其他的颜色 colorID=3 开始 + AvPriceColor:'rgb(190,190,190)', //均线 + Point:{ Color:"rgb(65,105,225)", Radius:2*GetDevicePixelRatio() }, + CloseIcon: + { + Family:'iconfont', + Text:"\ue60c", + Color:"rgb(112,128,144)", + Size:12 + } //关闭按钮 + }; + + this.DefaultTextColor="rgb(43,54,69)"; //图形中默认的字体颜色 + this.DefaultTextFont=14*GetDevicePixelRatio() +'px 微软雅黑'; //图形中默认的字体 + this.TitleFont=13*GetDevicePixelRatio() +'px 微软雅黑'; //指标显示,tooltip显示字体 + this.IndexTitleBGColor='rgb(217,219,220)'; //指标名字背景色 + this.OverlayIndexTitleBGColor='rgba(255,255,255,0.7)'; + + this.Title={ + TradeIndexColor:'rgb(105,105,105)', //交易指标颜色 + ColorIndexColor:'rgb(112,128,144)', //五彩K线颜色 + + VolColor:"rgb(43,54,69)", //标题成交量 + AmountColor:"rgb(43,54,69)", //成交金额 + DateTimeColor:"rgb(43,54,69)", //时间,日期 + SettingColor:"rgb(43,54,69)", //周期,复权 + NameColor:"rgb(43,54,69)" , //股票名称 + TurnoverRateColor:'rgb(43,54,69)', //换手率 + PositionColor:"rgb(43,54,69)" //持仓 + }; + + this.UpTextColor="rgb(238,21,21)"; //上涨文字颜色 + this.DownTextColor="rgb(25,158,0)"; //下跌文字颜色 + this.UnchagneTextColor="rgb(0,0,0)"; //平盘文字颜色 + this.CloseLineColor='rgb(0,191,255)'; //收盘价线颜色 + this.CloseLineAreaColor=['rgba(0,191,255,0.8)','rgba(0,191,255,0.2)']; //收盘价面积图颜色 + this.CloseLineWidth=2; //收盘价面积图颜色线段宽度 + + this.FrameBorderPen="rgb(225,236,242)"; //边框颜色 + this.FrameSplitPen="rgb(225,236,242)"; //刻度分割线 + this.FrameDotSplitPen='rgb(105,105,105)'; //分割虚线 + this.FrameYLineDash= null; //[5*GetDevicePixelRatio(), 5*GetDevicePixelRatio()]; //Y轴线段虚线点间距,填null 就是实线 + this.FrameXLineDash= null; //[5*GetDevicePixelRatio(), 5*GetDevicePixelRatio()]; //X轴线段虚线点间距,填null 就是实线 + this.FrameSplitTextColor="rgb(117,125,129)"; //刻度文字颜色 + this.FrameSplitTextFont=14*GetDevicePixelRatio() +"px 微软雅黑"; //坐标刻度文字字体 + this.FrameTitleBGColor="rgb(246,251,253)"; //标题栏背景色 + this.Frame={ + XBottomOffset:1*GetDevicePixelRatio(), //X轴文字向下偏移 + YTopOffset:2*GetDevicePixelRatio() //Y轴顶部文字向下偏移 + }; + + //百分比坐标文字颜色 + this.Frame.PercentageText= { + PriceColor:'rgb(117,125,129)', + PercentageColor:"rgb(117,125,129)", + SplitColor:"rgb(117,125,129)", + Font:14*GetDevicePixelRatio() +"px 微软雅黑" + }; + + //对数坐标 + this.FrameLogarithmic= { + OpenPriceFont: "bold "+14*GetDevicePixelRatio() +"px 微软雅黑", //开盘价刻度文字字体 + MinInterval: 45*GetDevicePixelRatio() //刻度最小间距 + }; + + //等比坐标 + this.FrameSplitIncrease= { + Increase:0.1 //涨幅 + }; + + //等分坐标 + this.FrameSplitAverage= { + Count:4 //分割个数 + }; + + //黄金分割坐标 + this.FrameGoldenSection= + { + Golden:[0, 0.191, 0.382, 0.5, 0.618, 0.809, 1] + }; + + //Y轴最新价格刻度颜色 + this.FrameLatestPrice = { + TextColor:'rgb(255,255,255)', //最新价格文字颜色 + UpBarColor:"rgb(238,21,21)", //上涨 + DownBarColor:"rgb(25,158,0)", //下跌 + UnchagneBarColor:"rgb(0,0,0)", //平盘 + BGAlpha:0.6 + }; + + this.FrameMargin=4; //左右一共的边距 + this.FrameLeftMargin=2; + this.FrameRightMargin=2; + + this.DragSubFrameBorder= { + TopBorderHeight:6*GetDevicePixelRatio(), //拖拽边框高度 + MinFrameHeight:50*GetDevicePixelRatio() //指标窗口最小高度 + }, + + //叠加指标框架 + this.OverlayFrame={ + BolderPen:'rgb(190,190,190)', //指标边框线 + TitleColor:'rgb(105,105,105)', //指标名字颜色 + TitleFont:11*GetDevicePixelRatio() +'px arial', //指标名字字体 + }; + + this.CorssCursorBGColor="rgb(43,54,69)"; //十字光标背景 + this.CorssCursorTextColor="rgb(255,255,255)"; //十字光文字颜色 + this.CorssCursorTextFont=14*GetDevicePixelRatio() +"px 微软雅黑"; + this.CorssCursorHPenColor="rgb(130,130,130)"; //十字光标线段颜色(水平) + this.CorssCursorVPenColor="rgb(130,130,130)"; //十字光标线段颜色(垂直) + this.CorssCursorXRangeBGColor="rgba(100,149,237,0.3)"; //十字光标X轴访问背景色 + + this.LockBGColor = "rgb(220, 220, 220)"; //指标锁区域颜色 + this.LockTextColor = "rgb(210, 34, 34)"; //指标锁提示信息文字颜色 + + this.Domain="https://opensource.zealink.com"; //API域名 + this.CacheDomain="https://opensourcecache.zealink.com"; //缓存域名 + this.PyIndexDomain='https://py.zealink.com'; //py指标计算域名 + + this.KLine={ + MaxMin: {Font:12*GetDevicePixelRatio() +'px 微软雅黑',Color:'rgb(43,54,69)', RightArrow:"→", LeftArrow:"←", HighYOffset:0, LowYOffset:0}, //K线最大最小值显示 + Info: //信息地雷 + { + Investor: + { + ApiUrl:'/API/NewsInteract', //互动易 + IconFont: { Family:'iconfont', Text:'\ue631' , HScreenText:'\ue684', Color:'#1c65db'} //SVG 文本 + }, + Announcement: //公告 + { + ApiUrl:'/API/ReportList', + IconFont: { Family:'iconfont', Text:'\ue633', HScreenText:'\ue685', Color:'#f5a521' }, //SVG 文本 + IconFont2: { Family:'iconfont', Text:'\ue634', HScreenText:'\ue686', Color:'#ed7520' }, //SVG 文本 //季报 + }, + Pforecast: //业绩预告 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue62e', HScreenText:'\ue687', Color:'#986cad' } //SVG 文本 + }, + Research: //调研 + { + ApiUrl:'/API/InvestorRelationsList', + IconFont: { Family:'iconfont', Text:'\ue632', HScreenText:'\ue688', Color:'#19b1b7' } //SVG 文本 + }, + BlockTrading: //大宗交易 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue630', HScreenText:'\ue689', Color:'#f39f7c' } //SVG 文本 + }, + TradeDetail: //龙虎榜 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue62f', HScreenText:'\ue68a' ,Color:'#b22626' } //SVG 文本 + }, + + //扩展图标库 + IconLibrary: + { + Family:'iconfont', + Icon: + [ + { Text:'\ue630', HScreenText:'\ue689', Color:'#f39f7c' }, + { Text:'\ue632', HScreenText:'\ue688', Color:'#19b1b7' }, + { Text:'\ue62f', HScreenText:'\ue68a' ,Color:'#b22626' }, + { Text:'\ue634', HScreenText:'\ue686', Color:'#ed7520' } + ] + }, + + }, + NumIcon: + { + Color:'rgb(251,80,80)',Family:'iconfont', + Text:[ '\ue649', + '\ue63b','\ue640','\ue63d','\ue63f','\ue645','\ue641','\ue647','\ue648','\ue646','\ue636', + '\ue635','\ue637','\ue638','\ue639','\ue63a','\ue63c','\ue63e','\ue642','\ue644','\ue643' + ] + }, + TradeIcon: //交易指标 图标 + { + Family:'iconfont', + Buy: { Color:'rgb(255,15,4)', Text:'\ue683', HScreenText:'\ue682'}, + Sell: { Color:'rgb(64,122,22)', Text:'\ue681',HScreenText:'\ue680'}, + } + }; + + this.Index={}; + //指标线段颜色 + this.Index.LineColor= + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + + ]; + + this.OverlaySymbol={ Random:0 }; //Random 颜色的随机数 + this.OverlaySymbol.Color= //叠加股票颜色 + [ + "rgb(38,198,218)", + "rgb(103,58,183)", + "rgb(0,191,165)", + "rgb(130,177,255)", + ]; + + //历史数据api + this.Index.StockHistoryDayApiUrl="https://opensource.zealink.com/API/StockHistoryDay"; + //市场多空 + this.Index.MarketLongShortApiUrl="https://opensource.zealink.com/API/FactorTiming"; + //市场关注度 + this.Index.MarketAttentionApiUrl="https://opensource.zealink.com/API/MarketAttention"; + //行业,指数热度 + this.Index.MarketHeatApiUrl="https://opensource.zealink.com/API/MarketHeat"; + //自定义指数热度 + this.Index.CustomIndexHeatApiUrl="https://opensource.zealink.com/API/QuadrantCalculate"; + + //指标不支持信息 + this.Index.NotSupport={Font:"14px 微软雅黑", TextColor:"rgb(52,52,52)"}; + + //画图工具 + this.DrawPicture={}; + this.DrawPicture.LineColor= + [ + "rgb(30,144,255)", + ]; + + this.DrawPicture.PointColor= + [ + "rgb(105,105,105)", + ]; + + this.KLineTrain = { + Font:'bold 14px arial', + LastDataIcon: {Color:'rgb(0,0,205)',Text:'⬇'}, + BuyIcon: {Color:'rgb(0,205,102 )',Text:'B'}, + SellIcon: {Color:'rgb(255,127,36 )',Text:'S'}, + + IconFont: + { + Family:'iconfont', + Buy:{ Text:'\ue64a', HScreenText:'\ue68a' ,Color:'rgb(255,140,0)' }, + Sell:{ Text:'\ue64b', HScreenText:'\ue68a' ,Color:'rgb(6,79,18)' }, + Last:{ Text:'\ue681', HScreenText:'\ue68a' ,Color:'rgb(55,0,255)' }, + } + }; + + //手机端tooltip + this.TooltipPaint = { + BGColor:'rgba(250,250,250,0.8)', //背景色 + BorderColor:'rgb(120,120,120)', //边框颜色 + TitleColor:'rgb(120,120,120)', //标题颜色 + TitleFont:13*GetDevicePixelRatio() +'px 微软雅黑', //字体 + DateTimeColor:'rgb(210,210,210)', + VolColor:"rgb(210,210,210)", //标题成交量 + AmountColor:"rgb(210,210,210)", //成交金额 + }; + + this.PCTooltip= { + LineHeight:25 //单行高度 + }; + + //弹幕 + this.Barrage= { + Font:16*GetDevicePixelRatio() +'px 微软雅黑', //字体 + Height:20, + Color:'RGB(109,109,109)' + } + + //走势图 信息地雷 + this.MinuteInfo={ + TextColor: 'rgb(84,143,255)', + Font: 14*GetDevicePixelRatio() +'px 微软雅黑', + PointColor:'rgb(38,113,254)', + LineColor:'rgb(120,167,255)', + TextBGColor:'rgba(255,255,255,0.8)' + } + + //画图工具-尺子 + this.ChartDrawRuler= + { + TitleColor:null, //K线信息标题颜色, (可选,不填使用涨跌颜色) + } + + //画图工具-波浪尺 + this.ChartDrawWaveRuler= + { + RulerWidth:10, //刻度尺长度 + RulerLineWidth:1, //刻度线粗细 + MaxScaleRuler:2, //尺子最大的高度比 + Font:14*GetDevicePixelRatio() +"px 微软雅黑", + ScaleRuler: + [ + {Value:0, Text:"0.0%"}, { Value:0.382, Text:"38.2%" }, { Value:0.618, Text:"61.8%" } ,{ Value:1, Text:"100%"}, + { Value:1.618, Text:"161.8%" }, { Value:2.0, Text:"200%" } + ] + } + + this.ChartDrawWaveRuler2Point= + { + RulerWidth:20, //刻度尺长度 + RulerLineWidth:1, //刻度线粗细 + MaxScaleRuler:2, //尺子最大的高度比 + Font:14*GetDevicePixelRatio() +"px 微软雅黑", + ScaleRuler: + [ + {Value:0, Text:"base"}, { Value:0.618, Text:"61.8%" } ,{ Value:1, Text:"100%"}, + { Value:1.618, Text:"161.8%" } + ] + } + + //多图标指标ChartMultiSVGIcon -> MULTI_SVGICON + //单图标指标ChartSingleText -> DRAWICON + this.DRAWICON= + { + Icon: + { + MaxSize:24, //图标最大 + MinSize:12, //图标最小 + YOffset:0, + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + } + }, + + Text: + { + MaxSize:50, //字体最大 + MinSize:12, //字体最小 + YOffset:0, + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'Arial' //字体 + } + } + + this.DRAWTEXT= + { + MaxSize:30, //字体最大 + MinSize:20, //字体最小 + YOffset:0, + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'微软雅黑' //字体 + } + + this.DRAWNUMBER= + { + MaxSize:30, //字体最大 + MinSize:20, //字体最小 + YOffset:0, + + Zoom: + { + Type:1, //0=放大(K线宽度*Value) 1=放大(K线+间距)*Value 2=(K线+间距)+2*Value; + Value:1 + }, + + FontName:'微软雅黑' //字体 + } + + this.DRAWABOVE= + { + YOffset:0 //y坐标向上偏移 + } + + //虚线配置 + this.DOTLINE= + { + LineDash:[3,5] //虚线配置 + } + + this.DRAWSL= + { + PixelWidth:15 //1个像素点宽度 + } + + this.CIRCLEDOT= + { + Radius:1.3*GetDevicePixelRatio() + } + + this.POINTDOT= + { + Radius:2*GetDevicePixelRatio() + } + + //筹码分布图 + this.StockChip= + { + InfoColor:'rgb(0,0,0)', //文字颜色 + DayInfoColor:'rgb(255,255,255)' //周期颜色内文字颜色 + } + + //深度图 + this.DepthChart= + { + BidColor: { Line:"rgb(82,176,123)", Area:"rgba(82,176,123,0.8)"}, //卖 + AskColor: { Line:"rgb(207,76,89)", Area:"rgba(207,76,89, 0.8)"}, //买 + LineWidth:4 + } + + this.DepthCorss= + { + BidColor: { Line:"rgb(82,176,123)" }, //卖 + AskColor: { Line:"rgb(207,76,89)" }, //买 + LineWidth:2, //线段宽度 + LineDash:[3,3], + Tooltip: + { + BGColor:'rgba(236,240,245, 0.8)', TextColor:"rgb(130,140,151)", + Border:{ Top:5, Left:20, Bottom:5, Center: 5}, + Font:14*GetDevicePixelRatio() +"px 微软雅黑", + LineHeight:16 //单行高度 + } + } + + //区间选择 + this.RectSelect= + { + LineColor:"rgb(64,64,64)", //竖线 + LineWidth:1*GetDevicePixelRatio(), + LineDotted:[3,3], + AreaColor:"rgba(234,234,234,0.5)", //面积 + } + + //自定义风格 + this.SetStyle=function(style) + { + if (style.TooltipBGColor) this.TooltipBGColor = style.TooltipBGColor; + if (style.TooltipAlpha) this.TooltipAlpha = style.TooltipAlpha; + if (style.SelectRectBGColor) this.SelectRectBGColor = style.SelectRectBGColor; + if (style.UpBarColor) this.UpBarColor = style.UpBarColor; + if (style.DownBarColor) this.DownBarColor = style.DownBarColor; + if (style.UnchagneBarColor) this.UnchagneBarColor = style.UnchagneBarColor; + if (style.EmptyBarBGColor) this.EmptyBarBGColor=style.EmptyBarBGColor; + if (style.Minute) + { + if (style.Minute.VolBarColor) this.Minute.VolBarColor = style.Minute.VolBarColor; + if (style.Minute.VolTitleColor) this.Minute.VolTitleColor = style.Minute.VolTitleColor; + if (style.Minute.PriceColor) this.Minute.PriceColor = style.Minute.PriceColor; + if (style.Minute.AvPriceColor) this.Minute.AvPriceColor = style.Minute.AvPriceColor; + if (style.Minute.AreaPriceColor) this.Minute.AreaPriceColor = style.Minute.AreaPriceColor; + if (style.Minute.PositionColor) this.Minute.PositionColor = style.Minute.PositionColor; + if (style.Minute.FrameSplitTextColor) this.Minute.FrameSplitTextColor = style.Minute.FrameSplitTextColor; + if (style.Minute.Before) + { + var item=style.Minute.Before; + if (item.BGColor) this.Minute.Before.BGColor=item.BGColor; + if (item.LineColor) this.Minute.Before.LineColor=item.LineColor; + if (item.VolColor) this.Minute.Before.VolColor=item.VolColor; + if (item.AvPriceColor) this.Minute.Before.AvPriceColor=item.AvPriceColor; + if (item.CloseIcon) this.Minute.Before.CloseIcon=item.CloseIcon; + if (item.Point) + { + if (item.Point.Color) this.Minute.Before.Point.Color=item.Point.Color; + if (item.Point.Radius) this.Minute.Before.Point.Radius=item.Point.Radius; + } + } + } + + if (style.DefaultTextColor) this.DefaultTextColor = style.DefaultTextColor; + if (style.DefaultTextFont) this.DefaultTextFont = style.DefaultTextFont; + if (style.TitleFont) this.TitleFont = style.TitleFont; + if (style.IndexTitleBGColor) this.IndexTitleBGColor=style.IndexTitleBGColor; + if (style.OverlayIndexTitleBGColor) this.OverlayIndexTitleBGColor=style.OverlayIndexTitleBGColor; + if (style.UpTextColor) this.UpTextColor = style.UpTextColor; + if (style.DownTextColor) this.DownTextColor = style.DownTextColor; + if (style.UnchagneTextColor) this.UnchagneTextColor = style.UnchagneTextColor; + if (style.CloseLineColor) this.CloseLineColor = style.CloseLineColor; + if (style.CloseLineAreaColor) this.CloseLineAreaColor = style.CloseLineAreaColor; + if (style.CloseLineWidth) this.CloseLineWidth=style.CloseLineWidth; + if (style.FrameBorderPen) this.FrameBorderPen = style.FrameBorderPen; + if (style.FrameSplitPen) this.FrameSplitPen = style.FrameSplitPen; + if (style.FrameDotSplitPen) this.FrameDotSplitPen = style.FrameDotSplitPen; + if (style.FrameSplitTextColor) this.FrameSplitTextColor = style.FrameSplitTextColor; + if (style.FrameSplitTextFont) this.FrameSplitTextFont = style.FrameSplitTextFont; + if (style.FrameTitleBGColor) this.FrameTitleBGColor = style.FrameTitleBGColor; + + if (style.Frame) + { + if (style.Frame.XBottomOffset) this.Frame.XBottomOffset=style.Frame.XBottomOffset; + if (style.Frame.YTopOffset) this.Frame.YTopOffset=style.Frame.YTopOffset; + if (style.Frame.PercentageText) + { + var item=style.Frame.PercentageText; + if (item.PriceColor) this.Frame.PercentageText.PriceColor=item.PriceColor; + if (item.PercentageColor) this.Frame.PercentageText.PercentageColor=item.PercentageColor; + if (item.SplitColor) this.Frame.PercentageText.SplitColor=item.SplitColor; + if (item.Font) this.Frame.PercentageText.Font=item.Font; + } + } + + if (style.FrameLatestPrice) + { + if (style.FrameLatestPrice.TextColor) this.FrameLatestPrice.TextColor = style.FrameLatestPrice.TextColor; + if (style.FrameLatestPrice.UpBarColor) this.FrameLatestPrice.UpBarColor = style.FrameLatestPrice.UpBarColor; + if (style.FrameLatestPrice.DownBarColor) this.FrameLatestPrice.DownBarColor = style.FrameLatestPrice.DownBarColor; + if (style.FrameLatestPrice.UnchagneBarColor) this.FrameLatestPrice.UnchagneBarColor = style.FrameLatestPrice.UnchagneBarColor; + if (style.FrameLatestPrice.BGAlpha) this.FrameLatestPrice.BGAlpha = style.FrameLatestPrice.BGAlpha; + } + + if (style.CorssCursorBGColor) this.CorssCursorBGColor = style.CorssCursorBGColor; + if (style.CorssCursorTextColor) this.CorssCursorTextColor = style.CorssCursorTextColor; + if (style.CorssCursorTextFont) this.CorssCursorTextFont = style.CorssCursorTextFont; + if (style.CorssCursorVPenColor) this.CorssCursorVPenColor = style.CorssCursorVPenColor; + if (style.CorssCursorHPenColor) this.CorssCursorHPenColor = style.CorssCursorHPenColor; + if (style.CorssCursorBorderColor) this.CorssCursorBorderColor=style.CorssCursorBorderColor; + if (style.CorssCursorXRangeBGColor) this.CorssCursorXRangeBGColor=style.CorssCursorXRangeBGColor; + + if (style.KLine) this.KLine = style.KLine; + + if (style.Index) + { + if (style.Index.LineColor) this.Index.LineColor = style.Index.LineColor; + if (style.Index.NotSupport) this.Index.NotSupport = style.Index.NotSupport; + } + + if (style.ColorArray) this.ColorArray = style.ColorArray; + + if (style.DrawPicture) + { + this.DrawPicture.LineColor = style.DrawPicture.LineColor; + this.DrawPicture.PointColor = style.DrawPicture.PointColor; + } + + if (style.TooltipPaint) + { + if (style.TooltipPaint.BGColor) this.TooltipPaint.BGColor=style.TooltipPaint.BGColor; + if (style.TooltipPaint.BorderColor) this.TooltipPaint.BorderColor=style.TooltipPaint.BorderColor; + if (style.TooltipPaint.TitleColor) this.TooltipPaint.TitleColor=style.TooltipPaint.TitleColor; + if (style.TooltipPaint.TitleFont) this.TooltipPaint.TitleFont=style.TooltipPaint.TitleFont; + + if (style.TooltipPaint.DateTimeColor) this.TooltipPaint.DateTimeColor=style.TooltipPaint.DateTimeColor; + if (style.TooltipPaint.VolColor) this.TooltipPaint.VolColor=style.TooltipPaint.VolColor; + if (style.TooltipPaint.AmountColor) this.TooltipPaint.AmountColor=style.TooltipPaint.AmountColor; + } + + if (style.MinuteInfo) + { + if (style.MinuteInfo.TextColor) this.MinuteInfo.TextColor=style.MinuteInfo.TextColor; + if (style.MinuteInfo.Font) this.MinuteInfo.Font=style.MinuteInfo.Font; + if (style.MinuteInfo.PointColor) this.MinuteInfo.PointColor=style.MinuteInfo.PointColor; + if (style.MinuteInfo.LineColor) this.MinuteInfo.LineColor=style.MinuteInfo.LineColor; + if (style.MinuteInfo.TextBGColor) this.MinuteInfo.TextBGColor=style.MinuteInfo.TextBGColor; + } + + if (style.Title) + { + if (style.Title.TradeIndexColor) this.Title.TradeIndexColor=style.Title.TradeIndexColor; + if (style.Title.ColorIndexColor) this.Title.ColorIndexColor=style.Title.ColorIndexColor; + + if (style.Title.VolColor) this.Title.VolColor=style.Title.VolColor; + if (style.Title.AmountColor) this.Title.AmountColor=style.Title.AmountColor; + if (style.Title.DateTimeColor) this.Title.DateTimeColor=style.Title.DateTimeColor; + if (style.Title.NameColor) this.Title.NameColor=style.Title.NameColor; + if (style.Title.SettingColor) this.Title.SettingColor=style.Title.SettingColor; + if (style.Title.TurnoverRateColor) this.Title.TurnoverRateColor=style.Title.TurnoverRateColor; + if (style.Title.PositionColor) this.Title.PositionColor=style.Title.PositionColor; + } + + if (style.DRAWICON) + { + if (style.DRAWICON.Icon) + { + var item=style.DRAWICON.Icon; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWICON.Icon.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWICON.Icon.MinSize=item.MinSize; + if (item.Zoom) this.DRAWICON.Icon.Zoom=item.Zoom; + if (IFrameSplitOperator.IsNumber(item.YOffset)) this.DRAWICON.Icon.YOffset=item.YOffset; + } + + if (style.DRAWICON.Text) + { + var item=style.DRAWICON.Text; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWICON.Text.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWICON.Text.MinSize=item.MinSize; + if (item.Zoom) this.DRAWICON.Text.Zoom=item.Zoom; + if (item.FontName) this.DRAWICON.Text.FontName=item.FontName; + if (IFrameSplitOperator.IsNumber(item.YOffset)) this.DRAWICON.Text.YOffset=item.YOffset; + } + } + + if (style.DRAWTEXT) + { + var item=style.DRAWTEXT; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWICON.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWICON.MinSize=item.MinSize; + if (item.Zoom) this.DRAWTEXT.Zoom=item.Zoom; + if (item.FontName) this.DRAWTEXT.FontName=item.FontName; + if (IFrameSplitOperator.IsNumber(item.YOffset)) this.DRAWTEXT.YOffset=item.YOffset; + } + + if (style.DRAWNUMBER) + { + var item=style.DRAWNUMBER; + if (IFrameSplitOperator.IsPlusNumber(item.MaxSize)) this.DRAWNUMBER.MaxSize=item.MaxSize; + if (IFrameSplitOperator.IsPlusNumber(item.MinSize)) this.DRAWNUMBER.MinSize=item.MinSize; + if (item.Zoom) this.DRAWNUMBER.Zoom=item.Zoom; + if (item.FontName) this.DRAWNUMBER.FontName=item.FontName; + if (IFrameSplitOperator.IsNumber(item.YOffset)) this.DRAWNUMBER.YOffset=item.YOffset; + } + + if (style.DRAWABOVE) + { + var item=style.DRAWABOVE; + if (IFrameSplitOperator.IsNumber(item.YOffset)) this.DRAWABOVE.YOffset=item.YOffset; + } + + if (style.DOTLINE) this.DOTLINE=style.DOTLINE; + if (style.DRAWSL) this.DOTLINE=style.DRAWSL; + + if (style.DragSubFrameBorder) this.DragSubFrameBorder=style.DragSubFrameBorder; + + if (style.StockChip) + { + var item=style.StockChip; + if (item.InfoColor) this.StockChip.InfoColor=item.InfoColor; + if (item.DayInfoColor) this.StockChip.DayInfoColor=item.DayInfoColor; + } + + if (style.DepthChart) + { + var item=style.DepthChart; + if (item.BidColor) + { + if (item.BidColor.Line) this.DepthChart.BidColor.Line=item.BidColor.Line; + if (item.BidColor.Area) this.DepthChart.BidColor.Area=item.BidColor.Area; + } + if (item.AskColor) + { + if (item.AskColor.Line) this.DepthChart.AskColor.Line=item.AskColor.Line; + if (item.AskColor.Area) this.DepthChart.AskColor.Area=item.AskColor.Area; + } + + if (item.LineWidth) this.DepthChart.LineWidth=item.LineWidth; + } + + if (style.DepthCorss) + { + var item=style.DepthCorss; + if (item.BidColor) + { + if (item.BidColor.Line) this.DepthCorss.BidColor.Line=item.BidColor.Line; + } + + if (item.AskColor) + { + if (item.AskColor.Line) this.DepthCorss.AskColor.Line=item.AskColor.Line; + } + + if (item.LineWidth) this.DepthCorss.LineWidth=item.LineWidth; + if (item.LineDash) this.DepthCorss.LineDash=item.LineDash; + + if (item.Tooltip) + { + var tooltip=item.Tooltip; + if (tooltip.BGColor) this.DepthCorss.Tooltip.BGColor=tooltip.BGColor; + if (tooltip.TextColor) this.DepthCorss.Tooltip.TextColor=tooltip.TextColor; + if (tooltip.Font) this.DepthCorss.Tooltip.Font=tooltip.Font; + if (tooltip.LineHeight) this.DepthCorss.Tooltip.LineHeight=tooltip.LineHeight; + + var border=tooltip.Border; + if (IFrameSplitOperator.IsNumber(border.Top)) this.DepthCorss.Tooltip.Border.Top=border.Top; + if (IFrameSplitOperator.IsNumber(border.Left)) this.DepthCorss.Tooltip.Border.Left=border.Left; + if (IFrameSplitOperator.IsNumber(border.Bottom)) this.DepthCorss.Tooltip.Border.Bottom=border.Bottom; + if (IFrameSplitOperator.IsNumber(border.Center)) this.DepthCorss.Tooltip.Border.Center=border.Center; + } + } + + if (style.CIRCLEDOT) + { + var item=style.CIRCLEDOT; + if (IFrameSplitOperator.IsNumber(item.Radius)) this.CIRCLEDOT.Radius=item.Radius; + } + + if (style.POINTDOT) + { + var item=style.POINTDOT; + if (IFrameSplitOperator.IsNumber(item.Radius)) this.POINTDOT.Radius=item.Radius; + } + + if (style.RectSelect) + { + var item=style.RectSelect; + if (item.LineColor) this.RectSelect.LineColor=item.LineColor; + if (item.LineWidth>0) this.RectSelect.LineWidth=item.LineWidth; + if (item.LineDotted) this.RectSelect.LineDotted=item.LineDotted; + if (item.AreaColor) this.RectSelect.AreaColor=item.AreaColor; + } + } +} + +var g_JSChartResource=new JSChartResource(); + + +///////////////////////////////////////////////////////////////////////////////// +// +// +// +var JSCHART_LANGUAGE_ID= +{ + LANGUAGE_CHINESE_ID:0, + LANGUAGE_ENGLISH_ID:1 +}; + +function JSChartLocalization() +{ + this.TextResource=new Map([ + //内部tooltip + ['Tooltip-Open', {CN:'开:', EN:'O:'}], + ['Tooltip-High', {CN:'高:', EN:'H:'}], + ['Tooltip-Low', {CN:'低:', EN:'L:'}], + ['Tooltip-Close', {CN:'收:', EN:'C:'}], + ['Tooltip-Increase', {CN:'幅:', EN:'I:'}], + ['Tooltip-Vol', {CN:'量:', EN:'V:'}], + ['Tooltip-Amount', {CN:'额:', EN:'A:'}], + ['Tooltip-AvPrice', {CN:'均:', EN:'AP:'}], + ['Tooltip-Price', {CN:'价:', EN:'P:'}], + ['Tooltip-Exchange', {CN:'换:', EN:'E:'}], + ['Tooltip-Position',{CN:'持:', EN:'P:'}], + + ['DivTooltip-Open', {CN:'开盘:', EN:'Open:'}], + ['DivTooltip-High', {CN:'最高:', EN:'High:'}], + ['DivTooltip-Low', {CN:'最低:', EN:'Low:'}], + ['DivTooltip-Close', {CN:'收盘:', EN:'Close:'}], + ['DivTooltip-Increase', {CN:'涨幅:', EN:'Increase:'}], + ['DivTooltip-Vol', {CN:'数量:', EN:'Volume:'}], + ['DivTooltip-Amount', {CN:'金额:', EN:'Amount:'}], + ['DivTooltip-Exchange', {CN:'换手:', EN:'Exchange:'}], + ['DivTooltip-Position', {CN:'持仓:', EN:'Position:'}], + ['DivTooltip-Price', {CN:'价格:', EN:'Open:'}], + + //K线动态标题 + ['KTitle-Open', {CN:'开:', EN:'O:'}], + ['KTitle-High', {CN:'高:', EN:'H:'}], + ['KTitle-Low', {CN:'低:', EN:'L:'}], + ['KTitle-Close', {CN:'收:', EN:'C:'}], + ['KTitle-Increase', {CN:'幅:', EN:'I:'}], + ['KTitle-Vol', {CN:'量:', EN:'V:'}], + ['KTitle-Amount', {CN:'额:', EN:'A:'}], + ['KTitle-Exchange', {CN:'换:', EN:'E:'}], + ['KTitle-Position', {CN:'持:', EN:'P:'}], + ['KTitle-Price', {CN:'价:', EN:'Price:'}], + + //走势图动态标题 + ['MTitle-AC-Price', {CN:'匹配价:', EN:'C:'}], + ['MTitle-AC-AvPrice', {CN:'匹配均价:', EN:'C:'}], + ['MTitle-AC-Increase', {CN:'竞价涨幅:', EN:'I:'}], + ['MTitle-AC-Vol', {CN:'匹配量:', EN:'V:'}], + ['MTitle-AC-NotMatchVol', {CN:'未匹配量:', EN:'NV:'}], + + //走势图标题 + ['MTitle-Close', {CN:'价:', EN:'C:'}], + ['MTitle-AvPrice', {CN:'均:', EN:'AC:'}], + ['MTitle-Increase', {CN:'幅:', EN:'I:'}], + ['MTitle-Vol', {CN:'量:', EN:'V:'}], + ['MTitle-Amount', {CN:'额:', EN:'A:'}], + ['MTitle-Position', {CN:'持:', EN:'P:'}], + + //周期 + ['日线', {CN:'日线', EN:'1D'}], + ['周线', {CN:'周线', EN:'1W'}], + ['双周', {CN:'双周', EN:"2W"}], + ['月线', {CN:'月线', EN:'1M'}], + ["半年", {CN:'半年', EN:'HY'}], + ['年线', {CN:'年线', EN:'1Y'}], + ['1分', {CN:'1分', EN:'1Min'}], + ['5分', {CN:'5分', EN:'5Min'}], + ['15分', {CN:'15分', EN:'15Min'}], + ['30分', {CN:'30分', EN:'30Min'}], + ['60分', {CN:'60分', EN:'60Min'}], + ['季线', {CN:'季线', EN:'1Q'}], + ['分笔', {CN:'分笔', EN:'Tick'}], + ['2小时', {CN:'2小时', EN:'2H'}], + ['4小时', {CN:'4小时', EN:'4H'}], + + //复权 + ['不复权', {CN:'不复权', EN:'No Right'}], + ['前复权', {CN:'前复权', EN:'Pro Right'}], + ['后复权', {CN:'后复权', EN:'Post Right'}], + + //week + ['日', {CN:'日', EN:'Sun.'}], + ['一', {CN:'一', EN:'Mon.'}], + ['二', {CN:'二', EN:'Tues.'}], + ['三', {CN:'三', EN:'Wed.'}], + ['四', {CN:'四', EN:'Thur.'}], + ['五', {CN:'五', EN:'Fri.'}], + ['六', {CN:'六', EN:'Sat.'}], + + ['1月', {CN:'1月', EN:'Jan'}], + ['2月', {CN:'2月', EN:'Feb'}], + ['3月', {CN:'3月', EN:'Mar'}], + ['4月', {CN:'4月', EN:'Apr'}], + ['5月', {CN:'5月', EN:'May'}], + ['6月', {CN:'6月', EN:'Jun'}], + ['7月', {CN:'7月', EN:'Jul'}], + ['8月', {CN:'8月', EN:'Aug'}], + ['9月', {CN:'9月', EN:'Sept'}], + ['10月', {CN:'10月', EN:'Oct'}], + ['11月', {CN:'11月', EN:'Nov'}], + ['12月', {CN:'12月', EN:'Dec'}], + + ['自定义分钟', {CN:'分', EN:'Min'}], + ['自定义日线', {CN:'日', EN:'D'}], + ['自定义秒', {CN:'秒', EN:'S'}], + + ["MVol-Vol", {CN:"成交量", EN:"Volume"} ], + ["MVol-Position", {CN:"持仓量", EN:"Position"} ] + + ]); + + this.GetText=function(key,language) + { + var item=this.TextResource.get(key); + if (!item) return ''; + + switch(language) + { + case JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID: + return item.CN; + case JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID: + return item.EN; + default: + return item.CN; + } + } + + this.SetTextResource=function(key,value) + { + this.TextResource.set(key,value) + } +}; + +var g_JSChartLocalization=new JSChartLocalization(); + + + +/* + 指标列表 指标信息都在这里,不够后面再加字段 +*/ +function JSIndexMap() +{ + +} + +JSIndexMap.Get=function(id) +{ + var indexMap=new Map( + [ + //公司自己的指标 + ["市场多空", {IsMainIndex:false, Create:function(){ return new MarketLongShortIndex()} }], + ["市场择时", {IsMainIndex:false, Create:function(){ return new MarketTimingIndex()} }], + ["市场关注度", {IsMainIndex:false, Create:function(){ return new MarketAttentionIndex()} }], + ["指数热度", {IsMainIndex:false, Create:function(){ return new MarketHeatIndex()} }], + ["财务粉饰", {IsMainIndex:false, Create:function(){ return new BenfordIndex()} }], + + ["自定义指数热度", {IsMainIndex:false, Create:function(){ return new CustonIndexHeatIndex()} , Name:'自定义指数热度'} ], //废弃 + + //能图指标 + ["能图-趋势", {IsMainIndex:false, Create:function(){ return new LighterIndex1()}, Name:'大盘/个股趋势' }], //废弃 + ["能图-位置研判", {IsMainIndex:false, Create:function(){ return new LighterIndex2()}, Name:'位置研判' }], //废弃 + ["能图-点位研判", {IsMainIndex:false, Create:function(){ return new LighterIndex3()}, Name:'点位研判' }], //废弃 + + ['CJL', { IsMainIndex:false, Create:function(){ return new JSIndex_CJL(); } } ], //期货指标 + ] + ); + + return indexMap.get(id); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// 指标计算方法 +// +// +// + +function HQIndexFormula() +{ + +} + +//指数平均数指标 EMA(close,10) +HQIndexFormula.EMA=function(data,dayCount) +{ + var result = []; + + var offset=0; + if (offset>=data.length) return result; + + //取首个有效数据 + for(;offsetdata.length) return result; + + var max=-10000; + for(var i=n,j=0;idata[max]) + max = j; + } + } + + result[i] = data[max]; + } + + return result; +} + +HQIndexFormula.LLV=function(data,n) +{ + var result = []; + if (n>data.length) return result; + + var min=-10000; + + for(var i=n;idata[min]?min:i; + } + else + { + for(var j=(min=i-n+1)+1;j<=i;++j) + { + if(data[j]=data.length) return result; + + result=data.slice(0,data.length-n); + + for(var i=0;idata2比较 返回 0/1 数组 +HQIndexFormula.ARRAY_GT=function(data,data2) +{ + var result=[]; + var IsNumber=typeof(data2)=="number"; + if (IsNumber) + { + for(var i in data) + { + result[i]=(data[i]>data2 ? 1:0); + } + } + else + { + var count=Math.max(data.length,data2.length) + + for(var i=0;idata2[i] ? 1:0; + else + result[i]=null; + } + } + + return result; +} + +//数组 data>=data2比较 返回 0/1 数组 +HQIndexFormula.ARRAY_GTE=function(data,data2) +{ + var result=[]; + var IsNumber=typeof(data2)=="number"; + if (IsNumber) + { + for(var i in data) + { + result[i]=(data[i]>=data2 ? 1:0); + } + } + else + { + var count=Math.max(data.length,data2.length) + + for(var i=0;i=data2[i] ? 1:0; + else + result[i]=null; + } + } + + return result; +} + +//数组 data=data.length) return result; + + var i=dayCount; + for(;i=data.length) return result; + + var index=0; + for(;indexdata2[index]&&data[index-1]=0) + { + var data=null; + if (this.Frame.Data) + data=this.Frame.Data; + else + data=this.Frame.SubFrame[0].Frame.Data; + if (data) data.DataOffset=obj.DataOffset; + } + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(1); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); //刷新画图 + + this.SendManualKLineUpdateEvent(bindData); + } + + //获取k线数据 + this.GetKDataInfo=function() + { + if (!this.ChartPaint[0]) return null; + + var chartKLine=this.ChartPaint[0]; + var obj={}; + if (chartKLine.Data && chartKLine.Data.Data) + obj.Data=chartKLine.Data.CloneData("HistoryData"); + obj.ShowRange=chartKLine.ShowRange; + obj.Period=this.Period; + obj.RightSpaceCount=this.RightSpaceCount; + + return obj; + } + + this.ChartOperator=function(obj) //图形控制函数 {ID:JSCHART_OPERATOR_ID, ...参数 } + { + var id=obj.ID; + if (id===JSCHART_OPERATOR_ID.OP_SCROLL_LEFT || id===JSCHART_OPERATOR_ID.OP_SCROLL_RIGHT ) //左右移动 { Step:移动数据个数 } + { + var isLeft=(id===JSCHART_OPERATOR_ID.OP_SCROLL_LEFT ? true:false); + var step=1; + if (obj.Step>0) step=obj.Step; + var oneStepWidth=this.GetMoveOneStepWidth(); + if(this.DataMove(step*oneStepWidth,isLeft)) //每次移动一个数据 + { + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.ResetFrameXYSplit(); + this.Draw(); + } + else + { + if (id===JSCHART_OPERATOR_ID.OP_SCROLL_RIGHT && this.DragDownloadData) + this.DragDownloadData(); + } + } + else if (id===JSCHART_OPERATOR_ID.OP_ZOOM_IN || id===JSCHART_OPERATOR_ID.OP_ZOOM_OUT) //缩放 + { + var cursorIndex={}; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (id===JSCHART_OPERATOR_ID.OP_ZOOM_IN) + { + if (!this.Frame.ZoomUp(cursorIndex)) return; + } + else + { + if (!this.Frame.ZoomDown(cursorIndex)) return; + } + this.CursorIndex=cursorIndex.Index; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + } + else if (id===JSCHART_OPERATOR_ID.OP_GOTO_HOME) + { + var hisData=this.ChartOperator_Temp_GetHistroyData();; + if (!hisData) return; //数据还没有到达 + + var showCount=this.Frame.SubFrame[0].Frame.XPointCount; //获取一屏显示的数据个数 + showCount-=this.RightSpaceCount; + var index=hisData.Data.length-showCount; + hisData.DataOffset=index; + this.CursorIndex=0; + + this.LastPoint.X=null; + this.LastPoint.Y=null; + + JSConsole.Chart.Log(`[KLineChartContainer::ChartOperator] OP_GOTO_HOME, dataOffset=${hisData.DataOffset} CursorIndex=${this.CursorIndex} PageSize=${showCount}`); + + this.ChartOperator_Temp_Update(); + } + else if (id===JSCHART_OPERATOR_ID.OP_GOTO_END) + { + var hisData=this.ChartOperator_Temp_GetHistroyData(); + if (!hisData) return; //数据还没有到达 + + hisData.DataOffset=0; + this.CursorIndex=0; + + this.LastPoint.X=null; + this.LastPoint.Y=null; + + JSConsole.Chart.Log(`[KLineChartContainer::ChartOperator] OP_GOTO_END `); + + this.ChartOperator_Temp_Update(); + } + else if (id==JSCHART_OPERATOR_ID.OP_LEFT_ZOOM_IN || id==JSCHART_OPERATOR_ID.OP_LEFT_ZOOM_OUT) //{ Step:增加/减少数量 } 左边增加/减少显示个数 + { + var hisData=this.ChartOperator_Temp_GetHistroyData(); + if (!hisData) return; //数据还没有到达 + + var dataCount=hisData.Data.length; + var showCount=this.Frame.SubFrame[0].Frame.XPointCount; //获取一屏显示的数据个数 + var dataOffset=hisData.DataOffset; + + if (id==JSCHART_OPERATOR_ID.OP_LEFT_ZOOM_IN) //增加 + { + if (IFrameSplitOperator.IsNumber(obj.Step)) + { + if (showCount>=dataCount) return; + var step=obj.Step; + if (step>dataOffset) step=dataOffset; + if (showCount+step>dataCount) step=dataCount-showCount; + if (step<=0) return; + + showCount+=step; + hisData.DataOffset-=step; + } + } + else if (id==JSCHART_OPERATOR_ID.OP_LEFT_ZOOM_OUT) //减少 + { + var pageSize = this.GetMaxMinPageSize(); + var minShowCount=pageSize.Min; + if (IFrameSplitOperator.IsNumber(obj.Step)) + { + if (showCount=dataCount) return; + var endPos=dataOffset+showCount; + var rightCount=dataCount-endPos; + if (rightCount<=0) return; + + var step=obj.Step; + if (step>rightCount) step=rightCount; + if (step<=0) return; + + showCount+=step; + } + } + else if (id==JSCHART_OPERATOR_ID.OP_RIGHT_ZOOM_OUT) //减少 + { + var pageSize = this.GetMaxMinPageSize(); + var minShowCount=pageSize.Min; + if (IFrameSplitOperator.IsNumber(obj.Step)) + { + if (showCountshowCount) step=showCount; + if (showCount-step0 && enableZoomUpDown) //放大 + { + var cursorIndex={ ZoomType:this.ZoomType }; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (this.Frame.ZoomUp(cursorIndex)) + { + JSConsole.Chart.Log("[KLineChartContainer::OnWheel] cursorIndex ",cursorIndex) + + this.CursorIndex=cursorIndex.Index; + this.UpdatePointByCursorIndex(); + this.UpdataDataoffset(); + this.UpdateFrameMaxMin(); + this.Draw(); + } + } + } + + if (!isInClient) return; + + if(e.preventDefault) e.preventDefault(); + else e.returnValue = false; + } + + //创建 + //windowCount 窗口个数 + this.Create=function(windowCount, option) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new ChartCorssCursor(); + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.StringFormatX=g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY=g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY.ExtendChartPaint=this.ExtendChartPaint; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame=new HQTradeFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=30; + this.Frame.ChartBorder.Left=5; + this.Frame.ChartBorder.Bottom=20; + this.Frame.Canvas=this.Canvas; + this.Frame.GetExtendChartRightWidth=()=> { return this.GetExtendChartRightWidth() } + this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + this.CreateExtendChart("RectSelectPaint"); //区间统计 + + //子窗口动态标题 + for(var i in this.Frame.SubFrame) + { + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[i].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + + this.TitlePaint.push(titlePaint); + } + + var bRegisterKeydown=true; + var bRegisterWheel=true; + + if (option) + { + if (option.KeyDown===false) + { + bRegisterKeydown=false; + JSConsole.Chart.Log('[KLineChartContainer::Create] not register keydown event.'); + } + + if (option.Wheel===false) + { + bRegisterWheel=false; + JSConsole.Chart.Log('[KLineChartContainer::Create] not register wheel event.'); + } + } + + if (bRegisterKeydown) this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e); }, true); //键盘消息 + if (bRegisterWheel) this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true); //上下滚动消息 + } + + //创建子窗口 + this.CreateChildWindow=function(windowCount) + { + for(var i=0;i { return this.GetEventCallback(id); } + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + border.BottomSpace=15*pixelTatio; //主图上下留空间 + border.TopSpace=15*pixelTatio; + } + else + { + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); }; + //frame.IsLocked = true; + } + + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitKLineX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.LanguageID=this.LanguageID; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + + if (i!=windowCount-1) frame.XSplitOperator.ShowText=false; + + for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=j; + if (i==0 && j==frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1]=j.toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + if (i==0) + subFrame.Height=20; + else + subFrame.Height=10; + + this.Frame.SubFrame[i]=subFrame; + } + } + + this.CreateSubFrameItem=function(id) + { + var border=new ChartBorder(); + border.UIElement=this.UIElement; + + var frame=new KLineFrame(); + frame.Canvas=this.Canvas; + frame.ChartBorder=border; + frame.Identify=id; //窗口序号 + + if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal; + + frame.HorizontalMax=20; + frame.HorizontalMin=10; + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitKLineX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.ShowText=false; + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); }; + + //K线数据绑定 + var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount; + frame.XPointCount=xPointCouont; + frame.Data=this.ChartPaint[0].Data; + + for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=j; + frame.HorizontalInfo[j].Message[1]=j.toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + subFrame.Height=10; + + return subFrame; + } + + //创建主图K线画法 + this.CreateMainKLine=function() + { + var kline=new ChartKLine(); + kline.Canvas=this.Canvas; + kline.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + kline.ChartFrame=this.Frame.SubFrame[0].Frame; + kline.Name="Main-KLine"; + kline.DrawType=this.KLineDrawType; + kline.GetEventCallback=(id)=>{ return this.GetEventCallback(id); }; + + this.ChartPaint[0]=kline; + + this.TitlePaint[0]=new DynamicKLineTitlePainting(); + this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas=this.Canvas; + this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint; //绑定叠加 + this.TitlePaint[0].LanguageID=this.LanguageID; + } + + //绑定主图K线数据 + this.BindMainData=function(hisData,showCount) + { + this.ChartPaint[0].Data=hisData; + this.ChartPaint[0].Symbol=this.Symbol; + + if (this.KLineSize) + { + if (this.KLineSize.DataWidth==null) + { + showCount=this.Frame.SubFrame[0].Frame.XPointCount-this.RightSpaceCount; + } + else + { + var obj=this.Frame.SetDataWidth(this.KLineSize.DataWidth); + showCount=obj.XPointCount-this.RightSpaceCount; + this.KLineSize.DataWidth=null; + } + } + + for(var i in this.Frame.SubFrame) + { + var item =this.Frame.SubFrame[i].Frame; + item.XPointCount=showCount+this.RightSpaceCount; + item.Data=this.ChartPaint[0].Data; + + item.XSplitOperator.Symbol=this.Symbol; + item.XSplitOperator.Period=this.Period; + } + + this.TitlePaint[0].Data=this.ChartPaint[0].Data; //动态标题 + this.TitlePaint[0].Symbol=this.Symbol; + this.TitlePaint[0].Name=this.Name; + this.TitlePaint[0].Period=this.Period; + + this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data; //十字光标 + this.Frame.Data=this.ChartPaint[0].Data; + + for(var i in this.OverlayChartPaint) //K线叠加 主图股票数据绑定到叠加上 + { + var item=this.OverlayChartPaint[i]; + item.MainData=this.ChartPaint[0].Data; + } + + var dataOffset=hisData.Data.length-showCount; + if (dataOffset<0) dataOffset=0; + this.ChartPaint[0].Data.DataOffset=dataOffset; + this.ChartPaint[0].Period=this.Period; + + this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol; + + this.CursorIndex=showCount; + if (this.CursorIndex+dataOffset>=hisData.Data.length) this.CursorIndex=hisData.Data.length-1-dataOffset; + if (this.CursorIndex<0) this.CursorIndex=0; //不一定对啊 + + if (this.CustomShow) //定制显示 1次有效 + { + this.SetCustomShow(this.CustomShow,hisData); + this.CustomShow=null; + } + } + + this.UpdateMainData=function(hisData, lastDataCount) + { + var frameHisdata=null; + if (!this.Frame.Data) frameHisdata=this.Frame.Data; + else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata=this.Frame.SubFrame[0].Frame.Data; + if (!frameHisdata) return; + var xPointCount=this.Frame.SubFrame[0].Frame.XPointCount; //当前一屏能显示的数据个数 + + var newDataCount=0; + if (lastDataCount>0 && hisData.Data.length>lastDataCount) + { + newDataCount=hisData.Data.length-lastDataCount; + JSConsole.Chart.Log(`[KLineChartContainer::UpdateMainData] [count=${lastDataCount}->${hisData.Data.length}], [newDataCount=${newDataCount}]`); + } + + this.ChartPaint[0].Data=hisData; + this.ChartPaint[0].Symbol=this.Symbol; + if (hisData.Data.length>xPointCount) //不满一屏的, 不需要调整索引 + this.ChartPaint[0].Data.DataOffset=frameHisdata.DataOffset+newDataCount; //加上数据增加的个数 + for(var i in this.Frame.SubFrame) + { + var item =this.Frame.SubFrame[i].Frame; + item.Data=this.ChartPaint[0].Data; + + if (i==0) //更新Y轴K线数据 + { + item.YSplitOperator.Symbol=this.Symbol; + item.YSplitOperator.Data=this.ChartPaint[0].Data; //K线数据 + item.YSplitOperator.Period=this.Period; //周期 + } + } + + this.TitlePaint[0].Data=this.ChartPaint[0].Data; //动态标题 + this.TitlePaint[0].Symbol=this.Symbol; + this.TitlePaint[0].Name=this.Name; + + this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data; //十字光标 + this.Frame.Data=this.ChartPaint[0].Data; + + for(var i in this.OverlayChartPaint) //主图股票数据绑定到叠加股票上 + { + var item=this.OverlayChartPaint[i]; + item.MainData=this.ChartPaint[0].Data; + } + + this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol; + } + + this.SetCustomShow=function(customShow,hisData) + { + if (!customShow || !customShow.Date || customShow.Date<19910101) return; + + var firstDate=customShow.Date; + var index=null; + for(var i =0;i=firstDate) + { + index=i; + break; + } + } + if (index===null) + { + JSConsole.Chart.Log(`[KLineChartContainer::SetCustomShow] Can't find first date=${firstDate}`); + return; + } + + var count=hisData.Data.length-index; + if (customShow.PageSize>0) count=customShow.PageSize; + + var customShowCount=count; + var pageSize=this.GetMaxMinPageSize(); + if (count>pageSize.Max) customShowCount=pageSize.Max; + else if (count=kData.Data.length) index=kData.Data.length-1; + + var item = kData.Data[index]; + + JSConsole.Chart.Log("[KLineChartContainer::OnMarkRectSelect] item", item); + + if (!this.SetRectSelectData(item)) return; + + if (paint.GetPointCount()==2) + { + var selectData=paint.GetSelectRectData(); + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_KEYDOWN_SELECT_RECT); + var pixelTatio = GetDevicePixelRatio(); + if (event && event.Callback) + { + var data= + { + X:corssCursor.LastPoint.X/pixelTatio, + Y:corssCursor.LastPoint.Y/pixelTatio, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + event.Callback(event,data,this); + } + + if (this.SelectRectRightMenu) + { + e.data= + { + Chart:this, + X:corssCursor.LastPoint.X/pixelTatio, + Y:corssCursor.LastPoint.Y/pixelTatio, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + this.SelectRectRightMenu.DoModal(e); + } + } + + + this.Draw(); + } + + //创建指定窗口指标 + this.CreateWindowIndex=function(windowIndex) + { + this.WindowIndex[windowIndex].Create(this,windowIndex); + } + + this.BindIndexData=function(windowIndex,hisData) + { + if (!this.WindowIndex[windowIndex]) return; + + if (typeof(this.WindowIndex[windowIndex].RequestData)=="function") //数据需要另外下载的. + { + this.WindowIndex[windowIndex].RequestData(this,windowIndex,hisData); + return; + } + if (typeof(this.WindowIndex[windowIndex].ExecuteScript)=='function') + { + this.WindowIndex[windowIndex].ExecuteScript(this,windowIndex,hisData); + return; + } + + this.WindowIndex[windowIndex].BindData(this,windowIndex,hisData); + } + + //叠加指标 + this.BindOverlayIndexData=function(overlayItem, windowIndex, hisData) + { + if (!overlayItem.Script) return; + + if (typeof(overlayItem.Script.RequestData)=='function') + { + overlayItem.Script.RequestData(this,windowIndex,hisData); + return; + } + + if (typeof(overlayItem.Script.ExecuteScript)=='function') + { + overlayItem.Script.ExecuteScript(this,windowIndex,hisData); + return; + } + + overlayItem.Script.BindData(this,windowIndex,hisData); + } + + //执行指示(专家指示 五彩K线) + this.BindInstructionIndexData=function(hisData) + { + if (this.ColorIndex && typeof(this.ColorIndex.ExecuteScript)=='function') //五彩K线 + { + this.ColorIndex.ExecuteScript(this,0,hisData); + } + + if (this.TradeIndex && typeof(this.TradeIndex.ExecuteScript)=='function') //交易指标 + { + this.TradeIndex.ExecuteScript(this,0,hisData); + } + } + + //获取子窗口的所有画法 + this.GetChartPaint=function(windowIndex) + { + var paint=new Array(); + for(var i in this.ChartPaint) + { + if (i==0) continue; //第1个K线数据除外 + + var item=this.ChartPaint[i]; + if (item.ChartFrame==this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + return paint; + } + + this.AutoUpdateEvent=function(bStart, explain) //自定更新事件, 是给websocket使用 + { + var eventID=bStart ? JSCHART_EVENT_ID.RECV_START_AUTOUPDATE:JSCHART_EVENT_ID.RECV_STOP_AUTOUPDATE; + if (!this.mapEvent.has(eventID)) return; + + var self=this; + var event=this.mapEvent.get(eventID); + var data={ Stock:{ Symbol:this.Symbol, Name:this.Name, Right:this.Right, Period:this.Period }, Explain: explain }; + if (bStart) + { + data.Callback=function(data) //数据到达更新回调 + { + if (ChartData.IsDayPeriod(self.Period,true)) self.RecvRealtimeData(data); + else if (ChartData.IsMinutePeriod(self.Period,true)) self.RecvMinuteRealtimeData(data); + else if (ChartData.IsSecondPeriod(self.Period)) self.RecvMinuteRealtimeData(data); + } + } + event.Callback(event,data,this); + } + + this.SendKLineUpdateEvent=function(bindData) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.RECV_KLINE_UPDATE_DATA); + if (event && event.Callback) + { + var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } } + event.Callback(event,data,this); + return true; + } + + return false; + } + + this.SendManualKLineUpdateEvent=function(bindData) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.RECV_KLINE_MANUAL_UPDATE_DATA); + if (event && event.Callback) + { + var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } } + event.Callback(event,data,this); + return true; + } + + return false; + } + + + this.RequestHistoryData=function() + { + var self=this; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.FlowCapitalReady=false; + this.ResetPage(); //重置分页 + this.ResetDragDownload(); + this.Draw(); + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestHistoryData', //类名:: + Explain:'日K数据', + Request:{ Url:self.KLineApiUrl, Type:'POST' , + Data: { symbol:self.Symbol, count:self.MaxReqeustDataCount, field: ["name","symbol","yclose","open","price","high","low","vol"] } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + var page=self.Page.Day; + if (page.Enable==true && page.Finish==false) + { + self.RequestHistoryPageData(); + } + else + { + self.AutoUpdateEvent(true,'KLineChartContainer::RequestHistoryData'); + self.AutoUpdate(); + } + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: this.KLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxReqeustDataCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + var page=self.Page.Day; + if (page.Enable==true && page.Finish==false) + { + self.RequestHistoryPageData(); + } + else + { + self.AutoUpdateEvent(true,'KLineChartContainer::RequestHistoryData'); + self.AutoUpdate(); + } + } + }); + } + + this.BindAllOverlayIndexData=function(hisData) + { + if (!this.Frame || !this.Frame.SubFrame) return; + + //叠加指标 + for(var i=0;i0 && !this.IsApiPeriod) //复权 + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula } ); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false) && !this.IsApiPeriod) //周期数据 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.Symbol=data.symbol; + this.Name=data.name; + + this.BindMainData(bindData,this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("RecvHistoryData"); + this.Frame.SetSizeChage(true); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + var firstSubFrame; + for(var i=0; i0 && bindData.Period<=3) //复权(日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right,{ AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.UpdateMainData(bindData,lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; i0 && ChartData.IsDayPeriod(bindData.Period,false)) //复权(日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right,{ AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.UpdateMainData(bindData,lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; i0) + newItem.YClose=this.SourceData.Data[this.SourceData.Data.length-1].YClose; + + this.SourceData.Data.push(newItem); + } + else + { + return; + } + + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=this.SourceData.DataType; + bindData.Symbol=this.Symbol; + + if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true) && MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol) && !this.IsApiPeriod) //复权(A股日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right,{ AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (!this.IsApiPeriod) + { + if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + } + + this.UpdateMainData(bindData,lastDataCount);//更新主图数据 + this.UpdateOverlayRealtimeData(data); //更新叠加股票数据 + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; i0 && MARKET_SUFFIX_NAME.IsSHSZStockA(data.symbol) && !this.IsApiPeriod) //复权数据 ,A股才有有复权 + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data, this.IsApiPeriod); //和主图数据拟合以后的数据 + bindData.Data=aryOverlayData; + + if (ChartData.IsDayPeriod(bindData.Period,false) && !this.IsApiPeriod) //周期数据 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + item.Data=bindData; + } + } + + this.RequestMinuteRealtimeData=function() + { + var self=this; + var arySymbol=[self.Symbol]; + for(var i in this.OverlayChartPaint) //叠加的股票更新 + { + var item=this.OverlayChartPaint[i]; + if (!item.Symbol) continue; + if (!item.MainData) continue; //等待主图股票数据未下载完 + if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue; + arySymbol.push(item.Symbol); + } + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestMinuteRealtimeData', //类名:: + Explain:'当天1分钟K线数据', + Request:{ Url:self.RealtimeApiUrl, Data:{ symbol: arySymbol, + field:["name","symbol","price","yclose","minutecount","minute","date","time"] }, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvMinuteRealtimeData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: this.RealtimeApiUrl, + data: + { + "field": ["name","symbol","price","yclose","minutecount","minute","date","time"], + "symbol": arySymbol, + "start": -1 + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvMinuteRealtimeData(data); + self.AutoUpdate(); + } + }); + } + + this.SetSourceDatatLimit=function(aryLimit) + { + this.SourceDataLimit=new Map(); + for(var i in aryLimit) + { + var item=aryLimit[i]; + this.SourceDataLimit.set(item.Period, item.MaxCount); //每个周期缓存数据最大个数 key=周期 value=最大个数 + + JSConsole.Chart.Log(`[KLineChartContainer::SetSourceDatatLimit] Period=${item.Period}, MaxCount=${item.MaxCount}`); + } + } + + this.ReduceSourceData=function() + { + if (!this.SourceDataLimit) return; + if (!this.SourceDataLimit.has(this.Period)) return; + + var limitCount=this.SourceDataLimit.get(this.Period); + + var frameHisdata=null; + if (!this.Frame.Data) frameHisdata=this.Frame.Data; + else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata=this.Frame.SubFrame[0].Frame.Data; + if (!frameHisdata) return; + + if (limitCount<50) return; + var dataOffset=frameHisdata.DataOffset; + var removeCount=0; + while(this.SourceData.Data.length>limitCount) + { + this.SourceData.Data.shift(); + --dataOffset; + ++removeCount; + } + + if (removeCount>0) + { + if (dataOffset<0) dataOffset=0; + frameHisdata.DataOffset=dataOffset; + JSConsole.Chart.Log(`[KLineChartContainer::ReduceSourceData] remove data ${removeCount}, dataOffset=${dataOffset}`); + } + } + + this.RecvMinuteRealtimeDataV2=function(data) //新版本的 + { + if (this.IsOnTouch==true) return; //正在操作中不更新数据 + var aryMinuteData=KLineChartContainer.JsonDataToMinuteHistoryData(data); + if (!aryMinuteData || aryMinuteData.length<=0) return; + if (this.IsApiPeriod) this.ReduceSourceData(); //减少数据 + var lastDataCount=this.GetHistoryDataCount(); //保存下上一次的数据个数 + if (!this.SourceData) return; + if (!this.SourceData.MergeMinuteData(aryMinuteData)) return; + + JSConsole.Chart.Log(`[KLineChartContainer::RecvMinuteRealtimeDataV2] update kline by 1 minute data [${lastDataCount}->${this.SourceData.Data.length}]`); + + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=this.SourceData.DataType; + bindData.Symbol=this.Symbol; + + if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true) && !this.IsApiPeriod) //复权(日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if ((ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) && !this.IsApiPeriod) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.UpdateMainData(bindData,lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; i${this.SourceData.Data.length}]`); + + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=this.SourceData.DataType; + bindData.Symbol=this.Symbol; + + if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true) && !this.IsApiPeriod) //复权(日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if ( (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) && !this.IsApiPeriod) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.UpdateMainData(bindData,lastDataCount); + this.UpdateOverlayMinuteRealtimeData(data); //更新叠加股票数据 + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; iresult.MaxRequestDataCount) result.MaxRequestDataCount=lCount; + } + else if (ChartData.IsMinutePeriod(this.Period,true)) + { + var date; + var lCount=0; + for(var i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + if (item.Date!=date) ++lCount; + date=item.Date; + } + if (lCount>result.MaxRequestMinuteDayCount) result.MaxRequestMinuteDayCount=lCount; + } + + return result; + } + + //分笔数据 + this.RequestTickData=function() + { + var self=this; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + self.ChartSplashPaint.EnableSplash(true); + self.Draw(); + + //下载缓存文件 + var cacheUrl=g_JSChartResource.CacheDomain+'/cache/dealday/today/'+self.Symbol+'.json' + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestTickData', //类名:: + Explain:'当天分笔数据', + Request:{ Url:cacheUrl, Data:{ symbol: self.Symbol }, Type:'GET' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvTickData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: cacheUrl, + data:{"symbol":self.Symbol}, + type:"get", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvTickData(data); + self.AutoUpdate(1); + }, + error: function(http,e) + { + self.ChartSplashPaint.EnableSplash(false); + self.AutoUpdate(); + //self.RecvError(http,e,param);; + } + }); + + } + + this.RecvTickData=function(data) + { + if (data.ver==2.0) + var aryDayData=KLineChartContainer.JsonDataToTickDataV2(data); + else + var aryDayData=KLineChartContainer.JsonDataToTickData(data); + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=aryDayData; + sourceData.DataType=2; //0=日线数据 1=分钟数据 2=分笔数据 + this.SourceData=sourceData; + if (this.BeforeBindMainData) this.BeforeBindMainData("RecvTickData"); + + //显示的数据 + var bindData=new ChartData(); + bindData.Data=aryDayData; + bindData.Right=this.Right; + bindData.Period=this.Period; + bindData.DataType=2; + + //绑定数据 + + this.Symbol=data.symbol; + this.Name=data.name; + this.BindMainData(bindData,this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("RecvTickData"); + + this.BindInstructionIndexData(bindData); //执行指示脚本 + + var firstSubFrame; + for(var i=0; i0) lastTime=source[source.length-1].Time; + var newCount=0; + for(var i in aryDayData) + { + var item=aryDayData[i]; + if (item.Time<=lastTime) continue; + source.push(item); + ++newCount; + } + if (newCount<=0 && redraw==false) return; + + //显示的数据 + var bindData=new ChartData(); + bindData.Data=source; + bindData.Right=this.Right; + bindData.Period=this.Period; + bindData.DataType=2; + + //绑定数据 + this.Symbol=data.symbol; + this.Name=data.name; + this.UpdateMainData(bindData,lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for(var i=0; iCUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END) + { + if (this.SourceData.DataType!=0) isDataTypeChange=true; + } + else if ((period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) || + (period>CUSTOM_SECOND_PERIOD_START && period<=CUSTOM_SECOND_PERIOD_END)) + { + if (this.SourceData.DataType!=1) isDataTypeChange=true; + else if (ChartData.IsSecondPeriod(period)) isDataTypeChange=true; + } + else + { + switch(period) + { + case 0: //日线 + case 1: //周 + case 2: //月 + case 3: //年 + case 9: //季线 + case 21: //双周 + if (this.SourceData.DataType!=0) isDataTypeChange=true; + break; + case 4: //1分钟 + case 5: //5分钟 + case 6: //15分钟 + case 7: //30分钟 + case 8: //60分钟 + case 11: //2小时 + case 12: //4小时 + if (this.SourceData.DataType!=1) isDataTypeChange=true; + break; + case 10: //分笔线 + if (this.SourceData.DataType!=2) isDataTypeChange=true; + break; + } + } + } + + this.Period=period; + if (right!=null) this.Right=right; + this.ReloadChartDrawPicture(); //切换周期了 清空画图工具 + this.ClearRectSelect(true); + this.Frame.ClearUpDonwFrameYData(); + this.ChartPaint[0].ClearCustomKLine(); + + if (isDataTypeChange==false && !this.IsApiPeriod) + { + this.Update( {UpdateCursorIndexType:2} ); //更新的时候 取消显示十字光标 + return; + } + + if (ChartData.IsDayPeriod(this.Period,true)) + { + this.CancelAutoUpdate(); //先停止定时器 + this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod'); //切换周期先停止更新 + this.ResetOverlaySymbolStatus(); + this.RequestHistoryData(); //请求日线数据 + //this.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period)) + { + this.CancelAutoUpdate(); //先停止定时器 + this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod'); //切换周期先停止更新 + this.ResetOverlaySymbolStatus(); + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + else if (ChartData.IsTickPeriod(this.Period)) + { + this.CancelAutoUpdate(); //先停止定时器 + this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod'); + this.RequestTickData(); //请求分笔数据 + } + } + + //复权切换 + this.ChangeRight=function(right) + { + if (!MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol)) return; //A股股票有复权 + if (ChartData.IsTickPeriod(this.Period)) return; //分笔没有复权 + + var upperSymbol=this.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) return; + + if (right<0 || right>2) return; + + if (this.Right==right) return; + + this.Right=right; + + if (!this.IsApiPeriod) + { + this.Update(); + return; + } + else //API周期数据 重新请求数据 + { + if (ChartData.IsDayPeriod(this.Period,true)) + { + this.CancelAutoUpdate(); //先停止定时器 + this.AutoUpdateEvent(false,'KLineChartContainer::ChangeRight'); //切换复权先停止更新 + this.ResetOverlaySymbolStatus(); + this.RequestHistoryData(); //请求日线数据 + } + else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period)) + { + this.CancelAutoUpdate(); //先停止定时器 + this.AutoUpdateEvent(false,'KLineChartContainer::ChangeRight'); //切换复权先停止更新 + this.ResetOverlaySymbolStatus(); + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + } + } + + //设置第1屏的起始日期 + this.SetFirstShowDate=function(obj) + { + if (!obj || !obj.Date) return; + + JSConsole.Chart.Log('[KLineChartContainer::SetFirstShowDate] obj=', obj); + + var hisData=null; + if (!this.Frame.Data) hisData=this.Frame.Data; + else hisData=this.Frame.SubFrame[0].Frame.Data; + if (!hisData) return; //数据还没有到达 + + var index=null; + if (ChartData.IsDayPeriod(this.Period,true)) + { + for(var i=0;i=obj.Date) + { + index=i; + break; + } + } + } + else if (ChartData.IsMinutePeriod(this.Period,true)) + { + let findTime=obj.Time; + if (IFrameSplitOperator.IsPlusNumber(findTime)) + { + for(var i=0;iobj.Date || (item.Date==obj.Date && item.Time>=findTime)) + { + index=i; + break; + } + } + } + else //只有日期 + { + for(var i=0;i=obj.Date) + { + index=i; + break; + } + } + } + + } + + if (index===null) + { + JSConsole.Chart.Log(`[KLineChartContainer::SetFirstShowDate] an't find first date=${obj.Date} time=${obj.Time}`, obj); + return; + } + + var count=hisData.Data.length-index; + var customShowCount=count; + if (obj.PageSize>0) customShowCount=obj.PageSize; + + var pageSize = this.GetMaxMinPageSize(); + if (pageSize.Max < customShowCount) customShowCount = pageSize.Max; + else if (pageSize.Min>customShowCount) customShowCount=pageSize.Min; + + for(var i in this.Frame.SubFrame) //设置一屏显示的数据个数 + { + var item =this.Frame.SubFrame[i].Frame; + item.XPointCount=customShowCount; + } + + hisData.DataOffset=index; + this.CursorIndex=0; + this.LastPoint.X=null; + this.LastPoint.Y=null; + + JSConsole.Chart.Log(`[KLineChartContainer::SetFirstShowDate] dataOffset=${hisData.DataOffset} CursorIndex=${this.CursorIndex} PageSize=${customShowCount}`); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.UpdatePointByCursorIndex(2); //更新十字光标位子 + this.Draw(); + } + + //删除某一个窗口的指标 + this.DeleteIndexPaint=function(windowIndex) + { + let paint=new Array(); //踢出当前窗口的指标画法 + for(let i in this.ChartPaint) + { + let item=this.ChartPaint[i]; + + if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null; //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.IsLocked=false; //解除上锁 + this.Frame.SubFrame[windowIndex].Frame.YSplitScale = null; //清空固定刻度 + + this.ChartPaint=paint; + + //清空东条标题 + var titleIndex=windowIndex+1; + this.TitlePaint[titleIndex].Data=[]; + this.TitlePaint[titleIndex].Title=null; + } + + //显示隐藏主图K线 + this.ShowKLine=function(isShow) + { + if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return; + this.ChartPaint[0].IsShow=isShow; + } + + this.SetInstructionData=function(type,instructionData) + { + if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return; + + var title=this.TitlePaint[1]; + if (type==2) //五彩K线 + { + this.ChartPaint[0].ColorData=instructionData.Data; + if (title) title.ColorIndex={Name:instructionData.Name, Param:instructionData.Param }; + } + else if (type==1) //专家指示 + { + this.ChartPaint[0].TradeData={Sell:instructionData.Sell, Buy:instructionData.Buy, Name:instructionData.Name, Param:instructionData.Param }; + if (title) title.TradeIndex={ Name:instructionData.Name, Param:instructionData.Param }; + } + } + + this.ChangeInstructionIndex=function(indexName) + { + let scriptData = new JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if(indexInfo.InstructionType!=1 && indexInfo.InstructionType!=2) return; + + indexInfo.ID=indexName; + this.ChangeInstructionScriptIndex(indexInfo); + + } + + this.ChangeInstructionScriptIndex=function(indexData) + { + if (indexData.InstructionType==1) //交易系统 + { + this.TradeIndex=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + } + else if (indexData.InstructionType==2) //五彩K线 + { + this.ColorIndex=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + } + else + { + return; + } + + var bindData=this.ChartPaint[0].Data; + this.BindInstructionIndexData(bindData); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.CancelInstructionIndex=function() //取消指示数据 + { + if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return; + + this.ColorIndex=null; + this.TradeIndex=null; + this.ChartPaint[0].ColorData=null; //五彩K线数据取消掉 + this.ChartPaint[0].TradeData=null; //交易系统数据取消 + + var title=this.TitlePaint[1]; + if (title) + { + title.TradeIndex=null; + title.ColorIndex=null; + } + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.SetFrameToolbar=function(windowIndex,window) + { + if (!window || !this.Frame.SubFrame[windowIndex] || !this.Frame.SubFrame[windowIndex].Frame) return; + + var frame=this.Frame.SubFrame[windowIndex].Frame; + var bChanged=false; + if (IFrameSplitOperator.IsBool(window.Modify)) + { + frame.ModifyIndex=window.Modify; + bChanged=true; + } + + if (IFrameSplitOperator.IsBool(window.Change)) + { + frame.ChangeIndex=window.Change; + bChanged=true; + } + + if (IFrameSplitOperator.IsBool(window.Close)) + { + frame.CloseIndex=window.Close; + bChanged=true; + } + + if (IFrameSplitOperator.IsBool(window.Overlay)) + { + frame.OverlayIndex=window.Overlay; + bChanged=true; + } + + //工具栏变 先刷新工具栏 + if (bChanged) + { + frame.SizeChange=true; + frame.ToolbarRect=null; //清空工具栏缓存 + frame.DrawToolbar(); + } + } + + //切换成 脚本指标 + this.ChangeScriptIndex=function(windowIndex,indexData,option) + { + this.DeleteIndexPaint(windowIndex); + this.WindowIndex[windowIndex]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + + if (option) + { + if (option.Window) this.SetFrameToolbar(windowIndex,option.Window); + } + + this.Frame.ClearUpDonwFrameYData({ Index:windowIndex }); + var bindData=this.ChartPaint[0].Data; + this.BindIndexData(windowIndex,bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //切换api指标 + this.ChangeAPIIndex=function(windowIndex,indexData) + { + this.DeleteIndexPaint(windowIndex); + //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 } + var apiItem=indexData.API; + this.WindowIndex[windowIndex]=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,indexData); + + if (indexData) + { + if (indexData.Window) this.SetFrameToolbar(windowIndex,indexData.Window); + } + + this.Frame.ClearUpDonwFrameYData({ Index:windowIndex }); + var bindData=this.ChartPaint[0].Data; + this.BindIndexData(windowIndex,bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //切换指标 指定切换窗口指标 + this.ChangeIndex=function(windowIndex,indexName,option) + { + if (option && option.API) //切换api指标 + return this.ChangeAPIIndex(windowIndex,option); + + var indexItem=JSIndexMap.Get(indexName); + if (!indexItem) + { + //查找系统指标 + let scriptData = new JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if (indexInfo.IsMainIndex) + { + windowIndex = 0; //主图指标只能在主图显示 + } + else + { + if (windowIndex == 0) windowIndex = 1; //幅图指标,不能再主图显示 + } + let indexData = indexInfo; + if (option) + { + if (option.FloatPrecision>=0) indexData.FloatPrecision=option.FloatPrecision; + if (option.StringFormat>0) indexData.StringFormat=option.StringFormat; + if (option.Args) indexData.Args=option.Args; + if (option.TitleFont) indexData.TitleFont=option.TitleFont; + if (option.IsShortTitle) indexData.IsShortTitle=option.IsShortTitle; + } + + return this.ChangeScriptIndex(windowIndex, indexData,option); + } + + //主图指标 + if (indexItem.IsMainIndex) + { + if (windowIndex>0) windowIndex=0; //主图指标只能在主图显示 + } + else + { + if (windowIndex==0) windowIndex=1; //幅图指标,不能再主图显示 + } + + var paint=new Array(); //踢出当前窗口的指标画法 + for(var i in this.ChartPaint) + { + var item=this.ChartPaint[i]; + + if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null; //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.IsLocked=false; //解除上锁 + this.Frame.SubFrame[windowIndex].Frame.YSplitScale = null; //清空固定刻度 + + this.ChartPaint=paint; + + //清空东条标题 + var titleIndex=windowIndex+1; + this.TitlePaint[titleIndex].Data=[]; + this.TitlePaint[titleIndex].Title=null; + + this.WindowIndex[windowIndex]=indexItem.Create(); + this.CreateWindowIndex(windowIndex); + + var bindData=this.ChartPaint[0].Data; + this.BindIndexData(windowIndex,bindData); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.ChangePyScriptIndex=function(windowIndex,indexData) + { + this.DeleteIndexPaint(windowIndex); + this.WindowIndex[windowIndex]=new PyScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + + var bindData=this.ChartPaint[0].Data; + this.BindIndexData(windowIndex,bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.AddOverlayIndex=function(obj) + { + var overlay=this.CreateOverlayWindowsIndex(obj); + if (!overlay) return; + + var bindData=this.ChartPaint[0].Data; + this.BindOverlayIndexData(overlay,obj.WindowIndex,bindData); + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //创建一个叠加指标 + this.CreateOverlayWindowsIndex=function(obj) // {WindowIndex:, IndexName:, Identify:, ShowRightText:, API:} + { + let indexName=obj.IndexName; + let windowIndex=obj.WindowIndex; + var apiItem=null, indexInfo=null, indexCustom=null; + if (obj.API) + { + apiItem=obj.API; + } + else if (obj.Script) //动态执行脚本 + { + indexInfo={ Script:obj.Script, ID:obj.indexName, Name:obj.indexName}; + if (obj.Name) indexInfo.Name=obj.Name; + } + else + { + let scriptData = new JSIndexScript(); + indexInfo = scriptData.Get(indexName); //系统指标 + if (!indexInfo) + { + indexCustom=JSIndexMap.Get(indexName); //定制指标 + if (!indexCustom) + { + console.warn(`[KLineChartContainer::CreateOverlayIndex] can not find index[${indexName}]`); + return null; + } + } + } + + var subFrame=this.Frame.SubFrame[windowIndex]; + subFrame.Interval=this.OverlayIndexFrameWidth; + var overlayFrame=new OverlayIndexItem(); + if (obj.Identify) overlayFrame.Identify=obj.Identify; //由外部指定id + var frame= this.ClassName==='KLineChartHScreenContainer' ? new OverlayKLineHScreenFrame() : new OverlayKLineFrame(); + frame.Canvas=this.Canvas; + frame.MainFrame=subFrame.Frame; + frame.ChartBorder=subFrame.Frame.ChartBorder; + if (obj.ShowRightText===true) frame.IsShow=true; + else if (obj.ShowRightText===false) frame.IsShow=false; + if (obj.IsShareY===true) frame.IsShareY=true; + + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=frame.ChartBorder; + frame.YSplitOperator.SplitCount=subFrame.Frame.YSplitOperator.SplitCount; + + overlayFrame.Frame=frame; + + if (apiItem) + { + var apiIndex=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,obj, true); + apiIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + overlayFrame.Script=apiIndex; + } + else if (indexInfo) + { + let indexData = indexInfo; + if (obj.Args) indexData.Args=obj.Args; //外部可以设置参数 + + var scriptIndex=new OverlayScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + overlayFrame.Script=scriptIndex; + } + else + { + var scriptIndex=indexCustom.Create(); + scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + scriptIndex.Create(this,windowIndex); + overlayFrame.Script=scriptIndex; + } + + subFrame.OverlayIndex.push(overlayFrame); + return overlayFrame; + } + + this.DeleteOverlayWindowsIndex=function(identify) //删除叠加指标 + { + if (!this.DeleteOverlayIndex(identify, null)) return; + + this.Frame.ResetXYSplit(true); + this.Draw(); + } + + this.ChangeKLineDrawType=function(drawType, isDraw) + { + if (this.KLineDrawType==drawType) return; + + this.KLineDrawType=drawType; + for(var i in this.ChartPaint) + { + var item=this.ChartPaint[i]; + if (i==0) item.DrawType=this.KLineDrawType; + else if (item.ClassName=='ChartVolStick') item.KLineDrawType=this.KLineDrawType + } + + for(var i in this.OverlayChartPaint) //叠加K线样式修改 + { + var item=this.OverlayChartPaint[i]; + item.DrawType=this.KLineDrawType; + } + + if (isDraw==false) return; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + //修改坐标类型 + //{ Type: 0=普通坐标 1=百分比坐标 (右边坐标刻度) 2=对数对标 3=等比坐标, IsReverse:是否反转坐标 } + this.ChangeCoordinateType=function(obj) + { + if (!this.Frame && !this.Frame.SubFrame) return; + if (!this.Frame.SubFrame.length) return; + + if (IFrameSplitOperator.IsNumber(obj)) //老版本 + { + var type=obj; + if (type==2) //反转坐标 + { + this.Frame.SubFrame[0].Frame.CoordinateType=1; + } + else if(type==1) + { + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=type; + } + else if (type==0) + { + this.Frame.SubFrame[0].Frame.CoordinateType=0; + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0; + } + else if (type==3) //对数坐标 + { + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=2; + } + else + { + return; + } + } + else + { + if (obj.Type>=0 && obj.Type<=5) this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=obj.Type; + if (obj.IsReverse===true) this.Frame.SubFrame[0].Frame.CoordinateType=1; + else if (obj.IsReverse==false) this.Frame.SubFrame[0].Frame.CoordinateType=0; + } + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + //设置指标窗口个数 + this.ChangeIndexWindowCount=function(count,option) + { + if (count<=0) return; + if (this.Frame.SubFrame.length==count) return; + + this.Frame.RestoreIndexWindows(); + + var currentLength=this.Frame.SubFrame.length; + if (currentLength>count) + { + for(var i=currentLength-1;i>=count;--i) + { + this.DeleteIndexPaint(i); + this.Frame.SubFrame[i].Frame.ClearToolbar(); + } + + this.Frame.SubFrame.splice(count,currentLength-count); + this.WindowIndex.splice(count,currentLength-count); + + //最后一个显示X轴坐标 + for(var i=0;i { return this.GetEventCallback(id); } + this.TitlePaint[i+1]=titlePaint; + } + + //最后一个显示X轴坐标 + for(var i=0;i0) + indexName=option.Windows; //外部设置增加窗口的默认指标 + + let scriptData = new JSIndexScript(); + for(var i=currentLength;i=0) this.Frame.SubFrame[i].Frame.IndexParamSpace=item.IndexParamSpace; + if (item.IsShowXLine==false) this.Frame.SubFrame[i].Frame.IsShowXLine=false; + if (item.IsShowYLine==false) this.Frame.SubFrame[i].Frame.IsShowYLine=false; + if (IFrameSplitOperator.IsBool(item.IsShowIndexTitle)) this.Frame.SubFrame[i].Frame.IsShowIndexTitle=item.IsShowIndexTitle; + + if (IFrameSplitOperator.IsBool(item.IsShowLeftText)) + { + subFrame.Frame.IsShowYText[0]=item.IsShowLeftText; + subFrame.Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText; //显示左边刻度 + } + if (IFrameSplitOperator.IsBool(item.IsShowRightText)) + { + subFrame.Frame.IsShowYText[1]=item.IsShowRightText; + subFrame.Frame.YSplitOperator.IsShowRightText=item.IsShowRightText; //显示右边刻度 + } + + if (item.OverlayIndexType) + { + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.Position)) subFrame.Frame.OverlayIndexType.Position=item.OverlayIndexType.Position; + if (IFrameSplitOperator.IsNumber(item.OverlayIndexType.LineSpace)) subFrame.Frame.OverlayIndexType.LineSpace=item.OverlayIndexType.LineSpace; + } + + var bindData=this.ChartPaint[0].Data; + this.BindIndexData(i,bindData); //执行脚本 + } + + //this.UpdataDataoffset(); //更新数据偏移 + } + } + + this.ChangeIndexTemplate=function(option) //切换指标模板 可以设置指标窗口个数 每个窗口的指标 + { + if (!option.Windows) return; + var count=option.Windows.length; + if (count<=0) return; + var currentLength=this.Frame.SubFrame.length; + + var period=null, right=null; + if (option.KLine) + { + if (IFrameSplitOperator.IsNumber(option.KLine.Period) && option.KLine.Period!=this.Period) period=option.KLine.Period; //周期 + if (IFrameSplitOperator.IsNumber(option.KLine.Right) && option.KLine.Right!=this.Right) right=option.KLine.Right; //复权 + } + + var bRefreshData= (period!=null || right!=null); + + //清空所有的指标图型 + for(var i=0;icount) + { + for(var i=currentLength-1;i>=count;--i) + { + this.Frame.SubFrame[i].Frame.ClearToolbar(); + } + + this.Frame.SubFrame.splice(count,currentLength-count); + this.WindowIndex.splice(count,currentLength-count); + } + else + { + for(var i=currentLength;i { return this.GetEventCallback(id); } + this.TitlePaint[i+1]=titlePaint; + } + } + + var systemScript = new JSIndexScript(); + var bindData=this.ChartPaint[0].Data; + for(var i=0;ii) frameItem=option.Frame[i]; + var titleIndex=windowIndex+1; + this.TitlePaint[titleIndex].Data=[]; + this.TitlePaint[titleIndex].Title=null; + + if (item.Script) //自定义X轴绘制事件 + { + this.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item); //脚本执行 + if (!bRefreshData) this.BindIndexData(windowIndex,bindData); //执行脚本 + } + else + { + var indexID=item.Index; + var indexItem=JSIndexMap.Get(indexID); + if (indexItem) + { + this.WindowIndex[i]=indexItem.Create(); + this.CreateWindowIndex(windowIndex); + if (!bRefreshData) this.BindIndexData(windowIndex,bindData); + } + else + { + var indexInfo = systemScript.Get(indexID); + if (indexInfo) + { + var args=indexInfo.Args; + if (option.Windows[i].Args) args=option.Windows[i].Args; + let indexData = + { + Name:indexInfo.Name, Script:indexInfo.Script, Args: args, ID:indexID , + //扩展属性 可以是空 + KLineType:indexInfo.KLineType, YSpecificMaxMin:indexInfo.YSpecificMaxMin, YSplitScale:indexInfo.YSplitScale, + FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition, + OutName:indexInfo.OutName + }; + if (item.TitleFont) indexData.TitleFont=item.TitleFont; + + this.WindowIndex[i]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + if (!bRefreshData) this.BindIndexData(windowIndex,bindData); //执行脚本 + } + } + } + + if (item.Modify!=null) this.Frame.SubFrame[i].Frame.ModifyIndex=item.Modify; + if (item.Change!=null) this.Frame.SubFrame[i].Frame.ChangeIndex=item.Change; + if (item.Close!=null) this.Frame.SubFrame[i].Frame.CloseIndex=item.Close; + if (item.Overlay!=null) chart.Frame.SubFrame[i].Frame.OverlayIndex=item.Overlay; + if (item.IsDrawTitleBG==true) this.Frame.SubFrame[i].Frame.IsDrawTitleBG=item.IsDrawTitleBG; + + + if (IFrameSplitOperator.IsNumber(item.TitleHeight)) this.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight; + else item.TitleHeight=this.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight; + if (item.IsShowTitleArraw==false) this.Frame.SubFrame[i].Frame.IsShowTitleArraw=false; + if (item.IsShowIndexName==false) this.Frame.SubFrame[i].Frame.IsShowIndexName=false; + if (item.IndexParamSpace>=0) this.Frame.SubFrame[i].Frame.IndexParamSpace=item.IndexParamSpace; + + + if (frameItem) + { + if (frameItem.SplitCount) this.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount=frameItem.SplitCount; + if (frameItem.IsShowXLine===false || frameItem.IsShowXLine===true) this.Frame.SubFrame[i].Frame.IsShowXLine=frameItem.IsShowXLine; + if (frameItem.IsShowYLine===false || frameItem.IsShowYLine===true) this.Frame.SubFrame[i].Frame.IsShowYLine=frameItem.IsShowYLine; + + if (frameItem.IsShowLeftText===false || frameItem.IsShowLeftText===true) + { + this.Frame.SubFrame[i].Frame.IsShowYText[0]=frameItem.IsShowLeftText; + this.Frame.SubFrame[i].Frame.YSplitOperator.IsShowLeftText=frameItem.IsShowLeftText; //显示左边刻度 + } + if (frameItem.IsShowRightText===false || frameItem.IsShowRightText===true) + { + this.Frame.SubFrame[i].Frame.IsShowYText[1]=frameItem.IsShowRightText; + this.Frame.SubFrame[i].Frame.YSplitOperator.IsShowRightText=frameItem.IsShowRightText; //显示右边刻度 + } + } + + } + + //最后一个显示X轴坐标 + for(var i=0;i=this.Frame.SubFrame.length) return; + + this.Frame.RestoreIndexWindows(); + + var delFrame=this.Frame.SubFrame[id].Frame; + this.DeleteIndexPaint(id); + this.Frame.SubFrame[id].Frame.ClearToolbar(); + this.Frame.SubFrame.splice(id,1); + this.WindowIndex.splice(id,1); + this.TitlePaint.splice(id+1,1); //删除对应的动态标题 + + for(var i=0;i0) + { + var aryDrawPicture=[]; + for(var i in this.ChartDrawPicture) + { + var item=this.ChartDrawPicture[i]; + if (item.Frame==delFrame) continue; + aryDrawPicture.push(item); + } + this.ChartDrawPicture=aryDrawPicture; + } + + this.Frame.SetSizeChage(true); + this.UpdateFrameMaxMin(); + this.ResetFrameXYSplit(); + this.Draw(); + } + + //获取当前的显示的指标 + this.GetIndexInfo=function() + { + var aryIndex=[]; + for(var i in this.WindowIndex) + { + var item=this.WindowIndex[i]; + var info={Name:item.Name}; + if (item.ID) info.ID=item.ID; + if (item.Arguments) //参数 + { + info.Args=[]; + for(var i in item.Arguments) + { + var argItem=item.Arguments[i]; + info.Args.push( {Name:argItem.Name, Value:argItem.Value} ); + } + } + + aryIndex.push(info); + } + + return aryIndex; + } + + this.CreateExtendChart=function(name, option) //创建扩展图形 + { + var chart; + switch(name) + { + case '筹码分布': + chart=new StockChip(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.Left=this.Frame.ChartBorder.Right; //左边间距使用当前框架间距 + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + this.Frame.ChartBorder.Right+=chart.Width; //创建筹码需要增加右边的间距 + return chart; + case '画图工具': + chart=new DrawToolsButton(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.Left=this.Frame.ChartBorder.Right; //左边间距使用当前框架间距 + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + this.Frame.ChartBorder.Right+=chart.Width; //创建筹码需要增加右边的间距 + return chart; + case 'KLineTooltip': + if (option.Create && typeof(option.Create)=='function') chart=option.Create(); + else chart=new KLineTooltipPaint(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + option.LanguageID=this.LanguageID; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + case "深度图": + chart=new DepthMapPaint(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + case '背景图': + chart=new BackgroundPaint(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + case "FrameSplitPaint": //框架分割 + chart=new FrameSplitPaint(); + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + + default: + chart=g_ExtendChartPaintFactory.Create(name); + if (!chart) return null; + + chart.Canvas=this.Canvas; + chart.ChartBorder=this.Frame.ChartBorder; + chart.ChartFrame=this.Frame; + chart.HQChart=this; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + } + } + + this.OnTouchFinished=function() + { + if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) + { + if (this.TouchStatus.CorssCursorShow==true && this.TouchDrawCount>0) return; + + this.TouchStatus.CorssCursorShow=false; + this.DrawDynamicInfo(); + return; + } + + if (this.CorssCursorTouchEnd===true) //手势离开十字光标消失 + { + if (this.TouchDrawCount>0) this.DrawDynamicInfo(); + return; + } + + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + if (item.ClassName==='KLineTooltipPaint') + { + this.DrawDynamicInfo(); + } + } + } + + //锁|解锁指标 { Index:指标名字,IsLocked:是否要锁上,Callback:回调 } + this.LockIndex=function(lockData) + { + if (!lockData) return; + if (!lockData.IndexName) return; + + for(let i in this.WindowIndex) + { + let item=this.WindowIndex[i]; + if (!item) conintue; + if (item.Name==lockData.IndexName) + { + item.SetLock(lockData); + this.Update(); + break; + } + } + } + + this.TryClickLock=function(x,y) + { + for(let i in this.Frame.SubFrame) + { + var item=this.Frame.SubFrame[i]; + if (!item.Frame.IsLocked) continue; + if (!item.Frame.LockPaint) continue; + + var tooltip=new TooltipData(); + if (!item.Frame.LockPaint.GetTooltipData(x,y,tooltip)) continue; + + tooltip.HQChart=this; + if (tooltip.Data.Callback) tooltip.Data.Callback(tooltip); + return true; + } + + return false; + } + + this.TryClickIndexTitle=function(x,y) + { + for(var i in this.TitlePaint) + { + var item=this.TitlePaint[i]; + if (!item.IsClickTitle) continue; + if (!item.IsClickTitle(x,y)) continue; + + var data={ Point:{X:x, Y:y}, Title:item.Title, FrameID:item.Frame.Identify }; + JSConsole.Chart.Log('[KLineChartContainer::TryClickIndexTitle] click title ', data); + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_INDEXTITLE); + if (event && event.Callback) event.Callback(event,data,this); + + return true; + } + + return false; + } + + this.SetSizeChange=function(bChanged) + { + this.Frame.SetSizeChage(bChanged); + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + item.SizeChange=bChanged; + } + } + this.SetSizeChage=this.SetSizeChange; //单词拼错了, 还没替换完 + + this.Update=function(option) //option: { UpdateCursorIndexType:更新十字光标方式 } + { + if (!this.SourceData) return; + if (this.BeforeBindMainData) this.BeforeBindMainData('Update'); + + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=this.SourceData.DataType; + bindData.Symbol=this.Symbol; + + if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true)) //复权(日线数据才复权) + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.BindMainData(bindData,this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("Update"); + + var firstSubFrame; + for(var i=0; i0 && MARKET_SUFFIX_NAME.IsSHSZStockA(item.Symbol)) //复权数据 + { + var rightData=bindData.GetRightData(bindData.Right,{ AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data, this.IsApiPeriod); //和主图数据拟合以后的数据 + bindData.Data=aryOverlayData; + + if (ChartData.IsDayPeriod(bindData.Period,false) && !this.IsApiPeriod) //周期数据 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + item.Data=bindData; + } + } + + this.ReqeustKLineInfoData( {FunctionName:"Update"} ); + + this.CreateChartDrawPictureByStorage(); //创建画图工具 + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + if (option && option.UpdateCursorIndexType>0) this.UpdatePointByCursorIndex(option.UpdateCursorIndexType); + else this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + //叠加指标 + for(var i=0;i0) + { + var windows=option.Windows; + var systemScript = new JSIndexScript(); + for(var i in windows) + { + if (i>=this.WindowIndex.length) break; //暂时不支持 动态增加/减少 + var item=windows[i]; + if (!item) continue; + + if (item.Script) + { + this.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item); //脚本执行 + } + else if (item.API) + { + var apiItem=item.API; + this.WindowIndex[i]=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,item); + } + else + { + var indexID=item.Index; + var indexInfo = systemScript.Get(indexID); + if (indexInfo) + { + var args=indexInfo.Args; + if (item.Args) indexInfo.Args=item.Args; + indexInfo.ID=indexID; + this.WindowIndex[i]=new ScriptIndex(indexInfo.Name,indexInfo.Script,indexInfo.Args,indexInfo); //脚本执行 + } + } + } + } + } + + this.ReloadChartDrawPicture(); + this.Frame.ClearUpDonwFrameYData(); + if (ChartData.IsDayPeriod(this.Period,true)) + { + this.RequestHistoryData(); //请求日线数据 + //this.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period)) + { + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + else if (ChartData.IsTickPeriod(this.Period)) + { + this.RequestTickData(); + } + } + + this.ReqeustKLineInfoData=function(obj) + { + if (obj && obj.FunctionName=="RecvDragDayData") //增量更新 + { + obj.Update=true; + } + else + { + if (this.ChartPaint.length>0) + { + var klinePaint=this.ChartPaint[0]; + klinePaint.InfoData=new Map(); + } + obj.Update=false; + } + + //信息地雷信息 + for(var i in this.ChartInfo) + { + this.ChartInfo[i].RequestData(this,obj); + } + } + + //设置K线信息地雷 + this.SetKLineInfo=function(aryInfo,bUpdate) + { + this.ChartInfo=[]; //先清空 + for(var i in aryInfo) + { + var infoItem=JSKLineInfoMap.Get(aryInfo[i]); + if (!infoItem) continue; + var item=infoItem.Create(); + item.MaxReqeustDataCount=this.MaxReqeustDataCount; + this.ChartInfo.push(item); + } + + if (bUpdate==true) this.ReqeustKLineInfoData({ FunctionName:"SetKLineInfo" }); + } + + //添加信息地雷 + this.AddKLineInfo=function(infoName,bUpdate) + { + var classInfo=JSKLineInfoMap.GetClassInfo(infoName); + if (!classInfo) + { + console.warn("[KLineChartContainer::AddKLineInfo] can't find infoname=", infoName); + return; + } + + for(var i in this.ChartInfo) + { + var item=this.ChartInfo[i]; + if (item.ClassName==classInfo.ClassName) //已经存在 + return; + } + + var infoItem=JSKLineInfoMap.Get(infoName); + if (!infoItem) return; + + var item=infoItem.Create(); + item.MaxReqeustDataCount=this.MaxReqeustDataCount; + this.ChartInfo.push(item); + + if (bUpdate==true) + { + item.RequestData(this); //信息地雷信息 + } + } + + //删除信息地理 + this.DeleteKLineInfo=function(infoName) + { + var classInfo=JSKLineInfoMap.GetClassInfo(infoName); + if (!classInfo) + { + console.warn("[KLineChartContainer::DeleteKLineInfo] can't find infoname=", infoName); + return; + } + + for(var i in this.ChartInfo) + { + var item=this.ChartInfo[i]; + if (item.ClassName==classInfo.ClassName) + { + this.ChartInfo.splice(i,1); + this.UpdataChartInfo(); + this.Draw(); + break; + } + } + } + + //清空所有的信息地理 + this.ClearKLineInfo=function() + { + if (!this.ChartInfo || this.ChartInfo.length<=0) return; + + this.ChartInfo=[]; + + var klinePaint=this.ChartPaint[0]; + klinePaint.InfoData=null; + this.Draw(); + } + + //增加叠加股票 只支持日线数据 + this.OverlaySymbol=function(symbol,option) + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (item.Symbol===symbol) + { + console.warn(`[KLineChartContainer::OverlaySymbol] overlay symbol=${symbol} exist.`) + return false; + } + } + + var paint=new ChartOverlayKLine(); + paint.Canvas=this.Canvas; + paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame=this.Frame.SubFrame[0].Frame; + paint.Name="Overlay-KLine"; + paint.DrawType=this.KLineDrawType; + paint.Symbol=symbol; + if (option && option.Color) paint.Color=option.Color; + else paint.Color=g_JSChartResource.OverlaySymbol.Color[g_JSChartResource.OverlaySymbol.Random%g_JSChartResource.OverlaySymbol.Color.length]; + paint.SetOption(option); + ++g_JSChartResource.OverlaySymbol.Random; + if (this.ChartPaint[0] && this.ChartPaint[0].Data && this.SourceData) paint.MainData=this.ChartPaint[0].Data; //绑定主图数据 + + this.OverlayChartPaint.push(paint); + + if (ChartData.IsDayPeriod(this.Period, true)) this.RequestOverlayHistoryData(); //请求日线数据 + else if (ChartData.IsMinutePeriod(this.Period,true)) this.RequestOverlayHistoryMinuteData(); //请求分钟历史数据 + + return true; + } + + this.RequestOverlayHistoryData=function() + { + if (!this.OverlayChartPaint.length) return; + if (!this.SourceData || !this.SourceData.Data) return; //主图数据还没有到完 + + var self = this; + var dataCount=this.GetRequestDataCount(); + var firstDate=this.SourceData.Data[0].Date; + for(var i in this.OverlayChartPaint) + { + let item=this.OverlayChartPaint[i]; + if (!item.MainData) continue; //等待主图股票数据未下载完 + if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue; + let symbol=item.Symbol; + if (!symbol) continue; + + item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID; + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestOverlayHistoryData', //类名:: + Explain:'叠加股票日K线数据', + Request:{ Url:self.KLineApiUrl, Data: { symbol: symbol, count: dataCount.MaxRequestDataCount,"first":{ date: firstDate }, + field:["name","symbol","yclose","open","price","high",'vol','amount'] }, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayHistoryData(data,item); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.KLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high"], + "symbol": symbol, + "start": -1, + "count": dataCount.MaxRequestDataCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayHistoryData(data,item); + } + }); + } + } + + this.RecvOverlayHistoryData=function(data,paint) + { + if (paint.IsDelete) return; + + var aryDayData=KLineChartContainer.JsonDataToHistoryData(data); + + //原始叠加数据 + var sourceData=new ChartData(); + sourceData.Data=aryDayData; + sourceData.DataType=0; + + var bindData=new ChartData(); + bindData.Data=aryDayData; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=0; + + if (bindData.Right>0 && MARKET_SUFFIX_NAME.IsSHSZStockA(data.symbol) && !this.IsApiPeriod) //复权数据 ,A股才有有复权 + { + var rightData=bindData.GetRightData(bindData.Right,{ AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data, this.IsApiPeriod); //和主图数据拟合以后的数据 + bindData.Data=aryOverlayData; + + if (ChartData.IsDayPeriod(bindData.Period,false) && !this.IsApiPeriod) //周期数据 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + paint.Data=bindData; + paint.SourceData=sourceData; + paint.Title=data.name; + paint.Symbol=data.symbol; + paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID; + + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=1; //调整为百份比坐标 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + } + + this.RequestOverlayHistoryMinuteData=function() + { + if (!this.OverlayChartPaint.length) return; + if (!this.SourceData || !this.SourceData.Data) return; //主图数据还没有到完 + + var self = this; + var dataCount=this.GetRequestDataCount(); + var firstDate=this.SourceData.Data[0].Date; + var firstTime=this.SourceData.Data[0].Time; + for(var i in this.OverlayChartPaint) + { + let item=this.OverlayChartPaint[i]; + if (!item.MainData) continue; //等待主图股票数据未下载完 + if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue; + let symbol=item.Symbol; + if (!symbol) continue; + + item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID; + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestOverlayHistoryMinuteData', //类名:: + Explain:'叠加股票分钟K线数据', + Request:{ Url:self.MinuteKLineApiUrl, Data: { symbol: symbol, count: dataCount.MaxRequestMinuteDayCount,"first":{ date: firstDate, time:firstTime }, + field:["name","symbol","yclose","open","price","high",'vol','amount'] }, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOveralyHistoryMinuteData(data,item); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high",'vol','amount'], + "symbol": symbol, + "start": -1, + "count": dataCount.MaxRequestMinuteDayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOveralyHistoryMinuteData(data,item); + } + }); + } + } + + this.RecvOveralyHistoryMinuteData=function(data,paint) + { + var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data); + if (!aryDayData) return; + + //原始叠加数据 + var sourceData=new ChartData(); + sourceData.Data=aryDayData; + sourceData.DataType=1; //0=日线数据 1=分钟数据 + + var bindData=new ChartData(); + bindData.Data=aryDayData; + bindData.Period=this.Period; + bindData.Right=this.Right; + bindData.DataType=1; + + var aryOverlayData=this.SourceData.GetOverlayMinuteData(bindData.Data, this.IsApiPeriod); //和主图数据拟合以后的数据 + bindData.Data=aryOverlayData; + + if (ChartData.IsMinutePeriod(bindData.Period,false) && !this.IsApiPeriod) //周期数据, API周期数据不用计算 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + paint.Data=bindData; + paint.SourceData=sourceData; + paint.Title=data.name; + paint.Symbol=data.symbol; + paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID; //数据下载完成 + + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=1; //调整为百份比坐标 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.ResetOverlaySymbolStatus=function() + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + item.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID; + } + } + + //取消叠加股票 + this.ClearOverlaySymbol=function() + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + item.IsDelete=true; + } + + this.OverlayChartPaint=[]; + this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint; //绑定叠加 + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0; //调整一般坐标 + this.UpdateFrameMaxMin(); + this.Draw(); + } + + //删除一个叠加股票 + this.DeleteOverlaySymbol=function(symbol) + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (item.Symbol===symbol) + { + item.IsDelete=true; + this.OverlayChartPaint.splice(i,1); + if (this.OverlayChartPaint.length<=0) this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0; //调整一般坐标 + this.UpdateFrameMaxMin(); + this.Draw(); + return true; + } + } + + console.warn(`[KLineChartContainer::DeleteOverlaySymbol] overlay symbol=${symbol} not exist.`) + return false; + } + + this.RequestFlowCapitalData=function() + { + if (!this.Symbol) return; + if (this.FlowCapitalReady==true) return; + + var upperSymbol=this.Symbol.toUpperCase(); + + var bNeedDonloadData=true; + if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol) || MARKET_SUFFIX_NAME.IsFutures(upperSymbol)) //数字货币, 期货 不需要下载流通股本 + bNeedDonloadData=false; + + if (this.EnableFlowCapital) //强制下载流通股 + { + if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) + { + if (this.EnableFlowCapital.BIT==true) bNeedDonloadData=true; + } + } + + if (!bNeedDonloadData) + { + JSConsole.Chart.Log(`[KLineChartContainer::RequestFlowCapitalData] symbol=${this.Symbol} not need download data.`); + this.FlowCapitalReady=true; + return; + } + + var self = this; + let fieldList=["name","date","symbol","capital.a"]; + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestFlowCapitalData', //类名:: + Explain:'流通股本数据', + Request:{ Url:self.StockHistoryDayApiUrl, Data: { symbol: [this.Symbol], orderfield:'date',field:fieldList }, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvFlowCapitalData(data); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.StockHistoryDayApiUrl, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "orderfield":"date" + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvFlowCapitalData(recvData); + } + }); + } + + this.RecvFlowCapitalData=function(data) + { + if (!data.stock || data.stock.length!=1) return; + + let stock=data.stock[0]; + var aryData=new Array(); + for(let i in stock.stockday) + { + var item=stock.stockday[i]; + let indexData=new SingleData(); + indexData.Date=item.date; + var financeData=item.capital; + if (!financeData) continue; + if (financeData.a>0) + { + indexData.Value=financeData.a; //流通股本(股) + aryData.push(indexData); + } + } + + if (this.IsApiPeriod) + { + var klineData=this.ChartPaint[0].Data; + var aryFixedData=null; + if (ChartData.IsMinutePeriod(this.Period,true)) //分钟数据 + { + aryFixedData=klineData.GetMinuteFittingFinanceData(aryData); + } + else if (ChartData.IsDayPeriod(this.Period,true)) //日线数据 + { + aryFixedData=klineData.GetFittingFinanceData(aryData); + } + + if (aryFixedData && klineData.Data && aryFixedData.length==klineData.Data.length) + { + for(var i in klineData.Data) + { + var item=klineData.Data[i]; + if (aryFixedData[i] && IFrameSplitOperator.IsNumber(aryFixedData[i].Value)) + item.FlowCapital=aryFixedData[i].Value; + } + } + } + else + { + if (ChartData.IsMinutePeriod(this.Period,true)) //分钟数据 + { + var aryFixedData=this.SourceData.GetMinuteFittingFinanceData(aryData); + for(let i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + if (aryFixedData[i] && IFrameSplitOperator.IsNumber(aryFixedData[i].Value)) + item.FlowCapital=aryFixedData[i].Value; + } + + var bindData=this.ChartPaint[0].Data; + var newBindData=new ChartData(); + newBindData.Data=this.SourceData.Data; + + if (ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 + { + var periodData=newBindData.GetPeriodData(bindData.Period); + newBindData.Data=periodData; + } + bindData.Data=newBindData.Data; + } + else + { + var aryFixedData=this.SourceData.GetFittingFinanceData(aryData); + for(let i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + if (aryFixedData[i] && IFrameSplitOperator.IsNumber(aryFixedData[i].Value)) + item.FlowCapital=aryFixedData[i].Value; + } + + var bindData=this.ChartPaint[0].Data; + var newBindData=new ChartData(); + newBindData.Data=this.SourceData.Data; + + if (bindData.Right>0) //复权 + { + var rightData=newBindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + newBindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false)) //周期数据 + { + var periodData=newBindData.GetPeriodData(bindData.Period); + newBindData.Data=periodData; + } + + bindData.Data=newBindData.Data; + } + } + + this.FlowCapitalReady=true; + var bDraw=false; + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + if (item.ClassName=='StockChip') + { + bDraw=true; + break; + } + } + + if (bDraw) this.Draw(); + } + + //创建画图工具 + this.CreateChartDrawPicture=function(name, option, callback) + { + var drawPicture=null; + var item=IChartDrawPicture.GetDrawPictureByName(name); + if (item) + { + drawPicture=item.Create(); + if (drawPicture.ClassName=='ChartDrawPictureText') drawPicture.HQChart=this; + } + + if (!drawPicture) //iconfont图标 + { + if (IChartDrawPicture.MapIonFont.has(name)) + { + var iconItem=IChartDrawPicture.MapIonFont.get(name); + drawPicture=new ChartDrawPictureIconFont(); + drawPicture.FontOption.Family=iconItem.Family + drawPicture.Text=iconItem.Text; + if (iconItem.Color) drawPicture.LineColor=iconItem.Color; + } + } + + if (!drawPicture) return false; + + drawPicture.Canvas=this.Canvas; + drawPicture.Status=0; + drawPicture.Symbol=this.Symbol; + drawPicture.Period=this.Period; + drawPicture.Right=this.Right; + drawPicture.Option=this.ChartDrawOption; + if (callback) drawPicture.FinishedCallback=callback; //完成通知上层回调 + if (option) drawPicture.SetOption(option); + var self=this; + drawPicture.Update=function() //更新回调函数 + { + self.DrawDynamicInfo(); + }; + this.CurrentChartDrawPicture=drawPicture; + //JSConsole.Chart.Log("[KLineChartContainer::CreateChartDrawPicture] ", name,this.CurrentChartDrawPicture); + return true; + } + + //xStep,yStep 移动的偏移量 + this.MoveChartDrawPicture=function(x,y,isPhone) + { + var drawPicture=this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + var pixelTatio = GetDevicePixelRatio(); //x,y 需要乘以放大倍速 + if (isPhone) pixelTatio=1; + var xStep=x*pixelTatio; + var yStep=y*pixelTatio; + //JSConsole.Chart.Log("xStep="+xStep+" yStep="+yStep); + drawPicture.Move(xStep,yStep); + + return true; + } + + + //数据长度变化 需要更新画图工具X轴索引 + this.UpdateChartDrawXValue=function() + { + for(var i in this.ChartDrawPicture) + { + var item=this.ChartDrawPicture[i]; + item.UpdateXValue(); + } + } + + + //注册鼠标右键事件 + this.OnRightMenu=function(x,y,e) + { + var pixelTatio = GetDevicePixelRatio(); //x,y 需要乘以放大倍速 + if (this.RightMenu) + { + var frameId=this.Frame.PtInFrame(x*pixelTatio,y*pixelTatio); + e.data={ Chart:this, FrameID:frameId }; + this.RightMenu.DoModal(e); + } + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CONTEXT_MENU); + if (event) + { + var frameId=this.Frame.PtInFrame(x*pixelTatio,y*pixelTatio); + var data={ X:x, Y:y, Event:e, FrameID:frameId }; + event.Callback(event,data,this); + } + } + + //重新加载画图工具(切换股票|周期) + this.ReloadChartDrawPicture=function() + { + this.ChartDrawPicture=[]; + this.ChartDrawStorageCache; + if (this.ChartDrawStorage) + { + this.ChartDrawStorageCache=this.ChartDrawStorage.GetDrawData( {Symbol:this.Symbol, Period:this.Period} ); + } + } + + this.CreateChartDrawPictureByStorage=function() //把缓存(this.ChartDrawStorageCache) 画图工具创建出来 + { + if (!this.ChartDrawStorageCache || this.ChartDrawStorageCache.length<=0) return; + + for(var i in this.ChartDrawStorageCache) + { + var item=this.ChartDrawStorageCache[i]; + if (item.FrameID<0 || !this.Frame.SubFrame || this.Frame.SubFrame.length0) + { + var fristKItem=hisData.Data[0]; + var aryInfo=[]; + for(var i in this.ChartInfo) + { + var infoItem=this.ChartInfo[i]; + for(var j in infoItem.Data) + { + var item=infoItem.Data[j]; + if (item.Date>=fristKItem.Date) //在K线范围内的才显示 + aryInfo.push(item); + } + } + aryInfo.sort(function(a,b) { return a.Date-b.Date }); //排序 + + for(var i=0;i0) //复权 + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 + { + var periodData=bindData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + } + + if (typeof(this.WindowIndex[index].ExecuteScript)=='function') + { + var hisData=this.ChartPaint[0].Data; + this.WindowIndex[index].ExecuteScript(this,index,hisData); + } + else + { + this.WindowIndex[index].BindData(this,index,bindData); + } + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.GetOverlayIndexByIdentify=function(identify) + { + for(var i=0; i=0) + { + this.CancelZoomUpDownFrameY(dragY); + } + } + + if (this.EnableZoomIndexWindow) + { + var frameId=this.Frame.PtInFrame(x,y); + JSConsole.Chart.Log("[KLineChartContainer::OnDoubleClick] frameId",frameId); + if (frameId>=this.Frame.ZoomStartWindowIndex) + { + if (this.ZoomIndexWindow(frameId, {X:x, Y:y})) + { + this.Frame.SetSizeChage(true); + this.Draw(); + return true; + } + } + } + + var event=null; + if (this.mapEvent.has(JSCHART_EVENT_ID.DBCLICK_KLINE)) event=this.mapEvent.get(JSCHART_EVENT_ID.DBCLICK_KLINE); + + if (!this.MinuteDialog && !event) return; + + var tooltip=new TooltipData(); + if (!this.PtInChartPaintTooltip(x,y,tooltip)) return; + if (!tooltip.Data) return; + + if (event) + { + if (this.ClickChartTimer!=null) //清空单击定时器 + { + clearTimeout(this.ClickChartTimer); + this.ClickChartTimer=null; + } + + var data={ Tooltip:tooltip, Stock:{Symbol:this.Symbol, Name:this.Name } } + event.Callback(event,data,this); + } + + if (this.MinuteDialog) + { + e.data={Chart:this,Tooltip:tooltip}; + this.MinuteDialog.DoModal(e); + } + } + + this.CancelAutoUpdate=function() //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + //数据自动更新 + this.AutoUpdate=function(waitTime) //waitTime 更新时间 + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + + var self = this; + var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus==0 || marketStatus==3) return; //闭市,盘后 + + var frequency=this.AutoUpdateFrequency; + if (marketStatus==1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.AutoUpdate(); + },frequency); + } + else if (marketStatus==2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function() + { + if (ChartData.IsDayPeriod(self.Period,true)) + { + self.RequestRealtimeData(); //更新最新行情 + //self.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(self.Period,true) || ChartData.IsSecondPeriod(self.Period)) + { + self.RequestMinuteRealtimeData(); //请求分钟数据 + } + else if (ChartData.IsTickPeriod(self.Period)) + { + self.RequestTickRealtimeData(); //请求最新分笔 + } + },frequency); + } + } + + this.GetMaxMinPageSize = function () + { + let pageSize={}; + let width = this.Frame.ChartBorder.GetWidth(); + let barWidth = (ZOOM_SEED[ZOOM_SEED.length - 1][0] + ZOOM_SEED[ZOOM_SEED.length - 1][1]); + pageSize.Max=parseInt(width / barWidth) - 2; + + barWidth= (ZOOM_SEED[0][0] + ZOOM_SEED[0][1]); + pageSize.Min=parseInt(width / barWidth) - 2; + + JSConsole.Chart.Log(`[KLineChartContainer::GetMaxMinPageSize] Max=${pageSize.Max} Min=${pageSize.Min}`); + + return pageSize; + } + + //获取图形控件的状态 + this.GetChartStatus=function() + { + var subFrame=this.Frame.SubFrame[0].Frame; + if (!subFrame) return null; + var hisData=subFrame.Data; + if (!hisData) return null; + + var status={ KLine:{ }, Zoom:{} }; + status.KLine.Count=hisData.Data.length; + status.KLine.Offset=hisData.DataOffset; + status.KLine.PageSize=subFrame.XPointCount; + status.Zoom.Index=subFrame.ZoomIndex; + status.Zoom.Max=ZOOM_SEED.length; + return status; + } + + //数据拖拽下载 + this.DragDownloadData=function() + { + var data=null; + if (!this.Frame.Data) data=this.Frame.Data; + else data=this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + if (data.DataOffset>0) return; + + if (ChartData.IsMinutePeriod(this.Period,true)) //下载分钟数据 + { + JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Minute:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`); + if (!this.DragDownload.Minute.Enable) return; + if (this.DragDownload.Minute.IsEnd) return; //全部下载完了 + if (this.DragDownload.Minute.Status!=0) return; + this.RequestDragMinuteData(); + } + else if (ChartData.IsDayPeriod(this.Period,true)) + { + JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Day:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`); + if (!this.DragDownload.Day.Enable) return; + if (this.DragDownload.Day.IsEnd) return; //全部下载完了 + if (this.DragDownload.Day.Status!=0) return; + this.RequestDragDayData(); + } + } + + this.RequestDragMinuteData=function() + { + var self=this; + this.AutoUpdateEvent(false,'KLineChartContainer::RequestDragMinuteData'); //停止自动更新 + this.CancelAutoUpdate(); + var download=this.DragDownload.Minute; + download.Status=1; + var firstItem=this.SourceData.Data[0]; //最新的一条数据 + var postData= + { + "field": ["name","symbol", "yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "enddate": firstItem.Date, + "endtime" :firstItem.Time, + "count": self.MaxRequestMinuteDayCount, + "first":{ date: firstItem.Date, time:firstItem.Time } + }; + + if (this.NetworkFilter) + { + var obj= + { + Name:'KLineChartContainer::RequestDragMinuteData', //类名::函数 + Explain:'拖拽1分钟K线数据下载', + Request:{ Url:this.DragMinuteKLineApiUrl, Type:'POST' , Data: postData, Period:this.Period }, + DragDownload:download, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvDragMinuteData(data); + download.Status=0; + self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragMinuteData'); //自动更新 + self.AutoUpdate(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: this.DragMinuteKLineApiUrl, + data:postData, + type:"post", + dataType: "json", + async:true, + success: function(data) + { + self.RecvDragMinuteData(data); + download.Status=0; + self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragMinuteData'); //自动更新 + self.AutoUpdate(); + } + }); + } + + this.RecvDragMinuteData=function(data) + { + var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data); + if (!aryDayData || aryDayData.length<=0) + { + this.DragDownload.Minute.IsEnd=true; + return; + } + + var lastDataCount=this.GetHistoryDataCount(); //保存下上一次的数据个数 + + var firstData=this.SourceData.Data[0]; + var endIndex=null; + for(var i=aryDayData.length-1;i>=0;--i) + { + var item=aryDayData[i]; + if (firstData.Date>item.Date || (firstData.Date==item.Date && firstData.Time>item.Time)) + { + endIndex=i; + break; + } + else if (firstData.Date==item.Date && firstData.Time==item.Time) + { + firstData.YClose=item.YClose; + endIndex=i-1; + break; + } + } + + if (endIndex==null && endIndex<0) return; + + for(var i=0;i=0;--i) + { + var item=aryDayData[i]; + if (firstData.Date>item.Date) + { + endIndex=i; + break; + } + else if (firstData.Date==item.Date) + { + firstData.YClose=item.YClose; + endIndex=i-1; + break; + } + } + if (endIndex==null && endIndex<0) return; + + for(var i=0; i=this.Frame.SubFrame.length) return; + + var item=this.Frame.SubFrame[windowId]; + if (item.Frame) item.Frame.CustomVerticalInfo=data; + } + + this.OnSize=function() + { + if (!this.Frame) return; + if (!this.Frame.OnSize) return; + + //this.Frame.CalculateChartBorder(); + var obj=this.Frame.OnSize(); + this.Frame.SetSizeChage(true); + if (obj.Changed) + { + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(2); + this.UpdateFrameMaxMin(); + } + + this.Draw(); + } + +} + +//API 返回数据 转化为array[] +KLineChartContainer.JsonDataToHistoryData=function(data) +{ + var aryDayData=[]; + if (!data.data) return aryDayData; + + var upperSymbol=null; + if (data.symbol) upperSymbol=data.symbol.toUpperCase(); + var isFutures=false; //是否是期货 + if (upperSymbol) isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); + + var list = data.data; + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, position=8; + var fclose=9, yfclose=10; //结算价, 前结算价 + var bfactor=11, afactor=12; //前, 后复权因子 + for (var i = 0; i < list.length; ++i) + { + var item = new HistoryData(); + var jsData=list[i]; + + item.Date = jsData[date]; + item.Open = jsData[open]; + item.YClose = jsData[yclose]; + item.Close = jsData[close]; + item.High = jsData[high]; + item.Low = jsData[low]; + item.Vol = jsData[vol]; //原始单位股 + item.Amount = jsData[amount]; + if (IFrameSplitOperator.IsNumber(jsData[position])) item.Position=jsData[position];//期货持仓 + if (IFrameSplitOperator.IsNumber(jsData[fclose])) item.FClose=jsData[fclose]; //期货结算价 + if (IFrameSplitOperator.IsNumber(jsData[yfclose])) item.YFClose=jsData[yfclose]; //期货前结算价 + + if (IFrameSplitOperator.IsNumber(jsData[bfactor])) item.BFactor=jsData[bfactor]; //前复权因子 + if (IFrameSplitOperator.IsNumber(jsData[afactor])) item.AFactor=jsData[afactor]; //后复权因子 + + if (!IFrameSplitOperator.IsNumber(item.Open)) continue; + + aryDayData.push(item); + } + + return aryDayData; +} + +KLineChartContainer.JsonDataToRealtimeData=function(data, symbol) +{ + if (!data.stock) return null; + + var stock; + for(var i in data.stock) //查找对应的股票数据 + { + var stockItem=data.stock[i]; + if (stockItem && stockItem.symbol==symbol) + { + stock=stockItem; + break; + } + } + if (!stock) return null; + + var upperSymbol=symbol.toUpperCase(); + var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + + var item=new HistoryData(); + item.Date=stock.date; + item.Open=stock.open; + item.YClose=stock.yclose; + item.High=stock.high; + item.Low=stock.low; + item.Vol=stock.vol; //股 + item.Amount=stock.amount; + item.Close=stock.price; + if (IFrameSplitOperator.IsNumber(stock.position)) item.Position=stock.position; //持仓量 + + if (IFrameSplitOperator.IsNumber(stock.bfactor)) item.BFactor=stock.bfactor; //前复权因子 + if (IFrameSplitOperator.IsNumber(stock.afactor)) item.AFactor=stock.afactor; //后复权因子 + return item; +} + +KLineChartContainer.JsonDataToMinuteRealtimeData=function(data,symbol) +{ + if (!data.stock) return null; + var stock; + for(var i in data.stock) //查找对应的股票数据 + { + var stockItem=data.stock[i]; + if (stockItem && stockItem.symbol==symbol) + { + stock=stockItem; + break; + } + } + if (!stock) return null; + + var upperSymbol=symbol.toUpperCase(); + var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); + var aryMinuteData=new Array(); + var preClose=stock.yclose; //前一个数据价格 + var date=stock.date; + if (isFutures && stock.yclearing)preClose=stock.yclearing; //期货使用昨结算价 + + for(var i in stock.minute) + { + var jsData=stock.minute[i]; + var item = new HistoryData(); + + item.Close=jsData.price; + item.Open=jsData.open; + item.High=jsData.high; + item.Low=jsData.low; + item.Vol=jsData.vol; //股 + item.Amount=jsData.amount; + if (jsData.date>0) item.Date=jsData.date; + else item.Date=date; + item.Time=jsData.time; + item.YClose=preClose; + + if (IFrameSplitOperator.IsNumber(jsData.position)) item.Position=jsData.position; //持仓量 + + if (!IFrameSplitOperator.IsNumber(item.Close)) //当前没有价格 使用上一个价格填充 + { + item.Close=preClose; + item.Open=item.High=item.Low=item.Close; + } + + //价格是0的 都用空 + if (!IFrameSplitOperator.IsNumber(item.Open)) item.Open=null; + if (!IFrameSplitOperator.IsNumber(item.Close)) item.Close=null; + if (!IFrameSplitOperator.IsNumber(item.High)) item.High=null; + if (!IFrameSplitOperator.IsNumber(item.Low)) item.Low=null; + + //上次价格 + if (IFrameSplitOperator.IsNumber(jsData.price)) preClose=jsData.price; + + aryMinuteData[i]=item; + } + + return aryMinuteData; +} + +//API 返回数据 转化为array[] +KLineChartContainer.JsonDataToMinuteHistoryData=function(data) +{ + var upperSymbol=null; + if (data.symbol) upperSymbol=data.symbol.toUpperCase(); + var isSHSZ=false; + if (upperSymbol) isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures=false; //是否是期货 + if (upperSymbol) isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); + var list = data.data; + var aryDayData=new Array(); + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8, position=9; + var yClose=null; + for (var i = 0; i < list.length; ++i) + { + var item = new HistoryData(); + var jsData=list[i]; + item.Date = jsData[date]; + item.Open = jsData[open]; + item.YClose = jsData[yclose]; + item.Close = jsData[close]; + item.High = jsData[high]; + item.Low = jsData[low]; + item.Vol = jsData[vol]; //股 + item.Amount = jsData[amount]; + item.Time=jsData[time]; + if (IFrameSplitOperator.IsNumber(jsData[position])) item.Position=jsData[position]; //期货持仓 + + if (!IFrameSplitOperator.IsNumber(item.YClose)) + { + if (IFrameSplitOperator.IsNumber(yClose)) item.YClose=yClose; + } + + if (IFrameSplitOperator.IsNumber(item.Close)) yClose=item.Close; + + aryDayData.push(item); + } + + /* 内部不处理无效数据, 确保外部传过来的数据是对的. + // 无效数据处理 + for(var i = 0; i < aryDayData.length; ++i) + { + var minData = aryDayData[i]; + if (minData == null) coninue; + if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0 + || isNaN(minData.Close) || minData.Close <= 0 ) + { + if (i == 0) + { + if (minData.YClose > 0) + { + minData.Open = minData.YClose; + minData.High = minData.YClose; + minData.Low = minData.YClose; + minData.Close = minData.YClose; + } + } + else // 用前一个有效数据填充 + { + for(var j = i-1; j >= 0; --j) + { + var minData2 = aryDayData[j]; + if (minData2 == null) coninue; + if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0) + { + if (minData.YClose <= 0) minData.YClose = minData2.Close; + minData.Open = minData2.Open; + minData.High = minData2.High; + minData.Low = minData2.Low; + minData.Close = minData2.Close; + break; + } + } + } + } + } + */ + return aryDayData; +} + +KLineChartContainer.JsonDataToTickData=function(data) +{ + var aryDayData=[]; + if (!data.detail) return aryDayData; + + var date=data.date; + var yClose=data.yclose; + for(var i in data.detail) + { + var item = new HistoryData(); + var tick=data.detail[i]; + if (!tick) continue; + + item.Date = date; + item.Time=tick[0]; + item.Low=item.High=item.Close=item.Open = tick[1]; + item.YClose = yClose; //当前数据的前收盘 + item.Vol = tick[2]; //原始单位股 + item.Amount = tick[3]; + item.Flag=tick[4]; + + if (!IFrameSplitOperator.IsNumber(item.Open)) continue; //停牌的数据剔除 + + //yClose=item.Close; + aryDayData.push(item); + } + + return aryDayData; +} + +KLineChartContainer.JsonDataToTickDataV2=function(data) +{ + var aryDayData=[]; + if (!IFrameSplitOperator.IsNonEmptyArray(data.data)) return aryDayData; + + for(var i=0;i=0) //区间选择 + { + if (moveSetp<5) return; + var obj={ X:e.clientX, Y:e.clientY, PointIndex:this.RectSelectDrag.Index, Name:"MoveRectSelectLine" }; + if (this.MoveRectSelectPoint(obj)) + { + drag.LastMove.X=e.clientX; + this.Draw(); + } + } + else + { + if (moveSetp<5 && yMoveSetp<5) return; + if (!this.EnableSelectRect) return; + + this.UIElement.style.cursor="default"; + var x=drag.Click.X-uielement.getBoundingClientRect().left; + var y=drag.Click.Y-uielement.getBoundingClientRect().top; + var x2=e.clientX-uielement.getBoundingClientRect().left; + var y2=e.clientY-uielement.getBoundingClientRect().top; + this.ShowSelectRect(x,y,x2,y2); + + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + } + } + + this.OnMinuteSelectRectMouseUp=function(e) + { + var drag=this.MouseDrag; + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + + var selectData=new SelectRectData(); + var pixelTatio = GetDevicePixelRatio(); + //区间起始位置 结束位子 + selectData.XStart=(drag.Click.X-uielement.getBoundingClientRect().left)*pixelTatio; + selectData.YStart=(drag.Click.Y-uielement.getBoundingClientRect().top)*pixelTatio; + selectData.XEnd=(drag.LastMove.X-uielement.getBoundingClientRect().left)*pixelTatio; + selectData.YEnd=(drag.LastMove.Y-uielement.getBoundingClientRect().top)*pixelTatio; + selectData.JSChartContainer=this; + selectData.Stock={Symbol:this.Symbol, Name:this.Name}; + + if (this.EnableSelectRect && !this.BorderDrag && this.GetSelectRectData(selectData)) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SELECT_RECT); + var paint=this.GetRectSelectPaint(); + if (event && event.Callback) + { + var data= + { + X:drag.LastMove.X-uielement.getBoundingClientRect().left, + Y:drag.LastMove.Y-uielement.getBoundingClientRect().top, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + event.Callback(event,data,this); + } + + if (this.SelectRectDialog) + { + e.data= + { + Chart:this, + X:drag.LastMove.X-uielement.getBoundingClientRect().left, + Y:drag.LastMove.Y-uielement.getBoundingClientRect().top, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + + this.SelectRectDialog.DoModal(e); + } + else + { + this.HideSelectRect(); + this.UpdateSelectRect(selectData.Start,selectData.End); + } + } + else + { + this.TryClickPaintEvent(JSCHART_EVENT_ID.ON_CLICKUP_CHART_PAINT,this.ClickDownPoint,e); + this.ClickEvent(e); + } + } + + this.OnDragSelectRectMouseUp=function(e) + { + var paint=this.GetRectSelectPaint(); + if (!paint) return; + + var selectData=paint.GetSelectRectData(); + if (!selectData) return; + + var pixelTatio = GetDevicePixelRatio(); + var corssCursor=this.ChartCorssCursor; //十字光标 + var x=corssCursor.LastPoint.X/pixelTatio; + var y=corssCursor.LastPoint.Y/pixelTatio; + + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DRAG_SELECT_RECT_MOUSEUP); + if (event) + { + var data= + { + X:x, + Y:y, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + event.Callback(event,data,this); + } + + if (this.SelectRectDialog) + { + e.data= + { + Chart:this, + X:x, + Y:y, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + + this.SelectRectDialog.DoModal(e); + } + + } + + this.UpdateSelectRect=function(start,end) + { + if (!this.SourceData || !this.SourceData.Data) return; + var paint=this.GetRectSelectPaint(); + if (!paint) return; + + var data=this.SourceData; + if (!data || !IFrameSplitOperator.IsNonEmptyArray(data.Data)) return; + var count=data.Data.length; + var startItem=data.Data[start]; + if (end>=count) end=count-1; + var endItem=data.Data[end]; + + JSConsole.Chart.Log('[MinuteChartContainer::UpdateSelectRect]',startItem,endItem); + paint.SetPoint(startItem, { Index:0 }); + paint.SetPoint(endItem, { Index:1 }); + + this.Draw(); + } + + this.MoveRectSelectPoint=function(obj) + { + var paint=this.GetRectSelectPaint(); + if (!paint) return false; + + if (!this.SourceData || !this.SourceData.Data) return false; + var kData=this.SourceData; + + if (!this.Frame.SubFrame[0]) return false; + var subFrame=this.Frame.SubFrame[0].Frame; + if (!subFrame) false; + + var pixelTatio = GetDevicePixelRatio(); + var x=(obj.X-uielement.getBoundingClientRect().left)*pixelTatio; + var index=subFrame.GetXData(x); + index=parseInt(index.toFixed(0)); + var dataIndex=index+kData.DataOffset; + if (dataIndex>=kData.Data.length) dataIndex=kData.Data.length-1; + + var item = kData.Data[dataIndex]; + JSConsole.Chart.Log("[MinuteChartContainer::MoveRectSelectPoint] point, item", obj.PointIndex, item); + + if (!paint.SetPoint(item,{ Index: obj.PointIndex })) return false; + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DRAG_SELECT_RECT); + if (event) + { + var selectData=paint.GetSelectRectData(); + var data={ X:obj.X, Y:obj.Y, SelectData:selectData, RectSelectPaint:paint }; + event.Callback(event,data,this); + } + + return true; + } + + + //手势 + this.OnTouchStart=function(e) + { + if(this.DragMode==0) return; + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + this.IsOnTouch=true; + this.TouchDrawCount=0; + this.PhonePinch=null; + this.StopDragTimer(); + + if (this.EnableScrollUpDown==false) e.preventDefault(); //上下拖动图形不能阻止事件 + + if (this.IsPhoneDragging(e)) + { + var drag= { Click:{}, LastMove:{} };//LastMove=最后移动的位置 + var touches=this.GetToucheData(e,this.IsForceLandscape); + + drag.Click.X=touches[0].clientX; + drag.Click.Y=touches[0].clientY; + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + var self=this; + + var T_ShowCorssCursor=function() //临时函数(Temp_) T_开头 + { + if (self.ChartCorssCursor.IsShow === true) //移动十字光标 + { + var pixelTatio = GetDevicePixelRatio(); + var x = drag.Click.X-uielement.getBoundingClientRect().left*pixelTatio; + var y = drag.Click.Y-uielement.getBoundingClientRect().top*pixelTatio; + self.OnMouseMove(x, y, e); + } + } + + if (this.ChartDrawOption.IsLockScreen) + { + this.MouseDrag=drag; + if (this.SelectChartDrawPicture) this.SelectChartDrawPicture.IsSelected=false; + this.SelectChartDrawPicture=null; + + if (this.CurrentChartDrawPicture) //画图工具模式 + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2) + this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y,true); + else + { + this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y,true); + //只有1个点 直接完成 + if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo({Corss:false, Tooltip:false}); + } + + if (e.cancelable) e.preventDefault(); + return; + } + else + { + var drawPictrueData={}; + var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速 + drawPictrueData.X=(touches[0].clientX-uielement.getBoundingClientRect().left); + drawPictrueData.Y=(touches[0].clientY-uielement.getBoundingClientRect().top); + if (this.GetChartDrawPictureByPoint(drawPictrueData)) + { + drawPictrueData.ChartDrawPicture.Status=20; + drawPictrueData.ChartDrawPicture.ValueToPoint(); + drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex; + drawPictrueData.ChartDrawPicture.IsSelected=true; + this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture; + this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture; + let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_DRAWPICTURE); //选中画图工具事件 + if (event && event.Callback) + { + let sendData={ DrawPicture: drawPictrueData.ChartDrawPicture }; + event.Callback(event,sendData,this); + } + + if (e.cancelable) e.preventDefault(); + return; + } + } + } + else + { + if (this.EnableScrollUpDown==true) + { + this.DragTimer=setTimeout(function() + { + if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y) + { + var mouseDrag=self.MouseDrag; + self.MouseDrag=null; + T_ShowCorssCursor(); + self.PreventTouchEvent(e) + } + }, 800); + } + + this.MouseDrag=drag; + this.SelectChartDrawPicture=null; + + if (this.EnableScrollUpDown==false) + T_ShowCorssCursor(); //移动十字光标 + } + + if (this.EnableZoomIndexWindow) + { + this.PhoneDBClick.AddTouchStart(touches[0].clientX, touches[0].clientY, Date.now()); + JSConsole.Chart.Log("[MinuteChartContainer::OnTouchStart] PhoneDBClick ", this.PhoneDBClick); + } + } + } + + this.OnTouchMove=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + if (this.IsPhoneDragging(e)) + { + var drag=this.MouseDrag; + if (drag==null) + { + var pixelTatio = GetDevicePixelRatio(); + var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio; + var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio; + this.OnMouseMove(x,y,e); + } + else + { + var moveAngle=this.GetMoveAngle(drag.LastMove,{X:touches[0].clientX, Y:touches[0].clientY}); + var moveSetp=Math.abs(drag.LastMove.X-touches[0].clientX); + var moveUpDown=Math.abs(drag.LastMove.Y-touches[0].clientY); + moveSetp=parseInt(moveSetp); + + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==1 || drawPicture.Status==2) + { + if(moveSetp<5 && moveUpDown<5) return; + if(this.SetChartDrawPictureSecondPoint(touches[0].clientX,touches[0].clientY,true)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==3) + { + if(this.SetChartDrawPictureThirdPoint(touches[0].clientX,touches[0].clientY,true)) + { + this.DrawDynamicInfo(); + } + } + else if (drawPicture.Status==20) //画图工具移动 + { + if(moveSetp<5 && moveUpDown<5) return; + + if(this.MoveChartDrawPicture(touches[0].clientX-drag.LastMove.X,touches[0].clientY-drag.LastMove.Y,true)) + { + this.DrawDynamicInfo(); + } + } + + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + } + else + { + //上下滚动 + if ( ((moveUpDown>0 && moveSetp<=3) || moveAngle<=this.TouchMoveMinAngle) && this.EnableScrollUpDown==true ) + { + this.StopDragTimer(); + return; + } + + this.PreventTouchEvent(e); + this.MouseDrag=null; + var pixelTatio = GetDevicePixelRatio(); + var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio; + var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio; + this.OnMouseMove(x,y,e); + } + } + } + + if (this.EnableScrollUpDown==false) + { + e.preventDefault(); + } + else + { + if (drag==null) + { + this.PreventTouchEvent(e); //十字光标出来了,阻止消息 + } + else + { + this.StopDragTimer(); //上下推动图片,停止定时器,消息传递下去 + } + } + } + + this.OnTouchEnd=function(e) + { + JSConsole.Chart.Log('[MinuteChartContainer::OnTouchEnd]',e); + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var bClearDrawPicture=true; + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3) + { + drawPicture.PointStatus=drawPicture.Status; + if (this.FinishChartDrawPicturePoint()) + this.DrawDynamicInfo(); + else + bClearDrawPicture=false; + } + else if (drawPicture.Status==20) + { + if (this.FinishMoveChartDrawPicture()) + this.DrawDynamicInfo(); + } + } + + if (this.EnableZoomIndexWindow) + { + var time=Date.now(); + this.PhoneDBClick.AddTouchEnd(time); + if (this.PhoneDBClick.IsVaildDBClick()) + { + this.OnTouchDBClick(this.PhoneDBClick.Start); + this.PhoneDBClick.Clear(); + } + } + + this.IsOnTouch = false; + this.StopDragTimer(); + this.OnTouchFinished(); + this.TouchDrawCount=0; + } + + + //键盘左右移动十字光标 + this.OnKeyDown=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var keyID = e.keyCode ? e.keyCode :e.which; + switch(keyID) + { + case 37: //left + this.CursorIndex=parseInt(this.CursorIndex); + if (this.CursorIndex<=0.99999) + { + if (!this.DataMoveLeft()) break; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + } + else + { + --this.CursorIndex; + this.UpdatePointByCursorIndex(); + this.DrawDynamicInfo(); + } + break; + case 39: //right + var xPointcount=0; + if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount; + else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount; + this.CursorIndex=parseInt(this.CursorIndex); + if (this.CursorIndex+1>=xPointcount) + { + if (!this.DataMoveRight()) break; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + } + else + { + //判断是否在最后一个数据上 + var data=null; + if (this.Frame.Data) data=this.Frame.Data; + else data=this.Frame.SubFrame[0].Frame.Data; + if (!data) break; + if (this.CursorIndex+data.DataOffset+1>=data.Data.length) break; + + ++this.CursorIndex; + this.UpdatePointByCursorIndex(); + this.DrawDynamicInfo(); + } + break; + case 46: //del + if (!this.SelectChartDrawPicture) break; + var drawPicture=this.SelectChartDrawPicture; + JSConsole.Chart.Log(drawPicture,"drawPicturedrawPicturedrawPicture") + this.SelectChartDrawPicture=null; + this.ClearChartDrawPicture(drawPicture); //删除选中的画图工具 + break; + case 32: //space + this.OnMarkRectSelect(e); + break; + default: + return; + } + + //不让滚动条滚动 + if(e.preventDefault) e.preventDefault(); + else e.returnValue = false; + } + + this.OnMarkRectSelect=function(e) + { + var corssCursor=this.ChartCorssCursor; //十字光标 + if (!corssCursor || corssCursor.Status==0) return; + if (!IFrameSplitOperator.IsNumber(corssCursor.CursorIndex)) return; + if (!this.SourceData || !this.SourceData.Data) return; + var paint=this.GetRectSelectPaint(); + if (!paint) return; + + var cursorIndex=corssCursor.CursorIndex; + JSConsole.Chart.Log("[MinuteChartContainer::OnMarkRectSelect] dataIndex", cursorIndex); + + var kData=this.SourceData; + cursorIndex=parseInt(cursorIndex.toFixed(0)); + var index=cursorIndex+kData.DataOffset; + if (index>=kData.Data.length) index=kData.Data.length-1; + + var item = kData.Data[index]; + + JSConsole.Chart.Log("[MinuteChartContainer::OnMarkRectSelect] item", item); + + if (!this.SetRectSelectData(item)) return; + + if (paint.GetPointCount()==2) + { + var selectData=paint.GetSelectRectData(); + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_KEYDOWN_SELECT_RECT); + var pixelTatio = GetDevicePixelRatio(); + if (event && event.Callback) + { + var data= + { + X:corssCursor.LastPoint.X/pixelTatio, + Y:corssCursor.LastPoint.Y/pixelTatio, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + event.Callback(event,data,this); + } + + if (this.SelectRectDialog) + { + e.data= + { + Chart:this, + X:corssCursor.LastPoint.X/pixelTatio, + Y:corssCursor.LastPoint.Y/pixelTatio, + SelectData:selectData, //区间选择的数据 + RectSelectPaint:paint //区间选择背景 + }; + this.SelectRectDialog.DoModal(e); + } + } + + this.Draw(); + } + + //注册鼠标右键事件 + this.OnRightMenu=function(x,y,e) + { + if (this.RightMenu) + { + var frameId=this.Frame.PtInFrame(x,y); + e.data={ Chart:this, FrameID:frameId }; + this.RightMenu.DoModal(e); + } + + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CONTEXT_MENU); + if (event) + { + var frameId=this.Frame.PtInFrame(x,y); + var data={ X:x, Y:y, Event:e, FrameID:frameId }; + event.Callback(event,data,this); + } + } + + this.OnWheel=function(e) + { + JSConsole.Chart.Log('[MinuteChartContainer::OnWheel]',e); + } + + this.OnDoubleClick=function(x,y,e) + { + JSConsole.Chart.Log("[MinuteChartContainer::OnDoubleClick]", e); + + if (this.ClickChartTimer!=null) + { + clearTimeout(this.ClickChartTimer); + this.ClickChartTimer=null; + } + + this.DBClickEvent(e); + + if (this.EnableZoomIndexWindow) + { + var frameId=this.Frame.PtInFrame(x,y); + JSConsole.Chart.Log("[MinuteChartContainer::OnDoubleClick] frameId",frameId); + if (frameId>=this.Frame.ZoomStartWindowIndex) + { + if (this.ZoomIndexWindow(frameId, {X:x, Y:y})) + { + this.Frame.SetSizeChage(true); + this.Draw(); + } + } + } + } + + this.DBClickEvent=function(e) + { + var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_DBCLICK); + if (!event || !event.Callback) return false; + + var pixelTatio = GetDevicePixelRatio(); + var x=(e.clientX-uielement.getBoundingClientRect().left)*pixelTatio; + var y=(e.clientY-uielement.getBoundingClientRect().top)*pixelTatio; + var data= { X:e.clientX, Y:e.clientY, FrameID:-1 , ClientPos:-1 }; + + var clientPos=this.PtInClient(x,y); + data.ClientPos=clientPos; + + if (clientPos>0) + { + var yValueExtend={}; + var yValue=this.Frame.GetYData(y,yValueExtend); + + if (IFrameSplitOperator.IsNumber(yValueExtend.FrameID) && yValueExtend.FrameID>=0) + { + var xValue=this.Frame.GetXData(x); + data.FrameID=yValueExtend.FrameID; + data.Data={ X:xValue, Y:yValue } ; + } + } + + event.Callback(event, data, this); + return true; + } + + this.UpdatePointByCursorIndex=function() + { + this.LastPoint.X=this.Frame.GetXFromIndex(this.CursorIndex); + + var index=this.CursorIndex; + index=parseInt(index.toFixed(0)); + var data=this.Frame.SourceData; + if (data.DataOffset+index>=data.Data.length) + { + return; + } + var item=data.Data[data.DataOffset+index]; + var close=null; + if (item.Before) close=item.Before.Close; + else close=item.Close + + this.LastPoint.Y=this.Frame.GetYFromData(close); + } + + + + //创建 + //windowCount 窗口个数 + this.Create=function(windowCount,option) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new ChartCorssCursor(); + this.ChartCorssCursor.PtInClient=(x,y)=>{ return this.PtInClient(x,y); } + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.StringFormatX=new HQMinuteTimeStringFormat(); + this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat(); + this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + this.ChartCorssCursor.CallAcutionXOperator=new CallAcutionXOperator(); + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame=new HQTradeFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=25; + this.Frame.ChartBorder.TitleHeight=0; + this.Frame.ChartBorder.Left=50; + this.Frame.ChartBorder.Bottom=20; + this.Frame.Canvas=this.Canvas; + this.Frame.GetExtendChartRightWidth=()=> { return this.GetExtendChartRightWidth() } + this.Frame.ZoomStartWindowIndex=2; + this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + this.CreateExtendChart("RectSelectPaint"); //区间统计 + + //子窗口动态标题 + for(var i in this.Frame.SubFrame) + { + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[i].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + this.TitlePaint.push(titlePaint); + } + + this.ChartCorssCursor.StringFormatX.Frame=this.Frame.SubFrame[0].Frame; + this.ChartCorssCursor.StringFormatY.Frame=this.Frame; + if (this.ChartCorssCursor.CallAcutionXOperator) + this.ChartCorssCursor.CallAcutionXOperator.Frame=this.Frame.SubFrame[0].Frame; + + var bRegisterKeydown=true; + var bRegisterWheel=true; + + if (option) + { + if (option.KeyDown===false) + { + bRegisterKeydown=false; + JSConsole.Chart.Log('[MinuteChartContainer::Create] not register keydown event.'); + } + + if (option.Wheel===false) + { + bRegisterWheel=false; + JSConsole.Chart.Log('[MinuteChartContainer::Create] not register wheel event.'); + } + } + + if (bRegisterKeydown) this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e);} , true); //键盘消息 + if (bRegisterWheel) this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true); //上下滚动消息 + } + + //创建子窗口 + this.CreateChildWindow=function(windowCount) + { + for(var i=0;i=2) + { + if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal; + } + + var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1]; + frame.HorizontalMax=DEFAULT_HORIZONTAL[0]; + frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1]; + + if (i==0) + { + frame.YSplitOperator=new FrameSplitMinutePriceY(); + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('price'); + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + else + { + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitMinuteX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + if (i!=windowCount-1) frame.XSplitOperator.ShowText=false; + frame.XSplitOperator.Operator(); + + for(var j in DEFAULT_HORIZONTAL) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j]; + if (i==0 && j==frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + if (i==0) + subFrame.Height=20; + else + subFrame.Height=10; + + this.Frame.SubFrame[i]=subFrame; + } + } + + this.CreateSubFrameItem=function(id) + { + var border=new ChartBorder(); + border.UIElement=this.UIElement; + + var frame=new MinuteFrame(); + frame.Canvas=this.Canvas; + frame.ChartBorder=border; + frame.Identify=id; //窗口序号 + frame.XPointCount=243; + + if (id>=2) + { + if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal; + } + + var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1]; + frame.HorizontalMax=DEFAULT_HORIZONTAL[0]; + frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1]; + + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitMinuteX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.ShowText=false; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + + //K线数据绑定 + var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount; + frame.XPointCount=xPointCouont; + frame.Data=this.ChartPaint[0].Data; + + for(var j in DEFAULT_HORIZONTAL) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j]; + frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + subFrame.Height=10; + + return subFrame; + } + + this.UpdateXShowText=function() + { + var bLastFrame=true; + for(var i=this.Frame.SubFrame.length-1;i>=0;--i) + { + var item=this.Frame.SubFrame[i].Frame; + var subFrame=this.Frame.SubFrame[i]; + + if (bLastFrame) + { + item.XSplitOperator.ShowText=true; + if (subFrame.Height>0) bLastFrame=false; + } + else + { + item.XSplitOperator.ShowText=false; + } + } + } + + //删除某一个窗口的指标 + this.DeleteIndexPaint=function(windowIndex) + { + let paint=new Array(); //踢出当前窗口的指标画法 + for(let i in this.ChartPaint) + { + let item=this.ChartPaint[i]; + + if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null; + this.Frame.SubFrame[windowIndex].Frame.IsLocked=false; //解除上锁 + + this.ChartPaint=paint; + + //清空东条标题 + var titleIndex=windowIndex+1; + this.TitlePaint[titleIndex].Data=[]; + this.TitlePaint[titleIndex].Title=null; + } + + this.CreateStockInfo=function() + { + this.ExtendChartPaint[0]=new StockInfoExtendChartPaint(); + this.ExtendChartPaint[0].Canvas=this.Canvas; + this.ExtendChartPaint[0].ChartBorder=this.Frame.ChartBorder; + this.ExtendChartPaint[0].ChartFrame=this.Frame; + + this.Frame.ChartBorder.Right=300; + } + + //创建主图K线画法 + this.CreateMainKLine=function() + { + //分钟线 + var minuteLine=new ChartMinutePriceLine(); + minuteLine.Canvas=this.Canvas; + minuteLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + minuteLine.ChartFrame=this.Frame.SubFrame[0].Frame; + minuteLine.Name="Minute-Line"; + minuteLine.Color=g_JSChartResource.Minute.PriceColor; + minuteLine.AreaColor=g_JSChartResource.Minute.AreaPriceColor; + minuteLine.GetEventCallback=(id)=>{ return this.GetEventCallback(id); }; + + this.ChartPaint[0]=minuteLine; + + //分钟线均线 + var averageLine=new ChartMinutePriceLine(); + averageLine.Canvas=this.Canvas; + averageLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + averageLine.ChartFrame=this.Frame.SubFrame[0].Frame; + averageLine.Name="Minute-Average-Line"; + averageLine.Color=g_JSChartResource.Minute.AvPriceColor; + averageLine.IsDrawArea=false; + this.ChartPaint[1]=averageLine; + + //成交量 + var volLine=new ChartMinuteVolumBar(); + volLine.Color=g_JSChartResource.Minute.VolBarColor; + volLine.Canvas=this.Canvas; + volLine.ChartBorder=this.Frame.SubFrame[1].Frame.ChartBorder; + volLine.ChartFrame=this.Frame.SubFrame[1].Frame; + volLine.Name="Minute-Vol-Bar"; + this.ChartPaint[2]=volLine; + + this.TitlePaint[0]=new DynamicMinuteTitlePainting(); + this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas=this.Canvas; + this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint; //绑定叠加 + this.TitlePaint[0].LanguageID=this.LanguageID; + this.TitlePaint[0].CallAcutionXOperator=new CallAcutionXOperator(); + this.TitlePaint[0].CallAcutionXOperator.Frame=this.Frame.SubFrame[0].Frame; + } + + //切换成 脚本指标 + this.ChangeScriptIndex=function(windowIndex,indexData,option) + { + this.DeleteIndexPaint(windowIndex); + this.WindowIndex[windowIndex]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + + var bindData=this.SourceData; + this.BindIndexData(windowIndex,bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.ChangeIndex=function(windowIndex,indexName,option) + { + if (this.Frame.SubFrame.length<3) return; + + //查找系统指标 + let scriptData = new JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if (windowIndex<2) windowIndex=2; + if (windowIndex>=this.Frame.SubFrame.length) windowIndex=2; + + let indexData = + { + Name:indexInfo.Name, Script:indexInfo.Script, Args: indexInfo.Args, ID:indexName , + //扩展属性 可以是空 + KLineType:indexInfo.KLineType, YSpecificMaxMin:indexInfo.YSpecificMaxMin, YSplitScale:indexInfo.YSplitScale, + FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition,StringFormat:indexInfo.StringFormat, + OutName:indexInfo.OutName + }; + + if (option) + { + if (option.FloatPrecision>=0) indexData.FloatPrecision=option.FloatPrecision; + if (option.StringFormat>0) indexData.StringFormat=option.StringFormat; + if (option.Args) indexData.Args=option.Args; + } + + return this.ChangeScriptIndex(windowIndex, indexData,option); + } + + //设置指标窗口个数 + this.ChangeIndexWindowCount=function(count) + { + if (count<2) return; //1,2个窗口固定的不能动 + if (this.Frame.SubFrame.length==count) return; + + var currentLength=this.Frame.SubFrame.length; + if (currentLength>count) + { + for(var i=currentLength-1;i>=count;--i) + { + this.DeleteIndexPaint(i); + var item=this.Frame.SubFrame[i].Frame; + if (item.ClearToolbar) item.ClearToolbar(); + } + + this.Frame.SubFrame.splice(count,currentLength-count); + this.WindowIndex.splice(count,currentLength-count); + } + else + { + //创建新的指标窗口 + for(var i=currentLength;i { return this.GetEventCallback(id); } + this.TitlePaint[i+1]=titlePaint; + } + + //创建指标 + const indexName=["RSI","MACD","DMA","DMI","KDJ","WR"]; + let scriptData = new JSIndexScript(); + for(var i=currentLength;i=this.Frame.SubFrame.length) return; + + var delFrame=this.Frame.SubFrame[id].Frame; + this.DeleteIndexPaint(id); + this.Frame.SubFrame[id].Frame.ClearToolbar(); + this.Frame.SubFrame.splice(id,1); + this.WindowIndex.splice(id,1); + this.TitlePaint.splice(id+1,1); //删除对应的动态标题 + + for(var i=0;i0) + { + var aryDrawPicture=[]; + for(var i in this.ChartDrawPicture) + { + var item=this.ChartDrawPicture[i]; + if (item.Frame==delFrame) continue; + aryDrawPicture.push(item); + } + + this.ChartDrawPicture=aryDrawPicture; + } + */ + + this.Frame.SetSizeChage(true); + this.UpdateFrameMaxMin(); + this.ResetFrameXYSplit(); + this.Draw(); + } + + //切换股票代码 + this.ChangeSymbol=function(symbol) + { + this.CancelAutoUpdate(); + this.Symbol=symbol; + this.ResetOverlaySymbolStatus(); + this.ReloadChartDrawPicture(); + + if (symbol) + { + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); //增加下载动画 + this.Draw(); + this.RequestData(); + } + else + { + this.DrawEmpty(); + } + } + + this.ChangeDayCount=function(count) + { + if (count<0 || count>10) return; + this.DayCount=count; + + if (this.DayCount>1) + { + this.ChartDrawPicture=[]; + } + else + { + this.ReloadChartDrawPicture(); + } + + this.ResetOverlaySymbolStatus(); + this.RequestData(); + } + + //叠加股票 + this.OverlaySymbol=function(symbol,option) + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (item.Symbol==symbol) + { + console.warn(`[MinuteChartContainer::OverlaySymbol] overlay symbol=${symbol} exist.`); + return false; + } + } + + var paint=new ChartOverlayMinutePriceLine(); + paint.Canvas=this.Canvas; + paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame=this.Frame.SubFrame[0].Frame; + paint.Name="Overlay-Minute"; + paint.Symbol=symbol; + if (option && option.Color) paint.Color=option.Color; //外部设置颜色 + else paint.Color=g_JSChartResource.OverlaySymbol.Color[g_JSChartResource.OverlaySymbol.Random%g_JSChartResource.OverlaySymbol.Color.length]; + ++g_JSChartResource.OverlaySymbol.Random; + if (this.ChartPaint[0].YClose>0 && this.ChartPaint[0].Data) //绑定主图数据 + { + paint.MainData=this.ChartPaint[0].Data; + paint.MainYClose=this.ChartPaint[0].YClose; + } + + this.OverlayChartPaint.push(paint); + + if (this.DayCount<=1) this.RequestOverlayMinuteData(); //请求数据 + else this.RequestOverlayHistoryMinuteData(); + + return true; + } + + this.ResetOverlaySymbolStatus=function() + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + item.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID; + } + } + + //删除一个叠加股票 + this.DeleteOverlaySymbol=function(symbol) + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + if (item.Symbol===symbol) + { + item.IsDelete=true; + this.OverlayChartPaint.splice(i,1); + this.UpdateFrameMaxMin(); + this.Draw(); + return true; + } + } + + console.warn(`[MinuteChartContainer::DeleteOverlaySymbol] overlay symbol=${symbol} not exist.`) + return false; + } + + //取消叠加股票 + this.ClearOverlaySymbol=function() + { + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + item.IsDelete=true; + } + this.OverlayChartPaint=[]; + this.Frame.SubFrame[0].Frame.YSplitOperator.OverlayChartPaint=this.OverlayChartPaint; + this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint; //绑定叠加 + this.UpdateFrameMaxMin(); + this.Draw(); + } + + this.ShowBeforeData=function(isShow, option) + { + this.ShowCallAuctionData({Left:isShow}, option); + } + + //集合竞价设置 obj={ Left:, Right: } + this.ShowCallAuctionData=function(obj, option) + { + if (!obj) return; + + var optionChanged=false; //配置修改 + if (IFrameSplitOperator.IsBool(obj.Left) && this.IsShowBeforeData!=obj.Left) + { + this.IsShowBeforeData=obj.Left; + optionChanged=true; + } + + if (IFrameSplitOperator.IsBool(obj.Right) && this.IsShowAfterData!=obj.Right) + { + this.IsShowAfterData=obj.Right; + optionChanged=true; + } + + if (obj.MultiDay) + { + var item=obj.MultiDay; + if (IFrameSplitOperator.IsBool(item.Left) && this.IsShowMultiDayBeforeData!=item.Left) + { + this.IsShowMultiDayBeforeData=item.Left; + optionChanged=true; + } + + if (IFrameSplitOperator.IsBool(item.Right) && this.IsShowMultiDayAfterData!=item.Right) + { + this.IsShowMultiDayAfterData=item.Right; + optionChanged=true; + } + } + + if (option) + { + if (option.BeforeOpen) + { + var item=option.BeforeOpen; + if (IFrameSplitOperator.IsNumber(item.Left) && this.ExtendWidth.Left!=item.Left) + { + this.ExtendWidth.Left=item.Left; + optionChanged=true; + } + } + + if (option.AfterClose) + { + var item=option.AfterClose; + if (IFrameSplitOperator.IsNumber(item.Right) && this.ExtendWidth.Right!=item.Right) + { + this.ExtendWidth.Right=item.Right; + optionChanged=true; + } + } + + if (option.MultiDay) + { + var item=option.MultiDay; + if (IFrameSplitOperator.IsNumber(item.Left) && this.MultiDayExtendWidth.Left!=item.Left) + { + this.MultiDayExtendWidth.Left=item.Left; + optionChanged=true; + } + + if (IFrameSplitOperator.IsNumber(item.Right)&& this.MultiDayExtendWidth.Right!=item.Right) + { + this.MultiDayExtendWidth.Right=item.Right; + optionChanged=true; + } + } + } + + if (optionChanged) this.RequestData(); + } + + this.RequestData=function() + { + if (this.DayCount<=1) this.RequestMinuteData(); + else this.RequestHistoryMinuteData(); + } + + this.ChangeDrawType=function(type) + { + if (this.ChartPaint.length<=0) return; + + if (type==0) this.ChartPaint[0].IsDrawArea=true; + else if (type==1) this.ChartPaint[0].IsDrawArea=false; + else return; + + this.Draw(); + } + + //请求历史分钟数据 + this.RequestHistoryMinuteData=function() + { + var self=this; + this.IsBeforeData=false; + this.IsAfterData=false; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + + if (this.NetworkFilter) + { + var obj= + { + Name:'MinuteChartContainer::RequestHistoryMinuteData', //类名::函数 + Explain:'多日分时数据', + Request:{ Url:self.HistoryMinuteApiUrl, Data:{daycount:self.DayCount, symbol:self.Symbol}, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol": self.Symbol, + "daycount": self.DayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + } + }); + } + + this.RecvHistoryMinuteData=function(data) + { + this.DayData=MinuteChartContainer.JsonDataToMinuteDataArray(data); + this.ColorLineData=MinuteChartContainer.JsonDataToHistoryMinuteLineColorData(data); + this.MultiDayBeforeOpenData=MinuteChartContainer.JosnDataToBeforeOpenDataArray(data); + this.MultiDayAfterCloseData=MinuteChartContainer.JosnDataToAfterCloseDataArray(data); + this.CaclutateCallCationYRange(); + this.Symbol=data.symbol; + this.Name=data.name; + this.SetCallCationDataBorder( + { + Left:false, Right:false, + MultiDay:{ Left:this.IsShowMultiDayBeforeData, Right:this.IsShowMultiDayAfterData } + } ); + this.CaclutateLimitPrice(this.DayData[0].YClose, data.data[0].limitprice); //计算涨停价格 + this.UpdateHistoryMinuteUI(); + this.RecvMinuteDataEvent(); + this.RequestOverlayHistoryMinuteData(); + + this.BindAllOverlayIndexData(this.SourceData); + + this.AutoUpdate(); + } + + this.CaclutateCallCationYRange=function() + { + //多日集合竞价Y轴统一成交量 + if (IFrameSplitOperator.IsNonEmptyArray(this.MultiDayAfterCloseData)) + { + var range={ Max:null, Min:null }; + for(var i in this.MultiDayAfterCloseData) + { + var item=this.MultiDayAfterCloseData[i]; + if (range.Max==null) range.Max=item.VolMax; + else if (range.Maxitem.VolMin) range.Min=item.VolMin; + } + + for(var i in this.MultiDayAfterCloseData) + { + var item=this.MultiDayAfterCloseData[i]; + item.VolMax=range.Max; + item.VolMin=range.Min; + } + } + + if (IFrameSplitOperator.IsNonEmptyArray(this.MultiDayBeforeOpenData)) + { + var range={ Max:null, Min:null }; + for(var i in this.MultiDayBeforeOpenData) + { + var item=this.MultiDayBeforeOpenData[i]; + if (range.Max==null) range.Max=item.VolMax; + else if (range.Maxitem.VolMin) range.Min=item.VolMin; + } + + for(var i in this.MultiDayBeforeOpenData) + { + var item=this.MultiDayBeforeOpenData[i]; + item.VolMax=range.Max; + item.VolMin=range.Min; + } + } + } + + this.UpdateHistoryMinuteUI=function() + { + var allMinuteData=this.HistoryMinuteDataToArray(this.DayData); + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=allMinuteData; + + this.SourceData=sourceData; + this.TradeDate=this.DayData[0].Date; + this.Frame.SetDayCount(this.DayData.length); + + this.BindMainData(sourceData,this.DayData[0].YClose); + var upperSymbol=this.Symbol.toUpperCase(); + //外汇 均线暂时不用 + if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) this.ChartPaint[1].Data=null; + + if (this.Frame.SubFrame.length>2) + { + var bindData=new ChartData(); + bindData.Data=allMinuteData; + for(var i=2; i=0;--i) + { + var item=data[i]; + for(var j in item.Data) + { + result.push(item.Data[j]); + } + } + return result; + } + + this.UpdateLatestMinuteData=function(data,date) + { + for(var i in this.DayData) + { + var item=this.DayData[i]; + if (item.Date===date) + { + item.Data=data; + break; + } + } + } + + //请求分钟数据 + this.RequestMinuteData=function() + { + var self=this; + + var fields= + [ + "name","symbol", + "yclose","open","price","high","low", + "vol","amount", + "date","time", + "minute","minutecount" + ]; + + var upperSymbol=this.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsFutures(upperSymbol)) + { //期货的需要加上结算价 + fields.push("clearing"); + fields.push("yclearing"); + } + + // 盘前数据(A股) + this.IsBeforeData=false; + if (this.IsShowBeforeData && this.DayCount===1 && MARKET_SUFFIX_NAME.IsSHSZStockA(self.Symbol)) + { + this.IsBeforeData=true; + fields.push('before'); + } + + this.IsAfterData=false; + if (this.IsShowAfterData && this.DayCount===1 && MARKET_SUFFIX_NAME.IsSHSZStockA(self.Symbol)) + { + this.IsAfterData=true; + } + + if (this.NetworkFilter) + { + var callCation= + { + Before:(this.IsShowBeforeData && this.DayCount===1), + After:(this.IsShowAfterData && this.DayCount===1) + } //集合竞价 + var obj= + { + Name:'MinuteChartContainer::RequestMinuteData', //类名::函数名 + Explain:'最新分时数据', + Request:{ Url:self.MinuteApiUrl, Data:{field:fields, symbol:[self.Symbol], callcation:callCation }, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteData(data); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.MinuteApiUrl, + data: + { + "field": fields, + "symbol": [self.Symbol], + "start": -1 + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteData(data); + } + }); + } + + this.RecvMinuteDataEvent=function() + { + if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_MINUTE_DATA)) return; + + var event=this.mapEvent.get(JSCHART_EVENT_ID.RECV_MINUTE_DATA); + var data={ MinuteData:this.SourceData, Stock:{Symbol:this.Symbol, Name:this.Name } } + event.Callback(event,data,this); + } + + this.UpdateLineColorData=function(data, date) + { + if (!this.ColorLineData) + { + this.ColorLineData=data; + return; + } + + + //移除当前的 + var aryColorLineData=this.ColorLineData.filter(function(item, index, arr) + { + return item.Date!=date; + }); + + if (IFrameSplitOperator.IsNonEmptyArray(data)) + { + for(var i in data) + { + aryColorLineData.push(data[i]); + } + } + + + this.ColorLineData=aryColorLineData; + } + + this.UpdateCallCationData=function(beforeOpenData,afterCloseData) + { + if (beforeOpenData && IFrameSplitOperator.IsNonEmptyArray(this.MultiDayBeforeOpenData)) + { + var lastItem=this.MultiDayBeforeOpenData[this.MultiDayBeforeOpenData.length-1]; + if (lastItem.Date==beforeOpenData.Date) + this.MultiDayBeforeOpenData[this.MultiDayBeforeOpenData.length-1]=beforeOpenData; + } + + if (afterCloseData && IFrameSplitOperator.IsNonEmptyArray(this.MultiDayAfterCloseData)) + { + var lastItem=this.MultiDayAfterCloseData[this.MultiDayBeforeOpenData.length-1]; + if (lastItem.Date==afterCloseData.Date) + this.MultiDayAfterCloseData[this.MultiDayAfterCloseData.length-1]=afterCloseData; + } + + this.CaclutateCallCationYRange(); + } + + this.RecvMinuteData=function(data) + { + if (!data) + { + JSConsole.Chart.Warn("[MinuteChartContainer::RecvMinuteData] recv data is null"); + return; + } + + var aryMinuteData=MinuteChartContainer.JsonDataToMinuteData(data); + var aryColorData=MinuteChartContainer.JsonDataToMinuteLineColorData(data); + this.BeforeOpenData=null; + this.AfterCloseData=null; + var beforeOpenData=MinuteChartContainer.JsonDataToBeforeOpenData(data); + var afterCloseData=MinuteChartContainer.JsonDataToAfterCloseData(data); + + if (this.IsBeforeData) this.BeforeOpenData=beforeOpenData; + if (this.IsAfterData) this.AfterCloseData=afterCloseData; + + if (this.DayCount>1) //多日走势图 + { + this.UpdateCallCationData(beforeOpenData,afterCloseData); + this.UpdateLineColorData(aryColorData,data.stock[0].date); + this.UpdateLatestMinuteData(aryMinuteData,data.stock[0].date); + this.UpdateHistoryMinuteUI(); + this.RecvMinuteDataEvent(); + this.RequestOverlayMinuteData(); //请求叠加数据 (主数据下载完再下载) + this.BindAllOverlayIndexData(this.SourceData); + this.AutoUpdate(); + return; + } + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=aryMinuteData; + + this.ColorLineData=aryColorData; + + this.TradeDate=data.stock[0].date; + this.Frame.SetDayCount(1); //单日数据 + + this.SourceData=sourceData; + this.Symbol=data.stock[0].symbol; + this.Name=data.stock[0].name; + + this.SetCallCationDataBorder( { Left:this.IsBeforeData, Right:this.IsAfterData , MultiDay:{ Left:false, Right:false }} ); + + var yClose=data.stock[0].yclose; + var upperSymbol=this.Symbol.toUpperCase(); + var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); + if (data.stock[0].yclearing>0 && isFutures) yClose=data.stock[0].yclearing; //期货使用前结算价 + this.CaclutateLimitPrice(yClose, data.stock[0].limitprice); //计算涨停价格 + var extendData=null; + if (data.stock[0].high>0 && data.stock[0].low>0) extendData={ High:data.stock[0].high, Low:data.stock[0].low }; + this.BindMainData(sourceData,yClose, extendData); + + if (this.Frame.SubFrame.length>2) + { + var bindData=new ChartData(); + bindData.Data=aryMinuteData; + for(var i=2; i0 && limitData.min>0) //API里带涨停价格 直接使用 + { + this.LimitPrice={ Max:limitData.max, Min:limitData.min }; + return; + } + + var range=MARKET_SUFFIX_NAME.GetLimitPriceRange(this.Symbol, this.Name); //通过规则获取涨停价格 + if (!range) + { + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} no limit price.`) + return; + } + + //var yClose=data.stock[0].yclose; + if (yClose<=0) return; + this.LimitPrice={ Max:yClose*(1+range.Max), Min:yClose*(1+range.Min) }; + + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} yClose:${yClose} max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`); + + this.LimitPrice.Max=parseFloat(this.LimitPrice.Max.toFixed(2)); + this.LimitPrice.Min=parseFloat(this.LimitPrice.Min.toFixed(2)); + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} tofixed(2) max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`); + } + + //请求叠加数据 (主数据下载完再下载)) + this.RequestOverlayMinuteData=function() + { + var self = this; + var date=this.TradeDate; //最后一个交易日期 + + for(var i in this.OverlayChartPaint) + { + let item=this.OverlayChartPaint[i]; + if (!item.MainData || !(item.MainYClose>0) ) continue; + if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue; + var symbol=item.Symbol; + if (!symbol) return; + + item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID; + + if (this.NetworkFilter) + { + var obj= + { + Name:'MinuteChartContainer::RequestOverlayMinuteData', //类名::函数名 + Explain:'叠加股票最新分时数据', + Request:{ Url:self.HistoryMinuteApiUrl, Data:{days:[date], symbol:symbol}, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayMinuteData(data,item); + }); + + if (obj.PreventDefault==true) continue; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + JSNetwork.HttpRequest({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol":symbol, + "days": [date], + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayMinuteData(data,item); + } + }); + } + } + + this.RecvOverlayMinuteData=function(data,paint) + { + if (paint.IsDelete) return; + + var aryMinuteData=MinuteChartContainer.JsonDataToMinuteDataArray(data); + + var sourceData=null; + var yClose; + if (this.DayCount>1) //多日数据 + { + if (aryMinuteData.length<=0) return; + + var minuteData=aryMinuteData[0]; + for(var i in paint.SourceData) + { + var item=paint.SourceData[i]; + if (item.Date==minuteData.Date) + { + paint.SourceData[i]=minuteData; + var allMinuteData=this.HistoryMinuteDataToArray(paint.SourceData); + var sourceData=new ChartData(); + sourceData.Data=allMinuteData; + yClose=minuteData.YClose; + break; + } + } + if (sourceData==null) return; + } + else + { + if (aryMinuteData.length>0) sourceData=aryMinuteData[0]; + else sourceData=new ChartData(); + yClose=sourceData.YClose; + } + + paint.Data=sourceData; + paint.Title=data.name; + paint.Symbol=data.symbol; + paint.YClose=yClose; + paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.RequestOverlayHistoryMinuteData=function() + { + var self = this; + var days=[]; + for(var i in this.DayData) + { + var item=this.DayData[i]; + days.push(item.Date); + } + if (days.length<=0) return; + + for(var i in this.OverlayChartPaint) + { + let item=this.OverlayChartPaint[i] + var symbol=item.Symbol; + if (!symbol) return; + if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue; + + item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID; + + if (this.NetworkFilter) + { + var obj= + { + Name:'MinuteChartContainer::RequestOverlayHistoryMinuteData', //类名::函数名 + Explain:'叠加股票多日分时数据', + Request:{ Url:self.HistoryMinuteApiUrl, Data:{days:days, symbol:symbol}, Type:'POST' }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayHistoryMinuteData(data,item); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.HistoryMinuteApiUrl, + data:{ "symbol": symbol, "days": days }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID; + self.RecvOverlayHistoryMinuteData(data,item); + } + }); + } + } + + this.RecvOverlayHistoryMinuteData=function(data,paint) //叠加历史的分钟数据 + { + var dayData=MinuteChartContainer.JsonDataToMinuteDataArray(data); + var overlayDayData=[]; + for(var i in this.DayData) + { + var item=this.DayData[i]; + var bFind=false; + for(var j in dayData) + { + if (item.Date==dayData[j].Date) + { + overlayDayData.push(dayData[i]); + bFind=true; + break; + } + } + if (!bFind) //当天不存在叠加数据, 存空 + { + var empytData=new ChartData(); + empytData.Date=item.Date; + } + } + + paint.SourceData=overlayDayData; + var allMinuteData=this.HistoryMinuteDataToArray(overlayDayData); + var yClose=overlayDayData[0].YClose; //取最近一个交易日前收盘最为中轴线 + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=allMinuteData; + + paint.Data=sourceData; + paint.Title=data.name; + paint.Symbol=data.symbol; + paint.YClose=yClose; + paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.CancelAutoUpdate=function() //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + //数据自动更新 + this.AutoUpdate=function() + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + + var self = this; + var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus==0 || marketStatus==3) return; //闭市,盘后 + + var frequency=this.AutoUpdateFrequency; + if (marketStatus==1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.AutoUpdate(); + },frequency); + } + else if (marketStatus==2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.ResetOverlaySymbolStatus(); + self.RequestMinuteData(); + },frequency); + } + } + + this.BindIndexData=function(windowIndex,hisData) + { + if (!this.WindowIndex[windowIndex]) return; + + if (typeof(this.WindowIndex[windowIndex].RequestData)=="function") //数据需要另外下载的. + { + this.WindowIndex[windowIndex].RequestData(this,windowIndex,hisData); + return; + } + if (typeof(this.WindowIndex[windowIndex].ExecuteScript)=='function') + { + this.WindowIndex[windowIndex].ExecuteScript(this,windowIndex,hisData); + return; + } + + this.WindowIndex[windowIndex].BindData(this,windowIndex,hisData); + } + + //绑定分钟数据 + this.BindMainData=function(minuteData,yClose, extendData) + { + var multiBeforeOpenData=this.IsShowMultiDayBeforeData?this.MultiDayBeforeOpenData:null; + var multiAfterCloseData=this.IsShowMultiDayAfterData?this.MultiDayAfterCloseData:null; + + //分钟数据 + var bindData=new ChartData(); + bindData.Data=minuteData.GetClose(); + this.ChartPaint[0].Data=bindData; + this.ChartPaint[0].YClose=yClose; + this.ChartPaint[0].NotSupportMessage=null; + this.ChartPaint[0].IsShowLead=false; + this.ChartPaint[0].LeadData=null; + this.ChartPaint[0].BeforeOpenData=this.BeforeOpenData; + this.ChartPaint[0].AfterCloseData=this.AfterCloseData; + this.ChartPaint[0].MultiDayBeforeOpenData=multiBeforeOpenData; + this.ChartPaint[0].MultiDayAfterCloseData=multiAfterCloseData; + this.ChartPaint[0].ColorLineData=this.ColorLineData; //自定义分段颜色 + this.ChartPaint[0].Source=minuteData; + + if (MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol) && this.DayCount==1 && this.IsShowLead) //指数显示领先指标 + { + var bindLeadData=new ChartData(); + bindLeadData.Data=minuteData.GetLead(); + this.ChartPaint[0].LeadData=bindLeadData; + this.ChartPaint[0].IsShowLead=true; + } + + var firstFrame=this.Frame.SubFrame[0].Frame; + + firstFrame.YSplitOperator.YClose=yClose; + firstFrame.YSplitOperator.Data=bindData; + this.Frame.Data=this.ChartPaint[0].Data; + this.Frame.SourceData=minuteData; + + for(var i in this.Frame.SubFrame) + { + var item=this.Frame.SubFrame[i].Frame; + item.Data=minuteData; //每个子窗口都绑定下数据 + } + + //均线 + bindData=new ChartData(); + bindData.Data=minuteData.GetMinuteAvPrice(); + this.ChartPaint[1].Data=bindData; + + var upperSymbol=this.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) //外汇没有均线 + this.ChartPaint[1].Data=null; + + firstFrame.YSplitOperator.AverageData=bindData; + firstFrame.YSplitOperator.OverlayChartPaint=this.OverlayChartPaint; + firstFrame.YSplitOperator.LimitPrice=this.LimitPrice; + firstFrame.YSplitOperator.MultiDayBeforeOpenData=multiBeforeOpenData; + firstFrame.YSplitOperator.MultiDayAfterCloseData=multiAfterCloseData; + if (extendData) + { + firstFrame.YSplitOperator.High=extendData.High; + firstFrame.YSplitOperator.Low=extendData.Low; + } + else + { + firstFrame.YSplitOperator.High=null; + firstFrame.YSplitOperator.Low=null; + } + + //成交量 + this.ChartPaint[2].Data=minuteData; + this.ChartPaint[2].YClose=yClose; + this.ChartPaint[2].Symbol=this.Symbol; + this.ChartPaint[2].BeforeOpenData=this.BeforeOpenData; + this.ChartPaint[2].AfterCloseData=this.AfterCloseData; + this.ChartPaint[2].MultiDayBeforeOpenData=multiBeforeOpenData; + this.ChartPaint[2].MultiDayAfterCloseData=multiAfterCloseData; + + for(var i in this.Frame.SubFrame) + { + var item=this.Frame.SubFrame[i]; + + item.Frame.YSplitOperator.BeforeOpenData=this.BeforeOpenData; + item.Frame.YSplitOperator.IsBeforeData=this.IsBeforeData; + item.Frame.YSplitOperator.AfterCloseData=this.AfterCloseData; + item.Frame.YSplitOperator.IsAfterData=this.IsAfterData; + item.Frame.YSplitOperator.MultiDayBeforeOpenData=multiBeforeOpenData; + item.Frame.YSplitOperator.MultiDayAfterCloseData=multiAfterCloseData; + } + + if(MARKET_SUFFIX_NAME.IsShowMinutePostionLine(upperSymbol)) + this.BindOverlayPositionData(minuteData,yClose); //期货,期权 持仓量 + else + this.ClearBindOverlayPositionData(); + + this.TitlePaint[0].Data=this.SourceData; //动态标题 + this.TitlePaint[0].Symbol=this.Symbol; + this.TitlePaint[0].Name=this.Name; + this.TitlePaint[0].YClose=yClose; + this.TitlePaint[0].BeforeOpenData=this.BeforeOpenData; + this.TitlePaint[0].AfterCloseData=this.AfterCloseData; + this.TitlePaint[0].MultiDayBeforeOpenData=this.IsShowMultiDayBeforeData?this.MultiDayBeforeOpenData:null; + this.TitlePaint[0].MultiDayAfterCloseData=this.IsShowMultiDayAfterData?this.MultiDayAfterCloseData:null; + if (this.TitlePaint[0].CallAcutionXOperator) + { + this.TitlePaint[0].CallAcutionXOperator.BeforeOpenData=this.BeforeOpenData; + this.TitlePaint[0].CallAcutionXOperator.AfterCloseData=this.AfterCloseData; + this.TitlePaint[0].CallAcutionXOperator.MultiDayBeforeOpenData=multiBeforeOpenData; + this.TitlePaint[0].CallAcutionXOperator.MultiDayAfterCloseData=multiAfterCloseData; + } + + if (this.ChartCorssCursor && this.ChartCorssCursor.StringFormatY) + { + this.ChartCorssCursor.StringFormatY.YClose=yClose; + this.ChartCorssCursor.StringFormatY.BeforeOpenData=this.BeforeOpenData; + this.ChartCorssCursor.StringFormatY.AfterCloseData=this.AfterCloseData; + this.ChartCorssCursor.StringFormatY.MultiDayBeforeOpenData=multiBeforeOpenData; + this.ChartCorssCursor.StringFormatY.MultiDayAfterCloseData=multiAfterCloseData; + + this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data; //十字光标 + this.ChartCorssCursor.StringFormatX.BeforeOpenData=this.BeforeOpenData; + this.ChartCorssCursor.StringFormatX.AfterCloseData=this.AfterCloseData; + + this.ChartCorssCursor.StringFormatX.MultiDayBeforeOpenData=multiBeforeOpenData; + this.ChartCorssCursor.StringFormatX.MultiDayAfterCloseData=multiAfterCloseData; + + if (this.ChartCorssCursor.CallAcutionXOperator) + { + this.ChartCorssCursor.CallAcutionXOperator.BeforeOpenData=this.BeforeOpenData; + this.ChartCorssCursor.CallAcutionXOperator.AfterCloseData=this.AfterCloseData; + this.ChartCorssCursor.CallAcutionXOperator.MultiDayBeforeOpenData=multiBeforeOpenData; + this.ChartCorssCursor.CallAcutionXOperator.MultiDayAfterCloseData=multiAfterCloseData; + } + } + + if (this.ExtendChartPaint[0]) + { + this.ExtendChartPaint[0].Symbol=this.Symbol; + this.ExtendChartPaint[0].Name=this.Name; + } + + for(var i in this.OverlayChartPaint) + { + var item=this.OverlayChartPaint[i]; + item.MainData=this.ChartPaint[0].Data; //叠加股票 + item.MainYClose=yClose; + } + } + + //绑定分钟叠加指标数据(持仓量) + this.BindOverlayPositionData=function(minuteData,yClose) + { + if (this.Frame.SubFrame.length<2) return; + + var chart=null; + var frame=null; + var subFrame=this.Frame.SubFrame[1]; //第2个窗口 + var overlayFrame=null; + for(var i in subFrame.OverlayIndex) + { + var item=subFrame.OverlayIndex[i]; + if (item.Identify=='Position_Line_Frame') + { + overlayFrame=item; + break; + } + } + + if (!overlayFrame) + { + overlayFrame=new OverlayIndexItem(); + overlayFrame.Identify='Position_Line_Frame'; + + if (this.ClassName=="MinuteChartContainer") frame=new OverlayMinuteFrame(); + else frame=new OverlayMinuteHScreenFrame(); + + frame.Canvas=this.Canvas; + frame.MainFrame=subFrame.Frame; + frame.ChartBorder=subFrame.Frame.ChartBorder; + overlayFrame.Frame=frame; + + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=frame.ChartBorder; + frame.YSplitOperator.SplitCount=subFrame.Frame.YSplitOperator.SplitCount; + + var chart=new ChartLine(); + chart.Canvas=this.Canvas + chart.Name='Position-Line'; + chart.ChartBorder=frame.ChartBorder; + chart.ChartFrame=frame + chart.Identify=overlayFrame.Identify; + chart.Color=g_JSChartResource.Minute.PositionColor; + overlayFrame.ChartPaint.push(chart); + + subFrame.OverlayIndex.push(overlayFrame); + subFrame.Frame.RightFrame=frame; //右边坐标绑定到主坐标上 + } + else + { + chart=overlayFrame.ChartPaint[0]; + frame=overlayFrame.Frame; + } + + var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount; + frame.XPointCount=xPointCouont; + subFrame.Frame.IsShowPositionTitle=true; + + var bindData=new ChartData(); + bindData.Data=minuteData.GetPosition(); + chart.Data=bindData; + } + + this.ClearBindOverlayPositionData=function() + { + if (this.Frame.SubFrame.length<2) return; + var subFrame=this.Frame.SubFrame[1]; //第2个窗口 + subFrame.Frame.RightFrame=null; + subFrame.Frame.IsShowPositionTitle=false; + for(var i in subFrame.OverlayIndex) + { + var item=subFrame.OverlayIndex[i]; + if (item.Identify=='Position_Line_Frame') + { + subFrame.OverlayIndex.splice(i,1); + break; + } + } + } + + //添加叠加指标 + this.AddOverlayIndex=function(obj) + { + var overlay=this.CreateOverlayWindowsIndex(obj); + if (!overlay) return; + + var bindData=this.SourceData; + this.BindOverlayIndexData(overlay,obj.WindowIndex,bindData); + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //创建一个叠加指标 + this.CreateOverlayWindowsIndex=function(obj) //{WindowIndex:, IndexName:, Identify:, ShowRightText:, API:} + { + let indexName=obj.IndexName; + let windowIndex=obj.WindowIndex; + var apiItem=null, indexInfo=null, indexCustom=null; + if (obj.API) + { + apiItem=obj.API; + } + else if (obj.Script) //动态执行脚本 + { + indexInfo={ Script:obj.Script, ID:obj.indexName, Name:obj.indexName}; + if (obj.Name) indexInfo.Name=obj.Name; + } + else + { + let scriptData = new JSIndexScript(); + indexInfo = scriptData.Get(indexName); //系统指标 + if (!indexInfo) + { + indexCustom=JSIndexMap.Get(indexName); //定制指标 + if (!indexCustom) + { + console.warn(`[MinuteChartContainer::CreateOverlayIndex] can not find index[${indexName}]`); + return null; + } + } + } + + var subFrame=this.Frame.SubFrame[windowIndex]; + subFrame.Interval=this.OverlayIndexFrameWidth; + var overlayFrame=new OverlayIndexItem(); + if (obj.Identify) overlayFrame.Identify=obj.Identify; //由外部指定id + var frame=null; + if (this.ClassName=="MinuteChartContainer") frame=new OverlayMinuteFrame(); + else frame=new OverlayMinuteHScreenFrame(); + frame.Canvas=this.Canvas; + frame.MainFrame=subFrame.Frame; + frame.ChartBorder=subFrame.Frame.ChartBorder; + if (obj.ShowRightText===true) frame.IsShow=true; + else if (obj.ShowRightText===false) frame.IsShow=false; + if (obj.IsShareY===true) frame.IsShareY=true; + + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=frame.ChartBorder; + frame.YSplitOperator.SplitCount=subFrame.Frame.YSplitOperator.SplitCount; + + overlayFrame.Frame=frame; + + if (apiItem) + { + var apiIndex=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,obj, true); + apiIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + overlayFrame.Script=apiIndex; + } + else if (indexInfo) + { + let indexData = indexInfo; + if (obj.Args) indexData.Args=obj.Args; //外部可以设置参数 + + var scriptIndex=new OverlayScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData); //脚本执行 + scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + overlayFrame.Script=scriptIndex; + } + else + { + var scriptIndex=indexCustom.Create(); + scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }; //叠加指标信息 + scriptIndex.Create(this,windowIndex); + overlayFrame.Script=scriptIndex; + } + + subFrame.OverlayIndex.push(overlayFrame); + return overlayFrame; + } + + this.DeleteOverlayWindowsIndex=function(identify) //删除叠加指标 + { + if (!this.DeleteOverlayIndex(identify, null)) return; + + this.Frame.ResetXYSplit(true); + this.Draw(); + } + + //计算叠加指标 + this.BindAllOverlayIndexData=function(hisData) + { + if (!this.Frame || !this.Frame.SubFrame) return; + + //叠加指标 + for(var i=0;i0) frame=this.Frame.SubFrame[0].Frame; + if (!frame) return; + + var value=frame.GetXData(x); + var index=parseInt(value.toFixed(0)); + + return index; + } + + //获取主数据 + this.GetSelectRectData=function(selectData) + { + if (Math.abs(selectData.XStart-selectData.XEnd)<5) return false; + + var startClientPos=this.PtInClient(selectData.XStart, selectData.YStart); + var endClientPos=this.PtInClient(selectData.XEnd, selectData.YEnd); + + selectData.StartClientPos=startClientPos; + selectData.EndClientPos=endClientPos; + + var data=this.SourceData; + if (!data) return false; + if (!IFrameSplitOperator.IsNonEmptyArray(data.Data)) return false; + + var start=this.GetDataIndexByPoint(selectData.XStart); + var end=this.GetDataIndexByPoint(selectData.XEnd); + + if (Math.abs(start-end)<2) return false; + + selectData.Data=data; + if (start>end) + { + selectData.Start=end; + selectData.End=start; + } + else + { + selectData.Start=start; + selectData.End=end; + } + + var count=data.Data.length; + if (selectData.End>=count) selectData.End=count-1; + + return true; + } + + this.Super_UpdateFrameMaxMin=this.UpdateFrameMaxMin; + this.UpdateFrameMaxMin=function() + { + this.Super_UpdateFrameMaxMin(); + + if (this.DayCount==1) //集合竞价多坐标,Y轴强制都计算 + { + var subFrame=this.Frame.SubFrame[1]; + if (subFrame.Frame) subFrame.Frame.XYSplit=true; + } + } +} + +//盘前数据 +MinuteChartContainer.JsonDataToBeforeOpenData=function(data) +{ + var preClose=data.stock[0].yclose; //前一个数据价格 + var stockData=data.stock[0]; + var date=stockData.date; //日期 + var beforeOpenData={ Data:[], TotalCount:15, Ver:1.0, Date:date }; + if (stockData.beforeinfo) + { + if (IFrameSplitOperator.IsNumber(stockData.beforeinfo.totalcount)) beforeOpenData.TotalCount=stockData.beforeinfo.totalcount; + if (IFrameSplitOperator.IsNumber(stockData.beforeinfo.ver)) beforeOpenData.Ver=stockData.beforeinfo.ver; + } + if (beforeOpenData.Ver==1.0) + { + for(var i in stockData.before) + { + var item=new BeforeOpenData(); + var jsData=stockData.before[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + if (!item.Price) item.Price=preClose; + else preClose=item.Price; + item.Vol[0]=jsData[2]/100; //沪深股票原始单位股 + item.Amount=jsData[3]; + item.DateTime=date.toString()+" "+item.Time.toString(); + + beforeOpenData.Data.push(item); + } + } + else if (beforeOpenData.Ver==2.0) + { + var max=0; + for(var i in stockData.before) + { + var item=new BeforeOpenData(); + var jsData=stockData.before[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.Vol[0]=jsData[2]; //匹配量 + item.Vol[1]=jsData[3]; //未匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]+item.Vol[1]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + beforeOpenData.Data.push(item); + } + + beforeOpenData.VolMax=max; + beforeOpenData.VolMin=0; + } + else if (beforeOpenData.Ver==3.0) + { + var max=0; + for(var i in stockData.before) + { + var item=new BeforeOpenData(); + var jsData=stockData.before[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.AvPrice=jsData[2]; //均价 + item.Vol[0]=jsData[3]; //匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + beforeOpenData.Data.push(item); + } + + beforeOpenData.VolMax=max; + beforeOpenData.VolMin=0; + } + + return beforeOpenData; +} + +//收盘集合竞价 +MinuteChartContainer.JsonDataToAfterCloseData=function(data) +{ + var preClose=data.stock[0].yclose; //前一个数据价格 + var stockData=data.stock[0]; + var date=stockData.date; //日期 + if (!stockData.afterinfo) return null; + + var afterCloseData={ Data:[], TotalCount:3*60, Ver:2.0, Date:date }; + var item=stockData.afterinfo; + if (IFrameSplitOperator.IsNumber(item.totalcount)) afterCloseData.TotalCount=item.totalcount; + if (IFrameSplitOperator.IsNumber(item.ver)) afterCloseData.Ver=item.ver; + + if (afterCloseData.Ver==2.0) + { + var max=0; + for(var i in stockData.after) + { + var item=new AfterCloseData(); + var jsData=stockData.after[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.Vol[0]=jsData[2]; //匹配量 + item.Vol[1]=jsData[3]; //未匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]+item.Vol[1]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + afterCloseData.Data.push(item); + } + + afterCloseData.VolMax=max; + afterCloseData.VolMin=0; + } + else if (afterCloseData.Ver==3.0) + { + var max=0; + for(var i in stockData.after) + { + var item=new AfterCloseData(); + var jsData=stockData.after[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.AvPrice=jsData[2]; //均价 + item.Vol[0]=jsData[3]; //匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + afterCloseData.Data.push(item); + } + + afterCloseData.VolMax=max; + afterCloseData.VolMin=0; + } + + return afterCloseData; +} + +//API 返回数据 转化为array[] +MinuteChartContainer.JsonDataToMinuteData=function(data,isBeforeData) +{ + var symbol=data.stock[0].symbol; + var upperSymbol=symbol.toUpperCase(); + var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); //国内期货, 纽约期货交易所 + var isSHO=MARKET_SUFFIX_NAME.IsSHO(upperSymbol); //上海股票期权 + var aryMinuteData=new Array(); + var preClose=data.stock[0].yclose; //前一个数据价格 + var preAvPrice=data.stock[0].yclose; //前一个均价 + var yClose=data.stock[0].yclose; + if (isFutures && data.stock[0].yclearing) yClose=preClose=preAvPrice=data.stock[0].yclearing; //期货使用昨结算价 + + var date=data.stock[0].date; //默认使用外部日期, 但跨天的 走势图使用内部的日期 + for(var i in data.stock[0].minute) + { + var jsData=data.stock[0].minute[i]; + var item=new MinuteData(); + + item.Close=jsData.price; + item.Open=jsData.open; + item.High=jsData.high; + item.Low=jsData.low; + if (isSHSZ) item.Vol=jsData.vol/100; //沪深股票原始单位股 + else item.Vol=jsData.vol; + item.Amount=jsData.amount; + if (jsData.date>0) date=jsData.date; //分钟数据中有日期 优先使用 + item.DateTime=date.toString()+" "+jsData.time.toString(); + item.Date=date; + item.Time=jsData.time; + if (isFutures || isSHO) item.Position=jsData.position; //期货 期权有持仓 + if (80) + { + item.Date=jsData[8]; //日期 + item.DateTime=item.Date.toString()+" "+jsData[0].toString(); + } + + item.Increase=jsData.increase; + item.Risefall=jsData.risefall; + item.AvPrice=jsData.avprice; + + if (IFrameSplitOperator.IsNumber(jsData.lead)) + item.Lead=jsData.lead; //领先指标 指数才有 + + if (!item.Close) //当前没有价格 使用上一个价格填充 + { + item.Close=preClose; + item.Open=item.High=item.Low=item.Close; + } + + if (!item.AvPrice) item.AvPrice=preAvPrice; + + //价格是0的 都用空 + if (item.Open<=0) item.Open=null; + if (item.Close<=0) item.Close=null; + if (item.AvPrice<=0) item.AvPrice=null; + if (item.High<=0) item.High=null; + if (item.Low<=0) item.Low=null; + + //if (isFutures) item.AvPrice=null; //期货均价暂时没有 + + if (yClose && item.Close) + item.Increase=(item.Close-yClose)/yClose*100; //涨幅 (最新价格-昨收)/昨收*100; + + + //均价太大 可能是后台算错了 + var checkValue=Math.abs(item.AvPrice-item.Close); + //JSConsole.Chart.Log(`[MinuteChartContainer::JsonDataToMinuteData] checkValue=${checkValue}, ${item.Close*0.13} `) + if (isSHSZ && checkValue>item.Close*0.13 ) + item.AvPrice=preAvPrice; + + //上次价格 + if (jsData.price>0) preClose=jsData.price; + if (jsData.avprice>0 && item.AvPrice===jsData.avprice) preAvPrice=jsData.avprice; + + aryMinuteData[i]=item; + } + + return aryMinuteData; +} + +MinuteChartContainer.JsonDataToMinuteLineColorData=function(data) +{ + if (!data || !data.stock[0]) return null; + var stockItem=data.stock[0]; + if (!stockItem.linecolor || !IFrameSplitOperator.IsNonEmptyArray(stockItem.linecolor.data)) return null; + + var aryLineColor=[]; + for(var i in stockItem.linecolor.data) + { + var item=stockItem.linecolor.data[i]; + if (!IFrameSplitOperator.IsNumber(item.type) || !IFrameSplitOperator.IsNumber(item.date) || !IFrameSplitOperator.IsNumber(item.start) || + !IFrameSplitOperator.IsNumber(item.end) || !item.color) continue; + + if (item.start>=item.end) continue; + + var newItem={ Type:item.type, Date:item.date, Start:item.start, End:item.end, Color:item.color }; + if (IFrameSplitOperator.IsPlusNumber(item.linewidth)) newItem.LineWidth=item.linewidth; + + aryLineColor.push(newItem); + } + + return aryLineColor; +} + +//多日日线数据API 转化成array[]; +MinuteChartContainer.JsonDataToMinuteDataArray=function(data) +{ + var symbol=data.symbol; + var upperSymbol=symbol.toUpperCase(); + var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isSHO=MARKET_SUFFIX_NAME.IsSHO(upperSymbol); //上海股票期权 + var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); //国内期货, 纽约期货交易所 + var result=[]; + for(var i in data.data) + { + var minuteData=[]; + var dayData=data.data[i]; + var date=dayData.date; + var yClose=dayData.yclose; //前收盘 计算涨幅 + var preClose=yClose; //前一个数据价格 + var preAvPrice=null; //上一个均价 + //var preAvPrice=data.stock[0].yclose; //前一个均价 + for(var j in dayData.minute) + { + var jsData=dayData.minute[j]; + if (jsData[2]) preClose=jsData[2]; //保存上一个收盘数据 + var item=new MinuteData(); + item.Close=jsData[2]; + item.Open=jsData[1]; + item.High=jsData[3]; + item.Low=jsData[4]; + if (isSHSZ) item.Vol=jsData[5]/100; //原始单位股 + else item.Vol=jsData[5]; + item.Amount=jsData[6]; + if (70) item.AvPrice=jsData[7]; //均价 + item.DateTime=date.toString()+" "+jsData[0].toString(); + item.Date=date; + item.Time=jsData[0]; + if (80) + { + item.Date=jsData[8]; //日期 + item.DateTime=item.Date.toString()+" "+jsData[0].toString(); + } + if ((isFutures || isSHO) && 9item.Close*0.13 ) item.AvPrice=preAvPrice; + } + + if (jsData.length>7 && jsData[7]>0 && item.AvPrice===jsData[7]) preAvPrice=jsData[7]; + + minuteData[j]=item; + } + + var newData=new ChartData(); + newData.Data=minuteData; + newData.YClose=yClose; + newData.Close=dayData.close; + newData.Date=date; + + result.push(newData); + } + + return result; +} + +MinuteChartContainer.JsonDataToHistoryMinuteLineColorData=function(data) +{ + if (!data) return null; + + var aryLineColor=[]; + for(var i in data.data) + { + var dayData=data.data[i]; + if (!dayData.linecolor) continue; + + for(var j in dayData.linecolor.data) + { + var item=dayData.linecolor.data[j]; + + if (!IFrameSplitOperator.IsNumber(item.type) || !IFrameSplitOperator.IsNumber(item.date) || !IFrameSplitOperator.IsNumber(item.start) || + !IFrameSplitOperator.IsNumber(item.end) || !item.color) continue; + + if (item.start>=item.end) continue; + + var newItem={ Type:item.type, Date:item.date, Start:item.start, End:item.end, Color:item.color }; + if (IFrameSplitOperator.IsPlusNumber(item.linewidth)) newItem.LineWidth=item.linewidth; + + aryLineColor.push(newItem); + } + } + + return aryLineColor.length>0? aryLineColor : null; +} + + +MinuteChartContainer.JsonDataToCallAuctionItem=function(data, callAuctionData, isBeforeOpen) +{ + var date=callAuctionData.Date; + + if (callAuctionData.Ver==1.0) + { + for(var i in data) + { + var item=isBeforeOpen? new BeforeOpenData() : new AfterCloseData(); + var jsData=data[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + if (!item.Price) item.Price=preClose; + else preClose=item.Price; + item.Vol[0]=jsData[2]/100; //沪深股票原始单位股 + item.Amount=jsData[3]; + item.DateTime=date.toString()+" "+item.Time.toString(); + + callAuctionData.Data.push(item); + } + } + else if (callAuctionData.Ver==2.0) + { + var max=0; + for(var i in data) + { + var item=isBeforeOpen? new BeforeOpenData() : new AfterCloseData(); + var jsData=data[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.Vol[0]=jsData[2]; //匹配量 + item.Vol[1]=jsData[3]; //未匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]+item.Vol[1]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + callAuctionData.Data.push(item); + } + + callAuctionData.VolMax=max; + callAuctionData.VolMin=0; + } + else if (beforeOpenData.Ver==3.0) + { + var max=0; + for(var i in data) + { + var item=isBeforeOpen? new BeforeOpenData() : new AfterCloseData(); + var jsData=data[i]; + item.Time=jsData[0]; + item.Date=date; + item.Price=jsData[1]; + item.AvPrice=jsData[2]; //均价 + item.Vol[0]=jsData[3]; //匹配量 + item.ColorID=jsData[4]; //柱子颜色ID + item.DateTime=date.toString()+" "+item.Time.toString(); + + var totalVol=item.Vol[0]; + if (IFrameSplitOperator.IsNumber(jsData[5])) totalVol=jsData[5]; + if (totalVol>max) max=totalVol; + + callAuctionData.Data.push(item); + } + + callAuctionData.VolMax=max; + callAuctionData.VolMin=0; + } +} + +MinuteChartContainer.JosnDataToBeforeOpenDataArray=function(data) +{ + if (!data || !data.data) return null; + + var aryDay=[]; + for(var i in data.data) + { + var dayItem=data.data[i]; + var beforeOpenData={ Data:[], TotalCount:15, Ver:1.0, Date:dayItem.date }; + if (dayItem.beforeinfo) + { + if (IFrameSplitOperator.IsNumber(dayItem.beforeinfo.totalcount)) beforeOpenData.TotalCount=dayItem.beforeinfo.totalcount; + if (IFrameSplitOperator.IsNumber(dayItem.beforeinfo.ver)) beforeOpenData.Ver=dayItem.beforeinfo.ver; + } + + MinuteChartContainer.JsonDataToCallAuctionItem(dayItem.before, beforeOpenData, true); + + aryDay.push(beforeOpenData); + } + + aryDay.sort((left, right)=>{return left.Date - right.Date}); + + return aryDay; +} + +MinuteChartContainer.JosnDataToAfterCloseDataArray=function(data) +{ + if (!data || !data.data) return null; + + var aryDay=[]; + for(var i in data.data) + { + var dayItem=data.data[i]; + var afterCloseData={ Data:[], TotalCount:15, Ver:1.0, Date:dayItem.date }; + + if (dayItem.afterinfo) + { + if (IFrameSplitOperator.IsNumber(dayItem.afterinfo.totalcount)) afterCloseData.TotalCount=dayItem.afterinfo.totalcount; + if (IFrameSplitOperator.IsNumber(dayItem.afterinfo.ver)) afterCloseData.Ver=dayItem.afterinfo.ver; + } + + MinuteChartContainer.JsonDataToCallAuctionItem(dayItem.after, afterCloseData, false); + + aryDay.push(afterCloseData); + } + + aryDay.sort((left, right)=>{return left.Date - right.Date}); + + return aryDay; +} + +/* + 历史分钟走势图 +*/ +function HistoryMinuteChartContainer(uielement) +{ + this.newMethod=MinuteChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.HistoryMinuteApiUrl="https://opensourcecache.zealink.com/cache/minuteday/day/"; + this.ClassName='HistoryMinuteChartContainer'; + + //创建主图K线画法 + this.CreateMainKLine=function() + { + //分钟线 + var minuteLine=new ChartMinutePriceLine(); + minuteLine.Canvas=this.Canvas; + minuteLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + minuteLine.ChartFrame=this.Frame.SubFrame[0].Frame; + minuteLine.Name="Minute-Line"; + minuteLine.Color=g_JSChartResource.Minute.PriceColor; + + this.ChartPaint[0]=minuteLine; + + //分钟线均线 + var averageLine=new ChartLine(); + averageLine.Canvas=this.Canvas; + averageLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + averageLine.ChartFrame=this.Frame.SubFrame[0].Frame; + averageLine.Name="Minute-Average-Line"; + averageLine.Color=g_JSChartResource.Minute.AvPriceColor; + this.ChartPaint[1]=averageLine; + + var averageLine=new ChartMinuteVolumBar(); + averageLine.Color=g_JSChartResource.Minute.VolBarColor; + averageLine.Canvas=this.Canvas; + averageLine.ChartBorder=this.Frame.SubFrame[1].Frame.ChartBorder; + averageLine.ChartFrame=this.Frame.SubFrame[1].Frame; + averageLine.Name="Minute-Vol-Bar"; + this.ChartPaint[2]=averageLine; + + + this.TitlePaint[0]=new DynamicMinuteTitlePainting(); + this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas=this.Canvas; + this.TitlePaint[0].IsShowDate=true; + + /* + //主图叠加画法 + var paint=new ChartOverlayKLine(); + paint.Canvas=this.Canvas; + paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame=this.Frame.SubFrame[0].Frame; + paint.Name="Overlay-KLine"; + this.OverlayChartPaint[0]=paint; + */ + + } + + //设置交易日期 + this.ChangeTradeDate=function(trdateDate) + { + if (!trdateDate) return; + + this.TradeDate=trdateDate; + this.RequestData(); //更新数据 + } + + this.RequestData=function() + { + var date=new Date(); + var nowDate=date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate(); + if (nowDate==this.TradeDate) this.RequestMinuteData(); + else this.RequestHistoryMinuteData(); + } + + //请求分钟数据 + this.RequestHistoryMinuteData=function() + { + var self=this; + var url=this.HistoryMinuteApiUrl+this.TradeDate.toString()+"/"+this.Symbol+".json"; + + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + }, + error:function(reqeust) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteError(reqeust); + } + }); + } + + this.RecvHistoryMinuteError=function(reqeust) + { + if (reqeust.status!=404) return; + + var sourceData=new ChartData(); + this.SourceData=sourceData; + + for(var i in this.ChartPaint) + { + this.ChartPaint[i].Data=sourceData; + if (i==0) this.ChartPaint[i].NotSupportMessage='没有权限访问!'; + } + + this.TitlePaint[0].Data=this.SourceData; //动态标题 + this.TitlePaint[0].Symbol=this.Symbol; + this.TitlePaint[0].Name=null; + + this.Draw(); + } + + this.RecvHistoryMinuteData=function(data) + { + var aryMinuteData=HistoryMinuteChartContainer.JsonDataToMinuteData(data); + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=aryMinuteData; + + this.TradeDate=data.date; + + this.SourceData=sourceData; + this.Symbol=data.symbol; + this.Name=data.name; + + this.BindMainData(sourceData,data.day.yclose); + + if (this.Frame.SubFrame.length>2) + { + var bindData=new ChartData(); + bindData.Data=aryMinuteData; + for(var i=2; i0) //当前这一分钟价格为空,使用上一分钟的数据 + { + item.Close=aryMinuteData[i-1].Close; + item.Open=aryMinuteData[i-1].Close; + item.High=item.Close; + item.Low=item.Close; + item.Vol=data.minute.vol[i]; //原始单位股 + item.Amount=data.minute.amount[i]; + item.DateTime=data.date.toString()+" "+data.minute.time[i].toString(); + //item.Increate=jsData.increate; + //item.Risefall=jsData.risefall; + item.AvPrice=aryMinuteData[i-1].AvPrice; + } + else + { + item.Close=data.minute.price[i]; + item.Open=data.minute.open[i]; + item.High=data.minute.high[i]; + item.Low=data.minute.low[i]; + item.Vol=data.minute.vol[i]; //原始单位股 + item.Amount=data.minute.amount[i]; + item.DateTime=data.date.toString()+" "+data.minute.time[i].toString(); + //item.Increate=jsData.increate; + //item.Risefall=jsData.risefall; + item.AvPrice=data.minute.avprice[i]; + } + + //价格是0的 都用空 + if (item.Open<=0) item.Open=null; + if (item.Close<=0) item.Close=null; + if (item.AvPrice<=0) item.AvPrice=null; + if (item.High<=0) item.High=null; + if (item.Low<=0) item.Low=null; + + aryMinuteData[i]=item; + } + + return aryMinuteData; +} + +///////////////////////////////////////////////////////////////////////////// +// 自定义指数 +// +function CustomKLineChartContainer(uielement) +{ + this.newMethod=KLineChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName='CustomKLineChartContainer'; + this.CustomKLineApiUrl=g_JSChartResource.Domain+"/API/IndexCalculate"; //自定义指数计算地址 + this.CustomStock; //成分 + this.QueryDate={Start:20180101,End:20180627} ; //计算时间区间 + + this.RequestHistoryData=function() + { + var self=this; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + JSNetwork.HttpRequest({ + url: this.CustomKLineApiUrl, + data: + { + "stock": self.CustomStock, + "Name": self.Symbol, + "date": { "startdate":self.QueryDate.Start,"enddate":self.QueryDate.End } + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + } + }); + } + + this.RecvHistoryData=function(data) + { + var aryDayData=KLineChartContainer.JsonDataToHistoryData(data); + + //原始数据 + var sourceData=new ChartData(); + sourceData.Data=aryDayData; + sourceData.DataType=0; //0=日线数据 1=分钟数据 + + //显示的数据 + var bindData=new ChartData(); + bindData.Data=aryDayData; + bindData.Right=this.Right; + bindData.Period=this.Period; + bindData.DataType=0; + + if (bindData.Right>0) //复权 + { + var rightData=bindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + bindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(this.Period,false)) //周期数据 + { + var periodData=sourceData.GetPeriodData(bindData.Period); + bindData.Data=periodData; + } + + //绑定数据 + this.SourceData=sourceData; + this.Name=data.name; + this.BindMainData(bindData,this.PageSize); + + for(var i=0; i=this.TrainStartDate.Date && item.Time>=this.TrainStartDate.Time) + return parseInt(i); + } + else + { + if (item.Date>=this.TrainStartDate.Date) + return parseInt(i); + } + } + else if (ChartData.IsDayPeriod(this.Period, true) || ChartData.IsTickPeriod(this.Period)) + { + if (item.Date>=this.TrainStartDate.Date) + return parseInt(i); + } + } + + return -1; + } + + this.AfterBindMainData=function(funcName) + { + if (!this.ChartPaintEx[0]) this.CreateBuySellPaint(); + + var hisData=this.ChartPaint[0].Data; + this.ChartPaintEx[0].Data=hisData; + + var showItem=hisData.Data[hisData.Data.length-1]; + + //最后一个显示数据 + this.TrainInfo.LastShowData=showItem; + this.TrainInfo.LastData = this.SourceData.Data[this.SourceData.Data.length - 1]; + + if (funcName!='Update') + this.UpdateTrainUICallback("开始"); + } + + this.BeforeBindMainData=function(funcName) + { + if (funcName=="Update") return; + + //全量数据 需要过滤 + this.KLineSourceData=new ChartData(); + this.KLineSourceData.Data=this.SourceData.Data.slice(0); + var count=this.SourceData.Data.length; + var lEnd=count-this.TrainDataCount-20; + var findIndex=this.GetKDataIndexByDateTime(this.SourceData.Data, this.TrainStartDate); + if (findIndex>=0) + { + lEnd=findIndex+1; + if (count-lEnd1) step=option.Step; + + var moveStep=0; + for(var i=0; i=this.KLineSourceData.Data.length) break; + + var kItem=this.KLineSourceData.Data[index]; + this.SourceData.Data.push(kItem); + + this.TrainInfo.End.Index=index; + this.TrainInfo.End.Date=kItem.Date; + this.TrainInfo.End.Time=kItem.Time; + --this.TrainDataCount; + ++moveStep; + + if (this.TrainDataCount<=0) break; + } + + if (moveStep==0) return false; + + //使用当前页数据个数移动K线 + var pageSize=this.Frame.GetCurrentPageSize(); + if (IFrameSplitOperator.IsNumber(pageSize)) + this.PageSize=pageSize-this.RightSpaceCount; + + if (option && option.PageSize>0) this.PageSize=option.PageSize; + + this.Update(); + + if (this.TrainDataCount<=0) + { + this.FinishTrainData(); + this.UpdateTrainUICallback("结束"); + return false; + } + + this.UpdateTrainUICallback("训练中"); + return true; + } + + this.UpdateTrainUICallback=function(description) + { + //新的监听事件 + if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP)) return; + var item=this.mapEvent.get(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP); + if (!item.Callback) return; + + var data= + { + TrainDataCount:this.TrainDataCount, + BuySellData:this.BuySellData, + KLine: + { + Start: { Index: this.TrainInfo.Start.Index, Date:this.TrainInfo.Start.Date }, + End:{ Index: this.TrainInfo.End.Index, Date:this.TrainInfo.End.Date } + }, + LastData:this.TrainInfo.LastData, + LastShowData:this.TrainInfo.LastShowData + }; + if (IFrameSplitOperator.IsNumber(this.TrainInfo.Start.Time)) data.KLine.Start.Time=this.TrainInfo.Start.Time; + if (IFrameSplitOperator.IsNumber(this.TrainInfo.End.Time)) data.KLine.End.Time=this.TrainInfo.End.Time; + if (description) data.Description=description + + if (this.TrainDataCount<=0) + { + data.Symbol=this.Symbol; + data.Name=this.Name; + } + + item.Callback(item,data,this); + } + + this.FinishTrainData=function() + { + JSConsole.Chart.Log('[KLineTrainChartContainer::FinishTrainData] trian end '); + } + + this.Stop=function() + { + if (this.AutoRunTimer!=null) clearInterval(this.AutoRunTimer); + this.AutoRunTimer=null; + } + + this.BuyOrSell=function(obj, bDraw) //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 } bDraw是否立即绘制图标 + { + var kItem=this.TrainInfo.LastShowData; + if (!kItem) return false; + + var buySellPaint=this.ChartPaintEx[0]; + if (!buySellPaint) return false; + + var hisData=this.ChartPaint[0].Data; + if (!hisData || hisData.Data.length<=0) return false; + + var index=hisData.Data.length-1; //数据索引 + var buyItem={ Date:this.TrainInfo.End.Date, Time:this.TrainInfo.End.Time, Price:obj.Price, Vol:obj.Vol, Op:0, ID:obj.ID }; + if (obj.Op==1) buyItem.Op=1; + var key=index; + buyItem.Key=key; + + this.BuySellData.push(buyItem); + buySellPaint.AddTradeItem(buyItem); + + if (bDraw==true) this.Draw(); + } + + this.RestartTrain=function(option) // { Symbol:, Period:周期, Right:复权, Train:{ DataCount:, DateTime: } } + { + JSConsole.Chart.Log('[KLineTrainChartContainer::RestartTrain] option ', option); + + this.TrainInfo={ Start:{ }, End:{ } }; + this.BuySellData=[]; + this.KLineSourceData=null; + + var buySellPaint=this.ChartPaintEx[0]; + if (buySellPaint) + { + buySellPaint.Data=null; + buySellPaint.ClearTradeData(); + } + + if (option.Symbol) this.Symbol=option.Symbol; + if (IFrameSplitOperator.IsNumber(option.Period)) this.Period=option.Period; + if (IFrameSplitOperator.IsNumber(option.Right)) this.Right=option.Right; + if (option.Train) + { + if (option.Train.DataCount>1) this.TrainDataCount=option.Train.DataCount; + if (option.Train.DateTime) this.TrainStartDate=option.Train.DateTime; + } + + var symbol=this.Symbol; + this.ChangeSymbol(symbol); + } + + this.RecvFlowCapitalData=function(data) + { + if (!data.stock || data.stock.length!=1) return; + + let stock=data.stock[0]; + var aryData=new Array(); + for(let i in stock.stockday) + { + var item=stock.stockday[i]; + let indexData=new SingleData(); + indexData.Date=item.date; + var financeData=item.capital; + if (!financeData) continue; + if (financeData.a>0) + { + indexData.Value=financeData.a; //流通股本(股) + aryData.push(indexData); + } + } + + if (ChartData.IsMinutePeriod(this.Period,true)) //分钟数据 + { + var aryFixedData=this.SourceData.GetMinuteFittingFinanceData(aryData); + for(let i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + item.FlowCapital=aryFixedData[i].Value; + } + + var bindData=this.ChartPaint[0].Data; + var newBindData=new ChartData(); + newBindData.Data=this.SourceData.Data; + + if (ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据 + { + var periodData=newBindData.GetPeriodData(bindData.Period); + newBindData.Data=periodData; + } + bindData.Data=newBindData.Data; + + var aryFixedData=this.KLineSourceData.GetMinuteFittingFinanceData(aryData); //数据添加到备份数据中 + for(let i in this.KLineSourceData.Data) + { + var item=this.KLineSourceData.Data[i]; + item.FlowCapital=aryFixedData[i].Value; + } + } + else + { + var aryFixedData=this.SourceData.GetFittingFinanceData(aryData); + for(let i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + item.FlowCapital=aryFixedData[i].Value; + } + + var bindData=this.ChartPaint[0].Data; + var newBindData=new ChartData(); + newBindData.Data=this.SourceData.Data; + + if (bindData.Right>0) //复权 + { + var rightData=newBindData.GetRightData(bindData.Right, { AlgorithmType: this.RightFormula }); + newBindData.Data=rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period,false)) //周期数据 + { + var periodData=newBindData.GetPeriodData(bindData.Period); + newBindData.Data=periodData; + } + + bindData.Data=newBindData.Data; + + var aryFixedData=this.KLineSourceData.GetFittingFinanceData(aryData); //数据添加到备份数据中 + for(let i in this.KLineSourceData.Data) + { + var item=this.KLineSourceData.Data[i]; + item.FlowCapital=aryFixedData[i].Value; + } + } + + this.FlowCapitalReady=true; + var bDraw=false; + for(var i in this.ExtendChartPaint) + { + var item=this.ExtendChartPaint[i]; + if (item.ClassName=='StockChip') + { + bDraw=true; + break; + } + } + + if (bDraw) this.Draw(); + } + + this.AutoUpdate() + { + + } +} + +//////////////////////////////////////////////////////////////////////////////// +// K线横屏显示 +// +function KLineChartHScreenContainer(uielement) +{ + this.newMethod=KLineChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName='KLineChartHScreenContainer'; + + this.OnMouseMove=function(x,y,e,isPhone) + { + this.LastPoint.X=x; + this.LastPoint.Y=y; + this.CursorIndex=this.Frame.GetXData(y); + + var option={ ParentFunction:'OnMouseMove', Point:{X:x, Y:y}, IsPhone:isPhone===true }; + + this.DrawDynamicInfo(option); + } + + uielement.onmousedown=function(e) //鼠标拖拽 + { + if(!this.JSChartContainer) return; + if(this.JSChartContainer.DragMode==0) return; + + var pixelTatio = GetDevicePixelRatio(); + if (this.JSChartContainer.TryClickLock) + { + var x = (e.clientX-this.getBoundingClientRect().left)*pixelTatio; + var y = (e.clientY-this.getBoundingClientRect().top)*pixelTatio; + if (this.JSChartContainer.TryClickLock(x,y)) return; + } + + + var drag= + { + "Click":{}, + "LastMove":{} //最后移动的位置 + }; + + drag.Click.X=e.clientX; + drag.Click.Y=e.clientY; + drag.LastMove.X=e.clientX; + drag.LastMove.Y=e.clientY; + + this.JSChartContainer.MouseDrag=drag; + document.JSChartContainer=this.JSChartContainer; + this.JSChartContainer.SelectChartDrawPicture=null; + + uielement.ondblclick=function(e) + { + var x = e.clientX-this.getBoundingClientRect().left; + var y = e.clientY-this.getBoundingClientRect().top; + + if(this.JSChartContainer) + this.JSChartContainer.OnDoubleClick(x,y,e); + } + + document.onmousemove=function(e) + { + if(!this.JSChartContainer) return; + //加载数据中,禁用鼠标事件 + if (this.JSChartContainer.ChartSplashPaint && this.JSChartContainer.ChartSplashPaint.IsEnableSplash == true) return; + + var drag=this.JSChartContainer.MouseDrag; + if (!drag) return; + + var moveSetp=Math.abs(drag.LastMove.Y-e.clientY); + + if (this.JSChartContainer.DragMode==1) //数据左右拖拽 + { + if (moveSetp<5) return; + + var oneStepWidth=this.JSChartContainer.GetMoveOneStepWidth(); + if (moveSetp0) //放大 + { + var cursorIndex={}; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomUp(cursorIndex)) return; + this.CursorIndex=cursorIndex.Index; + this.UpdatePointByCursorIndex(); + this.UpdataDataoffset(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + } + else //缩小 + { + var cursorIndex={}; + cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0)); + if (!this.Frame.ZoomDown(cursorIndex)) return; + this.CursorIndex=cursorIndex.Index; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + this.ShowTooltipByKeyDown(); + } + + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + } + } + + this.OnTouchEnd=function(e) + { + JSConsole.Chart.Log('[KLineChartHScreenContainer:OnTouchEnd]',e); + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var bClearDrawPicture=true; + if (this.CurrentChartDrawPicture) + { + var drawPicture=this.CurrentChartDrawPicture; + if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3) + { + drawPicture.PointStatus=drawPicture.Status; + if (this.FinishChartDrawPicturePoint()) + this.DrawDynamicInfo(); + else + bClearDrawPicture=false; + } + else if (drawPicture.Status==20) + { + if (this.FinishMoveChartDrawPicture()) + this.DrawDynamicInfo(); + } + } + + this.IsOnTouch = false; + this.StopDragTimer(); + this.OnTouchFinished(); + this.TouchDrawCount=0; + } + + //创建 + //windowCount 窗口个数 + this.Create=function(windowCount) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new ChartCorssCursor(); + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.StringFormatX=g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY=g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY.ExtendChartPaint=this.ExtendChartPaint; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame=new HQTradeHScreenFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=30; + this.Frame.ChartBorder.Left=5; + this.Frame.ChartBorder.Bottom=20; + this.Frame.Canvas=this.Canvas; + this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for(var i in this.Frame.SubFrame) + { + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[i].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + this.TitlePaint.push(titlePaint); + } + + this.UIElement.addEventListener("keydown", OnKeyDown, true); //键盘消息 + } + + //创建子窗口 + this.CreateChildWindow=function(windowCount) + { + for(var i=0;i { return this.GetEventCallback(id); } + //主图上下间距 + var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 + border.TopSpace=12*pixelTatio; + border.BottomSpace=12*pixelTatio; + } + else + { + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + //frame.IsLocked = true; + } + + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitKLineX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + + if (i!=windowCount-1) frame.XSplitOperator.ShowText=false; + + for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=j; + if (i==0 && j==frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1]=j.toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + if (i==0) + subFrame.Height=20; + else + subFrame.Height=10; + + this.Frame.SubFrame[i]=subFrame; + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// 走势图横屏显示 +// +function MinuteChartHScreenContainer(uielement) +{ + this.newMethod=MinuteChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName='MinuteChartHScreenContainer'; + + this.OnMouseMove=function(x,y,e,isPhone) + { + this.LastPoint.X=x; + this.LastPoint.Y=y; + this.CursorIndex=this.Frame.GetXData(y); + + var clientPos=this.PtInClient(x,y); + var option={ ParentFunction:'OnMouseMove', Point:{X:x, Y:y}, IsPhone:isPhone===true,ClientPos:clientPos }; + this.DrawDynamicInfo(option); + } + + //创建 + //windowCount 窗口个数 + this.Create=function(windowCount) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new ChartCorssCursor(); + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.StringFormatX=new HQMinuteTimeStringFormat(); + this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat(); + this.ChartCorssCursor.CallAcutionXOperator=new CallAcutionXOperator(); + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame=new HQTradeHScreenFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=25; + this.Frame.ChartBorder.Left=50; + this.Frame.ChartBorder.Bottom=20; + this.Frame.Canvas=this.Canvas; + this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for(var i in this.Frame.SubFrame) + { + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[i].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + this.TitlePaint.push(titlePaint); + } + + this.ChartCorssCursor.StringFormatX.Frame=this.Frame.SubFrame[0].Frame; + this.ChartCorssCursor.StringFormatY.Frame=this.Frame; + this.ChartCorssCursor.CallAcutionXOperator.Frame=this.Frame.SubFrame[0].Frame; + + this.UIElement.addEventListener("keydown", OnKeyDown, true); //键盘消息 + } + + //创建子窗口 + this.CreateChildWindow=function(windowCount) + { + for(var i=0;i { return this.GetEventCallback(id); } + } + else + { + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitMinuteX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + if (i!=windowCount-1) frame.XSplitOperator.ShowText=false; + frame.XSplitOperator.Operator(); + + for(var j in DEFAULT_HORIZONTAL) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j]; + if (i==0 && j==frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + if (i==0) + subFrame.Height=20; + else + subFrame.Height=10; + + this.Frame.SubFrame[i]=subFrame; + } + } +} + + +///////////////////////////////////////////////////////////////////////////////// +// 深度图 +// +function DepthChartContainer(uielement) +{ + this.newMethod=JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName="DepthChartContainer"; + this.Symbol; + + //数据 + this.MapAsk=new Map(); + this.MapBid=new Map(); + + this.IsAutoUpdate=false; //是否自动更新行情数据 + this.AutoUpdateFrequency=30000; //30秒更新一次数据 + this.AutoUpdateTimer; + + this.DefaultZoom=0.8; //默认显示80%的盘口 (0 - 1) + this.MaxVolRate=1.1; + + this.Create=function(option) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new DepthChartCorssCursor(); + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.HQChart=this; + //this.ChartCorssCursor.StringFormatX=g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + //this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + //this.ChartCorssCursor.StringFormatY=g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + //this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架 + this.Frame=new DepthChartFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=30; + this.Frame.ChartBorder.Left=5; + this.Frame.ChartBorder.Bottom=20; + this.Frame.ChartBorder.TitleHeight=0; + this.Frame.Canvas=this.Canvas; + + var ySplitOper=new FrameSplitY(); + ySplitOper.FrameSplitData=this.FrameSplitData.get('double'); + ySplitOper.LanguageID=this.LanguageID; + ySplitOper.Frame=this.Frame; + ySplitOper.SplitCount=5; + ySplitOper.LineType=3; + ySplitOper.IgnoreYValue=[0]; + //ySplitOper.SplitType=2; + ySplitOper.ChartBorder=this.Frame.ChartBorder; + this.Frame.YSplitOperator=ySplitOper; + + var xSplitOper=new FrameSplitXDepth(); + xSplitOper.Frame=this.Frame;; + xSplitOper.ChartBorder=this.Frame.ChartBorder;; + xSplitOper.LanguageID=this.LanguageID; + xSplitOper.LineType=3; + this.Frame.XSplitOperator=xSplitOper + + if (this.ChartCorssCursor) this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + var chartItem=new ChartOrderbookDepth(); + chartItem.Canvas=this.Canvas; + chartItem.ChartBorder=this.Frame.ChartBorder; + chartItem.ChartFrame=this.Frame; + chartItem.Name="深度图" + this.ChartPaint.push(chartItem); + + var bRegisterKeydown=true; + var bRegisterWheel=true; + if (option) + { + if (option.Wheel===false) + { + bRegisterWheel=false; + JSConsole.Chart.Log('[DepthChartContainer::Create] not register wheel event.'); + } + } + + if (bRegisterKeydown) this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e); }, true); //键盘消息 + if (bRegisterWheel) this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true); //上下滚动消息 + } + + this.OnWheel=function(e) + { + JSConsole.Chart.Log('[KLineChartContainer::OnWheel]',e); + var x = e.clientX-this.UIElement.getBoundingClientRect().left; + var y = e.clientY-this.UIElement.getBoundingClientRect().top; + + var isInClient=false; + this.Canvas.beginPath(); + this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight()); + isInClient=this.Canvas.isPointInPath(x,y); + + var wheelValue=e.wheelDelta; + if (!IFrameSplitOperator.IsObjectExist(e.wheelDelta)) + wheelValue=e.deltaY* -0.01; + + var enableZoomUpDown=true; //是否允许缩放 + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Wheel===false) enableZoomUpDown=false; + + if (isInClient && wheelValue<0 && enableZoomUpDown) //缩小 + { + if (this.Frame.ZoomDown()) + { + this.UpdateFrameMaxMin(); + this.Draw(); + } + } + else if (isInClient && wheelValue>0 && enableZoomUpDown) //放大 + { + if (this.Frame.ZoomUp()) + { + this.UpdateFrameMaxMin(); + this.Draw(); + } + } + + if(e.preventDefault) e.preventDefault(); + else e.returnValue = false; + } + + this.UIOnMouseDown=function(e) + { + } + + this.OnTouchStart=function(e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + this.IsOnTouch=true; + this.TouchDrawCount=0; + this.PhonePinch=null; + + var isSingleTouch=this.IsSingleTouch(e); + if (this.EnableScrollUpDown==false || !isSingleTouch) //多点触屏 + { + if (e.cancelable) e.preventDefault(); + } + + if (this.IsPhoneDragging(e)) + { + var drag= + { + "Click":{}, + "LastMove":{} //最后移动的位置 + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + drag.Click.X=touches[0].clientX; + drag.Click.Y=touches[0].clientY; + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + + this.MouseDrag=drag; + + this.MoveCorssCursor(drag.Click,e); + } + else if (this.IsPhonePinching(e)) + { + var phonePinch= + { + "Start":{}, + "Last":{} + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + + this.PhonePinch=phonePinch; + } + } + + this.OnTouchMove=function(e) + { + var touches=this.GetToucheData(e,false); + if (this.IsPhoneDragging(e)) + { + var drag=this.MouseDrag; + if (drag==null) + { + var pixelTatio = GetDevicePixelRatio(); + var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio; + var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio; + this.OnMouseMove(x,y,e); + } + else + { + var moveAngle=this.GetMoveAngle(drag.LastMove,{X:touches[0].clientX, Y:touches[0].clientY}); + var moveSetp=Math.abs(drag.LastMove.X-touches[0].clientX); + var moveUpDown=Math.abs(drag.LastMove.Y-touches[0].clientY); + moveSetp=parseInt(moveSetp); + + //上下滚动 + if ( ((moveUpDown>0 && moveSetp<=3) || moveAngle<=this.TouchMoveMinAngle) && this.EnableScrollUpDown==true ) + { + return; + } + + this.PreventTouchEvent(e); + this.MouseDrag=null; + var pixelTatio = GetDevicePixelRatio(); + var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio; + var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio; + this.OnMouseMove(x,y,e); + } + } + else if (this.IsPhonePinching(e)) + { + this.PreventTouchEvent(e); + var phonePinch=this.PhonePinch; + if (!phonePinch) return; + + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Touch===false) return; + + var yHeight=Math.abs(touches[0].pageY-touches[1].pageY); + var yLastHeight=Math.abs(phonePinch.Last.Y-phonePinch.Last.Y2); + var yStep=yHeight-yLastHeight; + + var xHeight=Math.abs(touches[0].pageX-touches[1].pageX); + var xLastHeight=Math.abs(phonePinch.Last.X-phonePinch.Last.X2); + var xStep=xHeight-xLastHeight; + var minStep=this.ZoomStepPixel; + if (Math.abs(yStep)0) //放大 + { + if (!this.Frame.ZoomUp()) return; + this.UpdateFrameMaxMin(); + this.Draw(); + } + else //缩小 + { + if (!this.Frame.ZoomDown()) return; + this.UpdateFrameMaxMin(); + this.Draw(); + } + + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + } + + this.PreventTouchEvent(e); + } + + this.OnTouchEnd=function(e) + { + JSConsole.Chart.Log('[DepthChartContainer::OnTouchEnd]',e); + this.IsOnTouch = false; + this.OnTouchFinished(); + this.TouchDrawCount=0; + } + + this.OnTouchFinished=function() + { + if (this.CorssCursorTouchEnd===true) //手势离开十字光标消失 + { + this.DrawDynamicInfo(); + return; + } + } + + this.ChangeSymbol=function(symbol) + { + this.CancelAutoUpdate(); //先停止定时器 + this.Symbol=symbol; + this.MapBid=new Map(); + this.MapAsk=new Map(); + this.Frame.VerticalRange.Differ=null; + + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + + this.RequestDepthData(); + } + + this.RequestDepthData=function() //全量历史数据 + { + var self=this; + if (this.NetworkFilter) + { + var obj= + { + Name:'DepthChartContainer::RequestDepthData', //类名:: + Explain:'深度图数据', + Request:{ Data: { symbol:self.Symbol } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvDepthData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + } + + this.RecvDepthData=function(data) + { + this.UpdateAskAndBid(data); + + var aryAsk=Array.from(this.MapAsk.values()); //卖 右边 + aryAsk.sort((a,b)=> { return a.Price-b.Price; }); + var sumVol=0; + for(var i in aryAsk) + { + var item=aryAsk[i]; + sumVol+=item.Vol; + + aryAsk[i]={Price:item.Price, Vol:sumVol }; + } + + var aryBid=Array.from(this.MapBid.values()); //买 左边 + aryBid.sort((a,b)=> { return b.Price-a.Price; }); + var sumVol=0; + for(var i in aryBid) + { + var item=aryBid[i]; + sumVol+=item.Vol; + + aryBid[i]={Price:item.Price, Vol:sumVol }; + } + + var drawData={ Asks:aryAsk, Bids:aryBid }; + var chart=this.ChartPaint[0]; + chart.Data=drawData; + + this.Frame.XSplitOperator.Symbol=this.Symbol; + this.ChartCorssCursor.Data=drawData; + this.ChartCorssCursor.Symbol=this.Symbol; + + this.UpdateFramePriceList(); + this.UpdateFrameMaxMin(); + + this.Draw(); + } + + this.UpdateAskAndBid=function(data) //更新数据 + { + if(data.datatype=="snapshot") //全量数据 + { + this.MapBid=new Map(); + this.MapAsk=new Map(); + } + + for(var i in data.asks) + { + var item=data.asks[i]; + var price=parseFloat(item[0]); + var vol=parseFloat(item[1]); + + if (this.MapAsk.has(price)) + { + var value=this.MapAsk.get(price); + if (vol<=0) this.MapAsk.delete(price); + else value.Vol=vol; + } + else + { + if (vol>0) this.MapAsk.set(price, { Price:price, Vol:vol}); + } + } + + for(var i in data.bids) + { + var item=data.bids[i]; + var price=parseFloat(item[0]); + var vol=parseFloat(item[1]); + + if (this.MapBid.has(price)) + { + var value=this.MapBid.get(price); + if (vol<=0) this.MapBid.delete(price); + else value.Vol=vol; + } + else + { + if (vol>0) this.MapBid.set(price, { Price:price, Vol:vol}); + } + } + } + + this.UpdateFramePriceList=function() + { + var aryAskPrice=Array.from(this.MapAsk.keys()); + var aryBidPrice=Array.from(this.MapBid.keys()); + + aryAskPrice.sort((a,b)=> { return a-b; }); + aryBidPrice.sort((a,b)=> { return a-b; }); + + var range={ Max:88, Min:8 }; + if (aryAskPrice.length>1 && aryBidPrice.length>1) + { + var askMin=aryAskPrice[0], askMax=aryAskPrice[aryAskPrice.length-1]; + var bidMin=aryBidPrice[0], bidMax=aryBidPrice[aryBidPrice.length-1]; + var askDifference=askMax-askMin; //卖差值 + var bidDifference=bidMax-bidMin; //买差值 + var difference=Math.max(askDifference, bidDifference); //取最大的差值,2边调整 + + var ask={Min:askMin, Max:askMin+difference}; + var bid={Max:bidMax, Min:bidMax-difference}; + range={ Max:ask.Max, Min:bid.Min }; + } + + this.Frame.SetPriceList(aryAskPrice,aryBidPrice); + var xRange=this.Frame.VerticalRange; + xRange.Max=range.Max; + xRange.Center=range.Min+(range.Max-range.Min)/2; + xRange.Min=range.Min; + xRange.MaxDiffer=difference; //差值 + xRange.Ask=ask; + xRange.Bid=bid; + if (!IFrameSplitOperator.IsNumber(xRange.Differ)) + xRange.Differ=difference*this.DefaultZoom; + + xRange.Min=xRange.Center-xRange.Differ; + xRange.Max=xRange.Center+xRange.Differ; + } + + this.UpdateFrameMaxMin=function() + { + var range=this.ChartPaint[0].GetMaxMin(); + + this.Frame.HorizontalMax=range.Max*this.MaxVolRate; + this.Frame.HorizontalMin=0; + this.Frame.XYSplit=true; + } + + this.CancelAutoUpdate=function() //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + this.StopAutoUpdate=function() + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + this.IsAutoUpdate=false; + } + + this.AutoUpdate=function() //数据自动更新 + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + + var self = this; + var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus==0 || marketStatus==3) return; //闭市,盘后 + + var frequency=this.AutoUpdateFrequency; + if (marketStatus==1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.AutoUpdate(); + },frequency); + } + else if (marketStatus==2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.RequestDepthData(); + },frequency); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// 简单的图形框架 +// +function SimlpleChartContainer(uielement) +{ + this.newMethod=JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.MainDataControl; //主数据类(对外的接口类) + //this.SubDataControl=new Array(); + + //创建 + this.Create=function() + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + //this.ChartCorssCursor=new ChartCorssCursor(); + //this.ChartCorssCursor.Canvas=this.Canvas; + //this.ChartCorssCursor.StringFormatX=new HQDateStringFormat(); + //this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat(); + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架容器 + this.Frame=new SimpleChartFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=30; + this.Frame.ChartBorder.Left=5; + this.Frame.ChartBorder.Bottom=20; + this.Frame.Canvas=this.Canvas; + if (this.ChartCorssCursor) this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateMainChart(); + + this.UIElement.addEventListener("keydown", OnKeyDown, true); //键盘消息 + } + + this.SetMainDataConotrl=function(dataControl) + { + if (!dataControl) return; + + this.MainDataControl=dataControl; + this.ChartPaint=[]; //图形 + + this.CreateMainChart(); + this.Draw(); + this.RequestData(); + } + + //创建主数据画法 + this.CreateMainChart=function() + { + if (!this.MainDataControl) return; + + for(let i in this.MainDataControl.DataType) + { + let item=this.MainDataControl.DataType[i]; + if (item.Type=="BAR") + { + var chartItem=new ChartBar(); + chartItem.Canvas=this.Canvas; + chartItem.ChartBorder=this.Frame.ChartBorder; + chartItem.ChartFrame=this.Frame; + chartItem.Name=item.Name; + if (item.Color) chartItem.UpBarColor=item.Color; + if (item.Color2) chartItem.DownBarColor=item.Color2; + + this.ChartPaint.push(chartItem); + } + else if (item.Type=="LINE") + { + var chartItem=new ChartLine(); + chartItem.Canvas=this.Canvas; + chartItem.ChartBorder=this.Frame.ChartBorder; + chartItem.ChartFrame=this.Frame; + chartItem.Name=item.Name; + if (item.Color) chartItem.Color=item.Color; + + this.ChartPaint.push(chartItem); + } + } + + this.Frame.YSplitOperator=new FrameSplitY(); + this.Frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + this.Frame.YSplitOperator.Frame=this.Frame; + this.Frame.YSplitOperator.ChartBorder=this.Frame.ChartBorder; + + this.Frame.XSplitOperator=new FrameSplitXData(); + this.Frame.XSplitOperator.Frame=this.Frame; + this.Frame.XSplitOperator.ChartBorder=this.Frame.ChartBorder; + + + // this.TitlePaint[0]=new DynamicKLineTitlePainting(); + // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + // this.TitlePaint[0].Canvas=this.Canvas; + } + + this.RequestData=function() + { + if(!this.MainDataControl) return; + + this.MainDataControl.JSChartContainer=this; + this.MainDataControl.RequestData(); + } + + this.UpdateMainData=function(dataControl) + { + + let lCount=0; + for(let i in dataControl.Data) + { + let itemData=new ChartData(); + itemData.Data=dataControl.Data[i]; + this.ChartPaint[i].Data=itemData; + if (lCount0) //周期数据 + { + this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换"); + param.HQChart.Draw(); + this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换"); + return false; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: g_JSChartResource.Index.MarketLongShortApiUrl, + data: + { + + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + self.SendEvent(hqChart,windowIndex,hisData); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.data.length<=0) return; + + var aryData=new Array(); + for(var i in recvData.data) + { + var item=recvData.data[i]; + var indexData=new SingleData(); + indexData.Date=item[0]; + indexData.Value=item[1]; + aryData.push(indexData); + } + + var aryFittingData=param.HistoryData.GetFittingData(aryData); + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + this.LongShortData=bindData.GetValue(); + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=this.CreatePaints(hqChart,windowIndex); + var isOverlay=this.IsOverlay(); + if (paint.length!=this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[2].Data.Data=this.LongShortData; + paint[0].Data.Data[0]=8; + paint[1].Data.Data[0]=1; + + var titleIndex=windowIndex+1; + + //指定[0,9] + if (isOverlay) + { + this.OverlayIndex.Frame.Frame.YSpecificMaxMin={Max:9,Min:0,Count:3}; + var titlePaint=hqChart.TitlePaint[titleIndex]; + var titleInfo={ Data:[], Title:'' }; + titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo); + for(var i in paint) + { + var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor); + if (i!=2) titleData.DataType="StraightLine"; + titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData; + } + } + else + { + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin={Max:9,Min:0,Count:3}; + for(var i in paint) + { + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor); + if (i!=2) hqChart.TitlePaint[titleIndex].Data[i].DataType="StraightLine"; + } + } + + return true; + } + +} + +//市场择时 +function MarketTimingIndex() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('Market-Timing'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("因子择时",null) + ); + + this.TimingData; //择时数据 + this.TitleColor=g_JSChartResource.FrameSplitTextColor + + this.CreateChart=function(id) + { + return new ChartMACD(); + } + + this.GetOutData=function() + { + return { TimingData:this.TimingData }; + } + + //请求数据 + this.RequestData=function(hqChart,windowIndex,hisData) + { + var self = this; + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + this.LongShortData=[]; + + if (param.HQChart.Period>0) //周期数据 + { + this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换"); + param.HQChart.Draw(); + this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换"); + return false; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: g_JSChartResource.Index.MarketLongShortApiUrl, + data: + { + + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + self.SendEvent(hqChart,windowIndex,hisData); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.data.length<=0) return; + + var aryData=new Array(); + for(var i in recvData.data) + { + var item=recvData.data[i]; + var indexData=new SingleData(); + indexData.Date=item[0]; + indexData.Value=item[2]; + aryData.push(indexData); + } + + var aryFittingData=param.HistoryData.GetFittingData(aryData); + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + this.TimingData=bindData.GetValue(); + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=this.CreatePaints(hqChart,windowIndex); + var isOverlay=this.IsOverlay(); + + if (paint.length!=this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data=this.TimingData; + paint[0].NotSupportMessage=null; + + var titleIndex=windowIndex+1; + + if (isOverlay) + { + var titlePaint=hqChart.TitlePaint[titleIndex]; + var titleInfo={ Data:[], Title:'' }; + titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo); + for(var i in paint) + { + var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor); + titleData.StringFormat=STRING_FORMAT_TYPE.THOUSANDS; + titleData.FloatPrecision=0; + titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData; + } + } + else + { + for(var i in paint) + { + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.TitleColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.THOUSANDS; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=0; + } + } + + return true; + } +} + +//市场关注度 +function MarketAttentionIndex() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('Market-Attention'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("市场关注度",null) + ); + + this.Data; //关注度数据 + this.TitleColor=g_JSChartResource.FrameSplitTextColor; + this.ApiUrl=g_JSChartResource.Index.MarketAttentionApiUrl; + + this.CreateChart=function(id) + { + return new ChartMACD(); + } + + this.GetOutData=function() + { + return { Data:this.Data }; + } + + //调整框架 + this.SetFrame=function(hqChart,windowIndex,hisData) + { + var isOverlay=this.IsOverlay(); + if (isOverlay) + this.OverlayIndex.Frame.Frame.YSpecificMaxMin={Max:6,Min:0,Count:3}; + else + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin={Max:6,Min:0,Count:3}; + } + + //请求数据 + this.RequestData=function(hqChart,windowIndex,hisData) + { + var self = this; + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + this.Data=[]; + + if (param.HQChart.Period>0) //周期数据 + { + this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换"); + param.HQChart.Draw(); + this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换"); + return false; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.ApiUrl, + data: + { + "symbol":param.HQChart.Symbol, + "startdate":20100101, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + self.SendEvent(hqChart,windowIndex,hisData); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.date.length<0) return; + + var aryData=new Array(); + for(var i in recvData.date) + { + var indexData=new SingleData(); + indexData.Date=recvData.date[i]; + indexData.Value=recvData.value[i]; + aryData.push(indexData); + } + + var aryFittingData=param.HistoryData.GetFittingData(aryData); + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + this.Data=bindData.GetValue(); + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); + this.SetFrame(param.HQChart,param.WindowIndex,param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=this.CreatePaints(hqChart,windowIndex); + var isOverlay=this.IsOverlay(); + + if (paint.length!=this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data=this.Data; + paint[0].NotSupportMessage=null; + + var titleIndex=windowIndex+1; + if (isOverlay) + { + var titlePaint=hqChart.TitlePaint[titleIndex]; + var titleInfo={ Data:[], Title:'' }; + titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo); + for(var i in paint) + { + var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor); + titleData.StringFormat=STRING_FORMAT_TYPE.THOUSANDS; + titleData.FloatPrecision=0; + titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData; + } + } + else + { + for(var i in paint) + { + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.TitleColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.THOUSANDS; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=0; + } + } + + return true; + } +} + + +/* + 行业,指数热度 +*/ +function MarketHeatIndex() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('Market-Heat'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("热度",5), + new IndexInfo('MA',10), + new IndexInfo('MA',null) + ); + + this.Data; //关注度数据 + + this.ApiUrl=g_JSChartResource.Index.MarketHeatApiUrl; + + this.Index[0].LineColor=g_JSChartResource.FrameSplitTextColor; + this.Index[1].LineColor=g_JSChartResource.Index.LineColor[0]; + this.Index[2].LineColor=g_JSChartResource.Index.LineColor[1]; + + this.CreateChart=function(id) + { + if (id==0) return new ChartMACD(); + + return new ChartLine(); + } + + //请求数据 + this.RequestData=function(hqChart,windowIndex,hisData) + { + var self = this; + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + this.Data=[]; + + if (param.HQChart.Period>0) //周期数据 + { + this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.ApiUrl, + data: + { + "symbol":param.HQChart.Symbol, + "startdate":20100101, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.date.length<0) return; + + var aryData=new Array(); + for(var i in recvData.date) + { + var indexData=new SingleData(); + indexData.Date=recvData.date[i]; + indexData.Value=recvData.value[i]; + aryData.push(indexData); + } + + var aryFittingData=param.HistoryData.GetFittingData(aryData); + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + this.Data=bindData.GetValue(); + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=this.CreatePaints(hqChart,windowIndex); + var isOverlay=this.IsOverlay(); + + if (paint.length!=this.Index.length) return false; + + paint[0].Data.Data=this.Data; + paint[0].NotSupportMessage=null; + + var MA=HQIndexFormula.MA(this.Data,this.Index[0].Param); + paint[1].Data.Data=MA; + + var MA2=HQIndexFormula.MA(this.Data,this.Index[1].Param); + paint[2].Data.Data=MA2; + + var titleIndex=windowIndex+1; + + if (isOverlay) + { + var titlePaint=hqChart.TitlePaint[titleIndex]; + var titleInfo={ Data:[], Title:'' }; + titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo); + for(var i in paint) + { + var name='' + if(i==0) name=hqChart.Name+this.Index[i].Name; + else name="MA"+this.Index[i-1].Param; + var titleData=new DynamicTitleData(paint[i].Data,name,this.Index[i].LineColor); + titleData.StringFormat=STRING_FORMAT_TYPE.DEFAULT; + titleData.FloatPrecision=2; + titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData; + } + } + else + { + for(var i in paint) + { + var name=""; //显示的名字特殊处理 + if(i==0) name=hqChart.Name+this.Index[i].Name; + else name="MA"+this.Index[i-1].Param; + + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,name,this.Index[i].LineColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.DEFAULT; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=2; + } + } + + //hqChart.TitlePaint[titleIndex].Explain="热度说明"; + + return true; + } + +} + +//自定义指数热度 +function CustonIndexHeatIndex() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('Market-Heat'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo('区域',3), + new IndexInfo("热度指数",10), + new IndexInfo('MA',5), + new IndexInfo('MA',10) + ); + + this.Data; //关注度数据 + + this.ApiUrl=g_JSChartResource.Index.CustomIndexHeatApiUrl; + + this.Index[1].LineColor=g_JSChartResource.Index.LineColor[1]; + this.Index[2].LineColor=g_JSChartResource.Index.LineColor[2]; + this.Index[3].LineColor=g_JSChartResource.Index.LineColor[3]; + + this.Create=function(hqChart,windowIndex) + { + for(var i in this.Index) + { + var paint=null; + if (i==0) + { + paint = new ChartStraightArea(); + } + else + { + paint=new ChartLine(); + paint.Color=this.Index[i].LineColor; + } + + paint.Canvas=hqChart.Canvas; + paint.Name=this.Name+"-"+i.toString(); + paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData=function(hqChart,windowIndex,hisData) + { + var self = this; + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + this.Data=[]; + + if (param.HQChart.Period>0) //周期数据 + { + this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.ApiUrl, + data: + { + "stock":param.HQChart.CustomStock, + "date":{"startdate":param.HQChart.QueryDate.Start,"enddate":param.HQChart.QueryDate.End}, + "day":[this.Index[0].Param,this.Index[1].Param], + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.data==null || recvData.data.length<0) return; + + //JSConsole.Chart.Log(recvData.data); + var aryData=new Array(); + for(let i in recvData.data) + { + let item=recvData.data[i]; + let indexData=new SingleData(); + indexData.Date=item[0]; + indexData.Value=item[1]; + aryData.push(indexData); + } + + var aryFittingData=param.HistoryData.GetFittingData(aryData); + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + this.Data=bindData.GetValue(); + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData=function(hqChart,windowIndex,hisData) + { + let paint=hqChart.GetChartPaint(windowIndex); + + if (paint.length!=this.Index.length) return false; + + paint[0].NotSupportMessage=null; + paint[0].Data.Data= + [ + { Value: 0, Value2: 0.2, Color: 'rgb(50,205,50)', Title: '热度1', TitleColor:'rgb(245,255 ,250)'}, + { Value: 0.2, Value2: 0.4, Color: 'rgb(255,140,0)', Title: '热度2', TitleColor:'rgb(245,255 ,250)'}, + { Value: 0.4, Value2: 0.8, Color: 'rgb(255,106,106)', Title: '热度3', TitleColor:'rgb(245,255 ,250)'}, + { Value: 0.8, Value2: 1, Color: 'rgb(208, 32 ,144)', Title: '热度4', TitleColor:'rgb(245,255 ,250)'} + ]; + + paint[1].Data.Data = this.Data; + + let MA=HQIndexFormula.MA(this.Data,this.Index[2].Param); + paint[2].Data.Data=MA; + + let MA2=HQIndexFormula.MA(this.Data,this.Index[3].Param); + paint[3].Data.Data=MA2; + + //指定框架最大最小[0,1] + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 1, Min: 0, Count: 3 }; + + let titleIndex=windowIndex+1; + + for(let i=1;i下线 , 上线,下线 ,2, 0),COLORRED,LINETHICK2; + STICKLINE(下线>上线,上线,下线,2,0),COLORBLUE,LINETHICK2; +*/ + +function LighterIndex1() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('Lighter-Trend'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("STICKLINE",null), + new IndexInfo('STICKLINE',null) + ); + + this.Index[0].LineColor='rgb(255,0,0)'; + this.Index[1].LineColor='rgb(0,0,255)'; + + this.Create=function(hqChart,windowIndex) + { + for(var i in this.Index) + { + var paint=new ChartStickLine(); + paint.Canvas=hqChart.Canvas; + paint.Name=this.Name; + paint.Name=this.Name+'-'+i.toString(); + paint.Color=this.Index[i].LineColor; + paint.LineWidth=2; + paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + hqChart.ChartPaint.push(paint); + } + } + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=hqChart.GetChartPaint(windowIndex); + + if (paint.length!=2) return false; + + var closeData=hisData.GetClose(); + var upData=HQIndexFormula.SMA(closeData,6.5,1); + var downData=HQIndexFormula.SMA(closeData,13.5,1); + + var stickLine=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(upData,downData),upData,downData);; + var stickLine2=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(downData,upData),upData,downData);; + + paint[0].Data.Data=stickLine; + paint[1].Data.Data=stickLine2; + + var titleIndex=windowIndex+1; + hqChart.TitlePaint[titleIndex].Title="大盘/个股 趋势函数"; + + return true; + } +} + +/* + 位置研判函数 + N:=34;M:=3; + 28,COLORFFFFFF; + STICKLINE(C>0,0,2,5,0),COLOR00008A; + STICKLINE(C>0,2,5,5,0),COLOR85008A; + STICKLINE(C>0,5,10,5,0),COLOR657600; + STICKLINE(C>0,10,21.5,5,0),COLOR690079; + STICKLINE(C>0,21.5,23,5,0),COLOR79B715; + STICKLINE(C>0,23,28,5,0),COLOR00008A; + VAR1:=EMA(100*(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N)),M)/4,COLORFFFF00,LINETHICK2; + + 财貌双拳:VAR1,COLORFFFF00,LINETHICK2; + DRAWTEXT(CURRBARSCOUNT=128,1,'底部区域'),COLOR00FFFF; + DRAWTEXT(CURRBARSCOUNT=128,3.5,'介入区域'),COLOR00FFFF; + DRAWTEXT(CURRBARSCOUNT=128,7.5,'稳健区域'),COLOR00FFFF; + DRAWTEXT(CURRBARSCOUNT=128,16,'高位区域'),COLOR00FFFF; + DRAWTEXT(CURRBARSCOUNT=128,22,'风险区域'),COLOR0000FF; + DRAWTEXT(CURRBARSCOUNT=128,25.5,'顶部区域'),COLORFF00FF; +*/ + +function LighterIndex2() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('位置研判函数'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("STICKLINE",34), + new IndexInfo('STICKLINE',3), + new IndexInfo('STICKLINE',null), + new IndexInfo('STICKLINE',null), + new IndexInfo('STICKLINE',null), + new IndexInfo('STICKLINE',null), + new IndexInfo('LINE',null), + new IndexInfo('TEXT',null) + ); + + this.Index[0].LineColor='rgb(0,0,138)'; + this.Index[1].LineColor='rgb(133,0,138)'; + this.Index[2].LineColor='rgb(101,118,0)'; + this.Index[3].LineColor='rgb(105,0,121)'; + this.Index[4].LineColor='rgb(121,183,21)'; + this.Index[5].LineColor='rgb(0,0,138)'; + this.Index[6].LineColor='rgb(255,0,0)'; + + this.Create=function(hqChart,windowIndex) + { + for(var i in this.Index) + { + var paint=null; + if (this.Index[i].Name=='LINE') + { + paint=new ChartLine(); + paint.Color=this.Index[i].LineColor; + } + else if (this.Index[i].Name=='TEXT') + { + paint=new ChartText(); + } + else + { + paint=new ChartStickLine(); + paint.Color=this.Index[i].LineColor; + paint.LineWidth=5; + } + + paint.Canvas=hqChart.Canvas; + paint.Name=this.Name; + paint.Name=this.Name+'-'+i.toString(); + paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + hqChart.ChartPaint.push(paint); + } + } + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=hqChart.GetChartPaint(windowIndex); + + if (paint.length!=this.Index.length) return false; + + var closeData=hisData.GetClose(); + var highData=hisData.GetHigh(); + var lowData=hisData.GetLow(); + + paint[0].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),0,2); + paint[1].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),2,5); + paint[2].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),5,10); + paint[3].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),10,21.5); + paint[4].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),21.5,23,5); + paint[5].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),23,28,5); + + //VAR1:=EMA(100*(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N)),M)/4 + var lineData=HQIndexFormula.ARRAY_DIVIDE( + HQIndexFormula.EMA( + HQIndexFormula.ARRAY_MULTIPLY( + HQIndexFormula.ARRAY_DIVIDE( + HQIndexFormula.ARRAY_SUBTRACT(closeData,HQIndexFormula.LLV(lowData,this.Index[0].Param)), + HQIndexFormula.ARRAY_SUBTRACT(HQIndexFormula.HHV(highData,this.Index[0].Param),HQIndexFormula.LLV(lowData,this.Index[0].Param)) + ), + 100), + this.Index[1].Param), + 4 + ); + + paint[6].Data.Data=lineData; + + //DRAWTEXT(CURRBARSCOUNT=128,1,'底部区域'),COLOR00FFFF; + //DRAWTEXT(CURRBARSCOUNT=128,3.5,'介入区域'),COLOR00FFFF; + //DRAWTEXT(CURRBARSCOUNT=128,7.5,'稳健区域'),COLOR00FFFF; + //DRAWTEXT(CURRBARSCOUNT=128,16,'高位区域'),COLOR00FFFF; + //DRAWTEXT(CURRBARSCOUNT=128,22,'风险区域'),COLOR0000FF; + //DRAWTEXT(CURRBARSCOUNT=128,25.5,'顶部区域'),COLORFF00FF; + + var TextData=new Array(); + TextData[0]={Value:1, Message:'底部区域',Color:'rgb(0,255,255)',Position:'Left'}; + TextData[1]={Value:3.5, Message:'介入区域',Color:'rgb(0,255,255)',Position:'Left'}; + TextData[2]={Value:7.5, Message:'稳健区域',Color:'rgb(0,255,255)',Position:'Left'}; + TextData[3]={Value:16, Message:'高位区域',Color:'rgb(0,255,255)',Position:'Left'}; + TextData[4]={Value:22, Message:'风险区域',Color:'rgb(0,0,255)',Position:'Left'}; + TextData[5]={Value:25.5,Message:'顶部区域',Color:'rgb(255,0,255)',Position:'Left'}; + + paint[7].Data.Data=TextData; + + var titleIndex=windowIndex+1; + hqChart.TitlePaint[titleIndex].Data[0]=new DynamicTitleData(paint[6].Data,"财貌双拳",paint[6].Color); + + hqChart.TitlePaint[titleIndex].Title=this.FormatIndexTitle(); + + return true; + } +} + + +/* + py指标 服务器端执行 +*/ +function PyScriptIndex(name,script,args,option) +{ + this.newMethod=BaseIndex; //派生 + this.newMethod(name); + delete this.newMethod; + + this.Script=script; //脚本 + this.Arguments=[]; //参数 + this.OutVar=[]; //输出数据 + this.ApiUrl=g_JSChartResource.PyIndexDomain+'/hq/code'; + if (args) this.Arguments=args; + + this.RequestData=function(hqChart,windowIndex,hisData) + { + this.OutVar=[]; + var self = this; + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + //参数 + var strParam=''; + for(let i in this.Arguments) + { + if (strParam.length>0) strParam+=','; + var item=this.Arguments[i]; + strParam+='"'+item.Name+'"'+':'+item.Value; + } + strParam='{'+strParam+'}'; + var indexParam=JSON.parse(strParam); + + var data=JSON.stringify( + { + code:this.Script, //脚本 + symbol:param.HQChart.Symbol, //股票代码 + period:param.HQChart.Period, //周期 0=日线 1=周线 2=月线 3=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 + right:param.HQChart.Right, //复权 0 不复权 1 前复权 2 后复权 + params:indexParam, //指标参数 + numcount:hqChart.MaxReqeustDataCount, + }); + + //请求数据 + JSNetwork.HttpRequest({ + url: this.ApiUrl, + data:data, + type:"post", + dataType: "json", + contentType:' application/json; charset=utf-8', + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + }, + complete:function(h) + { + //JSConsole.Chart.Log(h); + }, + error: function(http,e) + { + self.RecvError(http,e,param);; + + } + }); + + return true; + } + + this.RecvError=function(http,e,param) + { + JSConsole.Chart.Log("[PyScriptIndex::RecvError] error",e); + if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(e); + } + + this.RecvData=function(recvData,param) + { + if (recvData.code!=0) + { + JSConsole.Chart.Log("[PyScriptIndex::RecvData] failed.", recvData); + if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(recvData.msg); + return; //失败了 + } + + JSConsole.Chart.Log('[PyScriptIndex::RecvData] recv data.',recvData); + var aryDate=recvData.date; + for(var i in recvData.data) + { + var item=recvData.data[i]; + var indexData=[]; + for(var j=0;j=item.data.length) continue; + var indexItem=new SingleData(); //单列指标数据 + indexItem.Date=aryDate[j]; + indexItem.Value=item.data[j]; + indexData.push(indexItem); + } + + var aryFittingData=param.HistoryData.GetFittingData(indexData); //数据和主图K线拟合 + var bindData=new ChartData(); + bindData.Data=aryFittingData; + bindData.Period=param.HQChart.Period; //周期 + bindData.Right=param.HQChart.Right; //复权 + + var indexInfo={Name:item.name,Type:item.graph,LineWidth:item.width,Data:bindData.GetValue(),Color:item.color}; + this.OutVar.push(indexInfo); + } + + this.BindData(param.HQChart,param.WindowIndex,param.HistoryData); //把数据绑定到图形上 + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + this.BindData=function(hqChart,windowIndex,hisData) + { + hqChart.DeleteIndexPaint(windowIndex); //清空指标图形 + + for(let i in this.OutVar) + { + let item=this.OutVar[i]; + switch(item.Type) + { + case 'line': + this.CreateLine(hqChart,windowIndex,item,i); + break; + case 'colorstick': //上下柱子 + this.CreateColorStock(hqChart,windowIndex,item,i); + break; + } + } + + var titleIndex=windowIndex+1; + hqChart.TitlePaint[titleIndex].Title=this.Name; //这是指标名称 + + let indexParam=''; //指标参数 + for(let i in this.Arguments) + { + let item=this.Arguments[i]; + if (indexParam.length>0) indexParam+=','; + indexParam+=item.Value.toString(); + } + if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')'; + + return true; + } + + this.CreateLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartLine(); + line.Canvas=hqChart.Canvas; + line.DrawType=1; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=varItem.Color; + else line.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth>0) line.LineWidth=varItem.LineWidth; + if (varItem.IsShow==false) line.IsShow=false; + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + + hqChart.ChartPaint.push(line); + } + + this.CreateColorStock=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartMACD(); + chart.Canvas=hqChart.Canvas; + + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,this.GetDefaultColor(id)); + + hqChart.ChartPaint.push(chart); + } + + //给一个默认的颜色 + this.GetDefaultColor=function(id) + { + let COLOR_ARRAY= + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ]; + + let number=parseInt(id); + return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)]; + } + +} + + + +/* + json格式数据指标 用来显示本地数据 +*/ +function JsonDataIndex(name,script,args,option) +{ + this.newMethod=PyScriptIndex; //派生 + this.newMethod(name); + delete this.newMethod; + + this.JsonData; //json格式数据 + if (option.JsonData) this.JsonData=option.JsonData; + + this.RequestData=function(hqChart,windowIndex,hisData) + { + if (!this.JsonData) + { + console.warn("[PyScriptIndex::RequestData] JsonData is null"); + if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback('json 数据不能为空'); + } + else + { + var param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData + }; + + this.JsonData.code=0; + var recvData=this.JsonData; + this.RecvData(recvData,param); + } + } + +} + +//给一个默认的颜色 +PyScriptIndex.prototype.GetDefaultColor=function(id) +{ + let COLOR_ARRAY= + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ]; + + let number=parseInt(id); + return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)]; +} + + +/* + 点位研判函数 + + HJ_1:=REF(LOW,1); + HJ_2:=SMA(ABS(LOW-HJ_1),13,1)/SMA(MAX(LOW-HJ_1,0),13,1)*100; + HJ_3:=EMA(IF(CLOSE*1.2,HJ_2*13,HJ_2/13),13); + HJ_4:=LLV(LOW,34); + HJ_5:=HHV(HJ_3,34); + HJ_6:=IF(LLV(LOW,56),1,0); + HJ_7:=EMA(IF(LOW<=HJ_4,(HJ_3+HJ_5*2)/2,0),3)/618*HJ_6; + HJ_8:=HJ_7>REF(HJ_7,1); + HJ_9:=REF(LLV(LOW,100),3); + HJ_10:=REFDATE(HJ_9,DATE); + HJ_11:=LOW=HJ_10; + HJ_12:=HJ_8 AND HJ_11; + HJ_13:=HJ_12>REF(HJ_12,1); + 启动买点:HJ_13>REF(HJ_13,1),COLORRED,LINETHICK1; +*/ +function LighterIndex3() +{ + this.newMethod=BaseIndex; //派生 + this.newMethod('点位研判函数'); + delete this.newMethod; + + this.Index=new Array( + new IndexInfo("启动买点",null) + ); + + this.Index[0].LineColor='rgb(255,0,0)'; + + this.BindData=function(hqChart,windowIndex,hisData) + { + var paint=hqChart.GetChartPaint(windowIndex); + + if (paint.length!=this.Index.length) return false; + + var closeData=hisData.GetClose(); + var highData=hisData.GetHigh(); + var lowData=hisData.GetLow(); + + //HJ_1:=REF(LOW,1); + var hj_1=HQIndexFormula.REF(lowData,1); + + //HJ_2:=SMA(ABS(LOW-HJ_1),13,1)/SMA(MAX(LOW-HJ_1,0),13,1)*100; + var hj_2=HQIndexFormula.ARRAY_MULTIPLY( + HQIndexFormula.ARRAY_DIVIDE( + HQIndexFormula.SMA(HQIndexFormula.ABS(HQIndexFormula.ARRAY_SUBTRACT(lowData,hj_1)),13,1), + HQIndexFormula.SMA(HQIndexFormula.MAX(HQIndexFormula.ARRAY_SUBTRACT(lowData,hj_1),0),13,1) + ), + 100 + ); + + //HJ_3:=EMA(IF(CLOSE*1.2,HJ_2*13,HJ_2/13),13); + var hj_3=HQIndexFormula.EMA( + HQIndexFormula.ARRAY_IF(HQIndexFormula.ARRAY_MULTIPLY(closeData,1.2),HQIndexFormula.ARRAY_MULTIPLY(hj_2,13),HQIndexFormula.ARRAY_DIVIDE(hj_2,13)), + 13 + ); + + //HJ_4:=LLV(LOW,34); + var hj_4=HQIndexFormula.LLV(lowData,34); + + //HJ_5:=HHV(HJ_3,34); + var hj_5=HQIndexFormula.HHV(hj_3,34); + + //HJ_6:=IF(LLV(LOW,56),1,0); + var hj_6=HQIndexFormula.ARRAY_IF(HQIndexFormula.LLV(lowData,56),1,0); + + //HJ_7:=EMA(IF(LOW<=HJ_4,(HJ_3+HJ_5*2)/2,0),3)/618*HJ_6; + //hj_7_temp=(HJ_3+HJ_5*2)/2,0) 太长了 这部分单独算下 + var hj_7_temp=HQIndexFormula.ARRAY_DIVIDE(HQIndexFormula.ARRAY_ADD(hj_3,HQIndexFormula.ARRAY_MULTIPLY(hj_5,2)),2); + + var hj_7=HQIndexFormula.ARRAY_MULTIPLY( + HQIndexFormula.ARRAY_DIVIDE( + HQIndexFormula.EMA( + HQIndexFormula.ARRAY_IF(HQIndexFormula.ARRAY_LTE(lowData,hj_4),hj_7_temp,0), + 3 + ), + 618 + ), + hj_6 + ); + + //HJ_8:=HJ_7>REF(HJ_7,1); + var hj_8=HQIndexFormula.ARRAY_GT(hj_7,HQIndexFormula.REF(hj_7,1)); + + //HJ_9:=REF(LLV(LOW,100),3); + var hj_9=HQIndexFormula.REF(HQIndexFormula.LLV(lowData,100),3); + + //HJ_10:=REFDATE(HJ_9,DATE); 用当日的数据 产生数组 + var hj_10=HQIndexFormula.REFDATE(hj_9,-1); + + //HJ_11:=LOW=HJ_10; + var hj_11=HQIndexFormula.ARRAY_EQ(lowData,hj_10); + + //HJ_12:=HJ_8 AND HJ_11; + var hj_12=HQIndexFormula.ARRAY_AND(hj_8,hj_11); + + var buyData=null; + paint[0].Data.Data=hj_12; + + var titleIndex=windowIndex+1; + + for(var i in paint) + { + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor); + } + + hqChart.TitlePaint[titleIndex].Title=this.FormatIndexTitle(); + + return true; + } +} + + +/* + 信息地雷 +*/ + +/* + 信息地雷列表 +*/ +function JSKLineInfoMap() +{ +} + +JSKLineInfoMap.Get=function(id) +{ + var infoMap=new Map( + [ + ["互动易", {Create:function(){ return new InvestorInfo()} }], + ["公告", {Create:function(){ return new AnnouncementInfo()} }], + ["业绩预告", {Create:function(){ return new PforecastInfo()} }], + ["调研", {Create:function(){ return new ResearchInfo()} }], + ["大宗交易", {Create:function(){ return new BlockTrading()} }], + ["龙虎榜", {Create:function(){ return new TradeDetail()} }] + ] + ); + + return infoMap.get(id); +} + +JSKLineInfoMap.GetClassInfo=function(id) +{ + var infoMap=new Map( + [ + ["互动易", { ClassName:"InvestorInfo" }], + ["公告", { ClassName:"AnnouncementInfo" }], + ["业绩预告", { ClassName:"PforecastInfo" } ], + ["调研", { ClassName:"ResearchInfo" }], + ["大宗交易", { ClassName:"BlockTrading" }], + ["龙虎榜", { ClassName:"TradeDetail"} ] + ] + ); + + return infoMap.get(id); +} + +JSKLineInfoMap.GetIconUrl=function(type) +{ + switch(type) + { + case KLINE_INFO_TYPE.INVESTOR: + return g_JSChartResource.KLine.Info.Investor.Icon; + break; + case KLINE_INFO_TYPE.PFORECAST: + return g_JSChartResource.KLine.Info.Pforecast.Icon; + case KLINE_INFO_TYPE.ANNOUNCEMENT: + return g_JSChartResource.KLine.Info.Announcement.Icon; + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4: + return g_JSChartResource.KLine.Info.Announcement.IconQuarter; + case KLINE_INFO_TYPE.RESEARCH: + return g_JSChartResource.KLine.Info.Research.Icon; + case KLINE_INFO_TYPE.BLOCKTRADING: + return g_JSChartResource.KLine.Info.BlockTrading.Icon; + case KLINE_INFO_TYPE.TRADEDETAIL: + return g_JSChartResource.KLine.Info.TradeDetail.Icon; + default: + return g_JSChartResource.KLine.Info.Announcement.Icon; + } +} + +JSKLineInfoMap.GetIconLibrary=function(index) +{ + var iconLib=g_JSChartResource.KLine.Info.IconLibrary; + if (!iconLib || !iconLib.Icon) return g_JSChartResource.KLine.Info.Announcement.IconFont; + + if (index>=0 && index=KLINE_INFO_TYPE.ANNOUNCEMENT_EX_START && type<=KLINE_INFO_TYPE.ANNOUNCEMENT_EX_END) + { + var index=type-KLINE_INFO_TYPE.ANNOUNCEMENT_EX_START; + return JSKLineInfoMap.GetIconLibrary(index); + } + + switch(type) + { + case KLINE_INFO_TYPE.INVESTOR: + return g_JSChartResource.KLine.Info.Investor.IconFont; + break; + case KLINE_INFO_TYPE.PFORECAST: + return g_JSChartResource.KLine.Info.Pforecast.IconFont; + case KLINE_INFO_TYPE.ANNOUNCEMENT: + return g_JSChartResource.KLine.Info.Announcement.IconFont; + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3: + case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4: + return g_JSChartResource.KLine.Info.Announcement.IconFont2; + case KLINE_INFO_TYPE.RESEARCH: + return g_JSChartResource.KLine.Info.Research.IconFont; + case KLINE_INFO_TYPE.BLOCKTRADING: + return g_JSChartResource.KLine.Info.BlockTrading.IconFont; + case KLINE_INFO_TYPE.TRADEDETAIL: + return g_JSChartResource.KLine.Info.TradeDetail.IconFont; + default: + return g_JSChartResource.KLine.Info.Announcement.IconFont; + } +} + + +function IKLineInfo() +{ + this.MaxReqeustDataCount=1000; + this.StartDate=20160101; + this.Data; + this.ClassName='IKLineInfo'; + this.Explain="IKLineInfo"; + + this.GetToday=function() + { + var date=new Date(); + var today=date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate(); + return today; + } + + this.GetRequestData=function(hqChart) + { + var obj= + { + Symbol:hqChart.Symbol , + MaxReqeustDataCount: hqChart.MaxReqeustDataCount, //日线数据个数 + MaxRequestMinuteDayCount:hqChart.MaxRequestMinuteDayCount, //分钟数据请求的天数 + Period:hqChart.Period //周期 + }; + + return obj; + } + + this.NetworkFilter=function(hqChart,callInfo) + { + if (!hqChart.NetworkFilter) return false; + + var self=this; + + var param= + { + HQChart:hqChart, + }; + + var obj= + { + Name:`${this.ClassName}::RequestData`, //类名::函数 + Explain:this.Explain, + Request:this.GetRequestData(hqChart), + Self:this, + HQChart:hqChart, + PreventDefault:false + }; + + if (callInfo) + { + if (callInfo.Update==true) + { + obj.Update={ Start:{ Date: callInfo.StartDate } }; + param.Update={ Start:{ Date: callInfo.StartDate } }; + } + + obj.CallFunctionName=callInfo.FunctionName; //内部调用函数名 + } + + hqChart.NetworkFilter(obj, function(data) + { + self.RecvData(data,param); + }); + + if (obj.PreventDefault==true) return true; //已被上层替换,不调用默认的网络请求 + + return false; + } +} + + + +//互动易 +function InvestorInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='InvestorInfo'; + this.Explain='互动易' + this.RequestData=function(hqChart, obj) + { + var self = this; + var param= + { + HQChart:hqChart, + }; + + this.Data=[]; + if (this.NetworkFilter(hqChart,obj)) return; //已被上层替换,不调用默认的网络请求 + + var postData= + { + filed: ["question","answerdate","symbol","id"], + symbol: [param.HQChart.Symbol], + querydate:{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + start:0, + end:this.MaxReqeustDataCount, + }; + + //请求数据 + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Investor.ApiUrl; + JSNetwork.HttpRequest({ + url: url, + data:postData, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.list.length<=0) return; + + for(var i in recvData.list) + { + var item=recvData.list[i]; + var infoData=new KLineInfoData(); + infoData.Date=item.answerdate; + infoData.Title=item.question; + infoData.InfoType=KLINE_INFO_TYPE.INVESTOR; + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +//公告 支持增量更新 +function AnnouncementInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='AnnouncementInfo'; + this.Explain='公告'; + this.ApiType=1; //0=读API 1=读OSS缓存文件 + + this.RequestData=function(hqChart,obj) + { + var self = this; + var param= + { + HQChart:hqChart, + }; + + if (obj && obj.Update===true) //更新模式 不清内存数据 + { + + } + else + { + this.Data=[]; + } + + if (this.NetworkFilter(hqChart, obj)) return; //已被上层替换,不调用默认的网络请求 + + if (this.ApiType==1) //取缓存文件 + { + var url=`${g_JSChartResource.CacheDomain}/cache/analyze/shszreportlist/${param.HQChart.Symbol}.json`; + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + }, + error: function(http,e) + { + self.RecvError(http,e,param);; + } + }); + } + else //取api + { + //请求数据 + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Announcement.ApiUrl; + JSNetwork.HttpRequest({ + url: url, + data: + { + "filed": ["title","releasedate","symbol","id"], + "symbol": [param.HQChart.Symbol], + "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + "start":0, + "end":this.MaxReqeustDataCount, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + }, + error: function(http,e) + { + self.RecvError(http,e,param);; + } + }); + + } + } + + this.RecvData=function(recvData,param) + { + if (recvData.report.length<=0) return; + + var aryReport=[]; + for(var i in recvData.report) + { + var item=recvData.report[i]; + var infoData=new KLineInfoData(); + infoData.Date=item.releasedate; + infoData.Title=item.title; + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT; + for(var j in item.type) + { + var typeItem=item.type[j]; + switch(typeItem) + { + case "一季度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1; + break; + case "半年度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2; + break; + case "三季度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3; + break; + case "年度报告": + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4; + break; + } + } + + //目前只支持1个类型 + for(var j in item.typeex) + { + var id=item.typeex[j]; + infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_EX_START+id; + break; + } + + aryReport.push(infoData); + } + + if (recvData.Update===true && this.Data.length>0) + { + this.UpdateData(aryReport); + } + else + { + this.Data=aryReport; + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } + + + this.UpdateData=function(aryData) + { + if (!aryData || aryData.length<=0) return; + + var setKeys=new Set(); //通过 日期+类型+标题去重 + for(var i in this.Data) + { + var item=this.Data[i]; + var strKey=`${item.Date}-${item.InfoType}-${item.Title}`; + setKeys.add(strKey); + } + + var count=0; + for(var i in aryData) + { + var item=aryData[i]; + var strKey=`${item.Date}-${item.InfoType}-${item.Title}`; + if (setKeys.has(strKey)) continue; + + this.Data.push(item); + ++count; + } + + JSConsole.Chart.Log(`[AnnouncementInfo::UpdateData] add new count=${count}`); + } + + this.RecvError=function(http,e,param) + { + console.warn("[AnnouncementInfo::RecvError] error, http ",e, http); + //if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(e); + } +} + + + + //业绩预告 +function PforecastInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='PforecastInfo'; + + this.RequestData=function(hqChart,obj) + { + var self = this; + var param= + { + HQChart:hqChart, + }; + + this.Data=[]; + + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Pforecast.ApiUrl; + //请求数据 + JSNetwork.HttpRequest({ + url: url, + data: + { + "field": ["pforecast.type","pforecast.reportdate","fweek"], + "condition": + [ + {"item":["pforecast.reportdate","int32","gte",this.StartDate]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.stock.length!=1) return; + if (recvData.stock[0].stockday.length<=0) return; + + for(var i in recvData.stock[0].stockday) + { + var item=recvData.stock[0].stockday[i]; + if (item.pforecast.length>0) + { + var dataItem=item.pforecast[0]; + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.Title=dataItem.type; + infoData.InfoType=KLINE_INFO_TYPE.PFORECAST; + infoData.ExtendData={ Type:dataItem.type, ReportDate:dataItem.reportdate} + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + this.Data.push(infoData); + } + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +//投资者关系 (调研) +function ResearchInfo() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='ResearchInfo'; + + this.RequestData=function(hqChart,obj) + { + var self = this; + var param= + { + HQChart:hqChart + }; + + this.Data=[]; + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.Research.ApiUrl; + //请求数据 + JSNetwork.HttpRequest({ + url: url, + data: + { + "filed": ["releasedate","researchdate","level","symbol","id",'type'], + "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()}, + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.list.length<=0) return; + + for(var i in recvData.list) + { + var item=recvData.list[i]; + var infoData=new KLineInfoData(); + infoData.ID=item.id; + infoData.Date= item.researchdate; + infoData.InfoType=KLINE_INFO_TYPE.RESEARCH; + infoData.ExtendData={ Level:item.level , Type:item.type}; + this.Data.push(infoData); + + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +//大宗交易 +function BlockTrading() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='BlockTrading'; + + this.RequestData=function(hqChart,obj) + { + var self = this; + var param= + { + HQChart:hqChart + }; + + this.Data=[]; + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.BlockTrading.ApiUrl; + + //请求数据 + JSNetwork.HttpRequest({ + url: url, + data: + { + "field": ["blocktrading.price","blocktrading.vol","blocktrading.premium","fweek","price"], + "condition": + [ + {"item":["date","int32","gte",this.StartDate]}, + {"item":["blocktrading.vol","int32","gte","0"]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.stock.length!=1) return; + if (recvData.stock[0].stockday.length<=0) return; + + for(var i in recvData.stock[0].stockday) + { + var item=recvData.stock[0].stockday[i]; + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.InfoType=KLINE_INFO_TYPE.BLOCKTRADING; + infoData.ExtendData= + { + Price:item.blocktrading.price, //交易价格 + Premium:item.blocktrading.premium, //溢价 (百分比%) + Vol:item.blocktrading.vol, //交易金额单位(万元) + ClosePrice:item.price, //收盘价 + }; + + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + + +//龙虎榜 +function TradeDetail() +{ + this.newMethod=IKLineInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='TradeDetail'; + + this.RequestData=function(hqChart, obj) + { + var self = this; + var param= + { + HQChart:hqChart + }; + + this.Data=[]; + var url=g_JSChartResource.Domain+g_JSChartResource.KLine.Info.TradeDetail.ApiUrl; + + //请求数据 + JSNetwork.HttpRequest({ + url: url, + data: + { + "field": ["tradedetail.typeexplain","tradedetail.type","fweek"], + "condition": + [ + {"item":["date","int32","gte",this.StartDate]}, + {"item":["tradedetail.type","int32","gte","0"]} + ], + "symbol": [param.HQChart.Symbol], + "start":0, + "end":this.MaxReqeustDataCount, + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + if (recvData.stock.length!=1) return; + if (recvData.stock[0].stockday.length<=0) return; + + for(var i in recvData.stock[0].stockday) + { + var item=recvData.stock[0].stockday[i]; + + var infoData=new KLineInfoData(); + infoData.Date= item.date; + infoData.InfoType=KLINE_INFO_TYPE.TRADEDETAIL; + infoData.ExtendData={Detail:new Array()}; + + for(var j in item.tradedetail) + { + var tradeItem=item.tradedetail[j]; + infoData.ExtendData.Detail.push({"Type":tradeItem.type,"TypeExplain":tradeItem.typeexplain}); + } + + if(item.fweek) //未来周涨幅 + { + infoData.ExtendData.FWeek={}; + if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1; + if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4; + } + + this.Data.push(infoData); + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + +function JSMinuteInfoMap() +{ + +} + +JSMinuteInfoMap.InfoMap=new Map( +[ + ["大盘异动", {Create:function(){ return new MarketEventInfo()} }], +]); + +JSMinuteInfoMap.Get=function(id) +{ + return JSMinuteInfoMap.InfoMap.get(id); +} + +function IMinuteInfo() +{ + this.Data; + this.ClassName='IMinuteInfo'; +} + +////////////////////////////////////////////////////////////////////// +// 大盘异动 +// 结构 {Date:日期 Time:时间, Title:标题, Type:0 } +//////////////////////////////////////////////////////////////////// +function MarketEventInfo() +{ + this.newMethod=IMinuteInfo; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='MarketEventInfo'; + + this.RequestData=function(hqChart) + { + var self = this; + this.Data=[]; + var param= + { + HQChart:hqChart + }; + + var url=g_JSChartResource.CacheDomain+'/cache/analyze/shszevent/marketevent/concept/'+hqChart.TradeDate+'.json'; + + if (hqChart.NetworkFilter) + { + var obj= + { + Name:'MarketEventInfo::RequestData', //类名:: + Explain:'大盘异动', + Request:{ Url:url, Type:'Get', Data: { Date:hqChart.TradeDate, Symbol:hqChart.Symbol } }, + Self:this, + PreventDefault:false + }; + hqChart.NetworkFilter(obj, function(data) + { + self.RecvData(data,param); + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvData(recvData,param); + }, + error: function(http,e) + { + console.warn("[MarketEventInfo::RequestData] error, http ",e, http); + } + }); + + return true; + } + + this.RecvData=function(recvData,param) + { + for(var i in recvData.event) + { + var event=recvData.event[i]; + for(var j in event.data) + { + var item=event.data[j]; + if (Array.isArray(item)) + { + if (item.length<2) continue; + var info={Date:event.date, Time:item[0], Title:item[1], Type:0}; + if (item.length>=3 && item[2] && typeof(item[2])=='string') info.Color=item[2]; //[3]=字体颜色 + if (item.length>=4 && item[3] && typeof(item[3])=='string') info.BGColor=item[3]; //[3]=背景颜色 + this.Data.push(info); + } + else //新格式 + { + if (!IFrameSplitOperator.IsNumber(item.Date) || !IFrameSplitOperator.IsNumber(item.Time) || !item.Title) continue; + var info={ Date:item.Date, Time:item.Time, Title:item.Title, Type:0 }; + if (item.Color) info.Color=item.Color; + if (item.BGColor) info.BGColor=item.BGColor; + if (IFrameSplitOperator.IsNumber(item.Price)) info.Price=item.Price; + if (item.Content) info.Content=item.Content; + if (item.Link) info.Link=item.Link; + this.Data.push(info); + } + } + } + + param.HQChart.UpdataChartInfo(); + param.HQChart.Draw(); + } +} + + +//注意!!! 这个函数已经不用了 +//是否是指数代码 +function IsIndexSymbol(symbol) +{ + var upperSymbol=symbol.toUpperCase(); + if (upperSymbol.indexOf('.SH')>0) + { + upperSymbol=upperSymbol.replace('.SH',''); + if (upperSymbol.charAt(0)=='0' && parseInt(upperSymbol)<=3000) return true; + + } + else if (upperSymbol.indexOf('.SZ')>0) + { + upperSymbol=upperSymbol.replace('.SZ',''); + if (upperSymbol.charAt(0)=='3' && upperSymbol.charAt(1)=='9') return true; + } + else if (upperSymbol.indexOf('.CI')>0) //自定义指数 + { + return true; + } + + return false; +} + +//注意!!! 这个函数已经不用了 +//是否是基金代码 +function IsFundSymbol(symbol) +{ + if (!symbol) return false; + + var upperSymbol=symbol.toUpperCase(); + if (upperSymbol.indexOf('.SH')>0) + { + upperSymbol=upperSymbol.replace('.SH',''); //51XXXX.sh + if (upperSymbol.charAt(0)=='5' && upperSymbol.charAt(1)=='1') return true; + } + else if (upperSymbol.indexOf('.SZ')>0) + { + upperSymbol=upperSymbol.replace('.SZ',''); //15XXXX.sz, 16XXXX.sz, 17XXXX.sz, 18XXXX.sz + if (upperSymbol.charAt(0)=='1' && + (upperSymbol.charAt(1)=='5' || upperSymbol.charAt(1)=='6' || upperSymbol.charAt(1)=='7' || upperSymbol.charAt(1)=='8') ) return true; + } + + return false; +} + +//设置窗口基类 +function IDivDialog(divElement) +{ + this.DivElement=divElement; //父节点 + this.ID=null; //div id + this.TimeOut=null; //定时器 + + //隐藏窗口 + this.Hide=function() + { + $("#"+this.ID).hide(); + } + + //显示窗口 + this.Show=function(left,top,width,height) + { + var cssData={display:'block'}; + if (IFrameSplitOperator.IsNumber(left)) cssData.left=left+'px'; + if (IFrameSplitOperator.IsNumber(top)) cssData.top=top+'px'; + if (IFrameSplitOperator.IsNumber(width)) cssData.width=width+'px'; + if (IFrameSplitOperator.IsNumber(height)) cssData.height=height+'px'; + + $("#"+this.ID).css(cssData); + } +} + + +//修改指标 +function ModifyIndexDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.Title={ ID:Guid() }; //标题 + this.ParamList={ID:Guid() }; //参数列表 class='parameter-content' + this.ParamData=[]; //{ ID:参数ID, Value:参数值} + this.Identify; + this.HQChart; + + //创建 + this.Create=function() + { + this.ID=Guid(); + + var div=document.createElement('div'); + div.className='jchart-modifyindex-box'; + div.id=this.ID; + div.innerHTML= + "
\ +
\ + \ + \ +
\ +
MA
\ + \ +
"; + + this.DivElement.appendChild(div); + + //确定按钮 + $("#"+this.ID+" .submit").click( + { + divBox:this, + }, + function(event) + { + event.data.divBox.Hide(); + }); + + //给一个id 后面查找方便 + var titleElement=div.getElementsByTagName('span')[0]; + titleElement.id=this.Title.ID; + + var paramListElement=div.getElementsByClassName('parameter-content')[0]; + paramListElement.id=this.ParamList.ID; + } + + //设置标题 + this.SetTitle=function(title) + { + $("#"+this.Title.ID).html(title); + } + + //清空参数 + this.ClearParamList=function() + { + $("#"+this.ParamList.ID).empty(); + this.ParamData=[]; + } + + this.BindParam=function(chart,identify) + { + var windowIndex=chart.WindowIndex[identify]; + for(var i in windowIndex.Arguments) + { + var item=windowIndex.Arguments[i]; + if (item.Name==null || isNaN(item.Value)) break; + + var guid=Guid(); + var param = ''+ item.Name +'
'; + $("#"+this.ParamList.ID).append(param); + + this.ParamData.push({ID:guid,Value:item.Value}); + } + + //绑定参数修改事件 + var self=this; + for(var i in this.ParamData) + { + var item=this.ParamData[i]; + $("#"+item.ID).mouseup( + { + Chart:chart, + Identify:identify, + ParamIndex:i //参数序号 + }, + function(event) + { + var value = parseInt($(this).val()); //获取当前操作的input属性值,转化为整型 + if (!IFrameSplitOperator.IsNumber(value)) + { + alert("参数不能为空"); + return; + } + var chart=self.HQChart; + var identify=self.Identify; + var paramIndex=event.data.ParamIndex; + + chart.WindowIndex[identify].Arguments[paramIndex].Value = value; //为参数属性重新赋值 + chart.UpdateWindowIndex(identify); //调用更新窗口指标函数,参数用来定位窗口 + } + ) + + $("#"+item.ID).keyup( + { + Chart:chart, + Identify:identify, + ParamIndex:i //参数序号 + }, + function(event) + { + var value = parseInt($(this).val()); //获取当前操作的input属性值,转化为整型 + if (!IFrameSplitOperator.IsNumber(value)) + { + alert("参数不能为空"); + return; + } + var chart=self.HQChart; + var identify=self.Identify; + var paramIndex=event.data.ParamIndex; + + chart.WindowIndex[identify].Arguments[paramIndex].Value = value; //为参数属性重新赋值 + chart.UpdateWindowIndex(identify); //调用更新窗口指标函数,参数用来定位窗口 + } + ) + } + } + + //绑定取消事件 + this.BindCancel=function() + { + //取消按钮事件 + var self=this; + $("#"+this.ID+" .cancel").click( + function() + { + var chart=self.HQChart; + var identify=self.Identify; + self.RestoreParam(chart.WindowIndex[identify]); + chart.UpdateWindowIndex(identify); + self.Hide(); + } + ); + + //关闭和取消是一样的 + $("#"+this.ID+" #close").click( + function() + { + var chart=self.HQChart; + var identify=self.Identify; + + self.RestoreParam(chart.WindowIndex[identify]); + chart.UpdateWindowIndex(identify); + self.Hide(); + } + ); + } + + //还原参数 + this.RestoreParam=function(windowIndex) + { + for(var i in this.ParamData) + { + var item=this.ParamData[i]; + windowIndex.Arguments[i].Value=item.Value; + } + } + + //显示 + this.DoModal=function(event) + { + var chart=event.data.Chart; + var identify=event.data.Identify; + var dialog=chart.ModifyIndexDialog; + + if(!dialog) return; + + if (dialog.ID==null) dialog.Create(); //第1次 需要创建div + dialog.Identify=identify; + dialog.HQChart=chart; + dialog.SetTitle(chart.WindowIndex[identify].Name+" 指标参数设置"); //设置标题 + dialog.ClearParamList(); //清空参数 + dialog.BindParam(chart,identify); //绑定参数 + dialog.BindCancel(); //绑定取消和关闭事件 + + dialog.Show();//显示, 在css里调整居中 + } +} + +//换指标 +function ChangeIndexDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.DivElement=divElement; //父节点 + this.IndexTreeApiUrl="https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json"; //数据下载地址 + this.OverlayIndexTreeApiUrl="https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json"; //叠加指标列表数据下载地址 + this.IsOverlayIndex=false; + + this.Create=function() + { + var div=document.createElement('div'); + div.className='jchart-changeindex-box'; + div.id=this.ID=Guid(); + div.innerHTML= + '
\n' + + '
\n' + + ' 换指标\n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + ' \n' + + '
    \n' + + '
    \n' + + '
    \n' + + '
      \n' + + '
      \n' + + '
      \n' + + '
      '; + + this.DivElement.appendChild(div); + } + + //下载数据 如果上次下载过可以 可以不用下载 + this.ReqeustData=function() + { + if($("#" + this.ID + " .target-left ul li").length>0){ + return false; + } + var url = this.IndexTreeApiUrl; + if (this.IsOverlayIndex==true) url=this.OverlayIndexTreeApiUrl; + JSNetwork.HttpRequest({ + url: url, + type: 'get', + success: function (res) { + var item = res.list; + changeIndexLeftList(item); //处理左侧list列表 + changeIndexRightList(item); //处理右侧内容列表 + } + }); + + //处理左侧list列表 + function changeIndexLeftList(item) { + $(".target-left ul").html(''); + $.each(item,function(i,result){ + var htmlList; + htmlList = '
    • ' + result.node + '
    • '; + $(".target-left ul").append(htmlList); + }); + //默认选中第一项 + $(".target-left ul li:first-child").addClass("active-list"); + } + //处理右侧内容列表 + function changeIndexRightList(listNum) { + var contentHtml; + var conData = []; + $.each(listNum,function(index,result){ + conData.push(result.list); + }) + //页面初始化时显示第一个列表分类下的内容 + $.each(conData[0],function (i, res) { + contentHtml = '
    • '+ res.name +'
    • '; + $(".target-right ul").append(contentHtml); + }) + //切换list + $(".target-left ul").delegate("li","click",function () { + $(this).addClass("active-list").siblings().removeClass("active-list"); + var item = $(this).index(); + $(".target-right ul").html(""); + $.each(conData[item],function (i, res) { + contentHtml = '
    • '+ res.name +'
    • '; + $(".target-right ul").append(contentHtml); + }) + }) + } + } + + this.BindClose=function(chart) + { + //关闭按钮 + $("#"+this.ID+" .close-tar").click( + { + Chart:chart, + }, + function(event) + { + var chart=event.data.Chart; + chart.ChangeIndexDialog.Hide(); + } + ); + } + + //搜索事件 + this.BindSearch=function(chart) + { + $(".target-left input").on('input', + { + Chart:chart + }, + function(event) + { + let scriptData = new JSIndexScript(); + let result=scriptData.Search(event.target.value); + + $(".target-right ul").html(""); + for(var i in result) + { + var name=result[i]; + var contentHtml = '
    • '+ name +'
    • '; + $(".target-right ul").append(contentHtml); + } + + } + ); + } + + this.DoModal=function(event) + { + var chart=event.data.Chart; + var identify=event.data.Identify; + var dialog=chart.ChangeIndexDialog; + var isOverlay=event.data.IsOverlay; //是否叠加 + + if(!dialog) return; + + if (dialog.ID==null) dialog.Create(); //第1次 需要创建div + dialog.IsOverlayIndex=isOverlay; + dialog.ReqeustData(); //下载数据 + + //切换窗口指标类型 每次委托事件执行之前,先用undelegate()解除之前的所有绑定 + changeIndeWindow(); + function changeIndeWindow() + { + $(".target-right ul").undelegate().delegate("li","click",function () { + var idv = $(this).attr("id"); + if (isOverlay) + chart.AddOverlayIndex({WindowIndex:identify,IndexName:idv}); + else + chart.ChangeIndex(identify,idv); + $(this).addClass("active-list").siblings().removeClass("active-list"); + }); + } + + dialog.BindSearch(chart); + //关闭弹窗 + dialog.BindClose(chart); + dialog.Show(); + } +} + +//信息地理tooltip +function KLineInfoTooltip(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.UpColor=g_JSChartResource.UpTextColor; + this.DownColor=g_JSChartResource.DownTextColor; + this.UnchagneColor=g_JSChartResource.UnchagneTextColor; + + this.Create=function() + { + this.ID=Guid(); + + var div=document.createElement('div'); + div.className='jchart-klineinfo-tooltip'; + div.id=this.ID; + div.innerHTML="
      "; + this.DivElement.appendChild(div); + } + + + this.BindInfoList=function(infoType,infoList) + { + var strBox="
      共"+infoList.length+"条
      "; + var strText=[]; + for(var i in infoList) + { + var item=infoList[i]; + var strOld=item.Date; + if(infoType==KLINE_INFO_TYPE.PFORECAST) + { + var reportDate=item.ExtendData.ReportDate; + var year=parseInt(reportDate/10000); //年份 + var day=reportDate%10000; //比较 这个去掉年份的日期 + var reportType; + if(day == 1231){ + reportType = "年报" + }else if(day == 331){ + reportType = "一季度报" + }else if(day == 630){ + reportType = "半年度报" + }else if(day == 930){ + reportType = "三季度报" + } + + var weekData=""; + if (item.ExtendData.FWeek) + { + if (item.ExtendData.FWeek.Week1!=null) weekData+="一周后涨幅:"+ item.ExtendData.FWeek.Week1.toFixed(2)+"%"; + if (item.ExtendData.FWeek.Week4!=null) weekData+=" 四周后涨幅:"+ item.ExtendData.FWeek.Week4.toFixed(2)+"%"; + if (weekData.length>0) weekData="
        "+weekData+""; + } + var strDate=item.Date.toString(); + var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8); //转换时间格式 + strText+=""+strNew+"  "+year+reportType+item.Title+" "+weekData+""; + + } + else if (infoType==KLINE_INFO_TYPE.RESEARCH) //调研单独处理 + { + var levels=item.ExtendData.Level; + var recPerson=[]; + if(levels.length==0){ + recPerson = "" + }else{ + for(var j in levels) + { + if(levels[j]==0){ + recPerson+="证券代表   "; + }else if(levels[j]==1){ + recPerson+="董秘   "; + }else if(levels[j]==2){ + recPerson+="总经理   "; + }else if(levels[j]==3){ + recPerson+="董事长   "; + } + } + } + var strDate=item.Date.toString(); + var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8); //转换时间格式 + strText+=""+strNew+"   接待:   "+recPerson+""; + }else if(infoType==KLINE_INFO_TYPE.BLOCKTRADING) + { + var showPriceInfo = item.ExtendData; + var strDate=item.Date.toString(); + var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8); //转换时间格式 + strText+=""+strNew+"  成交价: "+showPriceInfo.Price.toFixed(2)+"收盘价: "+showPriceInfo.ClosePrice.toFixed(2)+ + "
      溢折价率: "+ + showPriceInfo.Premium.toFixed(2)+"%成交量(万股): "+showPriceInfo.Vol.toFixed(2)+"
      "; + } + else if (infoType==KLINE_INFO_TYPE.TRADEDETAIL) //龙虎榜 + { + /*var detail=[ + "日价格涨幅偏离值达到9.89%", + "日价格涨幅偏离值达格涨幅偏离值达格涨幅偏离值达到9.89%" + ]*/ + var detail=item.ExtendData.Detail; + //格式:日期 上榜原因: detail[0].TypeExplain + // detail[1].TypeExplain + // 一周后涨幅: xx 四周后涨幅: xx + var str=strOld.toString(); + var strNew=str.substring(0,4)+"-"+str.substring(4,6)+"-"+str.substring(6,8); //转换时间格式 + var reasons = []; + for(var i in detail){ + reasons += ""+detail[i].TypeExplain+"
      " + // reasons += detail[i] + "
      " + } + + strText = ""+strNew+"   上榜原因:  "+reasons+"
      一周后涨幅: "+ item.ExtendData.FWeek.Week1.toFixed(2)+ + "%   四周后涨幅: "+ + item.ExtendData.FWeek.Week4.toFixed(2)+"%
      "; + } + else + { + var str=strOld.toString(); + var strNew=str.substring(0,4)+"-"+str.substring(4,6)+"-"+str.substring(6,8); //转换时间格式 + strText+=""+strNew+"   "+item.Title+""; + } + } + var titleInnerBox = $(".title-length").html(strText); + + $("#"+this.ID).html(titleInnerBox); + + //当信息超过8条时,添加“共XX条”统计总数 + if(infoList.length > 8){ + $("#"+this.ID).append(strBox); + } + } + + + this.GetColor=function(price) + { + if(price>0) return this.UpColor; + else if (price<0) return this.DownColor; + else return this.UnchagneColor; + } + + //显示窗口,改函数仅为KLineInfoTooltip使用 + this.Show=function(left,top,width,height,tooltip,times) + { + //显示位置 + $("#"+this.ID).css({'display':'block','top':top+'px', "left":left+'px', "width":width+'px', "height":height+'px' }); + + function toolHide() { + tooltip.Hide(); + } + + if (this.TimeOut!=null) + clearTimeout(this.TimeOut); //清空上一次的定时器,防止定时器不停的被调用 + + //设置窗口定时隐藏 + this.TimeOut=setTimeout(toolHide,times); + + } + + + this.DoModal=function(event) + { + var chart=event.data.Chart; + var infoType=event.data.InfoType; //信息地雷类型 + var infoList=event.data.InfoList; //信息数据列表 + var tooltip=chart.KLineInfoTooltip; + + if(!tooltip) return; + if (tooltip.ID==null) tooltip.Create(); //第1次 需要创建div + + tooltip.BindInfoList(infoType,infoList); + + var left=event.pageX; + var top=event.pageY+10; + var widthTool=380; + var heightTool=$("#"+tooltip.ID).height(); + + if((left + widthTool) > chart.UIElement.getBoundingClientRect().width){ + left = left - widthTool; + } + /*if(top+heightTool>chart.UIElement.getBoundingClientRect().height){ + top=top-heightTool-45; + }*/ + + tooltip.Show(left,top,widthTool,"auto",tooltip,8000); + } + + //鼠标离开 + this.Leave=function(event) + { + var chart=event.data.Chart; + var tooltip=chart.KLineInfoTooltip; + + if(!tooltip || tooltip.ID==null) return; + + tooltip.Hide(); + } +} + +//历史K线上双击 弹出分钟走势图框 +function MinuteDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + + this.JSChart=null; + this.Height=500; + this.Width=600; + this.Symbol; + this.TradeDate; + this.HistoryData; + + //显示窗口 + this.Show=function(left,top,width,height) + { + var div=document.getElementById(this.ID); + if (!div) return false; + + var findDiv=div.getElementsByClassName("minute-hqchart"); + if (!findDiv || findDiv.length!=1) return false; + var klineDiv=findDiv[0]; + + if (IFrameSplitOperator.IsNumber(width)) div.style.width=width+"px"; + if (IFrameSplitOperator.IsNumber(height)) div.style.height=height+"px"; + if (IFrameSplitOperator.IsNumber(left)) div.style.left=left+"px"; + if (IFrameSplitOperator.IsNumber(top)) div.style.top=top+"px"; + + div.style.display='block'; + + var klineWdith=klineDiv.offsetWidth; + var klineTop=klineDiv.offsetTop; + klineDiv.style.width=klineWdith+"px"; + klineDiv.style.height=(height-klineTop-5)+"px"; + } + + this.Create=function() + { + this.ID=Guid(); + var div=document.createElement('div'); + div.className='jchart-kline-minute-box'; + div.id=this.ID; + var hqchartID=Guid(); + div.innerHTML=`
      `; + div.style.width=this.Height+'px'; + div.style.height=this.Width+'px'; + + this.DivElement.appendChild(div); + this.JSChart=JSChart.Init(document.getElementById(hqchartID)); + + var option= + { + Type:'历史分钟走势图', + Symbol:this.Symbol, //股票代码 + IsAutoUpdate:false, //是自动更新数据 + + IsShowRightMenu:false, //右键菜单 + HistoryMinute: { TradeDate:this.TradeDate, IsShowName:false, IsShowDate:false } //显示的交易日期 + }; + + this.JSChart.SetOption(option); + } + + this.BindClose=function(chart) + { + //关闭按钮 + $("#"+this.ID+" .close-munite").click( + { + Chart:chart + }, + function(event) + { + var chart=event.data.Chart; + chart.MinuteDialog.Hide(); + } + ); + } + + this.DoModal=function(event) + { + this.UpColor=g_JSChartResource.UpTextColor; + this.DownColor=g_JSChartResource.DownTextColor; + this.UnchagneColor=g_JSChartResource.UnchagneTextColor; + + var chart=event.data.Chart; + var tooltip=event.data.Tooltip; + var dialog=chart.MinuteDialog; + + dialog.Symbol=chart.Symbol; + dialog.TradeDate=tooltip.Data.Date; + + if(!dialog) return; + if (dialog.ID==null) + { + dialog.Create(); //第1次 需要创建div + } + else + { + dialog.JSChart.JSChartContainer.TradeDate=dialog.TradeDate; + dialog.JSChart.ChangeSymbol(this.Symbol); + } + + var left=event.clientX; + var top=event.clientY+10; + + var pixelTatio=GetDevicePixelRatio(); + dialog.Show(500/pixelTatio,100/pixelTatio,600,500); + dialog.JSChart.OnSize(); + + this.BindClose(chart); + + this.GetColor=function(price,yclse) + { + if(price>yclse) return this.UpColor; + else if (price"+""+strNewDate+" "+ + "开:"+strData.Open.toFixed(2)+""+ + "高:"+strData.High.toFixed(2)+""+ + "低:"+strData.Low.toFixed(2)+""+ + "收:"+strData.Close.toFixed(2)+""+ + "量:"+IFrameSplitOperator.FormatValueString(strData.Vol,2)+""+ + "额:"+IFrameSplitOperator.FormatValueString(strData.Amount,2)+""; + $(".minute-dialog-title span").html(str); + } +} + +function MinuteSelectRectDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.SelectData; + this.Dialog; + this.HQChart; + this.RectSelectPaint; + + //隐藏窗口 + this.Close=function() + { + if (this.Dialog) + { + this.DivElement.removeChild(this.Dialog); + this.Dialog=null; + this.ID=null; + } + + if (this.RectSelectPaint) this.RectSelectPaint.PreventClose=false; + } + + //创建 + this.Create=function() + { + this.ID=Guid(); + var div=document.createElement('div'); + div.className='jchart-select-statistics-box'; + div.id=this.ID; + div.innerHTML= + "
      \ +
      \ + 分时图区间统计\ + \ +
      \ +
      统计数据
      \ + \ +
      "; + + this.DivElement.appendChild(div); + this.Dialog=div; + + //关闭按钮 + $("#"+this.ID+" #close").click( + { + divBox:this, + }, + function(event) + { + event.data.divBox.Close(); + }); + } + + this.BindData=function() + { + var hqData=this.SelectData.Data; + var start=this.SelectData.Start; + var end=this.SelectData.End; + this.HQChart.UpdateSelectRect(start,end); + if (this.RectSelectPaint) this.RectSelectPaint.PreventClose=true; + + var showData= + { + Open:0,Close:0,High:0,Low:0, //起始价格, 结束价格, 最高, 最低 + Vol:0, Amount:0, + Date: + { + Start:{Time:null, Date:null }, + End:{ Time:null, Date:null } + }, + Count:0, + } + + for(var i=start; i<=end && iitem.Low) showData.Low=item.Low; + } + + if (showData.Vol>0) showData.AvPrice=showData.Amount/showData.Vol; //均价 + if (item.Open>0) + { + showData.Increase = (showData.Close - showData.Open) / showData.Open *100; //区间涨幅 + showData.Amplitude = (showData.High - showData.Low) / showData.Open * 100; //区间振幅 + } + + // JSConsole.Chart.Log('[KLineSelectRectDialog::BindData]', showData); + var defaultfloatPrecision=GetfloatPrecision(this.SelectData.Symbol); + var startDate=IFrameSplitOperator.FormatDateString(showData.Date.Start.Date); + var endDate=IFrameSplitOperator.FormatDateString(showData.Date.End.Date); + startDate+=' '+IFrameSplitOperator.FormatTimeString(showData.Date.Start.Time,"HH:MM"); + endDate+=" "+IFrameSplitOperator.FormatTimeString(showData.Date.End.Time,"HH:MM"); + + var startLeftClass="",startRightClass="",endLeftClass="",endRightClass=""; + if(start<=0) startLeftClass = "BtnBackground"; + if(start >= end) { + startRightClass = "BtnBackground"; + endLeftClass = "BtnBackground"; + } + if(end >= hqData.Data.length - 1) endRightClass = "BtnBackground"; + + var div=document.createElement('div'); + div.className='jchart-select-table-right'; + div.innerHTML= + '
      \n' + + ' 开始: '+ startDate +'<>\n' + + ' 结束: '+ endDate +'<>\n' + + ' 总个数: '+ showData.Count +'\n' + + '
      \n' + + ' \n' + + ' ' + + ' ' + + ' \n' + + ' ' + + ' ' + + ' \n' + + ' ' + + ' ' + + ' \n' + + '
      起始价: '+ showData.Open.toFixed(defaultfloatPrecision) +'终止价: '+ showData.Close.toFixed(defaultfloatPrecision) +'均价: '+ (IFrameSplitOperator.IsNumber(showData.AvPrice) ? showData.AvPrice.toFixed(defaultfloatPrecision):"--.--") +'
      最低价: '+ showData.Low.toFixed(defaultfloatPrecision) +'最高价: '+ showData.High.toFixed(defaultfloatPrecision) +'涨跌幅: '+ showData.Increase.toFixed(2) +'%
      振幅: '+ showData.Amplitude.toFixed(2) +'%成交量: '+ IFrameSplitOperator.FormatValueString(showData.Vol,2) +'股金额: '+ IFrameSplitOperator.FormatValueString(showData.Amount,2) +'元
      '; + + $(".parameter-content").html(div); + this.BindEvent(); + } + + this.BindEvent = function () + { + var _self = this; + if(_self.SelectData.Start > 0){ + $(".jchart-select-date .start-date-left").click(function () { + _self.SelectData.Start--; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + if(_self.SelectData.Start < _self.SelectData.End){ + $(".jchart-select-date .start-date-right").click(function () { + _self.SelectData.Start++; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + $(".jchart-select-date .end-date-left").click(function () { + _self.SelectData.End--; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + if(_self.SelectData.End < _self.SelectData.Data.Data.length - 1){ + $(".jchart-select-date .end-date-right").click(function () { + _self.SelectData.End++; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + } + + + //显示 + this.DoModal=function(event) + { + var chart=event.data.Chart; + if (this.ID==null) this.Create(); //第1次 需要创建div + this.SelectData=event.data.SelectData; + this.RectSelectPaint=event.data.RectSelectPaint; + this.HQChart=chart; + this.HQChart.HideSelectRect(); + this.BindData(); + + this.Show(); //通过CSS居中显示 + } + + +} + +//区间统计 +function KLineSelectRectDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.SelectData; + this.Dialog; + this.HQChart; + this.RectSelectPaint; + + //隐藏窗口 + this.Close=function() + { + if (this.Dialog) + { + this.DivElement.removeChild(this.Dialog); + this.Dialog=null; + } + + if (this.RectSelectPaint) this.RectSelectPaint.PreventClose=false; + } + + //创建 + this.Create=function() + { + this.ID=Guid(); + var div=document.createElement('div'); + div.className='jchart-select-statistics-box'; + div.id=this.ID; + div.innerHTML= + "
      \ +
      \ + 区间统计\ + \ +
      \ +
      统计数据
      \ + \ +
      "; + + this.DivElement.appendChild(div); + this.Dialog=div; + + //关闭按钮 + $("#"+this.ID+" #close").click( + { + divBox:this, + }, + function(event) + { + event.data.divBox.Close(); + }); + + //形态匹配 + $("#"+this.ID+" #match").click( + { + divBox:this, + }, + function(event) + { + event.data.divBox.KLineMatch(); + }); + } + + this.BindData=function() + { + var hqData=this.SelectData.Data; + var start=this.SelectData.Start; + var end=this.SelectData.End; + this.HQChart.UpdateSelectRect(start,end); + if (this.RectSelectPaint) this.RectSelectPaint.PreventClose=true; + + var showData= + { + Open:0,Close:0,High:0,Low:0, YClose:0, + Vol:0, Amount:0, + Date: + { + Start:{Time:null, Date:null }, + End:{ Time:null, Date:null } + }, + Count:0, + KLine:{ Up:0,Down:0,Unchanged:0 } //阳线|阴线|平线 + } + + for(var i=start; i<=end && iitem.Low) showData.Low=item.Low; + if (item.Close>item.Open) ++showData.KLine.Up; + else if (item.Close0) showData.AvPrice=showData.Amount/showData.Vol; //均价 + if (item.YClose>0) + { + showData.Increase = (showData.Close - showData.YClose) / showData.YClose *100; //涨幅 + showData.Amplitude = (showData.High - showData.Low) / showData.YClose * 100; //振幅 + } + + // JSConsole.Chart.Log('[KLineSelectRectDialog::BindData]', showData); + var defaultfloatPrecision=GetfloatPrecision(this.SelectData.Symbol); + var startDate=IFrameSplitOperator.FormatDateString(showData.Date.Start.Date); + var endDate=IFrameSplitOperator.FormatDateString(showData.Date.End.Date); + if (ChartData.IsMinutePeriod(this.HQChart.Period)) + { + startDate+=' '+IFrameSplitOperator.FormatTimeString(showData.Date.Start.Time,"HH:MM"); + endDate+=" "+IFrameSplitOperator.FormatTimeString(showData.Date.End.Time,"HH:MM"); + } + else if (ChartData.IsSecondPeriod(this.HQChart.Period) || ChartData.IsTickPeriod(this.HQChart.Period)) + { + startDate+=' '+IFrameSplitOperator.FormatTimeString(showData.Date.Start.Time,"HH:MM:SS"); + endDate+=" "+IFrameSplitOperator.FormatTimeString(showData.Date.End.Time,"HH:MM:SS"); + } + + var startLeftClass="",startRightClass="",endLeftClass="",endRightClass=""; + if(start<=0) startLeftClass = "BtnBackground"; + if(start >= end) { + startRightClass = "BtnBackground"; + endLeftClass = "BtnBackground"; + } + if(end >= hqData.Data.length - 1) endRightClass = "BtnBackground"; + + var div=document.createElement('div'); + div.className='jchart-select-table-right'; + div.innerHTML= + '
      \n' + + ' 开始: '+ startDate +'<>\n' + + ' 结束: '+ endDate +'<>\n' + + ' 总个数: '+ showData.Count +'\n' + + '
      \n' + + ' \n' + + ' ' + + ' ' + + ' \n' + + ' ' + + ' ' + + ' \n' + + ' ' + + ' ' + + ' \n' + + ' ' + + ' ' + + ' \n' + + '
      起始价: '+ showData.YClose.toFixed(defaultfloatPrecision) +'最终价: '+ showData.Close.toFixed(defaultfloatPrecision) +'均价: '+ (IFrameSplitOperator.IsNumber(showData.AvPrice) ? showData.AvPrice.toFixed(defaultfloatPrecision):"--.--") +'
      最低价: '+ showData.Low.toFixed(defaultfloatPrecision) +'最高价: '+ showData.High.toFixed(defaultfloatPrecision) +'涨跌幅: '+ showData.Increase.toFixed(2) +'%
      振幅: '+ showData.Amplitude.toFixed(2) +'%成交量: '+ IFrameSplitOperator.FormatValueString(showData.Vol,2) +'股金额: '+ IFrameSplitOperator.FormatValueString(showData.Amount,2) +'元
      阴线: '+ showData.KLine.Up +'阳线: '+ showData.KLine.Down +'平线: '+ showData.KLine.Unchanged +'
      '; + + $(".parameter-content").html(div); + this.BindEvent(); + } + + this.BindEvent = function () { + var _self = this; + if(_self.SelectData.Start > 0){ + $(".jchart-select-date .start-date-left").click(function () { + _self.SelectData.Start--; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + if(_self.SelectData.Start < _self.SelectData.End){ + $(".jchart-select-date .start-date-right").click(function () { + _self.SelectData.Start++; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + $(".jchart-select-date .end-date-left").click(function () { + _self.SelectData.End--; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + if(_self.SelectData.End < _self.SelectData.Data.Data.length - 1){ + $(".jchart-select-date .end-date-right").click(function () { + _self.SelectData.End++; + _self.BindData(); + _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End); + }) + } + } + + //显示 + this.DoModal=function(event) + { + var chart=event.data.Chart; + if (this.ID==null) this.Create(); //第1次 需要创建div + this.SelectData=event.data.SelectData; + this.RectSelectPaint=event.data.RectSelectPaint; + this.HQChart=chart; + this.HQChart.HideSelectRect(); + this.BindData(); + + this.Show(); //通过CSS居中显示 + } + + //形态匹配 + this.KLineMatch=function(data) + { + var waitDialog=new WaitDialog(this.DivElement); + waitDialog.DoModal( + { + data: + { + Title:'计算中......', + Chart:this.HQChart + } + }); + + this.Close(); //关闭窗口 + + var hqChart=this.HQChart; + var param= { Scope: { Plate:["CNA.ci"],Minsimilar:0.90 }, WaitDialog:waitDialog } //沪深A股, 相似度>=90% + hqChart.RequestKLineMatch(this.SelectData, param); + } +} + +//形态选股 +function KLineMatchDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.MatchData; //匹配的股票数据 + this.Sample; //样本数据 + this.Dialog; + this.HQChart; + + this.PageData; //分页数据 + + //隐藏窗口 + this.Close=function() + { + this.DivElement.removeChild(this.Dialog); + } + + //创建 + this.Create=function() + { + this.ID=Guid(); + var div=document.createElement('div'); + div.className='jchart-kline-match-box'; + div.id=this.ID; + div.innerHTML= + `
      +
      + 形态匹配 + +
      +
      +

      + + + + + + + + + + +
      股票名称匹配度时间段
      + +
      + +
      `.trim(); + + this.DivElement.appendChild(div); + this.Dialog=div; + + //关闭按钮 + $("#"+this.ID+" #close").click( + { + divBox:this, + }, + function(event) + { + event.data.divBox.Close(); + }); + } + + this.BindData=function() + { + JSConsole.Chart.Log(`[KLineMatchDialog::BindData] 形态源: ${this.Sample.Stock.Name} 区间:${this.Sample.Date.Start} - ${this.Sample.Date.End}`); + var count = this.MatchData.length + 1; + var pageData = {NewData:{},MetaData:[],PageCount:0,Count:count}; + var pageCount = 0; + var paginationHtml = ''; + + $('#'+this.ID+' .dataCount').html('个数:'+count); + + for(let i = 0; i < count ; i++){ + var dataObj = {}; + if(i == 0){ + dataObj = { + Symbol:this.Sample.Stock.Symbol, + Name:this.Sample.Stock.Name, + Rate:'形态源', + Color:'red', + Date:`${this.Sample.Date.Start}-${this.Sample.Date.End}` + }; + }else{ + let dataItem = this.MatchData[i - 1]; + dataObj = { + Symbol:dataItem.Symbol, + Name:dataItem.Name, + Rate:Number(dataItem.Similar * 100).toFixed(2), + Color:'', + Date:`${dataItem.Start}-${dataItem.End}` + }; + } + pageData.MetaData.push(dataObj); + } + + if(pageData.Count % 10 == 0){ + pageCount = pageData.Count / 10; + }else{ + pageCount = Math.floor(pageData.Count / 10) + 1; + } + pageData.PageCount = pageCount; + + this.PaginationMetaData(pageData); + this.PageData = pageData; + JSConsole.Chart.Log('[KLineMatchDialog::DoModal pageData]',pageData); + + this.RenderDom(1); + + this.PaginationInit('#'+this.ID,pageData.PageCount,this.paginationCallback); + // $('#' + this.ID + ' .pagination').html(paginationHtml); + + + } + this.RenderDom = function(page){ + let currentPageData = this.PageData.NewData[page]; + JSConsole.Chart.Log('[KLineMatchDialog::RenderDom currentPageData]',currentPageData); + let bodyHtml = ''; + for(let i = 0; i < currentPageData.length; i++){ + bodyHtml += ` + ${currentPageData[i].Name} + ${currentPageData[i].Rate} + ${currentPageData[i].Date} + `.trim(); + } + + $('#'+this.ID + ' .matchTable tbody').html(bodyHtml) + } + var _this = this; + this.paginationCallback = function(page) { + _this.RenderDom(page); + _this.PaginationInit('#'+_this.ID,_this.PageData.PageCount,_this.paginationCallback); //更新UI + } + this.PaginationInit = function(id, maxPageNum, callback) { //初始化分页 + var spanStr = ""; + var currentPageNum = $(id + " .pagination").data("current"); + var lastPageNum = 0; + var showCountPage = 5; //只显示5个数字项 + + if (currentPageNum < showCountPage) { //当前页小于预显示页数 + if (maxPageNum >= showCountPage) { + for (var j = 0; j < showCountPage; j++) { //上 1 2 3 4 5 下 + spanStr += (j + 1) != currentPageNum ? "" + (j + 1) + "" : "" + (j + 1) + ""; + } + } else { + for (var j = 0; j < maxPageNum; j++) { //上 1 2 3 4 5 下 + spanStr += (j + 1) != currentPageNum ? "" + (j + 1) + "" : "" + (j + 1) + ""; + } + } + } else { //大于5时,最终页数是当前页数加1 + lastPageNum = (currentPageNum + 1) > maxPageNum ? currentPageNum : (currentPageNum + 1); + + for (var i = currentPageNum - 3; i <= lastPageNum; i++) { //含最终项之前的五项 + spanStr += i != currentPageNum ? "" + i + "" : "" + i + ""; + } + } + + spanStr = "上一页" + spanStr + "下一页"; + $(id + " .pagination").html(spanStr); + $(id + " .pagination span").bind('click', { "maxpage": maxPageNum, "Callback": callback }, this.PaginationCurrentIndex); + // return spanStr; + } + + this.PaginationCurrentIndex = function(event) { //分页切换 + var text = $(this).text(); + JSConsole.Chart.Log('[::PaginationCurrentIndex text]',text); + var currentPageNum = Number($(this).parent().data("current")); + var maxPageNum = event.data.maxpage; + var callback = event.data.Callback; + var flag = 1; + if (text === "上一页") { + flag = currentPageNum === 1 ? currentPageNum : currentPageNum - 1; + } else if (text === "下一页") { + flag = currentPageNum === maxPageNum ? currentPageNum : currentPageNum + 1; + } else { + flag = Number(text); + } + $(this).parent().data("current", flag); //将当前页存到dom上 + callback(flag); + } + + this.PaginationMetaData = function(data){ //假分页数据,每页10条数据 + // data = {NewData:{},MetaData:[],PageCount:0,Callback:null}; + var newData = {}; + var metaData = data.MetaData; + var pageCount = data.PageCount; + + for(let i = 0; i < pageCount; i++){ + var itemArr = []; + for(let j = 0; j < 10; j++){ + var itemIndex = 10*i + j; + if(itemIndex <= metaData.length - 1){ + var item = metaData[itemIndex]; + itemArr.push(item); + }else { + break; + } + } + newData[i+1] = itemArr; + } + data.NewData = newData; + } + + //显示 + this.DoModal=function(event) + { + var chart=event.data.Chart; + if (this.ID==null) this.Create(); //第1次 需要创建div + this.MatchData=event.data.MatchData; + this.Sample=event.data.Sample; + this.HQChart=chart; + + this.BindData(); + + + //居中显示 + var border=chart.Frame.ChartBorder; + var scrollPos=GetScrollPosition(); + var left=border.GetWidth()/2; + var top=border.GetHeight()/2; + + this.Show(left,top,200,200); //显示 + } +} + +//等待动画窗口 +function WaitDialog(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.Title='加载中......'; + this.Dialog; + + //隐藏窗口 + this.Close=function() + { + if (this.Dialog) + { + this.DivElement.removeChild(this.Dialog); + this.Dialog=null; + } + } + + this.SetTitle=function(title) + { + this.Title=title; + if (!this.Dialog) return; + //TODO: 更新标题数据 + } + + this.Create=function() + { + this.ID=Guid(); + var div=document.createElement('div'); + div.className='jchart-wait-box'; + div.id=this.ID; + div.innerHTML= + `
      +
      + ${this.Title} +
      +
      `.trim(); + + this.DivElement.appendChild(div); + this.Dialog=div; + } + + //显示 + this.DoModal=function(event) + { + this.Title=event.data.Title; + var chart=event.data.Chart; + if (this.ID==null) this.Create(); //第1次 需要创建div + + //居中显示 + var border=chart.Frame.ChartBorder; + var scrollPos=GetScrollPosition(); + var left=border.GetWidth()/2; + var top=border.GetHeight()/2; + + this.Show(left,top,200,40); //显示 + } +} + +//K线右键菜单类 +//id:"kline" +function KLineRightMenu(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.option={}; + + this.Create = function () { + var _self = this; + + this.ID=Guid(); + + _self.BindData(); + _self.BindEvent(); + } + this.BindData=function(){ + var _self = this; + + var id=this.DivElement.id; + var $body = $("#" + id); + + $body.find("div[id^='topMenu_']").remove(); + $body.find("div[id^='childMenu_']").remove(); + + var $topMenu = $("
      "); + $topMenu.attr("id", "topMenu_"+_self.ID).addClass("context-menu-wrapper topmenu").hide(); + $body.append($topMenu); + + var $topTable = $(""); + $topTable.attr({ id: "topTable_" + _self.ID, cellspacing: "0", cellpadding: "0" }).addClass("context-menu"); + $topMenu.append($topTable); + + $topTable.append(_self.childrenList(_self.option.data)); + + for (var i = 0; i < _self.option.data.length; i++) { + var isHasChildren = typeof _self.option.data[i].children != "undefined"; + + if (isHasChildren) { + + var $childMenu = $("
      "); + $childMenu.attr({ id: "childMenu_"+_self.ID + i, "data-index": i }).addClass("context-menu-wrapper").hide(); + $body.append($childMenu); + + var $childTable = $("
      "); + $childTable.attr({ id: "childTable_" + _self.ID + i, cellspacing: "0", cellpadding: "0" }).addClass("context-menu"); + $childMenu.append($childTable); + + $childTable.append(_self.childrenList(_self.option.data[i].children)); + } + } + } + + this.Update=function() + { + var _self = this; + //var id=this.DivElement.id; + //var $body=$("#"+id); + // + //var $topTable = $("#topTable_" + _self.ID); + //$topTable.empty(); + //$topTable.append(_self.childrenList(_self.option.data)); + // + //for (var i = 0; i < _self.option.data.length; i++) { + // var isHasChildren = typeof _self.option.data[i].children != "undefined"; + // + // if (isHasChildren) { + // var $childTable = $("#childTable_" + _self.ID + i); + // $childTable.empty(); + // $childTable.append(_self.childrenList(_self.option.data[i].children)); + // } + //} + + _self.BindData(); + _self.BindEvent(); + } + + this.childrenList = function(list) { + var result = []; + + for (var i = 0; i < list.length; i++) { + var isBorder = typeof list[i].isBorder != "undefined" && list[i].isBorder; + + var $tr = $(""); + $tr.addClass("font_Arial context-menu"); + if (isBorder) + $tr.addClass("border"); + + var $td1 = $("
      "); + if(list[i].selected){ + $td1.addClass("spacer context-menu").html("√"); + }else{ + $td1.addClass("spacer context-menu"); + } + + var $td2 = $(""); + $td2.addClass("text").html(list[i].text); + + var $td3 = $(""); + $td3.addClass("right shortcut"); + + var $td4 = $(""); + $td4.addClass(typeof list[i].children != "undefined" ? "submenu-arrow" : "context-menu spacer"); + + $tr.append($td1).append($td2).append($td3).append($td4); + + result.push($tr); + } + return result; + } + + this.Show=function (obj) { + var _self = this; + $.extend(_self.option, obj); + + if (!_self.ID) _self.Create(); //判断是否重复创建 + else _self.Update(); //更新菜单状态 + + var $topMenu = $("#topMenu_"+_self.ID), + topWidth = $topMenu.outerWidth(), + topHeight = $topMenu.outerHeight(); + + var x = _self.option.x, + y = _self.option.y; + + if (topWidth > _self.option.position.X + _self.option.position.W- x) //超过了右边距 + x = x - topWidth; + + if (topHeight > _self.option.position.Y +_self.option.position.H - y)//超过了下边距 + y = y - topHeight; + + $topMenu.hide(); + $topMenu.css({ position:"absolute",left: x + "px", top: y + "px" }).show(); + + this.isInit = true; + } + + this.Hide=function () { + var _self = this; + if (typeof($)=="undefined") return; + $("#topMenu_" + _self.ID).hide(); + $("[id^='childMenu_" + _self.ID + "']").hide(); + } + + this.BindEvent=function () { + var _self = this; + var $childMenu = $("[id^='childMenu_" + _self.ID + "']"); + + $("#topTable_" + _self.ID).find("tr").mouseenter(function () { + var $this = $(this), + index = $this.index(), + $topMenu = $("#topMenu_" + _self.ID), + $child = $("#childMenu_" + _self.ID + index), + trWidth = $this.outerWidth(), + trHeight = $this.outerHeight(); + + var left = $topMenu.position().left + trWidth + 1; + var top = $topMenu.position().top + (trHeight * index); + + if (trWidth > _self.option.position.X + _self.option.position.W - left) //超过了右边距 + left = left - trWidth - $topMenu.outerWidth() - 2; + + if ($child.outerHeight() > _self.option.position.Y +_self.option.position.H - top)//超过了下边距 + top = $topMenu.position().top + $topMenu.outerHeight() - $child.outerHeight(); + + $childMenu.hide(); + $child.css({ left: left + "px", top: top + "px" }).show(); + }).mouseleave(function () { + var index = $(this).index(); + setTimeout(function () { + if ($("#childMenu_" + _self.ID + index).attr("data-isShow") != 1) { + $("#childMenu_" + _self.ID + index).hide(); + } + }, 10) + + }).click(function () { + var $this = $(this); + var index = $this.index(); + + if ($.type(_self.option.data[index].click) == "function") { + _self.option.data[index].click(_self.option.returnData); + $this.hide(); + } + }).contextmenu(function() + { + return false; //屏蔽系统右键菜单 + }); + + + $childMenu.mouseenter(function () { + $(this).attr("data-isShow", "1"); + }).mouseleave(function () { + $(this).attr("data-isShow", "0"); + }).contextmenu(function() + { + return false; //屏蔽系统右键菜单 + }); + + $childMenu.find("tr").click(function () { + var $this = $(this); + var divIndex = parseInt($this.closest("div").attr("data-index")); + var trIndex = $this.index(); + + if ($.type(_self.option.data[divIndex].children[trIndex].click) == "function") { + _self.option.data[divIndex].children[trIndex].click(_self.option.windowIndex || 1); + $childMenu.hide(); + } + }); + } + + this.GetPeriod=function (chart) + { + var data= + [ + { + text: "日线", Value:0, + click: function () { chart.ChangePeriod(0); } + }, + { + text: "周线",Value:1, + click: function () { chart.ChangePeriod(1); } + }, + { + text: "双周线",Value:21, + click: function () { chart.ChangePeriod(21); } + }, + { + text: "月线",Value:2, + click: function () { chart.ChangePeriod(2); } + }, + { + text: "季线",Value:9, + click: function () { chart.ChangePeriod(9); } + }, + { + text: "半年",Value:22, + click: function () { chart.ChangePeriod(22); } + }, + { + text: "年线",Value:3, + click: function () { chart.ChangePeriod(3); } + }, + { + text: "1分",Value:4, + click: function () { chart.ChangePeriod(4); } + }, + { + text: "5分",Value:5, + click: function () { chart.ChangePeriod(5); } + }, + { + text: "15分",Value:6, + click: function () { chart.ChangePeriod(6); } + }, + { + text: "30分",Value:7, + click: function () { chart.ChangePeriod(7); } + }, + { + text: "60分",Value:8, + click: function () { chart.ChangePeriod(8); } + }, + { + text: "2小时",Value:11, + click: function () { chart.ChangePeriod(11); } + }, + { + text: "4小时",Value:12, + click: function () { chart.ChangePeriod(12); } + }, + { + text: "分笔",Value:10, + click: function () { chart.ChangePeriod(10); } + }, + { + text: "自定义周期:3分钟",Value:20003, + click: function () { chart.ChangePeriod(20003); } + }, + { + text: "自定义周期:35分钟",Value:20035, + click: function () { chart.ChangePeriod(20035); } + }, + { + text: "自定义周期:8日",Value:40008, + click: function () { chart.ChangePeriod(40008); } + } + ]; + + for(var i in data) + { + var item=data[i]; + if (item.Value==chart.Period) + { + item.selected=true; + break; + } + } + + return data; + } + + this.GetRight=function(chart) + { + var data= + [ + { + text: "不复权", + click: function () { chart.ChangeRight(0); } + }, + { + text: "前复权", + click: function () { chart.ChangeRight(1); } + }, + { + text: "后复权", + click: function () { chart.ChangeRight(2); } + } + ]; + + if (chart.Right>=0 && chart.Right0) + { + data[data.length-1].isBorder=true; + data.push( + { + text: "取消叠加", + click: function () { chart.ClearOverlaySymbol();} + } + ); + } + + return data; + } + + //K线类型设置 + this.GetKLineType=function(chart) + { + var data= + [ + { + text: "K线(空心阳线)", + click: function () { chart.ChangeKLineDrawType(3);} + }, + { + text: "K线(实心阳线)", + click: function () { chart.ChangeKLineDrawType(0); } + }, + { + text: "美国线", + click: function () { chart.ChangeKLineDrawType(2); } + }, + { + text: "收盘线", + click: function () { chart.ChangeKLineDrawType(1); } + }, + { + text: "收盘面积", + click: function () { chart.ChangeKLineDrawType(4); } + } + ]; + + switch(chart.KLineDrawType) + { + case 0: + data[1].selected=true; + break; + case 1: + data[3].selected=true; + break; + case 2: + data[2].selected=true; + break; + case 3: + data[0].selected=true; + break; + case 4: + data[4].selected=true; + break; + } + return data; + } + + //指标窗口个数 + this.GetIndexWindowCount=function(chart) + { + var data= + [ + { + text: "1个窗口", + click: function () { chart.ChangeIndexWindowCount(1); } + }, + { + text: "2个窗口", + click: function () { chart.ChangeIndexWindowCount(2); } + }, + { + text: "3个窗口", + click: function () { chart.ChangeIndexWindowCount(3); } + }, + { + text: "4个窗口", + click: function () { chart.ChangeIndexWindowCount(4); } + }, + { + text: "5个窗口", + click: function () { chart.ChangeIndexWindowCount(5); } + } + ]; + + var count=chart.Frame.SubFrame.length; + if ((count-1)>=0 && (count-1)0) + { + if (chart.Frame.SubFrame[0].Frame.CoordinateType==1) + { + data[2].selected=true; + data[2].click=function() { chart.ChangeCoordinateType( { IsReverse:false } ); } //取消反转 + } + + if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==1) data[1].selected=true; //百分比 + else if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==0) data[0].selected=true; //普通坐标 + else if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==2) data[3].selected=true; //对数 + else if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==3) data[4].selected=true; //等比坐标 + else if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==4) data[5].selected=true; //等分坐标 + else if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==5) data[6].selected=true; //黄金分割 + } + + return data; + } + + //拖拽模式 + this.GetDragModeType=function(chart) + { + var data= + [ + { + text: "禁止拖拽", + click: function () { chart.DragMode=0; } + }, + { + text: "启动拖拽", + click: function () { chart.DragMode=1; } + }, + { + text: "区间选择", + click: function () { chart.DragMode=2; } + } + ]; + + if (chart.DragMode>=0 && chart.DragMode0) + { + data[data.length-1].isBorder=true; + var item={ text:'删除所有', click:function() { chart.ClearKLineInfo()} }; + data.push(item); + + } + + return data; + } + + this.CreateKlineInfoItem=function(infoName,bExist,chart) + { + var item= + { + text:infoName, + selected:bExist + } + + if (bExist) item.click=function() { chart.DeleteKLineInfo(infoName) }; + else item.click=function() { chart.AddKLineInfo(infoName,true) } + + return item; + } + + this.DoModal=function(event) + { + var chart=event.data.Chart; + var rightMenu=chart.RightMenu; + var x = event.offsetX; + var y = event.offsetY; + + var dataList=[{ + text: "分析周期", + children: this.GetPeriod(chart) + }, + { + text: "复权处理", + children: this.GetRight(chart) + }, + { + text: "指标切换", + children: this.GetIndex(chart) + }, + { + text:"五彩K线", + children: this.GetColorIndex(chart) + }, + { + text:'专家系统', + children: this.GetTradeIndex(chart) + }, + { + text:'信息地雷', + children: this.GetKLineInfo(chart) + }, + { + text: "叠加品种", + children: this.GetOverlay(chart) + }, + { + text:'主图线型', + children: this.GetKLineType(chart) + }, + { + text:"坐标类型", + children:this.GetCoordinateType(chart) + }, + { + text:'指标窗口个数', + children: this.GetIndexWindowCount(chart) + }, + { + text:'鼠标拖拽', + children: this.GetDragModeType(chart) + }, + { + text:"工具", + children:this.GetTools(chart) + } + ]; + + var upperSymbol=chart.Symbol.toUpperCase(); + if(MARKET_SUFFIX_NAME.IsSHSZIndex(chart.Symbol) || MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) + { + dataList.splice(1,1); + } + + var identify=event.data.FrameID; + var overlayIndex=this.GetOverlayIndex(chart,identify); + if (overlayIndex && overlayIndex.length>0) + { + var delOverlayIndexMenu={ text:'删除叠加指标', children:this.GetDeleteOverlayIndex(chart,overlayIndex) } + dataList.splice(3,0,delOverlayIndexMenu); + } + + JSConsole.Chart.Log('[KLineRightMenu::DoModal]',identify); + rightMenu.Show({ + windowIndex :identify, + x:x+chart.UIElement.offsetLeft, + y:y+chart.UIElement.offsetTop, + position:chart.Frame.Position, + data:dataList + }) + + $(document).click(function () { + rightMenu.Hide(); + }); + } + + this.GetOverlayIndex=function(chart, windowsIndex) + { + if (windowsIndex>=chart.Frame.SubFrame.length || windowsIndex<0) return []; + + var result=[]; + var item=chart.Frame.SubFrame[windowsIndex]; + for(var i in item.OverlayIndex) + { + var overlayItem=item.OverlayIndex[i]; + result.push({Name:overlayItem.Script.Name, Identify:overlayItem.Identify}); + } + + return result; + } + + this.GetDeleteOverlayIndex=function(chart,overlayIndex) + { + var data=[]; + for(var i in overlayIndex) + { + let identify=overlayIndex[i].Identify; + data.push({text:overlayIndex[i].Name, click:function() + { + chart.DeleteOverlayWindowsIndex(identify) + }}); + } + + return data; + } +} + +//K线区间选择右键菜单 +function KLineSelectRightMenu(divElement) +{ + this.newMethod=KLineRightMenu; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.DoModal=function(event) + { + var chart=event.data.Chart; + var rightMenu=this; + var x = event.data.X; + var y = event.data.Y; + + var dataList= + [ + { + text: "区间统计", + click: function () + { + JSConsole.Chart.Log('[KLineSelectRightMenu::click] 区间统计'); + rightMenu.Hide(); + var dialog=new KLineSelectRectDialog(divElement); + dialog.DoModal(event); + } + }, + { + text:'区间放大', + click:function() + { + JSConsole.Chart.Log('[KLineSelectRightMenu::click] 区间放大'); + var chart=event.data.Chart; + chart.ShowSelectData(event.data.SelectData); + } + } + ]; + + rightMenu.Show({ + x:x, + y:y, + position:chart.Frame.Position, + data:dataList + }); + } + + this.Show=function (obj) + { + var _self = this; + $.extend(_self.option, obj); + + //判断是否重复创建 + if (!_self.ID) _self.Create(); + //判断下如果DOM没了需要重新创建 + var divIdName='topMenu_'+_self.ID; + var divDom=document.getElementById(divIdName); + if (!divDom) _self.Create(); + + var $topMenu = $("#topMenu_"+_self.ID), + topWidth = $topMenu.outerWidth(), + topHeight = $topMenu.outerHeight(); + + $topMenu.contextmenu(function() + { + return false; //屏蔽系统右键菜单 + }); + + var x = _self.option.x, + y = _self.option.y; + + if (topWidth > _self.option.position.X + _self.option.position.W- x) //超过了右边距 + x = x - topWidth; + + if (topHeight > _self.option.position.Y +_self.option.position.H - y)//超过了下边距 + y = y - topHeight; + + $topMenu.hide(); + $topMenu.css({ position:"absolute",left: x + "px", top: y + "px" }).show(); + + $("#topMenu_" + _self.ID).find("tr").show(); //把菜单列表显示 + + this.isInit = true; + } +} + +//分钟数据右键菜单 +function MinuteRightMenu(divElement) +{ + this.newMethod=KLineRightMenu; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.DoModal=function(event) + { + var chart=event.data.Chart; + var rightMenu=chart.RightMenu; + var x = event.offsetX; + var y = event.offsetY; + + var dataList= + [ + { + text: "叠加品种", + children: this.GetOverlay(chart) + }, + { + text: "多日分时图", + children: this.GetDayCount(chart) + }, + { + text:'指标窗口个数', + children: this.GetIndexWindowCount(chart) + }, + { + text: "副图指标切换", + children: this.GetIndex(chart) + }, + { + text:"区间选择", + children:this.GetSelectRect(chart) + }, + + ]; + + var symbol=chart.Symbol; + if (MARKET_SUFFIX_NAME.IsSHSZStockA(symbol)) + { + dataList.push({text:'集合竞价',children: this.GetShowBeforeData(chart)}); + } + + dataList.push({text:"工具", children:this.GetTools(chart)}); + + var identify=event.data.FrameID; + JSConsole.Chart.Log('[MinuteRightMenu::DoModal]',identify); + rightMenu.Show({ + windowIndex :identify, + x:x+chart.UIElement.offsetLeft, + y:y+chart.UIElement.offsetTop, + position:chart.Frame.Position, + data:dataList + }) + + $(document).click(function () { + rightMenu.Hide(); + }); + } + + this.GetDayCount=function(chart) + { + var data= + [ + { + text: "当日分时图", + click: function () { chart.ChangeDayCount(1); }, + isBorder:true + }, + { + text: "最近2日", + click: function () { chart.ChangeDayCount(2); } + }, + { + text: "最近3日", + click: function () { chart.ChangeDayCount(3); } + }, + { + text: "最近4日", + click: function () { chart.ChangeDayCount(4); } + }, + { + text: "最近5日", + click: function () { chart.ChangeDayCount(5); } + }, + { + text: "最近6日", + click: function () { chart.ChangeDayCount(6); } + } + ]; + + if ((chart.DayCount-1)>=0 && (chart.DayCount-1)=0 && (count-1)'+ + ''+ + '\n' + + ''+ + ''; + }else{ + toolsDiv = + '

      ' + + '

      \n' + + '

      '; + } + + + this.SubToolsDiv.style.right = right/pixelTatio + "px"; + this.SubToolsDiv.style.top = top/pixelTatio + "px"; + this.SubToolsDiv.innerHTML = toolsDiv; + this.SubToolsDiv.style.position = "absolute"; + this.SubToolsDiv.style.display = "block"; + + var hqChart = this.HQChart; + var picture = this.ChartPicture; + var subToolDiv = this.SubToolsDiv; + $(".subtool-del").click(function(){ + hqChart.ClearChartDrawPicture(picture); + // subToolDiv.innerHTML = ""; + $(".subTolls").css("display","none"); + }); + var self = this; + $(".subtool-set").click(function(){ + $(self.SubToolsDiv).hide(); + //创建div设置窗口 + if (!this.SettingMenu) self.SettingMenu=new ChartPictureTextSettingMenu(frame.ChartBorder.UIElement.parentNode); + + self.SettingMenu.ChartPicture=picture; + self.SettingMenu.HQChart=hqChart; + self.SettingMenu.Position={Left:right + 80,Top:top + 20}; + self.SettingMenu.DoModal(); + }); + $(".changes-color").click(function () { + document.getElementById('color').click(); + $(".change-color").change(function () { + var color = $(".change-color").val(); + picture.LineColor = color; + picture.PointColor = color; + if (hqChart.ChartDrawStorage) hqChart.ChartDrawStorage.SaveDrawData(picture); //保存下 + }); + }) + + + JSConsole.Chart.Log("[ChartPictureSettingMenu::DoModal]", {Top:top,Left:left, Right:right}); + } +} + +//画图工具 文本设置窗口 +function ChartPictureTextSettingMenu(divElement) +{ + this.newMethod=IDivDialog; //派生 + this.newMethod(divElement); + delete this.newMethod; + + this.ChartPicture; + this.SettingDiv; + this.Position; + + this.BackupData; //画图工具备份数据 + + this.Close=function() + { + if (this.SettingDiv) this.DivElement.removeChild(this.SettingDiv); //直接删除 + } + + this.DoModal=function() + { + var text=this.ChartPicture.Text; //显示的文本 + var fontOption=this.ChartPicture.FontOption; //字体设置 + var lineColor=this.ChartPicture.LineColor; + //数据备份, 点取消的时候把备份数据设置回去 + this.BackupData= + { + Text:text, + LineColor:lineColor, + FontOption:{Family: fontOption.Family, Size: fontOption.Size, Weight: fontOption.Weight, Style: fontOption.Style } + }; + JSConsole.Chart.Log('[ChartPictureTextSettingMenu::DoModal] picture info',this.BackupData); + + var self=this; + var div=this.DivElement.getElementsByClassName('chartpicture-text-setting')[0]; + if (!div) + { + div=document.createElement("div"); + div.className='chartpicture-text-setting'; + this.DivElement.appendChild(div); + this.SettingDiv=div; + } + else + { + this.SettingDiv=div; + } + + var titleContainerStr = '
      '+ + '样式设置'+ + ''+ + '
      '; + + var fontSizeArray = [10,11,12,14,16,20,24,28,32,40]; + var fontArray = ['微软雅黑','宋体','Arial','仿宋']; + var sizeListStr = ""; + var fontListStr = ""; + fontArray.forEach(function(item,index){ + fontListStr += index !== 0 ? '

      '+item+'

      ' : '

      '+item+'

      '; + }); + fontSizeArray.forEach(function(item,index){ + sizeListStr += index !== 5 ? '

      '+item+'

      ' : '

      '+item+'

      '; + }); + var contentContainerStr = '
      '+ + '
      '+ + ''+ + '
      微软雅黑
      '+fontListStr+'
      '+ + '
      20
      '+sizeListStr+'
      '+ + ''+ + ''+ + '
      '+ + ''+ + '
      '; + var btnContainer = '
      '+ + '确认'+ + '取消'+ + '
      '; + + var pixelTatio = GetDevicePixelRatio(); + var DoModalStr = titleContainerStr+contentContainerStr+btnContainer; + this.SettingDiv.style.left = this.Position.Left/pixelTatio + "px"; + this.SettingDiv.style.top = this.Position.Top/pixelTatio + "px"; + this.SettingDiv.innerHTML=DoModalStr; + this.SettingDiv.style.position = "absolute"; + this.SettingDiv.style.display = "block"; + $(".chartpicture-text-setting .colorPicker").css({ //初始设置 + "borderColor":self.ChartPicture.LineColor, + "background-color":self.ChartPicture.LineColor + }); + + var family = this.ChartPicture.FontOption.Family; + $('.chartpicture-text-setting .fontSelect .choicedText').html(family); + fontArray.forEach(function(item,index){ + if(item == family){ + $('.chartpicture-text-setting .fontSelect p').removeClass('active'); + $('.chartpicture-text-setting .fontSelect p').eq(index).addClass('active'); + } + }); + + var size = this.ChartPicture.FontOption.Size; + $('.chartpicture-text-setting .fontSizeSelect .choicedText').html(size); + fontSizeArray.forEach(function(item,index){ + if(item == size){ + $('.chartpicture-text-setting .fontSizeSelect p').removeClass('active'); + $('.chartpicture-text-setting .fontSizeSelect p').eq(index).addClass('active'); + } + }); + + var weight = this.ChartPicture.FontOption.Weight; + if( weight != null && weight == 'bold'){ + $('.chartpicture-text-setting .strongFont').addClass('hot'); + } + + var style = this.ChartPicture.FontOption.Style; + if( style != null && style == 'italic'){ + $('.chartpicture-text-setting .italicsFont').addClass('hot'); + } + + var text = this.ChartPicture.Text; + $('.chartpicture-text-setting .tArea').val(text); //结束初始设置 + + var defaultTextOption = { Family:'微软雅黑', Size:20, Weight:null, Style:null }; + $(".chartpicture-text-setting #fontColor").change( + { + Picture:this.ChartPicture + }, + function(event) + { //颜色选择 + var value = $(this).val(); + $(this).parent().css({ + "borderColor":value, + "background-color":value + }); + var chart=event.data.Picture; + chart.LineColor = value; + if (chart.Update) chart.Update(); //更新界面 + } + ); + $(".chartpicture-text-setting .fontSelect,.chartpicture-text-setting .fontSizeSelect").click(function(){ + $(this).find('.selectList').toggle(); + $(this).toggleClass('hot'); + }); + $(".chartpicture-text-setting .fontSelect p").click( + { + Picture:this.ChartPicture + }, + function(event){ //字体选择 + var choicedText = $(this).closest(".fontSelect").find('.choicedText').html(); + var currentSelect = event.currentTarget.innerHTML; + if(choicedText !== currentSelect){ + $(this).closest(".fontSelect").find('.choicedText').html(currentSelect); + $(this).siblings().removeClass('active'); + $(this).addClass('active'); + var chart = event.data.Picture; + chart.FontOption.Family = currentSelect; + if (chart.Update) chart.Update(); //更新界面 + } + }); + $(".chartpicture-text-setting .fontSizeSelect p").click( + { + Picture:this.ChartPicture + }, + function(event){ //字号选择 + var choicedText = $(this).closest(".fontSizeSelect").find('.choicedText').html(); + var currentSelect = event.currentTarget.innerHTML; + if(choicedText !== currentSelect){ + $(this).closest(".fontSizeSelect").find('.choicedText').html(currentSelect); + $(this).siblings().removeClass('active'); + $(this).addClass('active'); + var chart = event.data.Picture; + chart.FontOption.Size = Number(currentSelect); + if (chart.Update) chart.Update(); //更新界面 + } + }); + $(".chartpicture-text-setting .strongFont").click( + { + Picture:this.ChartPicture + }, + function(event){ + $(this).toggleClass('hot'); + var classnames = $(this).attr('class'); + if(classnames.indexOf('hot') > 0){ + var chart = event.data.Picture; + chart.FontOption.Weight = 'bold'; + if (chart.Update) chart.Update(); //更新界面 + } + }); + $(".chartpicture-text-setting .italicsFont").click( + { + Picture:this.ChartPicture + }, + function(event){ + $(this).toggleClass('hot') + var classnames = $(this).attr('class'); + if(classnames.indexOf('hot') > 0){ + var chart = event.data.Picture; + chart.FontOption.Style = 'italic'; + if (chart.Update) chart.Update(); //更新界面 + } + }); + $(".chartpicture-text-setting .titleWrap .closeBtn,.chartpicture-text-setting .btnsContainer .cancelBtn").click( //取消 + { + Picture:this.ChartPicture + }, + function(event){ + var picture = event.data.Picture; + picture.Text = self.BackupData.Text; + picture.LineColor = self.BackupData.LineColor; + picture.FontOption = self.BackupData.FontOption; + if (picture.Update) picture.Update(); + self.Close(); + }); + $(".chartpicture-text-setting .tArea").keyup( //文本内容 + { + Picture:this.ChartPicture + }, + function(event){ + JSConsole.Chart.Log('[ChartPictureTextSettingMenu::DoModal] $(".chartpicture-text-setting .tArea").keyup()'); + var content = $(this).val(); + var chart = event.data.Picture; + chart.Text = content; + if (chart.Update) chart.Update(); //更新界面 + }); + + //确定按钮 + $(".chartpicture-text-setting .btnsContainer .okBtn").click( + function() + { + self.Close(); + if (self.HQChart && self.HQChart.ChartDrawStorage) self.HQChart.ChartDrawStorage.SaveDrawData(self.ChartPicture); //保存下 + } + ); + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// 各个品种分钟走势图坐标信息 +// +////////////////////////////////////////////////////////////////////////////////////// +var MARKET_SUFFIX_NAME= +{ + SH:'.SH', + SZ:'.SZ', + SHSZ_C_Index:'.CI', //自定义指数 + + SHO:'.SHO', //上海交易所 股票期权 + HK:'.HK', //港股 + FHK:'.FHK', //港股期货 + SHFE: '.SHF', //上期所 (Shanghai Futures Exchange) + SHFE2:'.SHFE', //上期所 (Shanghai Futures Exchange) + CFFEX: '.CFE', //中期所 (China Financial Futures Exchange) + DCE: '.DCE', //大连商品交易所(Dalian Commodity Exchange) + CZCE: '.CZC', //郑州期货交易所 + USA:'.USA', //美股 + FTSE:'.FTSE', //富时中国 + + BIT:'.BIT', //数字货币 如比特币 + BIZ:'.BIZ', //数字货币 + + FOREX:'.FOREX', //外汇 (Foreign Exchange) + + NYMEX:'.NYMEX', //纽约商品期货交易所(New York Mercantile Exchange) + COMEX:".COMEX", //纽约商品期货交易所(New York Mercantile Exchange) + NYBOT:".NYBOT", //美國紐約商品交易所 + CBOT:".CBOT", //芝商所 + + LME:".LME", //伦敦金属交易所 + + ET:'.ET', //其他未知的品种 + + IsET:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.ET) > 0; + }, + + IsETShowAvPrice:function(upperSymbol) //是否显示均价 + { + return false; + }, + + IsNYMEX:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.NYMEX)>0; + }, + + IsCOMEX:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.COMEX)>0; + }, + + IsNYBOT:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.NYBOT)>0; + }, + + IsCBOT:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.CBOT)>0; + }, + + IsLME:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.LME)>0; + }, + + IsForeignExchange(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.FOREX) > 0; + }, + + IsFTSE:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.FTSE) > 0; + }, + + IsFHK:function(upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.FHK) > 0; + }, + + IsBIT:function(upperSymbol) + { + if (!upperSymbol) return false; + if (upperSymbol.indexOf(this.BIT) > 0) return true; + if (upperSymbol.indexOf(this.BIZ) > 0) return true; + return false; + }, + + IsUSA:function(upperSymbol) //是否是美股 + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.USA) > 0; + }, + + IsSH: function (upperSymbol) + { + //需要精确匹配最后3位 + var pos = upperSymbol.length-this.SH.length; + var find = upperSymbol.indexOf(this.SH); + return find == pos; + }, + + IsSZ: function (upperSymbol) + { + var pos = upperSymbol.length - this.SZ.length; + var find = upperSymbol.indexOf(this.SZ); + return find == pos; + }, + + //自定义指数 + IsSHSZCustomIndex:function(upperSymbol) + { + var pos = upperSymbol.length - this.SHSZ_C_Index.length; + var find = upperSymbol.indexOf(this.SHSZ_C_Index); + return find == pos; + }, + + IsSHO: function(upperSymbol) + { + var pos = upperSymbol.length - this.SHO.length; + var find = upperSymbol.indexOf(this.SHO); + return find == pos; + }, + + IsHK: function (upperSymbol) + { + var pos = upperSymbol.length - this.HK.length; + var find = upperSymbol.indexOf(this.HK); + return find == pos; + }, + + IsSHFE: function (upperSymbol) + { + if (!upperSymbol) return false; + if (upperSymbol.indexOf(this.SHFE) > 0) return true; + if (upperSymbol.indexOf(this.SHFE2) > 0) return true; + return false; + }, + + IsCFFEX: function (upperSymbol) + { + if (!upperSymbol) return false; + if (upperSymbol.indexOf(this.CFFEX) > 0) return true; + + return false; + }, + + IsDCE: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.DCE) > 0; + }, + + IsCZCE: function (upperSymbol) + { + if (!upperSymbol) return false; + return upperSymbol.indexOf(this.CZCE) > 0; + }, + + IsChinaFutures:function(upperSymbol) //是否是国内期货 /期权 + { + return this.IsSHO(upperSymbol) || this.IsCFFEX(upperSymbol) || this.IsCZCE(upperSymbol) || this.IsDCE(upperSymbol) || this.IsSHFE(upperSymbol); + }, + + IsFutures:function(upperSymbol) //是否是期货 包含国外的 + { + return this.IsChinaFutures(upperSymbol) || + this.IsNYMEX(upperSymbol) || this.IsCOMEX(upperSymbol) || this.IsNYBOT(upperSymbol) || this.IsCBOT(upperSymbol) || + this.IsLME(upperSymbol); + }, + + IsSHSZ:function(upperSymbol) //是否是沪深的股票 + { + return this.IsSZ(upperSymbol)|| this.IsSH(upperSymbol) || this.IsSHSZCustomIndex(upperSymbol); + }, + + IsSHSZFund:function(upperSymbol) //是否是交易所基金 + { + if (!upperSymbol) return false; + + if (this.IsSH(upperSymbol)) //51XXXX.SH + { + if (upperSymbol.charAt(0)=='5' && upperSymbol.charAt(1)=='1') return true; + } + else if (this.IsSZ(upperSymbol)) //15XXXX.sz, 16XXXX.sz, 17XXXX.sz, 18XXXX.sz + { + if (upperSymbol.charAt(0)=='1' && + (upperSymbol.charAt(1)=='5' || upperSymbol.charAt(1)=='6' || upperSymbol.charAt(1)=='7' || upperSymbol.charAt(1)=='8') ) return true; + } + + return false; + }, + + IsSHSZIndex:function(symbol) //是否是沪深指数代码 + { + if (!symbol) return false; + var upperSymbol=symbol.toUpperCase(); + if (this.IsSH(upperSymbol)) + { + var temp=upperSymbol.replace('.SH',''); + if (upperSymbol.charAt(0)=='0' && parseInt(temp)<=3000) return true; + + } + else if (this.IsSZ(upperSymbol)) + { + if (upperSymbol.charAt(0)=='3' && upperSymbol.charAt(1)=='9') return true; + } + else if (this.IsSHSZCustomIndex(upperSymbol)) //自定义指数 + { + return true; + } + + return false; + }, + + IsSHSZStockA:function(symbol) //是否是沪深A股 + { + if (!symbol) return false; + var upperSymbol=symbol.toUpperCase(); + if (this.IsSH(upperSymbol)) + { + var temp=upperSymbol.replace('.SH',''); + if (upperSymbol.charAt(0)=='6') return true; + + } + else if (this.IsSZ(upperSymbol)) + { + if (upperSymbol.charAt(0)=='0') + { + if (upperSymbol.charAt(1)=='0' && upperSymbol.charAt(2)=='2') return true; //002 中小板 + if (upperSymbol.charAt(1)!='7' && upperSymbol.charAt(1)!='8') return true; + } + else if (upperSymbol.charAt(0)=='3') + { + if (upperSymbol.charAt(1)=='0' && upperSymbol.charAt(2)=='0') return true; //创业板 300XXX.sz + } + } + + return false; + }, + + IsSHStockSTAR:function(symbol) // 是否是科创板 Sci-Tech innovAtion boaRd (STAR Market) + { + if (!symbol) return false; + var upperSymbol=symbol.toUpperCase(); + if (!this.IsSH(upperSymbol)) return false; + if (upperSymbol.charAt(0)=='6' && upperSymbol.charAt(1)=='8' && upperSymbol.charAt(2)=='8') + return true; + + return false; + }, + + GetMarketStatus:function(symbol) //获取市场状态 0=闭市 1=盘前 2=盘中 3=盘后 + { + if (!symbol) return 0; + var upperSymbol=symbol.toUpperCase(); + var nowDate= new Date(); + var day = nowDate.getDay(); + var time = nowDate.getHours() * 100 + nowDate.getMinutes(); + if (this.IsUSA(upperSymbol)) + { + var usaDate=GetLocalTime(-4); + var day = usaDate.getDay(); + var time = usaDate.getHours() * 100 + usaDate.getMinutes(); + if(day == 6 || day== 0) return 0; //周末 + + //9:30 - 16:00 考虑夏令时间时间增加1小时 9:30 - 17:00 + if (time>1730) return 3; + if (time<930) return 1; + + return 2; + } + else if (this.IsBIT(upperSymbol)) //数字货币24小时 + { + return 2; + } + else if (this.IsForeignExchange(upperSymbol)) //外汇24小时 + { + return 2; + } + else if (this.IsFTSE(upperSymbol)) //富时中国 9:00-16:30 17:00-04:45 + { + if(day == 6 || day== 0) return 0; //周末 + if (time>=830 && time<=2359) return 2; + if (time>=0 && time<=500) return 2; + return 0; + } + else if (this.IsFHK(upperSymbol)) //港股指数期货 9:15-12:00 13:00-16:30 17:15-01:00 + { + if(day == 6 || day== 0) return 0; //周末 + if (time>=900 && time<=2359) return 2; + if (time>=0 && time<=320) return 2; + return 0; + } + else if (this.IsET(upperSymbol)) + { + return this.GetETMarketStatus(symbol); + } + else if (this.IsHK(upperSymbol)) //港股 + { + if(day == 6 || day== 0) return 0; //周末 + if(time>1630) return 3; + if(time<925) return 1; + return 2; + } + else if (this.IsNYMEX(upperSymbol)) + { + return this.GetNYMEXMarketStatus(upperSymbol); + } + else if (this.IsCOMEX(upperSymbol)) + { + return this.GetCOMEXMarketStatus(upperSymbol); + } + else if (this.IsNYBOT(upperSymbol)) + { + return this.GetNYBOTMarketStatus(upperSymbol); + } + else if (this.IsCBOT(upperSymbol)) + { + return this.GetCBOTMarketStatus(upperSymbol); + } + else if (this.IsLME(upperSymbol)) + { + return this.GetLMEMarketStatus(upperSymbol); + } + else if (this.IsChinaFutures(upperSymbol)) //国内期货 + { + if(day == 6 || day== 0) return 0; //周末 + + //21:00-2:30 + if(time>=2100) return 2; + if (time<=240) return 2; + + //8:55-11:30, 13:00-15:00 + if(time>=830 && time<=1510) return 2; + + return 1; + } + else //9:30 - 15:40 + { + if(day == 6 || day== 0) return 0; //周末 + if(time>1540) return 3; + if(time<925) return 1; + return 2; + } + + }, + + GetLimitPriceRange:function(symbol, name) //涨停范围 + { + if (!this.IsSHSZStockA(symbol)) return null; + if (this.IsSHStockSTAR(symbol)) return {Max:0.2 , Min:-0.2}; //科创板 [20%- -20%] + + if (!name) return null; + if (name.indexOf('ST')>=0) return { Max:0.05, Min:-0.05 }; //ST 股票 [5% - -5%] + + return {Max:0.1 , Min:-0.1}; //[10% - -10%] + }, + + GetDefaultDecimal:function(symbol) //默认小数位数 + { + return 2; + }, + + GetFHKDecimal:function(symbol) //港股指数期货 小数位数 + { + return 0; + }, + + GetFTSEDecimal:function(symbol) //富时中国A50期货 小数位数 + { + return 0; + }, + + GetBITDecimal:function(symbol) + { + return 2; + }, + + GetSHODecimal:function(symbol) + { + return 4; + }, + + GetETDecimal:function(symbol) + { + return 2; + }, + + GetForeignExchangeDecimal:function(symbol) + { + return 4; + }, + + GetNYMEXDecimal:function(symbol) //纽约期货交易所 + { + return g_NYMEXTimeData.GetDecimal(symbol); + }, + + GetCOMEXDecimal:function(symbol) + { + return g_COMEXTimeData.GetDecimal(symbol); + }, + + GetNYBOTDecimal:function(symbol) + { + return g_NYBOTTimeData.GetDecimal(symbol); + }, + + GetCBOTDecimal:function(symbol) + { + return g_CBOTTimeData.GetDecimal(symbol); + }, + + GetLMEDecimal:function(symbol) + { + return g_LMETimeData.GetDecimal(symbol); + }, + + GetETMarketStatus:function(symbol) + { + // 0=闭市 1=盘前 2=盘中 3=盘后 + return 2; + }, + + GetNYMEXMarketStatus:function(symbol) + { + return g_NYMEXTimeData.GetMarketStatus(symbol); + }, + + GetCOMEXMarketStatus:function(symbol) + { + return g_COMEXTimeData.GetMarketStatus(symbol); + }, + + GetNYBOTMarketStatus:function(symbol) + { + return g_NYBOTTimeData.GetMarketStatus(symbol); + }, + + GetCBOTMarketStatus:function(symbol) + { + return g_CBOTTimeData.GetMarketStatus(symbol); + }, + + GetLMEMarketStatus:function(symbol) + { + return g_LMETimeData.GetMarketStatus(symbol); + }, + + IsShowMinuteVolTitle:function(symbol) //是否画走势图成交量标题 + { + if (!symbol) return false; + var upperSymbol=symbol.toUpperCase(); + //if (this.IsChinaFutures(upperSymbol)) return true; + + return false; + }, + + IsShowMinutePostionLine:function(upperSymbol) //分时图 成交量图中是否显示持仓线 + { + if(MARKET_SUFFIX_NAME.IsFutures(upperSymbol) || MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) return true; + + return false; + }, + + IsShowMinuteColorVolBar:function(symobl) //是否分时图成绩量柱子使用彩色柱 + { + if (g_JSChartResource.Minute.VolBarColor) return true; + + return false; + } +} + + +//走势图分钟数据对应的时间 +function MinuteTimeStringData() +{ + this.SHSZ = null; //上海深证交易所时间 + this.SHO=null; //上海股票期权交易时间 + this.HK = null; //香港交易所时间 + this.Futures=new Map(); //期货交易时间 key=时间名称 Value=数据 + this.USA = null; //美股交易时间 + this.FTSE=null; //富时中国 + this.FHK=null; //港股指数期货 + this.ForeEx=null; //外汇 + this.BIT=null; //数字货币 + + this.Initialize = function () //初始化 默认只初始化沪深的 其他市场动态生成 + { + //this.SHSZ = this.CreateSHSZData(); + //this.HK = this.CreateHKData(); + } + + this.GetET=function(upperSymbol) //当天所有的分钟 + { + throw {Name:'MinuteTimeStringData::GetET', Error:'not implement'}; + } + + this.GetSHSZ=function(upperSymbol) //动态创建 + { + if (!this.SHSZ) this.SHSZ=this.CreateSHSZData(); + return this.SHSZ; + } + + this.GetSHO=function() + { + if (!this.SHO) this.SHO=this.CreateSHOData(); + return this.SHO; + } + + this.GetHK=function() + { + if (!this.HK) this.HK = this.CreateHKData(); + return this.HK; + } + + this.GetFutures=function(splitData) + { + if (!this.Futures.has(splitData.Name)) + { + var data = this.CreateTimeData(splitData.Data); + this.Futures.set(splitData.Name,data); + } + + return this.Futures.get(splitData.Name); + } + + this.GetUSA=function() + { + if (!this.USA) this.USA=this.CreateUSAData(0); + return this.USA; + } + + this.GetFTSE=function() + { + if (!this.FTSE) this.FTSE=this.CreateFTSEData(); + return this.FTSE; + } + + this.GetFHK=function() + { + if (!this.FHK) this.FHK=this.CreateFHKData(); + return this.FHK; + } + + this.GetForeignExchange=function() + { + if (!this.ForeEx) this.ForeEx=this.CreateForeignExchangeData(); + return this.ForeEx; + } + + this.GetBIT=function(upperSymbol) + { + if (!this.BIT) this.BIT=this.CreateBITData(); + return this.BIT; + } + + this.CreateSHSZData = function () + { + const TIME_SPLIT = + [ + { Start: 925, End: 925 }, + { Start: 930, End: 1130 }, + { Start: 1300, End: 1500 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateSHOData=function() + { + const TIME_SPLIT = + [ + { Start: 930, End: 1129 }, + { Start: 1300, End: 1500 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateHKData = function () + { + const TIME_SPLIT = + [ + { Start: 930, End: 1200 }, + { Start: 1300, End: 1600 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateFTSEData=function() + { + const TIME_SPLIT= + [ + { Start:1700, End:2359 }, + { Start:0, End:445 }, + { Start:900, End:1630 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateFHKData=function() + { + //港股指数期货 9:15-12:00 13:00-16:30 17:15-03:00 + const TIME_SPLIT= + [ + { Start:1715, End:2359 }, + { Start:0, End:300 }, + { Start:915, End:1200 }, + { Start:1300, End:1630 }, + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateUSAData=function(type) + { + if (type==1) //美国夏令时 + { + const TIME_SPLIT = + [ + { Start: 2130, End: 2359 }, + { Start: 0, End: 400 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + else if (type==2) //非夏令时 + { + const TIME_SPLIT = + [ + { Start: 2230, End: 2359 }, + { Start: 0, End: 500 } + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + else //使用美国本地时间 + { + const TIME_SPLIT = + [ + { Start: 930, End: 1600 } //美国东部时间9:30到16:00 + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + } + + this.CreateForeignExchangeData=function() + { + //外汇 7:00 - 6:59 + const TIME_SPLIT= + [ + { Start:600, End:2359 }, + { Start:0, End:559 }, + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateBITData=function() + { + //数字货币 7:00 - 6:59 + const TIME_SPLIT= + [ + { Start:600, End:2359 }, + { Start:0, End:559 }, + ]; + + return this.CreateTimeData(TIME_SPLIT); + } + + this.CreateTimeData = function (timeSplit) + { + var data = []; + for (var i in timeSplit) + { + var item = timeSplit[i]; + for (var j = item.Start; j <= item.End; ++j) + { + if (j % 100 >= 60) continue; //大于60分钟的数据去掉 + data.push(j); + } + } + return data; + } + + this.GetTimeData = function (symbol) + { + if (!symbol) return this.SHSZ; + + var upperSymbol = symbol.toLocaleUpperCase(); //转成大写 + if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol)) return this.GetSHSZ(upperSymbol); + if (MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) return this.GetSHO(); + if (MARKET_SUFFIX_NAME.IsHK(upperSymbol)) return this.GetHK(); + if (MARKET_SUFFIX_NAME.IsUSA(upperSymbol)) return this.GetUSA(true); + if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol)) + { + var splitData = g_FuturesTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) return this.GetFTSE(); + if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) return this.GetFHK(); + if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) return this.GetForeignExchange(); + if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) return this.GetET(upperSymbol); + if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) return this.GetBIT(upperSymbol); + + if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol)) //纽约期货交易所 + { + var splitData = g_NYMEXTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol)) //纽约期货交易所 + { + var splitData = g_COMEXTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol)) //纽约期货交易所 + { + var splitData = g_NYBOTTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol)) //芝商所 + { + var splitData = g_CBOTTimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + + if (MARKET_SUFFIX_NAME.IsLME(upperSymbol)) //伦敦LME + { + var splitData = g_LMETimeData.GetSplitData(upperSymbol); + if (!splitData) return null; + return this.GetFutures(splitData); + } + } +} + +//走势图刻度分钟线 +function MinuteCoordinateData() +{ + //沪深走势图时间刻度 + const SHZE_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [31, 0, "RGB(200,200,200)", "10:00"], + [61, 0, "RGB(200,200,200)", "10:30"], + [91, 0, "RGB(200,200,200)", "11:00"], + [122, 1, "RGB(200,200,200)", "13:00"], + [152, 0, "RGB(200,200,200)", "13:30"], + [182, 0, "RGB(200,200,200)", "14:00"], + [212, 0, "RGB(200,200,200)", "14:30"], + [242, 1, "RGB(200,200,200)", "15:00"], // 15:00 + ], + Simple: //简洁模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [61, 0, "RGB(200,200,200)", "10:30"], + [122, 1, "RGB(200,200,200)", "13:00"], + [182, 0, "RGB(200,200,200)", "14:00"], + [242, 1, "RGB(200,200,200)", "15:00"] + ], + Min: //最小模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [122, 1, "RGB(200,200,200)", "13:00"], + [242, 1, "RGB(200,200,200)", "15:00"] + ], + + Count: 243, + MiddleCount: 122, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 400) return this.Simple; + + return this.Full; + } + }; + + //上海股票期权时间刻度 + const SHO_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [30, 0, "RGB(200,200,200)", "10:00"], + [60, 0, "RGB(200,200,200)", "10:30"], + [90, 0, "RGB(200,200,200)", "11:00"], + [120, 1, "RGB(200,200,200)", "13:00"], + [150, 0, "RGB(200,200,200)", "13:30"], + [180, 0, "RGB(200,200,200)", "14:00"], + [210, 0, "RGB(200,200,200)", "14:30"], + [240, 1, "RGB(200,200,200)", "15:00"], // 15:00 + ], + Simple: //简洁模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [60, 0, "RGB(200,200,200)", "10:30"], + [120, 1, "RGB(200,200,200)", "13:00"], + [180, 0, "RGB(200,200,200)", "14:00"], + [240, 1, "RGB(200,200,200)", "15:00"] + ], + Min: //最小模式 + [ + [0, 0, "rgb(200,200,200)", "09:30"], + [120, 1, "RGB(200,200,200)", "13:00"], + [240, 1, "RGB(200,200,200)", "15:00"] + ], + + Count: 241, + MiddleCount: 120, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 400) return this.Simple; + + return this.Full; + } + }; + + //港股走势图时间刻度 + const HK_MINUTE_X_COORDINATE = + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [30, 0, "RGB(200,200,200)", "10:00"], + [60, 1, "RGB(200,200,200)", "10:30"], + [90, 0, "RGB(200,200,200)", "11:00"], + [120, 1, "RGB(200,200,200)", "11:30"], + [151, 0, "RGB(200,200,200)", "13:00"], + [181, 1, "RGB(200,200,200)", "13:30"], + [211, 0, "RGB(200,200,200)", "14:00"], + [241, 1, "RGB(200,200,200)", "14:30"], + [271, 0, "RGB(200,200,200)", "15:00"], + [301, 1, "RGB(200,200,200)", "15:30"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [60, 1, "RGB(200,200,200)", "10:30"], + [120, 1, "RGB(200,200,200)", "11:30"], + [211, 0, "RGB(200,200,200)", "14:00"], + [271, 0, "RGB(200,200,200)", "15:00"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "09:30"], + [151, 0, "RGB(200,200,200)", "13:00"], + [331, 1, "RGB(200,200,200)", "16:00"] + ], + + Count: 332, + MiddleCount: 151, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + }; + + //美股走势图时间刻度 + const USA_MINUTE_X_COORDINATE = + { + /* + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "21:30"], + [60, 0, "RGB(200,200,200)", "22:30"], + [120, 1, "RGB(200,200,200)", "23:30"], + [210, 0, "RGB(200,200,200)", "01:00"], + [270, 0, "RGB(200,200,200)", "02:00"], + [330, 0, "RGB(200,200,200)", "03:00"], + [390, 0, "RGB(200,200,200)", "04:00"], + ], + Simple: //简洁模式 + [ + [0, 0, "rgb(200,200,200)", "21:30"], + [160, 1, "RGB(200,200,200)", "00:00"], + [270, 0, "RGB(200,200,200)", "02:00"], + [390, 0, "RGB(200,200,200)", "04:00"], + ], + Min: //最小模式 + [ + [0, 0, "rgb(200,200,200)", "21:30"], + [160, 1, "RGB(200,200,200)", "00:00"], + [390, 0, "RGB(200,200,200)", "04:00"], + ], + */ + + //美国本地时间 + Full: //完整模式 + [ + [0, 0, "rgb(200,200,200)", "9:30"], + [30, 0, "RGB(200,200,200)", "10:00"], + [90, 1, "RGB(200,200,200)", "11:00"], + [150, 0, "RGB(200,200,200)", "12:00"], + [210, 0, "RGB(200,200,200)", "13:00"], + [270, 0, "RGB(200,200,200)", "14:00"], + [330, 0, "RGB(200,200,200)", "15:00"], + [390, 0, "RGB(200,200,200)", "16:00"], + ], + Simple: //简洁模式 + [ + [30, 0, "rgb(200,200,200)", "10:00"], + [150, 1, "RGB(200,200,200)", "12:00"], + [270, 0, "RGB(200,200,200)", "14:00"], + [390, 0, "RGB(200,200,200)", "16:00"], + ], + Min: //最小模式 + [ + [30, 0, "rgb(200,200,200)", "10:00"], + [210, 1, "RGB(200,200,200)", "13:00"], + [390, 0, "RGB(200,200,200)", "16:00"], + ], + + Count: 391, + MiddleCount: 211, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 400) return this.Simple; + + return this.Full; + } + }; + + //富时中国 + const FTSE_MINUTE_X_COORDINATE= + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + //[60, 0, "RGB(200,200,200)", "18:00"], + [120, 1, "RGB(200,200,200)", "19:00"], + //[180, 0, "RGB(200,200,200)", "20:00"], + [240, 1, "RGB(200,200,200)", "21:00"], + //[300, 0, "RGB(200,200,200)", "22:00"], + [360, 1, "RGB(200,200,200)", "23:00"], + //[420, 0, "RGB(200,200,200)", "00:00"], + [480, 1, "RGB(200,200,200)", "01:00"], + //[540, 0, "RGB(200,200,200)", "02:00"], + [600, 1, "RGB(200,200,200)", "03:00"], + //[660, 1, "RGB(200,200,200)", "04:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + //[766, 1, "RGB(200,200,200)", "10:00"], + [826, 1, "RGB(200,200,200)", "11:00"], + //[886, 1, "RGB(200,200,200)", "12:00"], + [946, 1, "RGB(200,200,200)", "13:00"], + //[1006, 1, "RGB(200,200,200)", "14:00"], + [1066, 1, "RGB(200,200,200)", "15:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + //[60, 0, "RGB(200,200,200)", "18:00"], + //[120, 1, "RGB(200,200,200)", "19:00"], + //[180, 0, "RGB(200,200,200)", "20:00"], + [240, 1, "RGB(200,200,200)", "21:00"], + //[300, 0, "RGB(200,200,200)", "22:00"], + //[360, 1, "RGB(200,200,200)", "23:30"], + //[420, 0, "RGB(200,200,200)", "00:00"], + [480, 1, "RGB(200,200,200)", "01:00"], + //[540, 0, "RGB(200,200,200)", "02:00"], + //[600, 1, "RGB(200,200,200)", "03:00"], + //[660, 1, "RGB(200,200,200)", "04:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + //[766, 1, "RGB(200,200,200)", "10:00"], + //[826, 1, "RGB(200,200,200)", "11:00"], + //[886, 1, "RGB(200,200,200)", "12:00"], + [946, 1, "RGB(200,200,200)", "13:00"], + //[1006, 1, "RGB(200,200,200)", "14:00"], + //[1066, 1, "RGB(200,200,200)", "15:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "17:00"], + [706, 1, "RGB(200,200,200)", "09:00"], + [1156, 1, "RGB(200,200,200)", "16:30"], + ], + + Count: 1157, + MiddleCount: 707, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + } + + //港股指数期货 + const FHK_MINUTE_X_COORDINATE= + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + [105, 1, "RGB(200,200,200)", "19:00"], + [225, 1, "RGB(200,200,200)", "21:00"], + [345, 1, "RGB(200,200,200)", "23:00"], + [586, 0, "RGB(200,200,200)", "09:15"], + [691, 1, "RGB(200,200,200)", "11:00"], + [812, 1, "RGB(200,200,200)", "14:00"], + [963, 1, "RGB(200,200,200)", "16:30"], + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + [225, 1, "RGB(200,200,200)", "21:00"], + [586, 0, "RGB(200,200,200)", "09:15"], + [752, 1, "RGB(200,200,200)", "13:00"], + [963, 1, "RGB(200,200,200)", "16:30"], + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "17:15"], + [586, 0, "RGB(200,200,200)", "09:15"], + [963, 1, "RGB(200,200,200)", "16:30"], + ], + + Count: 963, + MiddleCount: 526, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + } + + //外汇 + const FOREX_MINUTE_X_COORDINATE= + { + Full: //完整模式 + [ + [0, 1, "RGB(200,200,200)", "06:00"], + [120, 1, "RGB(200,200,200)", "08:00"], + [240, 1, "RGB(200,200,200)", "10:00"], + [360, 1, "RGB(200,200,200)", "12:00"], + [480, 0, "RGB(200,200,200)", "14:00"], + [600, 1, "RGB(200,200,200)", "16:00"], + [720, 1, "RGB(200,200,200)", "18:00"], + [840, 1, "RGB(200,200,200)", "20:00"], + [960, 1, "RGB(200,200,200)", "22:00"], + [1080, 1, "RGB(200,200,200)", "0:00"], + [1200, 1, "RGB(200,200,200)", "02:00"], + [1320, 1, "RGB(200,200,200)", "04:00"], + ], + Simple: //简洁模式 + [ + [0, 1, "RGB(200,200,200)", "06:00"], + [240, 1, "RGB(200,200,200)", "10:00"], + [480, 0, "RGB(200,200,200)", "14:00"], + [720, 1, "RGB(200,200,200)", "18:00"], + [960, 1, "RGB(200,200,200)", "22:00"], + [1200, 1, "RGB(200,200,200)", "02:00"], + ], + Min: //最小模式 + [ + [0, 1, "RGB(200,200,200)", "06:00"], + [480, 0, "RGB(200,200,200)", "14:00"], + [960, 1, "RGB(200,200,200)", "22:00"], + ], + + Count: 1440, + MiddleCount: 600, + + GetData: function (width) + { + if (width < 200) return this.Min; + else if (width < 450) return this.Simple; + + return this.Full; + } + } + + + + this.GetCoordinateData = function (symbol, width) + { + var data = null; + if (!symbol) + { + data = SHZE_MINUTE_X_COORDINATE; //默认沪深股票 + } + else + { + var upperSymbol = symbol.toLocaleUpperCase(); //转成大写 + if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol) || MARKET_SUFFIX_NAME.IsSHSZIndex(upperSymbol)) + data = this.GetSHSZData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) + data=this.GetSHOData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsHK(upperSymbol)) + data = HK_MINUTE_X_COORDINATE; + else if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol)) + return this.GetChinatFuturesData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsUSA(upperSymbol)) + data = this.GetUSAData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) + data=this.GetFTSEData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) + data=this.GetFHKData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) + data=this.GetForeignExchangeData(upperSymbol,width); + else if ((MARKET_SUFFIX_NAME.IsBIT(upperSymbol,width))) + data=this.GetBITData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) + data=this.GetETData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol,width)) + return data=this.GetNYMEXData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol,width)) + return data=this.GetCOMEXData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol,width)) + return data=this.GetNYBOTData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol,width)) + return data=this.GetCBOTData(upperSymbol,width); + else if (MARKET_SUFFIX_NAME.IsLME(upperSymbol,width)) + return data=this.GetLMEData(upperSymbol,width); + + } + + //JSConsole.Chart.Log('[MiuteCoordinateData]', width); + var result = { Count: data.Count, MiddleCount: data.MiddleCount, Data: data.GetData(width) }; + return result; + } + + this.GetSHSZData=function(upperSymbol,width) + { + var result=SHZE_MINUTE_X_COORDINATE; + return result; + } + + this.GetUSAData=function(upperSymbol,width) + { + var result=USA_MINUTE_X_COORDINATE; + + return result; + } + + this.GetSHOData=function(upperSymbol,width) + { + var result=SHO_MINUTE_X_COORDINATE; + return result; + } + + this.GetFuturesData = function (upperSymbol,width,timeData) + { + var splitData = timeData.GetSplitData(upperSymbol); + if (!splitData) return null; + var stringData = g_MinuteTimeStringData.GetFutures(splitData); + if (!stringData) return null; + var result = { Count: stringData.length }; + var coordinate=null; + var minWidth=200, simpleWidth=480; + /* + if (splitData.Name =='21:00-1:00,9:00-10:15,10:30-11:30,13:30-15:00') + { + minWidth=250; + simpleWidth=500; + } + */ + + if (width < minWidth) coordinate = splitData.Coordinate.Min; + else if (width < simpleWidth) coordinate = splitData.Coordinate.Simple; + else coordinate = splitData.Coordinate.Full; + + var data=[]; + for(var i=0;i430 && time<730) return 1; + + return 2; + } +} + + +function COMEXTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + this.FUTURES_LIST= + [ + { Symbol:"GC", Decimal:1, Time:0 }, //COMEX黄金 + { Symbol:"QO", Decimal:2, Time:0 }, //迷你黄金 + { Symbol:"MG", Decimal:1, Time:0 }, //微型黄金 + { Symbol:"QI", Decimal:4, Time:0 }, //迷你白银 + { Symbol:"SI", Decimal:3, Time:0 }, //COMEX白银 + { Symbol:"QI", Decimal:4, Time:0 }, //迷你白银 + { Symbol:"HG", Decimal:4, Time:0 } //COMEX铜 + ] + + this.MarketSuffix=".COMEX"; +} + +function NYBOTTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //美国标准时间 + this.TIME_SPLIT= + [ + { + Name:'9:00-2:20', + Data: + [ + //9:00-2:20 + { Start: 900, End: 2359 }, + { Start: 0, End: 220 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 100, Text: '1:00' } + ] + } + }, + { + Name:'15:30-1:00', + Data: + [ + //9:00-2:20 + { Start: 1530, End: 2359 }, + { Start: 0, End: 100 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1600, Text: '16:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' } + ], + Simple: //简洁模式 + [ + { Value: 1600, Text: '16:00' }, + //{ Value: 1600, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + //{ Value: 100, Text: '1:00' } + ], + Min: //最小模式 + [ + { Value: 1600, Text: '16:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 100, Text: '1:00' } + ] + } + } + ] + + //美国夏时令 + this.TIME_SPLIT2= + [ + { + Name:'10:00-3:20', + Data: + [ + //9:00-2:20 + { Start: 1000, End: 2359 }, + { Start: 0, End: 320 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 1000, Text: '10:00' }, + //{ Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + //{ Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 1000, Text: '10:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'16:30-2:00', + Data: + [ + { Start: 1630, End: 2359 }, + { Start: 0, End: 200 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 1700, Text: '17:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 1700, Text: '17:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 1900, Text: '19:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 2300, Text: '23:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 100, Text: '1:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 1700, Text: '17:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 200, Text: '2:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"SB", Decimal:2, Time:1 }, //11号白糖 + { Symbol:"CT", Decimal:2, Time:0 }, //棉花 + //{ Symbol:"KC", Decimal:2, Time:0 }, //咖啡 + //{ Symbol:"DX", Decimal:2, Time:0 }, //美元指数 + //{ Symbol:"CC", Decimal:2, Time:0 } //可可 + ] + + this.MarketSuffix=".NYBOT"; + + this.GetMarketStatus=function(upperSymbol) // 0=闭市 1=盘前 2=盘中 3=盘后 + { + var usaDate=GetLocalTime(-4); //需要转成美国时间的 周6 周日 + var day = usaDate.getDay(); + var time = usaDate.getHours() * 100 + usaDate.getMinutes(); + if(day == 6 || day== 0) return 0; //周末 + + var find=this.GetFuturesInfo(upperSymbol); + if (!find) return 2; + + if (find.Symbol=="SB") //Sugar No. 11 Futures 03:30 - 13:00 + { + if (time>300 && time<1400) return 2; + } + else if (find.Symbol=="CT") //美棉 21:00-14:20 + { + if( (time>=0 && time<=1500 ) || (time>=2000 && time<=2359) ) return 2; + return 1; + } + + return 0; + } +} + +//芝商所 +function CBOTTimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //夏令时间 + this.TIME_SPLIT= + [ + { + Name:'8:00-2:20', + Data: + [ + //6:00 - 5:00 + { Start: 800, End: 2359 }, + { Start: 0, End: 220 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'8:00-2:45', + Data: + [ + //6:00 - 5:00 + { Start: 800, End: 2359 }, + { Start: 0, End: 245 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + }, + { + Name:'6:00-5:00', + Data: + [ + //6:00 - 5:00 + { Start: 600, End: 2359 }, + { Start: 0, End: 500 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 600, Text: '6:00' }, + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' }, + { Value: 400, Text: '4:00' }, + ], + Simple: //简洁模式 + [ + { Value: 600, Text: '6:00' }, + //{ Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + //{ Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + //{ Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + //{ Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + //{ Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + //{ Value: 400, Text: '4:00' }, + ], + Min: //最小模式 + [ + { Value: 600, Text: '6:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 500, Text: '5:00' } + ] + } + } + ] + + //标准时间 + this.TIME_SPLIT2= + [ + { + Name:'9:00-3:20', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 320 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' } + //{ Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 300, Text: '3:00' } + ] + } + }, + { + Name:'9:00-3:45', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 345 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' } + //{ Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 300, Text: '3:00' } + ] + } + }, + { + Name:'7:00-6:00', + Data: + [ + { Start: 700, End: 2359 }, + { Start: 0, End: 600 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 700, Text: '7:00' }, + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' }, + { Value: 500, Text: '5:00' } + ], + Simple: //简洁模式 + [ + { Value: 700, Text: '7:00' }, + //{ Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + //{ Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + //{ Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + //{ Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + //{ Value: 1, Text: '1:00' }, + { Value: 300, Text: '3:00' } + //{ Value: 500, Text: '5:00' } + ], + Min: //最小模式 + [ + { Value: 700, Text: '7:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 500, Text: '5:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"ZC", Decimal:2, Time:0 }, //玉米 + { Symbol:"XC", Decimal:2, Time:1 }, //迷你玉米 + { Symbol:"ZS", Decimal:2, Time:0 }, //大豆 + { Symbol:"XK", Decimal:2, Time:1 }, //迷你大豆 + { Symbol:"ZL", Decimal:2, Time:0 }, //豆油 + { Symbol:"ZR", Decimal:2, Time:0 }, //稻谷 + { Symbol:"ZO", Decimal:2, Time:0 }, //燕麦 + { Symbol:"ZW", Decimal:2, Time:0 }, //小麦 + { Symbol:"XW", Decimal:2, Time:1 }, //迷你小麦 + { Symbol:"ZM", Decimal:1, Time:0 }, //豆粕 + + { Symbol:"EH", Decimal:3, Time:2 }, //乙醇 + + { Symbol:"YM", Decimal:0, Time:2 }, //小型道指 + { Symbol:"ES", Decimal:2, Time:2 }, //小型标普 + { Symbol:"NQ", Decimal:2, Time:2 }, //小型纳指 + + { Symbol:"TY", Decimal:4, Time:2 }, //10年美国债 + { Symbol:"TU", Decimal:4, Time:2 }, //2年美国债 + { Symbol:"FV", Decimal:4, Time:2 }, //5年美国债 + { Symbol:"US", Decimal:4, Time:2 }, //30年美国债 + { Symbol:"UL", Decimal:4, Time:2 }, //超国债 + ] + + this.MarketSuffix=".CBOT"; +} + +function LMETimeData() +{ + this.newMethod=NYMEXTimeData; //派生 + this.newMethod(); + delete this.newMethod; + + //标准时间 + this.TIME_SPLIT= + [ + { + Name:'LME 9:00-3:00', + Data: + [ + { Start: 900, End: 2359 }, + { Start: 0, End: 300 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + { Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + { Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + { Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' }, + { Value: 300, Text: '3:00' } + ], + Simple: //简洁模式 + [ + { Value: 900, Text: '9:00' }, + //{ Value: 1100, Text: '11:00' }, + { Value: 1300, Text: '13:00' }, + //{ Value: 1500, Text: '15:00' }, + { Value: 1700, Text: '17:00' }, + //{ Value: 1900, Text: '19:00' }, + { Value: 2100, Text: '21:00' }, + //{ Value: 2300, Text: '23:00' }, + { Value: 100, Text: '1:00' } + // { Value: 300, Text: '3:00' } + ], + Min: //最小模式 + [ + { Value: 900, Text: '9:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 300, Text: '3:00' } + ] + } + } + ] + + //夏令 + this.TIME_SPLIT= + [ + { + Name:'LME 8:00-2:00', + Data: + [ + { Start: 800, End: 2359 }, + { Start: 0, End: 200 }, + ], + Coordinate: + { + Full://完整模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + { Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + { Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' }, + { Value: 200, Text: '2:00' } + ], + Simple: //简洁模式 + [ + { Value: 800, Text: '8:00' }, + //{ Value: 1000, Text: '10:00' }, + { Value: 1200, Text: '12:00' }, + //{ Value: 1400, Text: '14:00' }, + { Value: 1600, Text: '16:00' }, + //{ Value: 1800, Text: '18:00' }, + { Value: 2000, Text: '20:00' }, + //{ Value: 2200, Text: '22:00' }, + { Value: 0, Text: '0:00' } + //{ Value: 200, Text: '2:00' } + ], + Min: //最小模式 + [ + { Value: 800, Text: '8:00' }, + { Value: 1800, Text: '18:00' }, + { Value: 200, Text: '2:00' } + ] + } + } + ] + + this.FUTURES_LIST= + [ + { Symbol:"SND", Decimal:0, Time:0 }, //综合锡03 + { Symbol:"AHD", Decimal:2, Time:0 }, //综合铝03 + { Symbol:"PBD", Decimal:2, Time:0 }, //综合铅03 + { Symbol:"ZSD", Decimal:2, Time:0 }, //综合锌03 + { Symbol:"CAD", Decimal:2, Time:0 }, //综合铜03 + { Symbol:"NID", Decimal:0, Time:0 }, //综合镍03 + ] + + this.MarketSuffix=".LME"; +} + +var g_MinuteTimeStringData = new MinuteTimeStringData(); +var g_MinuteCoordinateData = new MinuteCoordinateData(); +var g_FuturesTimeData = new FuturesTimeData(); +var g_NYMEXTimeData=new NYMEXTimeData(); +var g_COMEXTimeData=new COMEXTimeData(); +var g_NYBOTTimeData=new NYBOTTimeData(); +var g_CBOTTimeData=new CBOTTimeData(); +var g_LMETimeData=new LMETimeData(); + + +function GetfloatPrecision(symbol) //获取小数位数 +{ + var defaultfloatPrecision=2; //默认2位 + if (!symbol) return defaultfloatPrecision; + var upperSymbol=symbol.toUpperCase(); + + //全部由外部控制 + if (typeof(MARKET_SUFFIX_NAME.GetCustomDecimal)=='function') return MARKET_SUFFIX_NAME.GetCustomDecimal(upperSymbol); + + if (MARKET_SUFFIX_NAME.IsSHSZFund(upperSymbol)) defaultfloatPrecision=3; //基金3位小数 + else if (MARKET_SUFFIX_NAME.IsSHO(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetSHODecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) defaultfloatPrecision=g_FuturesTimeData.GetDecimal(upperSymbol); //期货小数位数读配置 + else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetFHKDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetFTSEDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetBITDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetETDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetForeignExchangeDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol)) defaultfloatPrecision=g_NYMEXTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsCOMEX(upperSymbol)) defaultfloatPrecision=g_COMEXTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsNYBOT(upperSymbol)) defaultfloatPrecision=g_NYBOTTimeData.GetDecimal(upperSymbol); + else if (MARKET_SUFFIX_NAME.IsCBOT(upperSymbol)) defaultfloatPrecision=g_CBOTTimeData.GetDecimal(upperSymbol); + else defaultfloatPrecision=MARKET_SUFFIX_NAME.GetDefaultDecimal(upperSymbol); + + return defaultfloatPrecision; +} + +//把不连续的分时数据转成连续的分时数据 +function GenerateMinuteStockJsonData(data) +{ + var stock = + { + symbol:data.symbol, name:data.name,time:data.time, date:data.date, + price:data.price, open:data.open, yclose:data.yclose, high:data.high, low:data.low, vol:data.vol,amount:data.amount, + minute:[] + }; + + var mapMinute=new Map(); + for(var i in data.minute) + { + var item=data.minute[i]; + mapMinute.set(item.time,item); + } + + var timeData=g_MinuteTimeStringData.GetTimeData(stock.symbol); + for(var i in timeData) //根据交易时间产生数据 + { + var time=timeData[i]; + var minuteItem={ time:time, vaild:false }; + if (mapMinute.has(time)) + { + var find=mapMinute.get(time); + minuteItem.vaild=true; + minuteItem.price=find.price; + minuteItem.open=find.open; + minuteItem.high=find.high; + minuteItem.low=find.low; + minuteItem.avprice=find.avprice; + minuteItem.vol=find.vol; + minuteItem.amount=find.amount; + if (IFrameSplitOperator.IsNumber(find.increase)) minuteItem.increase=find.increase; + if (IFrameSplitOperator.IsNumber(find.risefall)) minuteItem.risefall=find.risefall; + if (IFrameSplitOperator.IsNumber(find.position)) minuteItem.position=find.position; + } + stock.minute.push(minuteItem); + } + + var vaildCount=0; + for(var i=stock.minute.length-1;i>=0;--i) + { + vaildCount=i+1; + var item=stock.minute[i]; + if (item.vaild==true) break; + } + + stock.minute=stock.minute.slice(0,vaildCount); //去掉最后无用的数据 + + return stock; +} + +function GetLocalTime(i) //得到标准时区的时间的函数 +{ + if (typeof i !== 'number') return; + var d = new Date(); + //得到1970年一月一日到现在的秒数 + var len = d.getTime(); + //本地时间与GMT时间的时间偏移差 + var offset = d.getTimezoneOffset() * 60000; + //得到现在的格林尼治时间 + var utcTime = len + offset; + return new Date(utcTime + 3600000 * i); +} + + + + + + +/* + Copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 分析家语法编译执行器 (H5版本) +*/ + +//日志输出类 +if (!JSConsole) +{ + var JSConsole= + { + Chart:{ Log:console.log, Warn:console.warn }, //图形日志 + Complier:{ Log:console.log, Warn:console.warn } //编译器日志 + }; +} + +//API默认地址 +var g_JSComplierResource= +{ + Domain : "https://opensource.zealink.com", //API域名 + CacheDomain : "https://opensourcecache.zealink.com", //缓存域名 + + DrawIcon: + { + Family:'iconfont', + Data:new Map([ + [1, { Text:'\ue660', Color:'rgb(243,28,15)'}], //向上箭头 + [2, { Text:'\ue661', Color:'rgb(87,247,41)'}], //向下箭头 + [3, { Text:'\ue662', Color:'rgb(237,153,0)'}], + [4, { Text:'\ue663', Color:'rgb(237,153,0)'}], + [5, { Text:'\ue664', Color:'rgb(237,153,0)'}], + [6, { Text:'\ue665', Color:'rgb(140,57,208)'}], + [7, { Text:'\ue666', Color:'rgb(246,135,37)'}], + [8, { Text:'\ue667', Color:'rgb(85,231,56)'}], + [9, { Text:'\ue668', Color:'rgb(227,61,41)'}], + + [10, { Text:'\ue669', Color:'rgb(216,78,48)'}], + [11, { Text:'\ue66a', Color:'rgb(249,187,0)'}], //点赞 + [12, { Text:'\ue66b', Color:'rgb(249,187,0)'}], + [13, { Text:'\ue66c', Color:'rgb(216,42,0)'}], + [14, { Text:'\ue66d', Color:'rgb(58,90,236)'}], + [15, { Text:'\ue66e', Color:'rgb(227,149,0)'}], + [16, { Text:'\ue66f', Color:'rgb(208,82,78)'}], + [17, { Text:'\ue670', Color:'rgb(234,114,26)'}], + [18, { Text:'\ue671', Color:'rgb(116,25,255)'}], + [19, { Text:'\ue672', Color:'rgb(102,9,11)'}], + + [20, { Text:'\ue673', Color:'rgb(127,125,137)'}], + [21, { Text:'\ue674', Color:'rgb(110,188,255)'}], + [22, { Text:'\ue675', Color:'rgb(238,79,51)'}], + [23, { Text:'\ue676', Color:'rgb(244,71,0)'}], + [24, { Text:'\ue677', Color:'rgb(102,183,248)'}], + [25, { Text:'\ue678', Color:'rgb(234,88,231)'}], + [26, { Text:'\ue679', Color:'rgb(242,171,0)'}], + [27, { Text:'\ue67a', Color:'rgb(87,247,168)'}], + [28, { Text:'\ue67b', Color:'rgb(97,204,113)'}], + [29, { Text:'\ue67c', Color:'rgb(84,115,193)'}], + + [30, { Text:'\ue67d', Color:'rgb(141,51,255)'}], + [31, { Text:'\ue67e', Color:'rgb(200,126,24)'}], + [32, { Text:'\ue67f', Color:'rgb(195,41,32)'}], + [33, { Text:'\ue68f', Color:'rgb(215,85,194)'}], + [34, { Text:'\ue690', Color:'rgb(250,222,105)'}], + [35, { Text:'\ue691', Color:'rgb(112,249,224)'}], + [36, { Text:'\ue692', Color:'rgb(217,107,98)'}], + [37, { Text:'\ue693', Color:'rgb(114,231,17)'}], + [38, { Text:'\ue694', Color:'rgb(238,31,25)'}], + [39, { Text:'\ue695', Color:'rgb(92,247,113)'}], + + [40, { Text:'\ue696', Color:'rgb(175,175,175)'}], + [41, { Text:'\ue697', Color:'rgb(252,228,23)'}], + [42, { Text:'\ue698', Color:'rgb(88,195,235)'}], + [43, { Text:'\ue699', Color:'rgb(55,74,94)'}], + [44, { Text:'\ue69a', Color:'rgb(248,175,33)'}], + [45, { Text:'\ue69b', Color:'rgb(194,180,112)'}], + [46, { Text:'\ue69c', Color:'rgb(50,153,28)'}], + [47, { Text:'\ue69d', Color:'rgb(17,65,152)'}], + [48, { Text:'\ue69e', Color:'rgb(194,55,26)'}], + [49, { Text:'\ue69f', Color:'rgb(243,0,0)'}], + + /* + [11,{ Text:'\ue624', Color:'rgb(245,159,40)'}], + [12,{ Text:'\ue600', Color:'rgb(245,159,40)'}], + [13,{Text:'\ue70f',Color:'rgb(209,37,35)'}, ], //B + [14,{Text:'\ue64c',Color:'rgb(127,209,59)'} ], //S + [9, {Text:'\ue626',Color:'rgb(245,159,40)'} ], //$ + [36,{Text:'\ue68c',Color:'rgb(255,106,106)'} ], //关闭 红色 + [37,{Text:'\ue68c',Color:'rgb(46,139,87)'} ], //关闭 绿色 + [38,{Text:'\ue68d',Color:'rgb(238,44,44)'} ], //▲ + [39,{Text:'\ue68e',Color:'rgb(0,139,69)'} ], //▼ + [46,{Text:'\ue64d',Color:'rgb(51,51,51)'} ], //message + */ + ]) + }, + + CustomDrawIcon: + { + Data:new Map() //自定义图标 key=id + //value={ID:, Text:, Color, Family: } //svg + //value={ ID:1, Symbol:'↑', Color:'rgb(238,44,44)' } //文字 + }, + + CustomFunction: //定制函数 + { + Data:new Map() //自定义函数 key=函数名, Value:{ID:函数名, Callback: } + }, + + CustomVariant: //自定义变量 + { + Data:new Map() //自定义函数 key=变量名, Value:{ Name:变量名, Description:描述信息 } + }, + + GetDrawIcon:function(id) + { + var icon; + if (g_JSComplierResource.CustomDrawIcon.Data.has(id)) + { + const iconfont=g_JSComplierResource.CustomDrawIcon.Data.get(id); + if (iconfont.Symbol) //文字 + icon={ Symbol:iconfont.Symbol, Color:iconfont.Color, IconFont:false, ID:id }; + else //SVG图标 + icon={ Symbol:iconfont.Text, Color:iconfont.Color, Family:iconfont.Family, IconFont:true, ID:id }; + return icon; + } + + if (g_JSComplierResource.DrawIcon.Data.has(id)) + { + const iconfont=g_JSComplierResource.DrawIcon.Data.get(id); + icon={ Symbol:iconfont.Text, Color:iconfont.Color, Family:g_JSComplierResource.DrawIcon.Family, IconFont:true, ID:id }; + return icon; + } + + return null; + }, + + GetDrawTextIcon:function(id) + { + //图标对应的字符代码 + let mapIcon=new Map([ + [1,{Symbol:'↑',Color:'rgb(238,44,44)'} ],[2,{Symbol:'↓',Color:'rgb(0,139,69)'} ], + [3,{Symbol:'😧'} ],[4,{Symbol:'😨'} ],[5,{Symbol:'😁'} ],[6,{Symbol:'😱'} ], + [7,{Symbol:'B',Color:'rgb(238,44,44)'} ],[8,{Symbol:'S',Color:'rgb(0,139,69)'} ], + [9,{Symbol:'💰'} ],[10,{Symbol:'📪'} ],[11,{Symbol:'👆'} ],[12,{Symbol:'👇'} ], + [13,{Symbol:'B',Color:'rgb(178,34,34)'}, ],[14,{Symbol:'S',Color:'rgb(0,139,69)'} ], + [36,{Symbol:'Χ',Color:'rgb(238,44,44)'} ],[37,{Symbol:'X',Color:'rgb(0,139,69)'} ], + [38,{Symbol:'▲',Color:'rgb(238,44,44)'} ],[39,{Symbol:'▼',Color:'rgb(0,139,69)'} ], + [40,{Symbol:'◉',Color:'rgb(238,44,44)'}], [41,{Symbol:'◈',Color:'rgb(238,44,44)'}], + [42,{Symbol:'📌'}], [43,{Symbol:'💎'}], [44,{Symbol:'🥇'}],[45,{Symbol:'🥈'}],[46,{Symbol:'🥉'}],[47,{Symbol:'🏅'}] + ]); + + var icon=mapIcon.get(id); + return icon; + }, + + IsCustomFunction:function(name) + { + if (g_JSComplierResource.CustomFunction.Data.has(name)) return true; + return false; + }, + + IsCustomVariant:function(name) + { + if (g_JSComplierResource.CustomVariant.Data.has(name)) return true; + return false; + } +} + +var Messages = { + BadGetterArity: 'Getter must not have any formal parameters', + BadSetterArity: 'Setter must have exactly one formal parameter', + BadSetterRestParameter: 'Setter function argument must not be a rest parameter', + ConstructorIsAsync: 'Class constructor may not be an async method', + ConstructorSpecialMethod: 'Class constructor may not be an accessor', + DeclarationMissingInitializer: 'Missing initializer in %0 declaration', + DefaultRestParameter: 'Unexpected token =', + DuplicateBinding: 'Duplicate binding %0', + DuplicateConstructor: 'A class may only have one constructor', + DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', + ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer', + GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts', + IllegalBreak: 'Illegal break statement', + IllegalContinue: 'Illegal continue statement', + IllegalExportDeclaration: 'Unexpected token', + IllegalImportDeclaration: 'Unexpected token', + IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list', + IllegalReturn: 'Illegal return statement', + InvalidEscapedReservedWord: 'Keyword must not contain escaped characters', + InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence', + InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInForIn: 'Invalid left-hand side in for-in', + InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', + InvalidModuleSpecifier: 'Unexpected token', + InvalidRegExp: 'Invalid regular expression', + LetInLexicalBinding: 'let is disallowed as a lexically bound name', + MissingFromClause: 'Unexpected token', + MultipleDefaultsInSwitch: 'More than one default clause in switch statement', + NewlineAfterThrow: 'Illegal newline after throw', + NoAsAfterImportNamespace: 'Unexpected token', + NoCatchOrFinally: 'Missing catch or finally after try', + ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', + Redeclaration: '%0 \'%1\' has already been declared', + StaticPrototype: 'Classes may not have static property named prototype', + StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', + StrictDelete: 'Delete of an unqualified identifier in strict mode.', + StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block', + StrictFunctionName: 'Function name may not be eval or arguments in strict mode', + StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', + StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + StrictModeWith: 'Strict mode code may not include a with statement', + StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', + StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', + StrictReservedWord: 'Use of future reserved word in strict mode', + StrictVarName: 'Variable name may not be eval or arguments in strict mode', + TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', + UnexpectedEOS: 'Unexpected end of input', + UnexpectedIdentifier: 'Unexpected identifier', + UnexpectedNumber: 'Unexpected number', + UnexpectedReserved: 'Unexpected reserved word', + UnexpectedString: 'Unexpected string', + UnexpectedTemplate: 'Unexpected quasi %0', + UnexpectedToken: 'Unexpected token %0', + UnexpectedTokenIllegal: 'Unexpected token ILLEGAL', + UnknownLabel: 'Undefined label \'%0\'', + UnterminatedRegExp: 'Invalid regular expression: missing /' +}; + +var Regex = { + // Unicode v8.0.0 NonAsciiIdentifierStart: + NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, + // Unicode v8.0.0 NonAsciiIdentifierPart: + NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ +} + +var Character = +{ + FromCodePoint: function (cp) { + return (cp < 0x10000) ? String.fromCharCode(cp) : + String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); + }, + + //是否是空格 https://tc39.github.io/ecma262/#sec-white-space + IsWhiteSpace:function(cp) + { + return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) || + (cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0); + }, + + //是否换行 https://tc39.github.io/ecma262/#sec-line-terminators + IsLineTerminator:function(cp) + { + return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029); + }, + + // https://tc39.github.io/ecma262/#sec-names-and-keywords + IsIdentifierStart:function(cp) + { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp === 0x5C) || + //【】 + (cp===0x3010 || cp===0x3011) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(Character.FromCodePoint(cp))); + }, + + IsIdentifierPart: function (cp) + { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp >= 0x30 && cp <= 0x39) || + (cp === 0x5C) || (cp===0x23) || + //【】 + (cp===0x3010 || cp===0x3011) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(Character.FromCodePoint(cp))); + }, + + // https://tc39.github.io/ecma262/#sec-literals-numeric-literals + IsDecimalDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x39); // 0..9 + }, + + IsHexDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x39) || (cp >= 0x41 && cp <= 0x46) || (cp >= 0x61 && cp <= 0x66); // a..f + }, + + isOctalDigit: function (cp) + { + return (cp >= 0x30 && cp <= 0x37); // 0..7 + } +} + +var TOKEN_NAME={}; +TOKEN_NAME[1 /* BooleanLiteral */] = 'Boolean'; +TOKEN_NAME[2 /* EOF */] = ''; +TOKEN_NAME[3 /* Identifier */] = 'Identifier'; +TOKEN_NAME[4 /* Keyword */] = 'Keyword'; +TOKEN_NAME[5 /* NullLiteral */] = 'Null'; +TOKEN_NAME[6 /* NumericLiteral */] = 'Numeric'; +TOKEN_NAME[7 /* Punctuator */] = 'Punctuator'; +TOKEN_NAME[8 /* StringLiteral */] = 'String'; +TOKEN_NAME[9 /* RegularExpression */] = 'RegularExpression'; +TOKEN_NAME[10 /* Template */] = 'Template'; + +//公共方法 +function JSComplierHelper() +{ + +} + +JSComplierHelper.GetPeriodInfo=function(obj) +{ + const PERIOD_LIST= + [ + {Name:'MIN1', Period:4, Order:1}, + {Name:'MIN5', Period:5, Order:2}, + {Name:'MIN15', Period:6, Order:3}, + {Name:'MIN30', Period:7, Order:4}, + {Name:'MIN60', Period:8, Order:5}, + + {Name:'DAY', Period:0, Order:1000}, + {Name:'MULTIDAY', Period:40002, Order:1002}, + {Name:'DAY2', Period:40002, Order:1002}, + {Name:'DAY3', Period:40003, Order:1003}, + {Name:'DAY4', Period:40004, Order:1004}, + {Name:'DAY5', Period:40005, Order:1005}, + {Name:'WEEK', Period:1, Order:1005}, + + {Name:'DAY6', Period:40006, Order:1006}, + {Name:'DAY7', Period:40007, Order:1007}, + {Name:'DAY8', Period:40008, Order:1008}, + {Name:'DAY9', Period:40009, Order:1009}, + {Name:'DAY10', Period:40009, Order:1010}, + {Name:"WEEK2", Period:21, Order:1010}, + + {Name:'DAY11', Period:40011, Order:1011}, + {Name:'DAY12', Period:40012, Order:1012}, + {Name:'DAY13', Period:40013, Order:1013}, + {Name:'DAY14', Period:40014, Order:1014}, + + {Name:'MONTH', Period:2, Order:1030}, + {Name:"SEASON", Period:9, Order:1090}, + {Name:"HALFYEAR", Period:22, Order:1180}, + {Name:"YEAR", Period:3, Order:1365} + ]; + + if (obj.Name) + { + for(var i in PERIOD_LIST) + { + if (obj.Name && PERIOD_LIST[i].Name==obj.Name) + return PERIOD_LIST[i]; + } + + return null; + } + + if (IFrameSplitOperator.IsNumber(obj.PeriodID)) + { + for(var i in PERIOD_LIST) + { + if (PERIOD_LIST[i].Period==obj.PeriodID) + return PERIOD_LIST[i]; + } + + return null; + } + + return null; +} + +JSComplierHelper.GetConvertValueName=function(funcName) +{ + var valueName; + if (funcName=='COVER_C') valueName='CLOSE'; + else if (funcName=='COVER_O') valueName="OPEN"; + else if (funcName=='COVER_H') valueName="HIGH"; + else if (funcName=='COVER_L') valueName="LOW"; + else if (funcName=='COVER_A') valueName="AMOUNT"; + else if (funcName=='COVER_V') valueName="VOL"; + + return valueName; +} + + + +//编译异常, 错误类 +function ErrorHandler() +{ + this.Error=[]; + + this.RecordError=function(error) + { + this.Error.push(error); + } + + this.ConstructError=function(msg,column) + { + let error=new Error(msg); + //通过自己抛异常并自己截获 来获取调用堆栈信息 + try + { + throw error; + } + catch(base) + { + if (Object.create && Object.defineProperties) + { + error=Object.create(base); + error.Column=column; + } + } + + return error; + } + + this.CreateError=function(index, line, col, description, word) + { + let msg='Line ' + line + ': ' + description; + let error=this.ConstructError(msg,col); + error.Index=index; + error.LineNumber=line; + error.Description=description; + error.Word=word; //错误单词 + return error; + } + + this.ThrowError=function(index, line, col, description, word) + { + let error=this.CreateError(index,line,col,description,word); + throw error; + } + + //重新下载数据 + this.ThrowDownloadJob=function(index, line, col, description,job) + { + let error=this.CreateError(index,line,col,description); + error.Job=job; + throw error; + } +} + +//扫描类 +function Scanner(code, ErrorHandler) +{ + this.Source=code; + this.ErrorHandler=ErrorHandler; + this.Length=code.length; + this.Index=0; + this.LineNumber=(code.length>0)?1:0; + this.LineStart=0; + this.CurlyStack=[]; + + this.SaveState=function() //保存当前扫描状态 + { + return { Index:this.Index, LineNumber:this.LineNumber, LineStart:this.LineStart }; + } + + this.RestoreState=function(state) //还原扫描状态 + { + this.Index=state.Index; + this.LineNumber=state.LineNumber; + this.LineStart=state.LineStart; + } + + this.IsEOF=function() //否是已经结束 + { + return this.Index>=this.Length; + } + + this.IsKeyword=function(id) + { + return false; + } + + this.CodePointAt = function (i) + { + let cp = this.Source.charCodeAt(i); + if (cp >= 0xD800 && cp <= 0xDBFF) + { + let second = this.Source.charCodeAt(i + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { + var first = cp; + cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + return cp; + } + + this.Lex=function() + { + if (this.IsEOF()) return { Type:2/*EOF*/, Value:'', LineNumber:this.LineNumber, LineStart:this.LineStart, Start:this.Index, End:this.Index }; + let cp=this.Source.charCodeAt(this.Index); + + //变量名 或 关键字 + if (Character.IsIdentifierStart(cp)) return this.ScanIdentifier(); + + //( ) ; 开头 操作符扫描 + if (cp === 0x28 || cp === 0x29 || cp === 0x3B) return this.ScanPunctuator(); + + //' " 开头 字符串扫描 + if (cp === 0x27 || cp === 0x22) return this.ScanStringLiteral(); + + //. 开头 浮点型 + if (cp==0x2E) + { + if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index + 1))) + return this.ScanNumericLiteral(); + + return this.ScanPunctuator(); + } + + //数字 + if (Character.IsDecimalDigit(cp)) return this.ScanNumericLiteral(); + + if (cp >= 0xD800 && cp < 0xDFFF) + { + if (Character.IsIdentifierStart(this.CodePointAt(this.Index))) return this.ScanIdentifier(); + } + + return this.ScanPunctuator(); + + } + + //关键字 变量名 https://tc39.github.io/ecma262/#sec-names-and-keywords + this.ScanIdentifier=function() + { + let type; + let start=this.Index; + //0x5C 反斜杠 + let id=(this.Source.charCodeAt(start)=== 0x5C) ? this.GetComplexIdentifier() : this.GetIdentifier(); + + if (id.length) type=3; //Identifier + else if (this.IsKeyword(id)) type=4; //Keyword + else if (id==null) type=5; //NullLiteral + else if (id=='true' || id=='false') type=1; //BooleanLiteral + else type=3; //Identifier + + if (type!=3 && (start+id.length!=this.Index)) + { + let restore=this.Index; + this.Index=start; + throw Messages.InvalidEscapedReservedWord; + this.Index=restore; + } + + if (id=='AND' || id=='OR') type=7 /*Punctuator*/; + + return { Type:type, Value:id, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index}; + } + + this.GetIdentifier=function() + { + let start=this.Index++; //start 保存进来的位置 + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + if (ch==0x5C) + { + this.Index=start; + return this.GetComplexIdentifier(); + } + else if (ch >= 0xD800 && ch < 0xDFFF) + { + this.Index=start; + return this.GetComplexIdentifier(); + } + + if (Character.IsIdentifierPart(ch)) ++this.Index; + else break; + } + + return this.Source.slice(start,this.Index); + } + + //操作符 https://tc39.github.io/ecma262/#sec-punctuators + this.ScanPunctuator=function() + { + let start=this.Index; + let str=this.Source[this.Index]; + switch(str) + { + case '(': + ++this.Index; + break; + case ')': + case ';': + case ',': + ++this.Index; + break; + case '.': + ++this.Index; + /*if (this.Source[this.Index] === '.' && this.Source[this.Index + 1] === '.') + { + //Spread operator: ... + this.Index += 2; + str = '...'; + } + */ + break; + default: + str=this.Source.substr(this.Index,3); + if (str=='AND') + { + this.Index+=3; + } + else + { + str = this.Source.substr(this.Index, 2); + if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '<=' || str === '>=' || str === '=>' || str==':=' || str=='OR' || str=='<>') + { + this.Index += 2; + } + else + { + str=this.Source[this.Index]; + if ('<>=!+-*%&|^/:'.indexOf(str) >= 0) ++this.Index; + } + } + } + + if (this.Index==start) + this.ThrowUnecpectedToken(); + + return { Type:7/*Punctuator*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index }; + } + + //字符串 https://tc39.github.io/ecma262/#sec-literals-string-literals + this.ScanStringLiteral=function() + { + let start=this.Index; + let quote=this.Source[this.Index]; + + ++this.Index; + var octal=false; + let str=''; + while(!this.IsEOF()) + { + let ch=this.Source[this.Index++]; + if (ch==quote) + { + quote=''; + break; + } + else if (ch=='\\') //字符串转义 + { + throw "not complete"; + } + else if (Character.IsLineTerminator(ch.charCodeAt(0))) + { + break; + } + else + { + str+=ch; + } + } + + if (quote!='') + { + this.Index=start; + this.ThrowUnecpectedToken(); + } + + return {Type:8/*StringLiteral*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index}; + } + + this.ScanNumericLiteral=function() + { + let start=this.Index; + let ch=this.Source[this.Index]; + let num=''; + if (ch!='.') + { + num=this.Source[this.Index++]; + ch=this.Source[this.Index]; + // Hex number starts with '0x'. 16进制 + if (num=='0') + { + if (ch=='x' || ch=='X') + { + ++this.Index; + return this.ScanHexLiteral(start); + } + } + + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + + ch=this.Source[this.Index]; + } + + if (ch=='.') + { + num+=this.Source[this.Index++]; + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + ch=this.Source[this.Index]; + } + + //科学计数法 + if (ch=='e' || ch=='E') + { + num+=this.Source[this.Index++]; + ch=this.Source[this.Index]; + if (ch=='+' || ch=='-') num+=this.Source[this.Index]; + if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index))) + { + num+=this.Source[this.Index++]; + } + } + else + { + this.ThrowUnecpectedToken(); + } + } + + if (Character.IsIdentifierStart(this.Source.charCodeAt(this.Index))) + { + this.ThrowUnecpectedToken(); + } + + return { Type:6/*NumericLiteral*/, Value:parseFloat(num), LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index }; + } + + //空格 或 注释 + this.ScanComments=function() + { + let comments; + let start=(this.Index==0); + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + if (Character.IsWhiteSpace(ch)) //过滤掉空格 + { + ++this.Index; + } + else if (Character.IsLineTerminator(ch)) + { + ++this.Index; + if (ch==0x0D && this.Source.charCodeAt(this.Index)==0x0A) ++this.Index; //回车+换行 + ++this.LineNumber; + this.LineStart=this.Index; + start=true; + } + else if (ch==0x2F) // //注释 + { + ch=this.Source.charCodeAt(this.Index+1); + if (ch==0x2F) + { + this.Index+=2; + let comment=this.SkipSingleLineComment(2); + start=true; + } + else + { + break; + } + } + else if (ch==0x7B) //{ } 注释 + { + this.Index+=1; + let comment = this.SkipMultiLineComment(); + } + else + { + break; + } + } + + return comments; + } + + this.SkipMultiLineComment=function() + { + var comments = []; + while(!this.IsEOF()) + { + var ch=this.Source.charCodeAt(this.Index); + if (Character.IsLineTerminator(ch)) + { + ++this.LineNumber; + ++this.Index; + this.LineStart=this.Index; + } + else if (ch==0x7D) + { + this.Index+=1; + return comments; + } + else + { + ++this.Index; + } + } + + return comments; + } + + //单行注释 https://tc39.github.io/ecma262/#sec-comments + this.SkipSingleLineComment=function(offset) + { + let comments=[]; + while(!this.IsEOF()) + { + let ch=this.Source.charCodeAt(this.Index); + ++this.Index; + if (Character.IsLineTerminator(ch)) + { + if (ch === 13 && this.Source.charCodeAt(this.Index) === 10) + ++this.Index; + + ++this.LineNumber; + this.LineStart=this.Index; + return comments; + } + } + + return comments; + } + + this.ThrowUnecpectedToken=function(message,word) + { + if (!message) message = Messages.UnexpectedTokenIllegal; + return this.ErrorHandler.ThrowError(this.Index, this.LineNumber, this.Index - this.LineStart + 1, message, word); + } + +} + +function Tokenizer(code) +{ + this.ErrorHandler=new ErrorHandler(); //错误信息处理类 + this.Scanner=new Scanner(code,this.ErrorHandler); + this.Buffer=[]; + + this.GetNextToken=function() + { + if (this.Buffer.length==0) + { + let comments=this.Scanner.ScanComments(); + if (!this.Scanner.IsEOF()) + { + let token=this.Scanner.Lex(); + + let entry={ Type:TOKEN_NAME[token.Type], Value:this.Scanner.Source.slice(token.Start, token.End)}; + + this.Buffer.push(entry); + } + } + + return this.Buffer.shift(); + } +} + +var Syntax = { + AssignmentExpression: 'AssignmentExpression', + AssignmentPattern: 'AssignmentPattern', + ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AwaitExpression: 'AwaitExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExportAllDeclaration: 'ExportAllDeclaration', + ExportDefaultDeclaration: 'ExportDefaultDeclaration', + ExportNamedDeclaration: 'ExportNamedDeclaration', + ExportSpecifier: 'ExportSpecifier', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForOfStatement: 'ForOfStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', + ImportSpecifier: 'ImportSpecifier', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + MetaProperty: 'MetaProperty', + MethodDefinition: 'MethodDefinition', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', + Program: 'Program', + Property: 'Property', + RestElement: 'RestElement', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SpreadElement: 'SpreadElement', + Super: 'Super', + SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' +}; + + +function Node(ErrorHandler) +{ + this.IsNeedIndexData=false; //是否需要大盘数据 + this.IsNeedLatestIndexData=false; //是否需要下载最新大盘数据 + this.IsNeedLatestData=false; //是否需要最新的个股行情数据 + this.IsNeedSymbolData=false; //是否需要下载股票数据 + this.IsNeedMarginData=new Set(); + this.IsNeedNewsAnalysisData=new Set(); //新闻统计数据 + this.NeedBlockIncreaseData=[]; //是否需要市场涨跌股票数据统计 + this.IsNeedSymbolExData=new Set(); //下载股票行情的其他数据 + this.NeedHK2SHSZData=[]; //下载北上资金数据 + this.IsNeedSectionFinance=new Map(); //下载截面财务数据 { key= 报告期 , Set() 字段} + + this.FunctionData=[]; //{ID:, Args:, FunctionName: } + //FINVALUE(ID),FINONE(ID,Y,MMDD) + + this.IsAPIData=[] //加载API数据 + + this.ExecuteIndex=[]; //执行调用指标 + this.OtherSymbolData=[]; //其他股票数据 key=股票代码(小写) + this.PeriodSymbolData=[]; //跨周期数据 { Period:, VarName: } + + this.ErrorHandler=ErrorHandler; + + this.GetDataJobList=function() //下载数据任务列表 + { + let jobs=[]; + if (this.IsNeedSymbolData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA}); + if (this.IsNeedIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA}); + if (this.IsNeedLatestData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA}); + if (this.IsNeedLatestIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_LATEST_INDEX_DATA}); + + //涨跌停家数统计 + for(var i in this.NeedBlockIncreaseData) + { + jobs.push(this.NeedBlockIncreaseData[i]); + } + + //加载融资融券 + for(var jobID of this.IsNeedMarginData) + { + jobs.push({ID:jobID}); + } + + //加载北上资金 + for(var i in this.NeedHK2SHSZData) + { + jobs.push(this.NeedHK2SHSZData[i]); + } + + //加载新闻统计 + for(var jobID of this.IsNeedNewsAnalysisData) + { + jobs.push({ID:jobID}); + } + + //行情其他数据 + for(var jobID of this.IsNeedSymbolExData) + { + jobs.push({ID:jobID}); + } + + //获取截面数据下载任务 + for(var item of this.IsNeedSectionFinance) + { + jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_SF, SF:item}); + } + + for(var i in this.IsAPIData) + { + var item=this.IsAPIData[i]; + jobs.push(item); + } + + for(var i in this.ExecuteIndex) + { + var item=this.ExecuteIndex[i]; + jobs.push(item); + } + + for(var i in this.OtherSymbolData) + { + var item=this.OtherSymbolData[i]; + jobs.push(item); + } + + for(var i in this.PeriodSymbolData) + { + var item=this.PeriodSymbolData[i]; + jobs.push(item); + } + + for(var i in this.FunctionData) + { + var item=this.FunctionData[i]; + jobs.push(item); + } + + return jobs; + } + + this.VerifySymbolVariable=function(varName, token) + { + let setIndexName=new Set(['INDEXA','INDEXC','INDEXH','INDEXL',"INDEXO","INDEXV",'INDEXDEC','INDEXADV']); + if (setIndexName.has(varName)) + { + this.IsNeedIndexData=true; + return; + } + + let setSymbolDataName=new Set(['CLOSE','C','VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT','AMO','VOLINSTK']); + if (setSymbolDataName.has(varName)) + { + this.IsNeedSymbolData=true; + return; + } + + if (varName==='VOLR') + { + if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA)) + this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA); + } + + //CAPITAL流通股本(手), EXCHANGE 换手率, TOTALCAPITAL 总股本(手) + let setVariantName=new Set( + [ + "CAPITAL","TOTALCAPITAL","EXCHANGE", + "HYBLOCK","DYBLOCK","GNBLOCK","FGBLOCK","ZSBLOCK","ZHBLOCK","ZDBLOCK","HYZSCODE", + "GNBLOCKNUM","FGBLOCKNUM","ZSBLOCKNUM","ZHBLOCKNUM","ZDBLOCKNUM", + "HYSYL","HYSJL" + ]); + + if (setVariantName.has(varName)) + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:varName }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (varName=='FROMOPEN') + { + this.IsNeedLatestIndexData=true; + return; + } + + if (varName=='ADVANCE' || varName=="DECLINE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA, IsSelfSymbol:true, FunctionName:varName }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.NeedBlockIncreaseData.push(item); + return; + } + + if (varName.indexOf('#')>0) + { + var aryWrods = varName.split('#'); + if (aryWrods.length!=2) + { + var errorMessage=`${varName}, 跨周期语法错误, 变量名#周期`; + this.ThrowUnexpectedToken(token,errorMessage); + } + else + { + const VAR_NAME=["C", "CLOSE", "O","OPEN", "H","HIGH", "L", "LOW", "AMOUNT", "AMO", "VOL", "V" , "VOLINSTK"]; + if (VAR_NAME.indexOf(aryWrods[0])<0) + { + var errorMessage=`${varName}, 跨周期语法错误, 数据只支持'OPEN,HIGH,LOW,CLOSE,VOL,AMOUNT,VOLINSTK'`; + this.ThrowUnexpectedToken(token,errorMessage); + } + + const VAR_PERIOD_NAME= + [ + "MIN1","MIN5","MIN15","MIN30","MIN60","DAY","WEEK","MONTH","SEASON","YEAR","HALFYEAR","WEEK2", + "MULTIDAY","DAY2","DAY3","DAY4","DAY5","DAY6","DAY7","DAY8","DAY9","DAY10", + "DAY11","DAY12","DAY13","DAY14","DAY15" + ]; + if (VAR_PERIOD_NAME.indexOf(aryWrods[1])<0) + { + var errorMessage=`${varName}, 跨周期语法错误, 周期只支持'MIN1,MIN5,MIN15,MIN30,MIN60,DAY,WEEK,MONTH,SEASON,YEAR'`; + this.ThrowUnexpectedToken(token,errorMessage); + } + + var item={ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_PERIOD_DATA, VarName:varName, ValueName:aryWrods[0], PeriodName:aryWrods[1] }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.PeriodSymbolData.push(item); + } + + return; + } + + if (g_JSComplierResource.IsCustomVariant(varName)) //自定义函数 + { + var item={ VariantName:varName, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + } + + this.VerifySymbolLiteral=function(value,token) + { + if (IFrameSplitOperator.IsString(value)) + { + if (value.indexOf('$')>0) + { + var aryValue=value.split('$'); + if (aryValue.length!=2) return; + + var item= { Literal:value, ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_OTHER_SYMBOL_DATA }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + + this.OtherSymbolData.push(item); + } + } + } + + this.VerifySymbolFunction=function(callee,args, token) + { + //自定义函数 可以覆盖系统内置函数 + if (g_JSComplierResource.IsCustomFunction(callee.Name)) + { + var item={FunctionName:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA, Args:args} + if (token) item.Token={ Index:token.Start, Line:token.LineNumber}; + this.FunctionData.push(item); + return; + } + + if (callee.Name=='DYNAINFO') + { + this.IsNeedLatestData=true; + return; + } + + //财务函数 + if (callee.Name=='FINANCE') + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="FINVALUE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="FINONE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=='GPJYVALUE') + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="SCJYVALUE") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SCJYVALUE, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name==='MARGIN') + { + let jobID=JS_EXECUTE_JOB_ID.GetMarginJobID(args[0].Value); + if (jobID && !this.IsNeedMarginData.has(jobID)) this.IsNeedMarginData.add(jobID); + return; + } + + if (callee.Name==='NEWS') + { + let jobID=JS_EXECUTE_JOB_ID.GetNewsAnalysisID(args[0].Value); + if (jobID && !this.IsNeedNewsAnalysisData.has(jobID)) this.IsNeedNewsAnalysisData.add(jobID); + return; + } + + if (callee.Name==='HK2SHSZ') //北上资金 + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SHSZ, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.NeedHK2SHSZData.push(item); + return; + } + + if (callee.Name=='COST' || callee.Name=='WINNER' || callee.Name=='PPART' || callee.Name=='COSTEX' || + callee.Name=='LWINNER' || callee.Name=='PWINNER') //需要流通股 + { + //下载流通股 + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:[7], FunctionName:"FINANCE", FunctionName2:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name=="INBLOCK") + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:"INBLOCK" }; //下载所有板块 + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.FunctionData.push(item); + return; + } + + if (callee.Name==='BETA') //beta需要下载上证指数 + { + this.IsNeedIndexData=true; + return; + } + + if (callee.Name=='UPCOUNT' || callee.Name=='DOWNCOUNT') //上涨下跌个数 + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.NeedBlockIncreaseData.push(item); + return; + } + + if (callee.Name=='STKINDI') //指标调用 + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_EXECUTE_INDEX, Args:args, FunctionName:callee.Name }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.ExecuteIndex.push(item); + return; + } + + if (callee.Name=='COVER_C' || callee.Name=='COVER_O' || callee.Name=='COVER_H' || callee.Name=='COVER_L' || callee.Name=='COVER_A' || callee.Name=='COVER_V') //跨周期函数 + { + var periodName=args[0].Value; + const VAR_PERIOD_NAME=["MIN1","MIN5","MIN15","MIN30","MIN60","DAY","WEEK","MONTH","SEASON","YEAR"]; + if (VAR_PERIOD_NAME.indexOf(periodName)<0) + { + var errorMessage=`${callee.Name}, 跨周期函数错误, 周期只支持'MIN1,MIN5,MIN15,MIN30,MIN60,DAY,WEEK,MONTH,SEASON,YEAR'`; + this.ThrowUnexpectedToken(token,errorMessage); + } + + var valueName=JSComplierHelper.GetConvertValueName(callee.Name); + var item={ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_PERIOD_DATA, FunctionName:callee.Name, ValueName:valueName, PeriodName:periodName }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.PeriodSymbolData.push(item); + return; + } + + if (callee.Name=='SF') //Section finance + { + let period=JS_EXECUTE_JOB_ID.GetSectionReportPeriod(args[0].Value,args[1].Value); //报告期 + if (!period) return; + let jobID=JS_EXECUTE_JOB_ID.GetSectionFinanceID(args[2].Value); + if (!jobID) return; + var sfkey=period.Year+ '-' + period.Quarter; + + if (!this.IsNeedSectionFinance.has(sfkey)) + { + var finacne={ Period:period, Fields:new Set([jobID]) }; + this.IsNeedSectionFinance.set(sfkey, finacne); + } + else + { + var finacne=this.IsNeedSectionFinance.get(sfkey); + if (!finacne.Fields.has(jobID)) finacne.Fields.add(jobID); + } + + return; + } + + if (callee.Name=="LOADAPIDATA") //加载API数据 + { + var item= { Name:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA,Args:args }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber}; + + this.IsAPIData.push(item); + return; + } + + let setStockDataName=new Set(['CLOSE',"C",'VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT','AMO','VOLINSTK']); + if (setStockDataName.has(callee.Name)) + { + var item= { Name:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_OTHER_SYMBOL_DATA,Args:args }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber}; + + this.OtherSymbolData.push(item); + return; + } + + } + + this.VerifyMemberVariable=function(object,property,token) + { + var item={ ID:JS_EXECUTE_JOB_ID.JOB_EXECUTE_INDEX, Member:{Object:object, Property:property} }; + if (token) item.Token={ Index:token.Start, Line:token.LineNumber }; + this.ExecuteIndex.push(item); + return; + } + + this.ExpressionStatement=function(expression) + { + return { Type:Syntax.ExpressionStatement, Expression:expression }; + } + + this.Script=function(body) + { + return {Type:Syntax.Program, Body:body, SourceType:'通达信脚本' }; + } + + this.SequenceExpression=function(expression) + { + return {Type:Syntax.SequenceExpression, Expression:expression }; + } + + this.BinaryExpression=function(operator, left, right) + { + let logical = (operator === '||' || operator === '&&' || operator=='AND' || operator=='OR'); + let type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression; + + return { Type:type, Operator:operator, Left:left, Right:right }; + } + + this.Literal=function(value,raw,token) + { + this.VerifySymbolLiteral(value, token); + return { Type:Syntax.Literal, Value:value, Raw:raw }; + } + + this.Identifier=function(name, token) + { + this.VerifySymbolVariable(name, token); + + return { Type:Syntax.Identifier, Name:name}; + } + + //成员变量, 不需要检测 + this.MemberIdentifier=function(name, token) + { + return { Type:Syntax.Identifier, Name:name}; + } + + this.AssignmentExpression=function (operator, left, right) + { + return { Type:Syntax.AssignmentExpression, Operator:operator, Left:left, Right:right }; + } + + this.UnaryExpression=function(operator, argument) + { + return { Type:Syntax.UnaryExpression, Operator:operator, Argument:argument, Prefix:true }; + } + + this.EmptyStatement=function() + { + return { Type:Syntax.EmptyStatement }; + } + + this.CallExpression=function(callee, args, token) + { + this.VerifySymbolFunction(callee, args, token); + + return { Type:Syntax.CallExpression, Callee:callee, Arguments:args }; + } + + this.StaticMemberExpression=function(object, property,token) + { + this.VerifyMemberVariable(object,property, token); + return { Type:Syntax.MemberExpression, Computed:false, Object:object, Property:property }; + } + + this. Directive=function(expression, directive) + { + return { Type:Syntax.ExpressionStatement, Expression : expression, Directive: directive }; + } + + //////////////////////////////////////////////////////////////// + this.ThrowUnexpectedToken=function(token,message) + { + throw this.UnexpectedTokenError(token,message); + } + + this.UnexpectedTokenError=function(token,message) + { + let msg=message || Messages.UnexpectedToken; + let value='ILLEGAL'; + if (token) + { + if (!message) { } + value=token.Value; + } + + msg=msg.replace("%0",value); + if (token && typeof(token.LineNumber)=='number') + { + let index=token.Start; + let line=token.LineNumber; + let column=-1 + return this.ErrorHandler.CreateError(index,line,column,msg); + } + } +} + + + +function JSParser(code) +{ + this.ErrorHandler=new ErrorHandler(); + this.Scanner=new Scanner(code, this.ErrorHandler); + this.Node=new Node(this.ErrorHandler); //节点创建 + + this.LookAhead={Type:2, Value:'', LineNumber:this.Scanner.LineNumber, LineStart:0, Start:0, End:0 }; + this.HasLineTerminator=false; + this.Context = { + IsModule: false, + await: false, + allowIn: true, + allowStrictDirective: true, + allowYield: true, + FirstCoverInitializedNameError: null, + IsAssignmentTarget: false, + IsBindingElement: false, + InFunctionBody: false, + inIteration: false, + inSwitch: false, + labelSet: {}, + Strict: false + }; + + this.PeratorPrecedence = + { + ')': 0, + ';': 0, + ',': 0, + ']': 0, + '||': 1, + 'OR':1, + '&&': 2, + 'AND':2, + '|': 3, + '^': 4, + '&': 5, + '==': 6, + '=': 6, + '!=': 6, + '<>':6, + '===': 6, + '!==': 6, + '<': 7, + '>': 7, + '<=': 7, + '>=': 7, + '<<': 8, + '>>': 8, + '>>>': 8, + '+': 9, + '-': 9, + '*': 11, + '/': 11, + '%': 11 + }; + + this.StartMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 }; + this.LastMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 }; + + this.Initialize=function() + { + this.NextToken(); + this.LastMarker={ Index:this.Scanner.Index, Line:this.Scanner.LineNumber, Column:this.Scanner.Index-this.Scanner.LineStart }; + } + + + this.CreateNode=function() + { + return { Index:this.StartMarker.Index, Line:this.StartMarker.Line, Column:this.StartMarker.Column }; + } + + this.StartNode=function(token, lastLineStart) + { + if (lastLineStart==void 0) { lastLineStart=0; } + + let column = token.Start - token.LineStart; + let line = token.LineNumber; + if (column < 0) + { + column += lastLineStart; + line--; + } + + return { Index: token.Start, Line: line, Column: column }; + } + + this.Match=function(value) + { + return this.LookAhead.Type==7 /*Punctuator*/ && this.LookAhead.Value==value; + } + + this.Expect=function(value) + { + let token=this.NextToken(); + if (token.Type!=7 /*Punctuator*/ || token.Value!=value) + this.ThrowUnexpectedToken(token); + } + + //是否是赋值操作符 + this.MatchAssign=function() + { + if (this.LookAhead.Type!=7 /*Punctuator*/) return false; + let op=this.LookAhead.Value; + + return op==':' || op==':='; + } + + this.GetTokenRaw=function(token) + { + return this.Scanner.Source.slice(token.Start, token.End); + } + + this.NextToken=function() + { + let token=this.LookAhead; + this.LastMarker.Index=this.Scanner.Index; + this.LastMarker.Line=this.Scanner.LineNumber; + this.LastMarker.Column=this.Scanner.Index-this.Scanner.LineStart; + this.CollectComments(); //过滤注释 空格 + + if (this.Scanner.Index !== this.StartMarker.Index) + { + this.StartMarker.Index = this.Scanner.Index; + this.StartMarker.Line = this.Scanner.LineNumber; + this.StartMarker.Column = this.Scanner.Index - this.Scanner.LineStart; + } + + let next=this.Scanner.Lex(); + this.HasLineTerminator=(token.LineNumber!=next.LineNumber); + if (next && this.Context.Strict && next.Type==3/*Identifier */) + { + //TODO: + } + + this.LookAhead=next; + + return token; + } + + this.CollectComments=function() + { + this.Scanner.ScanComments(); + } + + this.ParseScript=function() + { + let node=this.CreateNode(); + let body=this.ParseDirectivePrologues(); + + while(this.LookAhead.Type!=2 /*EOF*/) + { + body.push(this.ParseStatementListItem()) + } + + return this.Finalize(node,this.Node.Script(body)); + } + + //https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive + this.ParseDirective=function() + { + let token=this.LookAhead; + let node=this.CreateNode(); + let expr=this.ParseExpression(); + + let directive = (expr.Type === Syntax.Literal) ? this.GetTokenRaw(token).slice(1, -1) : null; + this.ConsumeSemicolon(); + + return this.Finalize(node, directive ? this.Node.Directive(expr, directive) : this.Node.ExpressionStatement(expr)); + } + + this.ParseDirectivePrologues=function() + { + let firstRestricted=null; + let body=[]; + while(true) + { + let token=this.LookAhead; + if (token.Type!=8 /*StringLiteral*/) break; + + let statement=this.ParseDirective(); + body.push(statement); + } + + return body; + } + + // https://tc39.github.io/ecma262/#sec-block + this.ParseStatementListItem=function() + { + let statement; + this.Context.IsAssignmentTarget=true; + this.Context.IsBindingElement=true; + if (this.LookAhead.Type==4 /*Keyword*/) + { + + } + else + { + statement=this.ParseStatement(); + } + + return statement; + } + + // https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations + this.ParseStatement=function() + { + let statement; + switch(this.LookAhead.Type) + { + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + case 10 /* Template */: + case 9 /* RegularExpression */: + statement = this.ParseExpressionStatement(); + break; + case 7 /* Punctuator */: + let value = this.LookAhead.Value; + if (value === '(') statement = this.ParseExpressionStatement(); + else if (value === ';') statement = this.ParseEmptyStatement(); + else statement = this.ParseExpressionStatement(); + break; + case 3 /* Identifier */: + statement = this.ParseLabelledStatement(); + break; + case 4 /* Keyword */: + break; + default: + statement="error"; + } + + return statement; + } + + // https://tc39.github.io/ecma262/#sec-empty-statement + this.ParseEmptyStatement=function() + { + let node=this.CreateNode(); + this.Expect(';'); + return this.Finalize(node, this.Node.EmptyStatement()); + } + + //https://tc39.github.io/ecma262/#sec-labelled-statements + this.ParseLabelledStatement=function() + { + let node=this.CreateNode(); + let expr=this.ParseExpression(); + this.ConsumeSemicolon(); + let statement = new this.Node.ExpressionStatement(expr); + + return this.Finalize(node, statement); + } + + // https://tc39.github.io/ecma262/#sec-comma-operator + this.ParseExpression=function() + { + let startToken=this.LookAhead; + let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + if (this.Match(',')) + { + let expressions=[]; + expressions.push(expr); + while(this.LookAhead.Type!=2 /*EOF*/) + { + if (!this.Match(',')) break; + this.NextToken(); + expressions.push(this.IsolateCoverGrammar(this.ParseAssignmentExpression)); + } + + expr=this.Finalize(this.StartNode(startToken),this.Node.SequenceExpression(expressions)); + } + + return expr; + } + + this.ParseAssignmentExpression=function() + { + let expr; + + let startToken=this.LookAhead; + let token=startToken; + expr=this.ParseConditionalExpression(); + + if (this.MatchAssign()) + { + if (!this.Context.IsAssignmentTarget) + { + let marker=expr.Marker; + this.ThrowUnexpectedError(marker.Index,marker.Line,marker.Column,Messages.InvalidLHSInAssignment); + } + + if (!this.Match('=') && !this.Match(':')) + { + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + } + else + { + this.ReinterpretExpressionAsPattern(expr); + } + + token=this.NextToken(); + let operator=token.Value; + let right=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + expr=this.Finalize(this.StartNode(startToken), this.Node.AssignmentExpression(operator, expr, right)); + this.Context.FirstCoverInitializedNameError=null; + } + + return expr; + } + + this.ParseConditionalExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseBinaryExpression); + + return expr; + } + + this.ParseBinaryExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseExponentiationExpression); + let token=this.LookAhead; + var prec=this.BinaryPrecedence(token); + if (prec>0) + { + this.NextToken(); + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + let markers=[startToken,this.LookAhead]; + let left=expr; + let right=this.IsolateCoverGrammar(this.ParseExponentiationExpression); + let stack=[left,token.Value,right]; + let precedences = [prec]; + while(true) + { + prec=this.BinaryPrecedence(this.LookAhead); + if (prec<=0) break; + + while(stack.length>2 && prec<=precedences[precedences.length-1]) + { + right=stack.pop(); + let operator=stack.pop(); + precedences.pop(); + left=stack.pop(); + markers.pop(); + let node=this.StartNode(markers[markers.length - 1]); + stack.push(this.Finalize(node, this.Node.BinaryExpression(operator, left, right))); + } + + //Shift + stack.push(this.NextToken().Value); + precedences.push(prec); + markers.push(this.LookAhead); + stack.push(this.IsolateCoverGrammar(this.ParseExponentiationExpression)); + } + + let i=stack.length-1; + expr=stack[i]; + let lastMarker=markers.pop(); + while(i>1) + { + let marker=markers.pop(); + let lastLineStart=lastMarker && lastMarker.LineStart; + let node=this.StartNode(marker, lastLineStart); + let operator=stack[i-1]; + expr=this.Finalize(node, this.Node.BinaryExpression(operator, stack[i - 2], expr)); + i-=2; + lastMarker=marker; + } + } + + return expr; + } + + this.ParseExponentiationExpression=function() + { + let startToken=this.LookAhead; + let expr=this.InheritCoverGrammar(this.ParseUnaryExpression); + + return expr; + } + + this.ParseUnaryExpression=function() + { + let expr; + if (this.Match('+') || this.Match('-')) + { + let node=this.StartNode(this.LookAhead); + let token=this.NextToken(); + expr=this.InheritCoverGrammar(this.ParseUnaryExpression); + expr=this.Finalize(node, this.Node.UnaryExpression(token.Value, expr)); + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + } + else + { + expr=this.ParseUpdateExpression(); + } + + return expr; + } + + // https://tc39.github.io/ecma262/#sec-update-expressions + this.ParseUpdateExpression=function() + { + let expr; + let startToken=this.LookAhead; + expr=this.InheritCoverGrammar(this.ParseLeftHandSideExpressionAllowCall); + + return expr; + } + + this.ParseLeftHandSideExpressionAllowCall=function() + { + let startToken=this.LookAhead; + let expr; + expr=this.InheritCoverGrammar(this.ParsePrimaryExpression); + + while(true) + { + if (this.Match('.')) + { + this.Context.IsBindingElement = false; + this.Context.IsAssignmentTarget = true; + this.Expect('.'); + const property = this.ParseMemberIdentifierName(); + expr = this.Finalize(this.StartNode(startToken), this.Node.StaticMemberExpression(expr, property,startToken)); + + } + else if (this.Match('(')) + { + this.Context.IsBindingElement=false; + this.Context.IsAssignmentTarget=false; + var args=this.ParseArguments(); //解析 调用参数 + expr=this.Finalize(this.StartNode(startToken), this.Node.CallExpression(expr,args,startToken)); + } + else + { + break; + } + } + + return expr; + } + + /* + BooleanLiteral = 1, + EOF=2, + Identifier=3, + Keyword=4, + NullLiteral=5, + NumericLiteral=6, + Punctuator=7, + StringLiteral=9, + RegularExpression=9, + Template=10 + */ + this.IsIdentifierName=function(token) + { + return token.Type === 3 //Identifier + || token.Type === 4 //Keyword + || token.Type === 1 //BooleanLiteral + || token.Type === 5 ;//NullLiteral; + } + + this.ParseIdentifierName=function() + { + const node = this.CreateNode(); + const token = this.NextToken(); + if (!this.IsIdentifierName(token)) + { + this.ThrowUnexpectedToken(token); + } + + return this.Finalize(node, this.Node.Identifier(token.Value, token)); + } + + this.ParseMemberIdentifierName=function() + { + const node = this.CreateNode(); + const token = this.NextToken(); + if (!this.IsIdentifierName(token)) + { + this.ThrowUnexpectedToken(token); + } + + return this.Finalize(node, this.Node.MemberIdentifier(token.Value, token)); + } + + // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions + this.ParseArguments=function() + { + this.Expect('('); + var args=[]; + if (!this.Match(')')) + { + while(true) + { + let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression); + args.push(expr); + + if (this.Match(')')) break; + + this.ExpectCommaSeparator(); + + if (this.Match(')')) break; + } + } + + this.Expect(')'); + return args; + } + + // Quietly expect a comma when in tolerant mode, otherwise delegates to expect(). + this.ExpectCommaSeparator=function() + { + this.Expect(','); + } + + // https://tc39.github.io/ecma262/#sec-primary-expression + this.ParsePrimaryExpression=function() + { + let node=this.CreateNode(); + let expr; + var token, raw; + switch(this.LookAhead.Type) + { + case 3:/* Identifier */ + token=this.NextToken(); + expr=this.Finalize(node, this.Node.Identifier(token.Value, token)); + break; + case 6:/* NumericLiteral */ + case 8:/* StringLiteral */ + this.Context.IsAssignmentTarget=false; + this.Context.IsBindingElement=false; + token=this.NextToken(); + raw=this.GetTokenRaw(token); + expr=this.Finalize(node, this.Node.Literal(token.Value,raw,token)); + break; + case 7:/* Punctuator */ + switch(this.LookAhead.Value) + { + case '(': + this.Context.IsBindingElement=false; + expr=this.InheritCoverGrammar(this.ParseGroupExpression); + break; + default: + expr=this.ThrowUnexpectedToken(this.NextToken()) + } + break; + default: + expr = this.ThrowUnexpectedToken(this.NextToken()); + } + + return expr; + } + + this.ParseGroupExpression=function() + { + let expr; + this.Expect('('); + if (this.Match(')')) + { + this.NextToken(); + } + else + { + let startToken=this.LookAhead; + let params=[]; + let arrow=false; + this.Context.IsBindingElement=true; + expr=this.InheritCoverGrammar(this.ParseAssignmentExpression); + if (this.Match(',')) + { + let expressions=[]; + this.Context.IsAssignmentTarget=false; + expressions.push(expr); + while(this.LookAhead.Type!=2 /* EOF */) + { + if (!this.Match(',')) break; + + this.NextToken(); + if (this.Match(')')) + { + + } + } + } + + if (!arrow) + { + this.Expect(')'); + this.Context.IsBindingElement=false; + } + } + + return expr; + } + + // https://tc39.github.io/ecma262/#sec-expression-statement + this.ParseExpressionStatement=function() + { + let node=this.CreateNode(); + let expr=this.ParseExpression(); + this.ConsumeSemicolon(); + + return this.Finalize(node,this.Node.ExpressionStatement(expr)); + } + + this.ConsumeSemicolon=function() + { + if (this.Match(';')) + { + this.NextToken(); + } + else if (!this.HasLineTerminator) + { + //if (this.LookAhead.Type!=2/*EOF*/ && !this.Match('}')) + + this.LastMarker.Index=this.StartMarker.Index; + this.LastMarker.Line=this.StartMarker.Line; + this.LastMarker.Column=this.StartMarker.Column; + } + } + + this.ReinterpretExpressionAsPattern=function(expr) + { + switch(expr.Type) + { + case Syntax.Identifier: + case Syntax.MemberExpression: + case Syntax.AssignmentExpression: + break; + default: + break; + } + } + + this.Finalize=function(marker,node) + { + node.Marker={ Line:marker.Line, Column:marker.Column, Index:marker.Index }; + return node; + } + + this.BinaryPrecedence = function (token) + { + let op = token.Value; + let precedence; + + if (token.Type === 7 /* Punctuator */) precedence = this.PeratorPrecedence[op] || 0; + else precedence = 0; + + return precedence; + }; + + this.IsolateCoverGrammar=function(parseFunction) + { + let previousIsBindingElement=this.Context.IsBindingElement; + let previousIsAssignmentTarget=this.Context.IsAssignmentTarget; + let previousFirstCoverInitializedNameError=this.Context.FirstCoverInitializedNameError; + + this.Context.IsBindingElement=true; + this.Context.IsAssignmentTarget=true; + this.Context.FirstCoverInitializedNameError=null; + let result=parseFunction.call(this); + + if (this.Context.FirstCoverInitializedNameError!=null) + { + //错误 this.throwUnexpectedToken(this.context.firstCoverInitializedNameError); + } + + this.Context.IsBindingElement=previousIsBindingElement; + this.Context.IsAssignmentTarget=previousIsAssignmentTarget; + this.Context.FirstCoverInitializedNameError=previousFirstCoverInitializedNameError; + + return result; + } + + this.InheritCoverGrammar = function (parseFunction) + { + let previousIsBindingElement = this.Context.IsBindingElement; + let previousIsAssignmentTarget = this.Context.IsAssignmentTarget; + let previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError; + this.Context.IsBindingElement = true; + this.Context.IsAssignmentTarget = true; + this.Context.FirstCoverInitializedNameError = null; + + let result = parseFunction.call(this); + + this.Context.IsBindingElement = this.Context.IsBindingElement && previousIsBindingElement; + this.Context.IsAssignmentTarget = this.Context.IsAssignmentTarget && previousIsAssignmentTarget; + this.Context.FirstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.Context.FirstCoverInitializedNameError; + + return result; + }; + + this.ThrowUnexpectedToken=function(token,message) + { + throw this.UnexpectedTokenError(token,message); + } + + this.ThrowUnexpectedError=function(index,line,column,message) + { + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(index,line,column,msg); + } + + this.UnexpectedTokenError=function(token,message) + { + let msg=message || Messages.UnexpectedToken; + let value='ILLEGAL'; + if (token) + { + if (!message) + { + + } + value=token.Value; + } + + msg=msg.replace("%0",value); + if (token && typeof(token.LineNumber)=='number') + { + let index=token.Start; + let line=token.LineNumber; + let lastMarkerLineStart=this.LastMarker.Index-this.LastMarker.Column; + let column=token.Start-lastMarkerLineStart+1; + return this.ErrorHandler.CreateError(index,line,column,msg); + } + else + { + let index=this.LastMarker.Index; + let line=this.LastMarker.Line; + let column=this.LastMarker.Column+1; + return this.ErrorHandler.CreateError(index,line,column,msg); + } + } +} + + +/* + 算法类 +*/ +function JSAlgorithm(errorHandler,symbolData) +{ + this.ErrorHandler=errorHandler; + this.SymbolData=symbolData; //股票数据 + + //相加 + this.Add=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值相加 + if (isNumber && isNumber2) return data+data2; + + //都是数组相加 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;idata2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;idata2[i] ? 1:0); + } + } + + return result; + } + + if (isNumber) //单数据-数组 + { + for(let i in data2) + { + result[i]=null; + if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>data2[i] ? 1 : 0); + } + } + else //数组-单数据 + { + for(let i in data) + { + result[i]=null; + if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>data2 ? 1 : 0); + } + } + + return result; + } + + //大于等于 + this.GTE=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值比较 + if (isNumber && isNumber2) return (data>=data2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;i=data2[i] ? 1:0); + } + } + + return result; + } + + if (isNumber) //单数据-数组 + { + for(let i in data2) + { + result[i]=null; + if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>=data2[i] ? 1 : 0); + } + } + else //数组-单数据 + { + for(let i in data) + { + result[i]=null; + if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>=data2 ? 1 : 0); + } + } + + return result; + } + + //小于 + this.LT=function(data,data2) + { + let isNumber=typeof(data)=='number'; + let isNumber2=typeof(data2)=='number'; + + //单数值比较 + if (isNumber && isNumber2) return (data=data2 ? 1 : 0); + + //都是数组比较 + let result=[]; + if (!isNumber && !isNumber2) + { + let count=Math.max(data.length, data2.length); + for(let i=0;iOPEN,HIGH,LOW)表示该周期收阴则返回最高值,否则返回最低值 + */ + + this.IFN=function(data,trueData,falseData) + { + return this.IF(data,falseData,trueData); + } + + //指标函数 函数名全部大写 + + //引用若干周期前的数据(平滑处理). + //用法: REF(X,A),引用A周期前的X值.A可以是变量. + //平滑处理:当引用不到数据时进行的操作.此函数中,平滑时使用上一个周期的引用值. + //例如: REF(CLOSE,BARSCOUNT(C)-1)表示第二根K线的收盘价. + this.REF=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(0,data.length-n); + + //平滑处理 + var firstData=data[0]; + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value<=i) result[i]=data[i-value]; + else if (i) result[i]=result[i-1]; + else result[i]=data[i]; + } + } + + return result; + } + + //引用若干周期前的数据(未作平滑处理). + //用法: REFV(X,A),引用A周期前的X值.A可以是变量. + //平滑处理:当引用不到数据时进行的操作. + //例如: REFV(CLOSE,BARSCOUNT(C)-1)表示第二根K线的收盘价. + this.REFV=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(0,data.length-n); + + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value<=i) result[i]=data[i-value]; + } + } + + return result; + } + + //属于未来函数,引用若干周期后的数据(平滑处理). + //用法: REFX(X,A),引用A周期后的X值.A可以是变量. + //平滑处理:当引用不到数据时进行的操作.此函数中,平滑时使用上一个周期的引用值. + //例如: TT:=IF(C>O,1,2); + // REFX(CLOSE,TT);表示阳线引用下一周期的收盘价,阴线引用日后第二周期的收盘价. + this.REFX=function(data,n) + { + let result=[]; + if (typeof(n)=='number') + { + if (data.length<=0) return result; + if (n>=data.length) return result; + + result=data.slice(n,data.length); + + //平滑处理 + var lastData=data[data.length-1]; + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value+i=data.length) return result; + + result=data.slice(n,data.length); + + //平滑处理 + for(let i=0;i=n.length) continue; + var value=n[i]; + if (value>=0 && value+i0) + { + maxNumber=aryNumber[0]; + for(var i=1; i0) + { + maxAryData=aryData[0].slice(0); + for(var i=1;i0) + { + minNumber=aryNumber[0]; + for(var i=1; i0) + { + minAryData=aryData[0].slice(0); + for(var i=1;i=0;--j) + { + var value=data[i-j]; + if (!this.IsNumber(value)) + { + value=preValue; //空数据就取上一个数据 + data[i-j]=value; + } + else + { + preValue=value; + } + sum+=value; + } + + result[i]=sum/dayCount; + } + } + else + { + for(var i=0;i=dayCount.length) continue; + var sumCount=dayCount[i]; + if (!this.IsNumber(sumCount)) continue; + if (sumCount<=0) continue; + + var sum=0; + var count=0; + for(var j=i, k=0;j>=0 && k0) result[i]=sum/count; + } + } + + return result; + } + + //指数平均数指标 EMA(close,10) + //N 支持周期数组 + this.EMA=function(data,dayCount) + { + var result = []; + if (data.length<=0) return result; + + if (Array.isArray(dayCount)) + { + for(var i=0;ii+1) period=i+1; + //EMA(N) = 2/(N+1)*C + (N-1)/(N+1)*EMA', EMA'为前一天的ema + var EMAFactor=[ 2/ (period + 1), (period - 1) / (period + 1)]; + + var ema=null; + var lastEMA=null; + for(var j=0;j= 0 && ji+1) period=i+1; + + var lastSMA=null; + var sma=null; + for(var j=0;j=period) continue; + var sum=0, count=0; + for(var j=start, k=1;j= 0; --j) + { + var value = data[i-j]; + if (!this.IsNumber(value)) + { + value = preValue; + data[i-j] = value; + } + else + preValue = value; + + count += dayCount - j; + sum += value * (dayCount - j); + } + result[i] = sum / count; + } + return result; + } + } + /* + 返回平滑移动平均 + 用法:MEMA(X,N):X的N日平滑移动平均,如Y=(X+Y'*(N-1))/N + MEMA(X,N)相当于SMA(X,N,1) + */ + this.MEMA=function(data, dayCount) + { + let result=[]; + if (!data || !data.length) return result; + var i = 0, j = 0; + for (j = 0; j < data.length && !this.IsNumber(data[j]); ++j) + { + result[j] = null; + } + i = j; + if (dayCount < 1 || i+dayCount >= data.length) return result; + var sum = 0; + var data = data.slice(0); + for (; i < j+dayCount; ++i) + { + result[i] = null; + if (!this.IsNumber(data[i]) && i-1 >= 0) + data[i] = data[i-1]; + sum += data[i]; + } + result[i-1] = sum / dayCount; + for (; i < data.length; ++i) + { + if (this.IsNumber(result[i-1]) && this.IsNumber(data[i])) + result[i] = (data[i]+result[i-1]*(dayCount-1)) / dayCount; + else if (i-1 > -1 && this.IsNumber(result[i-1])) + result[i] = result[i-1]; + else + result[i] = null; + } + return result; + } + /* + 加权移动平均 + 返回加权移动平均 + 用法:EXPMA(X,M):X的M日加权移动平均 + EXPMA[i]=buffer[i]*para+(1-para)*EXPMA[i-1] para=2/(1+__para) + */ + this.EXPMA=function(data,dayCount) + { + let result=[]; + if (dayCount>=data.length) return result; + + let i=dayCount; + for(;i=data.length) return result; + + var index=0; + for(;index= start; --i) + { + var total = 0; + for (j = i, total = 0; j >= start && total < data2[i]; --j) + total += data[j]; + if (j < start) result[i] = null; + else result[i] = i - j; + } + for(i = start+1; i < data.length; ++i) + { + if (result[i]==null) + result[i] = result[i-1]; + } + return result; + } + /* + 求相反数. + 用法:REVERSE(X)返回-X. + 例如:REVERSE(CLOSE)返回-CLOSE + */ + this.REVERSE=function(data) + { + var result = []; + var i = 0; + for (; iOPEN, 20)表示统计20周期内收阳的周期数 + N 支持数组 + */ + this.COUNT=function(data,n) + { + if (Array.isArray(n)) + { + var start=null; + var dataCount=data.length; + for(var i=0;i=0 && k=n.length) continue; + + max=null; + var count=n[i]; + if (count>0 && count<=i) + { + for(j=i-count;j<=i;++j) + { + if (max==null || maxdata.length) return result; + if (n<=0) n=data.length-1; + + var nMax=0; + for(nMax=0;nMax=data[nMax]) nMax=i; + result[i]=data[nMax]; + } + + for(;i=n.length) continue; + + var min=null; + var count=n[i]; + if (count>0 && count<=i) + { + for(var j=i-count;j<=i;++j) + { + if (min==null || min>data[j]) min=data[j]; + } + } + else + { + count=i; + for(var j=0;j<=i;++j) + { + if (min==null || min>data[j]) min=data[j]; + } + } + + result[i]=min; + } + } + else + { + if (n>data.length) return result; + if (n<=0) n=data.length-1; + + var nMin=0; + for(nMin=0;nMindata[nMin]?nMin:i; + } + else + { + for(j=nMin=(i-n+1);j<=i;++j) + { + nMin=data[j]>data[nMin]?nMin:j; + } + } + + result[i]=data[nMin]; + } + } + + return result; + } + + this.STD=function(data,n) + { + var result=[]; + + var total=0; + var averageData=[]; //平均值 + for(var i=n-1;idata2[index] && data[index-1]data2 && data[index-1]data) ? 1:0; + } + } + + return result; + } + + //累乘 + this.MULAR=function(data,n) + { + var result=[]; + if (!Array.isArray(n)) + { + if(data.length=0 && k0)) continue; + + for(var j=i, k=0 ;j>=0 && kdatanum) return result; + for(E=0; i < datanum && j < num; ++i,++j) + E += data[i]/num; + if (j == num) + { + DEV = 0; + for(i--; k < num; k++) + DEV += (data[i-k]-E) * (data[i-k]-E); + result[i] = DEV; + i++; + } + for(; i < datanum; ++i) + { + E += (data[i] - data[i-num]) / num; + for(DEV=0, k = 0; k < num; ++k) + DEV += (data[i-k]-E) * (data[i-k]-E); + result[i] = DEV; + } + return result; + } + + //NOT 取反 + //求逻辑非。 + //用法: NOT(X) 返回非X,即当X=0时返回1,否则返回0。 + //例如: NOT(ISUP) 表示平盘或收阴。 + this.NOT=function(data) + { + let isNumber=typeof(data)=='number'; + if (isNumber) return data? 0:1; + + let result=[]; + for(let i in data) + { + result[i]=null; + if (this.IsNumber(data[i])) result[i]=data[i]?0:1; + } + + return result; + } + + //FORCAST 线性回归预测值 + //FORCAST(X,N)  返回线性回归预测值。 + this.FORCAST=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num < 1 || num >= datanum) + return result; + var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope; + var i, j,x; + for(j = 0; j < datanum && !this.IsNumber(data[j]); ++j) + { + result[j] = null; + } + for(i = j+num-1; i < datanum; ++i) + { + Ex = Ey = Sxy = Sxx = 0; + for(j = 0, x=num; j < num && j <= i; ++j, --x) + { + Ex += x; + Ey += data[i - j]; + } + Ex /= num; + Ey /= num; + for(j = 0, x=num; j < num && j <= i; ++j,--x) + { + Sxy += (x-Ex)*(data[i-j]-Ey); + Sxx += (x-Ex)*(x-Ex); + } + Slope = Sxy / Sxx; + Const = Ey - Ex*Slope; + result[i] = Slope * num + Const; + } + + return result; + } + + //TSMA(X,N):求X在N个周期内的时间序列三角移动平均 + //TSMA(a,n) 算法如下: + //ysum=a[i]+a[i-1]+...+a[i-n+1] + //xsum=i+i-1+..+i-n+1 + //xxsum=i*i+(i-1)*(i-1)+...+(i-n+1)*(i-n+1) + //xysum=i*a[i]+(i-1)*a[i-1]+...+(i-n+1)*a[i-n+1] + //k=(xysum -(ysum/n)*xsum)/(xxsum- xsum/n * xsum) //斜率 + //b= ysum/n - k*xsum/n + //forcast[i]=k*i+b //线性回归 + //tsma[i] = forcast[i]+k //线性回归+斜率 + this.TSMA=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num < 1 || num >= datanum) + return result; + var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope; + var i, j,x; + for(j = 0; j < datanum && !this.IsNumber(data[j]); ++j) + { + result[j] = null; + } + for(i = j+num-1; i < datanum; ++i) + { + Ex = Ey = Sxy = Sxx = 0; + for(j = 0, x=num; j < num && j <= i; ++j, --x) + { + Ex += x; + Ey += data[i - j]; + } + Ex /= num; + Ey /= num; + for(j = 0, x=num; j < num && j <= i; ++j,--x) + { + Sxy += (x-Ex)*(data[i-j]-Ey); + Sxx += (x-Ex)*(x-Ex); + } + Slope = Sxy / Sxx; + Const = Ey - Ex*Slope; + result[i] = (Slope * num + Const) + Slope; + } + + return result; + } + + //SLOPE 线性回归斜率 + //SLOPE(X,N)  返回线性回归斜率。 + this.SLOPE=function(data,n) + { + let result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + if (n<1 || !data.length) return result; + if (n>=data.length) return result; + + let start=0; + for(let i=0;i= datanum) + return result; + var i = 0, j = 0; + for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX = 0, SigmaX = 0, MidResult; + for (; i < datanum && j < num; ++i, ++j) + { + SigmaPowerX += data[i] * data[i]; + SigmaX += data[i]; + } + if (j == num) + { + MidResult = num*SigmaPowerX - SigmaX*SigmaX; + result[i-1] = Math.sqrt(MidResult) / num; + } + for(; i < datanum; ++i) + { + SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num]; + SigmaX += data[i] - data[i-num]; + MidResult = num*SigmaPowerX - SigmaX*SigmaX; + result[i] = Math.sqrt(MidResult) / num; + } + + return result; + } + + //VAR 估算样本方差 + //VAR(X,N)  返回估算样本方差。 + this.VAR=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num <= 1 || num >= datanum) + return result; + var i, j; + for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX, SigmaX; + for (j = 0, i = i+num-1; i < datanum; ++i) + { + SigmaPowerX = SigmaX = 0; + for(j=0; j < num && j <= i; ++j) + { + SigmaPowerX += data[i-j] * data[i-j]; + SigmaX += data[i-j]; + } + result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / num * (num -1); + } + + return result; + } + + //VARP 总体样本方差 + //VARP(X,N)  返回总体样本方差 。 + this.VARP=function(data,n) + { + var result=[]; + if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型 + var num = n; + var datanum = data.length; + if (num < 1 || num >= datanum) + return result; + var i = 0, j = 0; + for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i) + { + result[i] = null; + } + var SigmaPowerX = 0, SigmaX = 0; + for (; i < datanum && j < num; ++i, ++j) + { + SigmaPowerX += data[i] * data[i]; + SigmaX += data[i]; + } + if (j == num) + result[i-1] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num); + for(; i < datanum; ++i) + { + SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num]; + SigmaX += data[i] - data[i-num]; + result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num); + } + + return result; + } + + //RANGE(A,B,C)表示A>B AND AMath.min(range,range2) && data=range.length) continue; + + rangeValue=range[i]; + } + else + { + rangeValue=range; + } + if (!this.IsNumber(rangeValue)) continue; + + if (!isNumber3) + { + if (i>=range2.length) continue; + + rangeValue2=range2[i]; + } + else + { + rangeValue2=range2; + } + if (!this.IsNumber(rangeValue2)) continue; + + + result[i]= (value>Math.min(rangeValue,rangeValue2) && valueOPEN,10) + 表示10日内存在着阳线 + */ + this.EXIST=function(data,n) + { + if (typeof(data)=='number') return 0; + + if (Array.isArray(n)) + { + var result=[]; + if (data.length<=0) return result; + + for( var i in data) //初始化 + { + result[i]=0; + } + + for(var i=0;ii+1) period=i+1; + + var bExist=false; + var index=0, value=0; + for(var j=0;j0) + { + bExist=true; + break; + } + } + + result[i]=bExist?1:0; + } + + return result; + } + else + { + n=parseInt(n); + var latestID=null; //最新满足条件的数据索引 + var result=[]; + var value; + for(let i=0;i0) latestID=i; + + if (latestID!=null && i-latestIDOPEN,5)查找阳线,5天内再次出现的阳线不被记录在内 + */ + this.FILTER=function(data,n) + { + var result=[]; + for(let i=0,j=0; i0) day=0; + else if (day!=null) ++day; + + if (day!=null) result[i]=day; + } + + return result; + } + + /* + N周期内第一个条件成立到当前的周期数. + 用法: + BARSSINCEN(X,N):N周期内第一次X不为0到现在的天数,N为常量 + 例如: + BARSSINCEN(HIGH>10,10)表示10个周期内股价超过10元时到当前的周期数 + */ + this.BARSSINCEN=function(data,n) + { + var result=[]; + if (this.IsNumber(n) && Array.isArray(data)) + { + var nPeriod=n; + if (nPeriod<1) nPeriod=data.length; + var i=this.GetFirstVaildIndex(data); + if (i>=data.length) return result; + var j=0; + if (i <= nPeriod - 1) j = nPeriod - 1; + else j = i; + + result[j] = j - i; + + for (; j < data.length; ++j) + { + if (this.IsNumber(result[j - 1])) + { + if (result[j - 1] + 1 < nPeriod) + { + result[j] = result[j - 1] + 1; + } + else + { + for (var k = j - nPeriod+1; k <= j; ++k) + { + if (!(Math.abs(data[k]) < 0.000001)) + { + result[j] = j - k; + break; + } + } + } + } + else + { + if (!(Math.abs(data[j]) < 0.000001)) + result[j] = 0; + } + } + } + + return result; + } + + /* + 第一个条件成立到当前的周期数. + 用法: + BARSSINCE(X):第一次X不为0到现在的天数 + 例如: + BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数 + */ + this.BARSSINCE=function(data) + { + var result=[]; + var day=null; + + for(let i=0;i=-1 && item<=1) + { + result[i]=Math.asin(item); + } + else if (i-1>=0) + { + var preItem=result[i-1]; + if (this.IsNumber(preItem)) result[i]=preItem; + } + } + } + + return result; + } + } + + + //反余弦值. 用法: ACOS(X)返回X的反余弦值 + this.ACOS=function(data) + { + if (!Array.isArray(data)) + { + if (this.IsNumber(data)) return Math.acos(data); + + return null; + } + else + { + var result=[]; + for(let i in data) + { + var item=data[i]; + result[i]=null; + if (this.IsNumber(item)) + { + if (item>=-1 && item<=1) + { + result[i]=Math.acos(item); + } + else if (i-1>=0) + { + var preItem=result[i-1]; + if (this.IsNumber(preItem)) result[i]=preItem; + } + } + } + + return result; + } + } + + /* + LAST(X,A,B):持续存在. + 用法: + LAST(CLOSE>OPEN,10,5) + 表示从前10日到前5日内一直阳线 + 若A为0,表示从第一天开始,B为0,表示到最后日止 + */ + this.LAST=function(data,n,n2) + { + if (!Array.isArray(data)) return []; + + var result=[]; + var lCount=data.length; + var nStart = n; + var nEnd = n2, k = 0, i = 0, j = 0, t = 0; + for (; k < lCount; ++k) + { + if (data[k]) break; + } + + for (i = k, t = k - nEnd + 1; i= dRate*data[m] ? (data[i]>data[m] ? i : -i) : m; + } + + for (; i= data[i - 1] && data[i] >= data[i + 1]) + { + if (lState<0) + { + if ((data[i] - data[-lState]) * 100= lLastPos; j--) + dest[j]=data[-lState] + (-lState - j)*dif; + lLastPos = -lState; + lState = i; + } + } + else if (data[i]>data[lState]) lState = i; + } + else if (data[i] <= data[i - 1] && data[i] <= data[i + 1]) + { + if (lState>0) + { + if ((data[lState] - data[i]) * 100= nDataCount - 2) + { + if (lState>0 && data[nDataCount - 1] >= data[lState]) lState = nDataCount - 1; + if (lState<0 && data[nDataCount - 1] <= data[-lState]) lState = 1 - nDataCount; + } + + if (lState>0) + { + dif = (data[lState] - data[j = lLastPos]) / (lState - lLastPos ); + dest[j++]=data[lLastPos]; + for (; j <= lState; ++j) + dest[j]=data[lLastPos] + (j - lLastPos)*dif; + } + else + { + dif = (data[lLastPos] - data[j = -lState]) / (-lState - lLastPos); + dest[j--]=data[-lState]; + for (; j >= lLastPos; j--) + dest[j]=(data[-lState] + (-lState - j)*dif); + } + if ((lState = Math.abs(lState))= data[lState]) + { + dif = (data[nDataCount - 1] - data[j = lState]) / (nDataCount - lState); + dest[j++]=(data[lState]); + for (; j= lState; j--) + dest[j]=(data[nDataCount - 1] + (nDataCount - j)*dif); + } + } + + return dest; + } + + this.JSDraw=null; + this.CalculateZIGLine=function(firstData,secondData,thridData,data,result) + { + if (this.JSDraw==null) this.JSDraw=new JSDraw(this.ErrorHandler); + var isUp=secondData.Up; + var findData=firstData; + if (isUp) + { + for(var i=firstData.ID+1;isubItem) findData={ID:i, Value:subItem}; + } + } + + secondData.Value=findData.Value; + secondData.ID=findData.ID; + + var lineCache={ Start:{ID:firstData.ID, Value:firstData.Value}, End:{ID:secondData.ID,Value:secondData.Value}}; + var lineData=this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值 + for(var i in lineData) + { + var lineItem=lineData[i]; + result[lineItem.ID]=lineItem.Value; + } + + if (thridData.ID==data.length-1) //最后一组数据 + { + //最后2个点的数据连成线 + lineCache={ Start:{ID:secondData.ID, Value:secondData.Value}, End:{ID:thridData.ID,Value:thridData.Value} }; + lineData=this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值 + for(var i in lineData) + { + var lineItem=lineData[i]; + result[lineItem.ID]=lineItem.Value; + } + } + else + { + firstData.ID=secondData.ID; + firstData.Value=secondData.Value; + + secondData.ID=thridData.ID; + secondData.Value=thridData.Value; + secondData.Up=firstData.Value < secondData.Value; + } + } + + /* + 属于未来函数,前M个ZIG转向波谷到当前距离. + 用法: + TROUGHBARS(K,N,M)表示之字转向ZIG(K,N)的前M个波谷到当前的周期数,M必须大于等于1 + 例如: + TROUGHBARS(2,5,2)表示%5最低价ZIG转向的前2个波谷到当前的周期数 + */ + this.TROUGHBARS=function(data,n,n2) + { + var zigData=this.ZIG(data,n); //计算ZIG + var dest=[]; + + var lEnd =n2; + if (lEnd<1) return dest; + + var nDataCount = zigData.length; + var trough = []; + for(var i=0;izigData[i - 1]; ++i); + + for (; izigData[i-1]; ++i); + + for(; izigData[i - 1]; ++i); + + for (peak[0] = --i; izigData[i + 1]) + { + if (lFlag) + { + if (lEnd) + { + var tempPeak=peak.slice(0); + for(var j=0;jzigData[i - 1]; ++i); + + for (peak[0] = --i; izigData[i + 1]) + { + if (lFlag) + { + if (lEnd) + { + var tempPeak=peak.slice(0); + for(var j=0;jOPEN,N) + 表示N日内一直阳线(N应大于0,小于总周期数,N支持变量) + */ + this.EVERY=function(data,n) + { + var result=[]; + if (n<1) return result; + var i=0; + for(;i1) return result; + + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low; + for(var i=0;i 2000 || dMinPrice < 0 || dMaxPrice>2000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'COST() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + //去掉小数 + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var i=0;i= aryCapital.length) continue; + if (aryCapital[i]>1) + { + var kItem=kData[i] + dHSL = kItem.Vol/aryCapital[i]; + + for( var j=0;j=dTotalVol*rate) + { + dCost=(dMaxPrice-dMinPrice)*j/lSpeed+dMinPrice; + break; + } + } + } + + result[i]=dCost; + } + + return result; + } + + /* + this.COST=function(data) + { + var result=[]; + var exchangeID=201; + var exchangeData=this.SymbolData.GetFinanceCacheData(exchangeID); //换手率 + if (!exchangeData) return result; + + var isNumber=Array.isArray(data)?false:true; + var singleData=null; + if (isNumber) singleData=parseFloat(data); + var compareData=null; + + for(let i=this.SymbolData.Data.Data.length-1, j=0,k=0 ; i>=0; --i) + { + result[i]=null; + var chipData=this.CalculateChip(i,exchangeData,this.SymbolData.Data.Data,1); + if (chipData.Max==null || chipData.Min==null || chipData.Max<=0 || chipData.Min<=0) continue; + + var max=parseInt(chipData.Max*100); + var min=parseInt(chipData.Min*100); + + if (singleData!=null) + { + compareData=singleData; + } + else + { + if (i>=data.length) continue; + compareData=data[i]; + } + + var totalVol=0,vol=0; + var aryMap=new Map(); + for(j=i; j>=0; --j) + { + var item=chipData.Data[j]; + var start=parseInt(item.Low*100); + var end=parseInt(item.High*100); + if ((end-start+1)<=0) continue; + + var iAverageVolume=item.Vol; + iAverageVolume=iAverageVolume/(end-start+1); + if (iAverageVolume<=0) continue; + + for(k=start;k<=end && k<=max;++k) + { + if (aryMap.has(k)) + { + vol=aryMap.get(k); + aryMap.set(k,vol+iAverageVolume); + } + else + { + aryMap.set(k,iAverageVolume); + } + } + + totalVol+=item.Vol; + } + + //计算获利盘 + vol=0; + for(var priceData of aryMap) + { + vol+=priceData[1]; + result[i]=priceData[0]/100; + if (vol/totalVol*100>compareData) + break; + } + } + + return result; + } + */ + + /* + 获利盘比例. + 用法: + WINNER(CLOSE),表示以当前收市价卖出的获利盘比例,例如返回0.1表示10%获利盘;WINNER(10.5)表示10.5元价格的获利盘比例 + 该函数仅对日线分析周期有效 + */ + this.WINNER=function(data, node) + { + var result=[]; + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low; + for(var i=0;i 1000 || dMinPrice < 0 || dMaxPrice>1000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'WINNER() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + //去掉小数 + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var i=0;i= aryCapital.length) continue; + if (!(aryCapital[i]>1)) continue; + var kItem=kData[i] + dHSL = kItem.Vol/aryCapital[i]; + + for( var j=0;j 0) result[i]=dVol / dTotalVol; + else if (i - 1 >= 0) result[i] = result[i - 1]; + } + + return result; + } + + /* + this.WINNER=function(data) + { + var result=[]; + var exchangeID=201; + var exchangeData=this.SymbolData.GetFinanceCacheData(exchangeID); //换手率 + if (!exchangeData) return result; + + var isNumber=Array.isArray(data)?false:true; + var singleData=null; + if (isNumber) singleData=parseInt(parseFloat(data)*100); + var compareData=null; + + for(let i=this.SymbolData.Data.Data.length-1, j=0,k=0 ; i>=0; --i) + { + result[i]=null; + var chipData=this.CalculateChip(i,exchangeData,this.SymbolData.Data.Data,1); + if (chipData.Max==null || chipData.Min==null || chipData.Max<=0 || chipData.Min<=0) continue; + + var max=parseInt(chipData.Max*100); + var min=parseInt(chipData.Min*100); + + if (singleData!=null) + { + compareData=singleData; + } + else + { + if (i>=data.length) continue; + compareData=parseInt(data[i]*100); + } + + var totalVol=0,vol=0; + for(j=i; j>=0; --j) + { + var item=chipData.Data[j]; + var start=parseInt(item.Low*100); + var end=parseInt(item.High*100); + if ((end-start+1)<=0) continue; + + var iAverageVolume=item.Vol; + iAverageVolume=iAverageVolume/(end-start+1); + if (iAverageVolume<=0) continue; + + var profitVol=0; //获利的成交量 + if (compareData>end) profitVol=item.Vol; + else if (compareData0) result[i]=vol/totalVol; + } + + return result; + } + */ + + /* + 区间成本. + 用法: + 例如COSTEX(CLOSE,REF(CLOSE,1)),表示近两日收盘价格间筹码的成本 + 该函数仅对日线分析周期有效 + */ + this.COSTEX=function(data, data2, node) + { + var result=[]; + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low; + for(var i=0;i 1000 || dMinPrice < 0 || dMaxPrice>1000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'COSTEX() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + //去掉小数 + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var i=0;i= aryCapital.length) continue; + if (aryCapital[i]>1) + { + var kItem=kData[i] + dHSL = kItem.Vol/aryCapital[i]; + + for( var j=0;j 0.001 && dVol!=0 ) result[i]=dPerVolRange / dVol; + else if (i-1>=0) result[i] = result[i - 1]; + } + } + + return result; + } + + /* + 远期成本分布比例. + 用法: + PPART(10),表示10前的成本占总成本的比例,0.2表示20% + */ + this.PPART=function(n,node) + { + var result=[]; + var startDay=n; + if (startDay<0) return result; + + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + + for (var i = startDay, j = 0; i < kData.length; ++i) + { + var start = i - startDay; + if (start < 0) continue; + + var partVol = 0; + for (j = 0; j < startDay; ++j) //前n日成交量和 + { + var kItem=kData[j + start]; + partVol += kItem.Vol; + } + + if (i < aryCapital.length) + { + if (aryCapital[i]>0) + { + var value=1 - (partVol / aryCapital[i]); + result[i]=value; + } + } + } + + return result; + } + + /* + 近期获利盘比例. + 用法: LWINNER(5,CLOSE),表示最近5天的那部分成本以当前收市价卖出的获利盘比例 + 例如: 返回0.1表示10%获利盘 + */ + this.LWINNER=function(n, data, node) + { + var startDay =n; + if (startDay<=0) this.ThrowUnexpectedNode(node,'LWINNER() 第1个参数错误'); + + var result=[]; + var kData=this.SymbolData.Data.Data; + if (!kData || kData.length<=0) return result; + var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本 + var dTotalVol=0,dVol=0; + for (var i = startDay-1, j=0, k=0 ; i < kData.length; ++i) + { + var index = i - startDay + 1; + + var dMaxPrice=kData[index].High,dMinPrice=kData[index].Low; + for(var j=0;j 1000 || dMinPrice < 0 || dMaxPrice>1000 || dMinPrice < 0) + this.ThrowUnexpectedNode(node,'LWINNER() 历史K线最大最小值错误, 超出(0,1000)范围'); + + var lMaxPrice = parseInt(dMaxPrice * 100 + 1); + var lMinPrice = parseInt(dMinPrice * 100 - 1); + var lLow = 0, lHigh = 0, lClose = 0; + dMaxPrice = lMaxPrice / 100.0; + dMinPrice = lMinPrice / 100.0; + var lSpeed = lMaxPrice - lMinPrice + 1; + if (lSpeed < 1) return result; + + var aryVolPrice=[],aryPerVol=[]; + for(var j=0;j 0 && dVol > 0 && dVol <= dTotalVol) result[i]=dVol / dTotalVol; + else if (i - 1 >= 0) result[i] = result[i - 1]; + } + + return result; + } + + this.PWINNER=function(n, data, node) + { + var result=[]; + return result; + } + + //计算截至到某一天的历史所有筹码 + this.CalculateChip=function(index,exchangeData,hisData,dRate) + { + var result={Min:null,Max:null,Data:[]}; + var seed=1;//筹码历史衰减换手系数 + var max=null, min=null; + for(let i=index; i>=0; --i) + { + let item={}; //Vol:量 High:最高 Low:最低 + var kData=hisData[i]; + if (i==index) item.Vol=kData.Vol*exchangeData[i]; + else item.Vol=kData.Vol*seed; + + item.Date=kData.Date; + item.High=kData.High; + item.Low=kData.Low; + + if (max==null) max=item.High; + else if (maxdata.length) return result; + + var days=0; + for(let i=0;idata[i-1]) ++days; + else days=0; + + if (days==n) + { + result[i]=1; + --days; + } + } + + return result; + } + + /* + 返回是否连跌周期. + 用法: + DOWNNDAY(CLOSE,M) + 表示连跌M个周期,M为常量 + */ + this.DOWNNDAY=function(data,n) + { + var result=[]; + if (n<1) return result; + if (data==null || n>data.length) return result; + + var days=0; + for(let i=0;iY + 用法: + NDAY(CLOSE,OPEN,3) + 表示连续3日收阳线 + */ + this.NDAY=function(data,data2,n) + { + var result=[]; + if (n<1) return result; + if (!Array.isArray(data) && !Array.isArray(data2)) return result; + if (data==null || data2==null ) return result; + + if (Array.isArray(data) && Array.isArray(data2)) + { + if (n>=data.length || n>=data2.length) return result; + var count=Math.max(data.length,data2.length); + var days=0; + for(let i=0;i=data.length || i>=data2.length) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i])) + { + days=0; + continue; + } + + if (data[i]>data2[i]) ++days; + else days=0; + + if (days==n) + { + result[i]=1; + --days; + } + } + } + else if (Array.isArray(data) && !Array.isArray(data2)) + { + if (n>=data.length || !this.IsNumber(data2)) return; + var days=0; + for(let i in data) + { + result[i]=0; + if (!this.IsNumber(data[i])) + { + days=0; + continue; + } + + if (data[i]>data2) ++days; + else days=0; + + if (days==n) + { + result[i]=1; + --days; + } + } + } + else if (!Array.isArray(data) && Array.isArray(data2)) + { + if (n>=data2.length || !this.IsNumber(data)) return; + var days=0; + for(let i in data2) + { + result[i]=0; + if (!this.IsNumber(data2[i])) + { + days=0; + continue; + } + + if (data>data2[i]) ++days; + else days=0; + + if (days==n) + { + result[i]=1; + --days; + } + } + } + + return result; + } + + /* + 两条线维持一定周期后交叉. + 用法:LONGCROSS(A,B,N)表示A在N周期内都小于B,本周期从下方向上穿过B时返回1,否则返回0 + */ + this.LONGCROSS=function(data,data2,n) + { + var result=[]; + var count=Math.max(data.length,data2.length); + for(let i=0;i=data.length || i>=data2.length) continue; + if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]) || !this.IsNumber(data[i-1]) || !this.IsNumber(data2[i-1])) continue; + + if (data[i]>data2[i] && data[i-1]=0;++j) + { + if (data[i-j]>=data2[i-j]) + { + result[i]=0; + break; + } + } + } + + return result; + } + + /* + EXISTR(X,A,B):是否存在(前几日到前几日间). + 例如: EXISTR(CLOSE>OPEN,10,5) + 表示从前10日内到前5日内存在着阳线 + 若A为0,表示从第一天开始,B为0,表示到最后日止 + */ + this.EXISTR=function(data,n,n2) + { + var result=[]; + if (!Array.isArray(data)) return result; + + n=parseInt(n); + n2=parseInt(n2); + if (n<=0) n=data.length; + if (n2<=0) n2=1; + if (n2>n) return result; + + var result=[]; + var value; + for(let i=0,j=0;i=n2;--j) + { + var value=data[i-j]; + if (this.IsNumber(value) && value) + { + result[i]=1; + break; + } + } + } + + return result; + } + + /* + RELATE(X,Y,N) 返回X和Y的N周期的相关系数 + RELATE(X,Y,N)=(∑[(Xi-Avg(X))(Yi-Avg(y))])/N ÷ √((∑(Xi-Avg(X))^2)/N * (∑(Yi-Avg(Y))^2)/N) + 其中 avg(x)表示x的N周期均值: avg(X) = (∑Xi)/N + √(...)表示开平方 + */ + this.RELATE=function(data,data2,n) + { + var result=[]; + if (n<1) n=1; + + if (!Array.isArray(data)|| !Array.isArray(data2)) return result; + + var dataAverage=this.CalculateAverage(data,n); + var data2Average=this.CalculateAverage(data2,n); + + var count=Math.max(data.length,data2.length); + for(let i=0,j=0;i=data.length || i>=data2.length || i>=dataAverage.length || i>=data2Average.length) continue; + + var average=dataAverage[i]; + var average2=data2Average[i]; + + var total=0,total2=0,total3=0; + for(j=i-n+1;j<=i;++j) + { + total+=(data[j]-average)*(data2[j]-average2); //∑[(Xi-Avg(X))(Yi-Avg(y))]) + total2+=Math.pow(data[j]-average,2); //∑(Xi-Avg(X))^2 + total3+=Math.pow(data2[j]-average2,2); //∑(Yi-Avg(Y))^2) + } + + result[i]=(total/n)/(Math.sqrt(total2/n)*Math.sqrt(total3/n)); + } + + return result; + } + + //计算数组n周期内的均值 + this.CalculateAverage=function(data,n) + { + var result=[]; + if (n<1) return result; + + var total=0; + + for(var i=0;i=data.length || i>=data2.length || i>=dataAverage.length || i>=data2Average.length) continue; + + var average=dataAverage[i]; + var average2=data2Average[i]; + + var total=0; + for(j=i-n+1;j<=i;++j) + { + total+=(data[j]-average)*(data2[j]-average2); + } + + result[i]=(total/n); + } + + + return result; + } + + /* + 求上一高点到当前的周期数. + 用法: + HHVBARS(X,N):求N周期内X最高值到当前周期数,N=0表示从第一个有效值开始统计 + 例如: + HHVBARS(HIGH,0)求得历史新高到到当前的周期数 + */ + this.HHVBARS=function(data,n) + { + var result=[]; + if (!Array.isArray(data)) return result; + if (Array.isArray(n)) + { + for(var i=0;i=data[nMax]) nMax=j; + } + + if (nMax!=null) + result[i]=(i-nMax); + } + } + else + { + if (n<1) n=data.length; + + var nMax=null; //最大值索引 + for(var i=0;i=data[nMax]) nMax=i; + if(n==data.length) result[i]=(i-nMax); + } + + for(;i=data[nMax]) nMax=i; + } + else + { + nMax=i-n+1; + for(j=nMax;j<=i;++j) //计算区间最大值 + { + if (data[j]>=data[nMax]) nMax=j; + } + } + + result[i]=i-nMax; + } + } + + return result; + } + + /* + 求上一低点到当前的周期数. + 用法: LLVBARS(X,N):求N周期内X最低值到当前周期数,N=0表示从第一个有效值开始统计 + 例如: LLVBARS(HIGH,20)求得20日最低点到当前的周期数 + */ + this.LLVBARS=function(data,n) + { + var result=[]; + if (!Array.isArray(data)) return result; + + if (Array.isArray(n)) + { + for(var i=0;i0 && stockItem.YClose>0) stockProfit[i]=(stockItem.Close-stockItem.YClose)/stockItem.YClose; + if (indexItem.Close>0 && indexItem.YClose>0) indexProfit[i]=(indexItem.Close-indexItem.YClose)/indexItem.YClose; + } + + //计算均值数组 + var averageStockProfit=this.CalculateAverage(stockProfit,n); + var averageIndexProfit=this.CalculateAverage(indexProfit,n); + + for(var i=0,j=0;i=stockProfit.length || i>=indexProfit.length || i>=averageStockProfit.length || i>=averageIndexProfit.length) continue; + + var averageStock=averageStockProfit[i]; + var averageIndex=averageIndexProfit[i]; + + var covariance=0; //协方差 + var variance=0; //方差 + for(j=i-n+1;j<=i;++j) + { + var value=(indexProfit[j]-averageIndex); + var value2=(stockProfit[j]-averageStock); + covariance+=value*value2; + variance+=value*value; + } + + if (this.IsDivideNumber(variance) && this.IsNumber(covariance)) + result[i]=covariance/variance; //(covariance/n)/(variance/n)=covariance/variance; + } + + return result; + } + + /* + 用法:BETA2(X,Y,N)为X与Y的N周期相关放大系数,表示Y变化1%,则X将变化N% + 例如:BETA2(CLOSE,INDEXC,10)表示收盘价与大盘指数之间的10周期相关放大率 + */ + this.BETA2=function(x,y,n) + { + var result=[]; + if (n<=0) n=1; + + var xProfit=[null]; //x数据的涨幅 + var yProfit=[null]; //y数据的涨幅 + + var count=Math.max(x.length,y.length); + + var lastItem={X:x[0], Y:y[0]}; + for(var i=1;i0) xProfit[i]=(xItem-lastItem.X)/lastItem.X; + if (lastItem.Y>0) yProfit[i]=(yItem-lastItem.Y)/lastItem.Y; + + lastItem={X:xItem, Y:yItem}; + } + + //计算均值数组 + var averageXProfit=this.CalculateAverage(xProfit,n); + var averageYProfit=this.CalculateAverage(yProfit,n); + + for(var i=0,j=0;i=xProfit.length || i>=yProfit.length || i>=averageXProfit.length || i>=averageYProfit.length) continue; + + var averageX=averageXProfit[i]; + var averageY=averageYProfit[i]; + + var covariance=0; //协方差 + var variance=0; //方差 + for(j=i-n+1;j<=i;++j) + { + var value=(xProfit[j]-averageX); + var value2=(yProfit[j]-averageY); + covariance+=value*value2; + variance+=value*value; + } + + if (this.IsDivideNumber(variance) && this.IsNumber(covariance)) + result[i]=covariance/variance; //(covariance/n)/(variance/n)=covariance/variance; + } + + return result; + } + + /* + 抛物转向. + 用法: + SAR(N,S,M),N为计算周期,S为步长,M为极值 + 例如: + SAR(10,2,20)表示计算10日抛物转向,步长为2%,极限值为20% + */ + this.SAR=function(n,step,exValue) + { + var result=[]; + var stockData= this.SymbolData.Data; + if (n>=stockData.Data.length) return result; + + var high=null,low=null; + for(var i=0;iitem.Low) low=item.Low; + } + + const SAR_LONG=0, SAR_SHORT=1; + var position=SAR_LONG; + result[n-1]=low; + var nextSar=low, sip=stockData.Data[0].High,af=step/100; + for(var i=n;iysip) + { + sip=item.High; + af=Math.min(af+step/100,exValue/100); + } + nextSar=Math.min(item.Low,yitem.Low); + nextSar=Math.min(nextSar,result[i-1]+af*(sip-result[i-1])); + } + } + else if (position==SAR_SHORT) + { + if(item.High>result[i-1]) + { + position=SAR_LONG; + sip=item.High; + af=step/100; + nextSar =Math.min(item.Low,yitem.Low); + nextSar =Math.min(nextSar,result[i-1]+af*(sip-ysip)); + } + else + { + position = SAR_SHORT; + if(item.Lowsar[index]; + + for(var i=index+1;isar[i] && !flag)? 1:0; + + flag=item.Close>sar[i]; + } + + return result; + } + + /* + 属于未来函数,将当前位置到若干周期前的数据设为1. + 用法: + BACKSET(X,N),若X非0,则将当前位置到N周期前的数值设为1. + 例如: + BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0 + */ + this.BACKSET=function(condition,n) + { + var result=[]; + if (!condition) return result; + var dataCount=condition.length; + if (!this.IsNumber(dataCount) || dataCount<=0) return result; + + if (Array.isArray(n)) + { + for(var i=0;i=0 && k=0;--i) + { + var value=condition[i]; + if (this.IsNumber(value) && value) + { + for(j=i;j>i-num;--j) + { + result[j]=1; + } + } + } + + if (condition[i]) + { + for(j=i;j>=pos;--j) result[j]=1; + } + } + return result; + } + + //用法:BETWEEN(A,B,C)表示A处于B和C之间时返回1,否则返回0 + //例如:BETWEEN(CLOSE,MA(CLOSE,20),MA(CLOSE,10))表示收盘价介于10日均线和20日均线之间 + this.BETWEEN=function(condition, data, data2) + { + var result=[]; + var isNumber=typeof(condition)=='number'; + var isNumber2=typeof(data)=='number'; + var isNumber3=typeof(data2)=='number'; + + if (isNumber && isNumber2 && isNumber3) //单数值 + { + return (condition>=data && condition<=data2) ? 1 : 0; + } + + for(var i in condition) + { + result[i]=0; + var item=condition[i]; + var left=null, right=null; + + if (isNumber2) left=data; + else if (iright) + { + if (item>=right && item<=left) + result[i]=1; + } + else + { + if (item<=right && item>=left) + result[i]=1; + } + } + + return result; + } + + //STRCAT(A,B):将两个字符串A,B(非序列化)相加成一个字符串C. + //用法: STRCAT('多头','开仓')将两个字符串'多头','开仓'相加成一个字符串'多头开仓' + this.STRCAT=function(str1, str2) + { + var result=[]; + if (IFrameSplitOperator.IsString(str1) && IFrameSplitOperator.IsString(str2)) + result=str1+str2; + return result; + } + + //VARCAT(A,B):将两个字符串A,B相加成一个字符串C. + //用法: DRAWTEXT(CLOSE>OPEN,LOW,VARCAT('多头',VAR2STR(C,2))) 将两个字符串相加成一个字符串并按条件显示出来 + this.VARCAT=function(data,data2) + { + var result=[]; + if (Array.isArray(data) && Array.isArray(data2)) + { + var nCount=Math.max(data.length, data2.length); + var strValue=""; + for(var i=0;i=0; --i) + { + var item=data[i]; + if (this.IsNumber(item)) + { + result=item.toFixed(n); + return result; + } + } + } + else + { + if (this.IsNumber(data)) + result=data.toFixed(n); + } + + return result; + } + + //VAR2STR(A,N):取A的每一个值转为字符串,小数位数N. + //用法: VAR2STR(C,3)表示取收盘价,以3位小数转为字符串 + this.VAR2STR=function(data,n) + { + var result=[]; + if (Array.isArray(data)) + { + for(var i=0;iOPEN)表示统计连续收阳的周期数 + */ + this.BARSLASTCOUNT=function(data) + { + var result=null; + if (Array.isArray(data)) + { + result=[]; + if (data.length>0) + { + var count=0; + for(var i=data.length-1;i>=0;--i) + { + count=0; + for(var j=i;j>=0;--j) + { + if (data[j]) ++count; + else break; + } + result[i]=count; + } + } + } + else + { + if (data) result=1; + else result=0; + } + return result; + } + + //取整. + //用法: INTPART(A)返回沿A绝对值减小方向最接近的整数 + //例如:INTPART(12.3)求得12,INTPART(-3.5)求得-3 + this.INTPART=function(data) + { + var result=null; + if (Array.isArray(data)) + { + result=[]; + for(var i in data) + { + var item=data[i]; + if (this.IsNumber(item)) result[i]=parseInt(item); + else result[i]=null; + } + } + else if (this.IsNumber(data)) + { + result=parseInt(data); + } + + return result; + } + + //用法:CONST(A),取A最后的值为常量. + //例如:CONST(INDEXC),表示取大盘现价 + this.CONST=function(data) + { + if (Array.isArray(data)) + { + var count=data.length; + if (count>0) + return data[count-1]; + + return null; + } + else + { + return data; + } + } + + //当前值是近多少周期内的最大值. + //用法: TOPRANGE(X):X是近多少周期内X的最大值 + //例如: TOPRANGE(HIGH)表示当前最高价是近多少周期内最高价的最大值 + this.TOPRANGE=function(data) + { + if (this.IsNumber(data)) return 0; + + var result=[]; + + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1; i>=0;--i) + { + result[i]=0; + var item=data[i]; + if (!this.IsNumber(item)) continue; + + var value=0; + for(var j=i-1;j>=0;--j) + { + if (data[j]>item) + { + break; + } + ++value; + } + + result[i]=value; + + } + } + + return result; + } + + //当前值是近多少周期内的最小值. + //用法:LOWRANGE(X):X是近多少周期内X的最小值 + //例如:LOWRANGE(LOW)表示当前最低价是近多少周期内最低价的最小值 + this.LOWRANGE=function(data) + { + if (this.IsNumber(data)) return 0; + + var result=[]; + + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1; i>=0;--i) + { + result[i]=0; + var item=data[i]; + if (!this.IsNumber(item)) continue; + + var value=0; + for(var j=i-1;j>=0;--j) + { + if (data[j]=0;--i) + { + result[i]=null; + var aryValue=[]; + for(var j=n;j0) + { + aryValue.sort(function(a,b) { return a-b;}); + var index=t-1; + if (index<0) index=0; + else if (index>=aryValue.length) index=aryValue.length-1; + result[i]=aryValue[index]; + } + } + } + + return result; + } + + //N周期前的M周期内的第T个最大值. + //用法:FINDHIGH(VAR,N,M,T):VAR在N日前的M天内第T个最高价 + this.FINDHIGH=function(data,n,m,t) + { + if (this.IsNumber(data)) return data; + + var result=[]; + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1;i>=0;--i) + { + result[i]=null; + var aryValue=[]; + for(var j=n;j0) + { + aryValue.sort(function(a,b) { return b-a;}); + var index=t-1; + if (index<0) index=0; + else if (index>=aryValue.length) index=aryValue.length-1; + result[i]=aryValue[index]; + } + } + } + + return result; + } + + //N周期前的M周期内的第T个最大值到当前周期的周期数. + //用法:FINDHIGHBARS(VAR,N,M,T):VAR在N日前的M天内第T个最高价到当前周期的周期数 + this.FINDHIGHBARS=function(data, n, m, t) + { + if (this.IsNumber(data)) return (m-n-t); + + var result=[]; + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1;i>=0;--i) + { + result[i]=null; + var aryValue=[]; + for(var j=n;j0) + { + aryValue.sort(function(a,b) { return b.Value-a.Value;}); + var index=t-1; + if (index<0) index=0; + else if (index>=aryValue.length) index=aryValue.length-1; + result[i]=aryValue[index].Period; + } + } + } + + return result; + } + + //N周期前的M周期内的第T个最小值到当前周期的周期数. + //用法:FINDLOWBARS(VAR,N,M,T):VAR在N日前的M天内第T个最低价到当前周期的周期数. + this.FINDLOWBARS=function(data, n, m, t) + { + if (this.IsNumber(data)) return (m-n-t); + + var result=[]; + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1;i>=0;--i) + { + result[i]=null; + var aryValue=[]; + for(var j=n;j0) + { + aryValue.sort(function(a,b) { return a.Value-b.Value;}); + var index=t-1; + if (index<0) index=0; + else if (index>=aryValue.length) index=aryValue.length-1; + result[i]=aryValue[index].Period; + } + } + } + + return result; + } + + //求高值名次. + //用法:HOD(X,N):求当前X数据是N周期内的第几个高值,N=0则从第一个有效值开始. + //例如:HOD(HIGH,20)返回是20日的第几个高价 + this.HOD=function(data, n) + { + var result=[]; + if (IFrameSplitOperator.IsNumber(data)) return 1; + + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1;i>=0;--i) + { + var value=data[i]; + if (!IFrameSplitOperator.IsNumber(value)) continue; + if (Array.isArray(n)) var subCount=parseInt(n[i]); + else var subCount=parseInt(n); + if (n<=0) subCount=i; + var index=1; + for(var j=i-1, k=1; j>=0 && kvalue) ++index; + } + + result[i]=index; + } + } + + return result; + } + + //求低值名次. + //用法:LOD(X,N):求当前X数据是N周期内的第几个低值,N=0则从第一个有效值开始. + //例如:LOD(LOW,20)返回是20日的第几个低价 + this.LOD=function(data, n) + { + var result=[]; + if (IFrameSplitOperator.IsNumber(data)) return 1; + + if (Array.isArray(data)) + { + var count=data.length; + for(var i=count-1;i>=0;--i) + { + var value=data[i]; + if (!IFrameSplitOperator.IsNumber(value)) continue; + if (Array.isArray(n)) var subCount=parseInt(n[i]); + else var subCount=parseInt(n); + if (n<=0) subCount=i; + var index=1; + for(var j=i-1, k=1; j>=0 && k=1.1)表示下一个涨停板到当前的周期数 + this.BARSNEXT=function(data) + { + if (!Array.isArray(data)) return 0; + + var result=[]; + + for(var i=0;i0) + { + result[i]=k; + break; + } + } + } + + + return result; + } + + //取随机数. + //用法:RAND(N),返回一个范围在1-N的随机整数 + this.RAND=function(n) + { + if (Array.isArray(n)) + { + var result=[]; + for(var i in n) + { + result[i]=null; + var item=n[i]; + var value=parseInt(item); + if (value<=0) continue; + + result[i]=Math.ceil(Math.random()*value); + } + + return result; + } + else + { + var value=parseInt(n); + if (value<=0) return null; + + var stockData= this.SymbolData.Data; + var count=stockData.Data.length; + var result=[]; + for(var i=0;i=0;--i) + { + if (this.IsNumber(n[i])) + { + period=n[i]; + break; + } + } + } + else + { + period=n; + } + + if (this.IsNumber(period)) + { + if (period>1) return result; + var index=0; + var value=0; + for(index;index=ROUND2_SEED.length) decimal=ROUND2_SEED.length-1; + + if (this.IsNumber(data)) + { + return Math.round(data*ROUND2_SEED[decimal])/ROUND2_SEED[decimal]; + } + + var result=[]; + if (Array.isArray(data)) + { + for(var i in data) + { + var item=data[i]; + if (this.IsNumber(item)) + { + result[i]=Math.round(item*ROUND2_SEED[decimal])/ROUND2_SEED[decimal]; + } + else + { + result[i]=null; + } + } + } + + return result; + } + + /* 文华 + TRMA(X,N): 求X在N个周期的三角移动平均值。 + + 算法:三角移动平均线公式,是采用算数移动平均,并且对第一个移动平均线再一次应用算数移动平均。 + TRMA(X,N) 算法如下 + ma_half= MA(X,N/2) + trma=MA(ma_half,N/2) + + 注: + 1、N包含当前k线。 + 2、当N为有效值,但当前的k线数不足N根,函数返回空值。 + 3、N为0或空值的情况下,函数返回空值。 + + 例1: + TRMA5:TRMA(CLOSE,5);//计算5个周期内收盘价的三角移动平均。(N不能被2整除) + //TRMA(CLOSE,5)=MA(MA(CLOSE,(5+1)/2)),(5+1)/2); + 例2: + TRMA10:TRMA(CLOSE,10);// 计算10个周期内收盘价的三角移动平均。(N能被2整除) + TRMA(CLOSE,10)=MA(MA(CLOSE,10/2),(10/2)+1)); + */ + + this.TRMA=function(data,n) + { + if (!this.IsNumber(n) || n<=0) return []; + n=parseInt(n); + var nFalf=0,nFalf2=0; + if (n%2==0) + { + nFalf=parseInt(n/2); + nFalf2=nFalf+1; + } + else + { + nFalf=parseInt((n+1)/2); + nFalf2=nFalf; + } + + var maFalf=this.MA(data,nFalf); + var result=this.MA(maFalf,nFalf2); + return result; + } + + //VALUEWHEN(COND,X) + //当COND条件成立时,取X的当前值,否则取VALUEWHEN的上个值. + this.VALUEWHEN=function(cond,data) + { + if (Array.isArray(cond)) + { + var result=[]; + if (Array.isArray(data)) + { + var preValue=null; + for(var i in cond) + { + if (i>=data.length) + { + result[i]=preValue; + continue; + } + + var item=data[i]; + if (cond[i]) + { + result[i]=item; + preValue=item; + } + else + { + result[i]=preValue; + } + } + } + else + { + var preValue=null; + for(var i in cond) + { + if (cond[i]) + { + result[i]=data; + preValue=data; + } + else + { + result[i]=preValue; + } + } + } + + return result; + } + else + { + return cond? 1:0; + } + } + + /* + HARMEAN(X,N) 求X在N个周期内的调和平均值。 + + 算法举例:HARMEAN(X,5)=1/[(1/X1+1/X2+1/X3+1/X4+1/X5)/5] + + 注: + 1、N包含当前k线。 + 2、调和平均值与倒数的简单平均值互为倒数。 + 3、当N为有效值,但当前的k线数不足N根,函数返回空值。 + 4、N为0或空值的情况下,函数返回空值。 + 5、X为0或空值的情况下,函数返回空值。 + 6、N可以为变量。 + + 例:HM5:=HARMEAN(C,5);//求5周期收盘价的调和平均值。 + */ + this.HARMEAN=function(data, n) + { + var result=[]; + + if (Array.isArray(data)) + { + if (Array.isArray(n)) + { + for(var i=0;i=n.length) + { + result[i]=null; + continue; + } + + var count=parseInt(n[i]); + if (count<=0 || count>i) + { + result[i]=null; + continue; + } + + var sum=0; + for(var j=0;ji) + { + result[i]=null; + continue; + } + + var sum=0; + for(var j=0;j=startValue) + { + var year=parseInt(value/10000); + var month=parseInt((value%10000)/100); + var day=parseInt(value%100); + var dateItem=new Date(`${year}-${month}-${day}`); + return Math.round((dateItem-dateItem)/ONE_DAY); + } + } + + return result; + } + + //求1990.12.19后第若干天的日期. + //用法:DAYTODATE(N) + //DAYTODATE(N).返回1990.12.19后第N天的日期.有效天数为(0-20000) + //例如:DAYTODATE(0)返回901219. + this.DAYTODATE=function(data) + { + var startDate=new Date('1990-12-19') + var result=[]; + + if (Array.isArray(data)) + { + for(var i in data) + { + result[i]=null; + var item=data[i]; + if (!this.IsNumber(item)) continue; + startDate.setDate(startDate.getDate()+item); + var value=startDate.getFullYear()*10000+(startDate.getMonth()+1)*100+startDate.getDate(); + value-=19000000; + result[i]=value; + startDate.setDate(startDate.getDate()-item); + } + } + else if (this.IsNumber(data)) + { + startDate.setDate(startDate.getDate()+data); + var value=startDate.getFullYear()*10000+(startDate.getMonth()+1)*100+startDate.getDate(); + value-=19000000; + return value; + } + + return result; + } + + //函数调用 + this.CallFunction=function(name,args,node,symbolData) + { + switch(name) + { + case 'MAX': + case "MAX6": + return this.MAX(args,node); + + case 'MIN': + case "MIN6": + return this.MIN(args,node); + case 'REF': + return this.REF(args[0], args[1]); + case "REFV": + return this.REFV(args[0], args[1]); + case 'REFX': + return this.REFX(args[0], args[1]); + case "REFXV": + return this.REFXV(args[0], args[1]); + case 'ABS': + return this.ABS(args[0]); + case 'MA': + return this.MA(args[0], args[1]); + case "EMA": + return this.EMA(args[0], args[1]); + case "SMA": + return this.SMA(args[0], args[1],args[2]); + case "DMA": + return this.DMA(args[0], args[1]); + case "XMA": + return this.XMA(args[0], args[1]); + case 'EXPMA': + return this.EXPMA(args[0], args[1]); + case 'EXPMEMA': + return this.EXPMEMA(args[0], args[1]); + case 'COUNT': + return this.COUNT(args[0], args[1]); + case 'LLV': + return this.LLV(args[0], args[1]); + case 'LLVBARS': + return this.LLVBARS(args[0], args[1]); + case 'HHV': + return this.HHV(args[0], args[1]); + case 'HHVBARS': + return this.HHVBARS(args[0], args[1]); + case 'MULAR': + return this.MULAR(args[0], args[1]); + case 'CROSS': + return this.CROSS(args[0], args[1]); + case 'LONGCROSS': + return this.LONGCROSS(args[0], args[1], args[2]); + case 'AVEDEV': + return this.AVEDEV(args[0], args[1]); + case 'STD': + return this.STD(args[0], args[1]); + case 'IF': + case 'IFF': + return this.IF(args[0], args[1], args[2]); + case 'IFN': + return this.IFN(args[0], args[1], args[2]); + case 'NOT': + return this.NOT(args[0]); + case 'SUM': + return this.SUM(args[0], args[1]); + case 'RANGE': + return this.RANGE(args[0],args[1],args[2]); + case 'EXIST': + return this.EXIST(args[0],args[1]); + case 'EXISTR': + return this.EXISTR(args[0],args[1],args[2]); + case 'FILTER': + return this.FILTER(args[0],args[1]); + case 'TFILTER': + return this.TFILTER(args[0],args[1],args[2]); + case 'SLOPE': + return this.SLOPE(args[0],args[1]); + case 'BARSLAST': + return this.BARSLAST(args[0]); + case 'BARSCOUNT': + return this.BARSCOUNT(args[0]); + case 'BARSSINCEN': + return this.BARSSINCEN(args[0],args[1]); + case 'BARSSINCE': + return this.BARSSINCE(args[0]); + case 'LAST': + return this.LAST(args[0],args[1],args[2]); + case 'EVERY': + return this.EVERY(args[0],args[1]); + case 'DEVSQ': + return this.DEVSQ(args[0], args[1]); + case 'ZIG': + return this.ZIG(args[0],args[1]); + case 'TROUGHBARS': + return this.TROUGHBARS(args[0],args[1],args[2]); + case "TROUGH": + return this.TROUGH(args[0],args[1],args[2]); + case 'PEAKBARS': + return this.PEAKBARS(args[0],args[1],args[2]); + case 'PEAK': + return this.PEAK(args[0],args[1],args[2]); + case 'COST': + return this.COST(args[0]); + case 'WINNER': + return this.WINNER(args[0],node); + case 'PPART': + return this.PPART(args[0],node); + case "COSTEX": + return this.COSTEX(args[0],args[1],node); + case "LWINNER": + return this.LWINNER(args[0],args[1],node); + case "PWINNER": + return this.PWINNER(args[0],args[1],node); + case 'FORCAST': + return this.FORCAST(args[0], args[1]); + case "TSMA": + return this.TSMA(args[0], args[1]); + case 'STDP': + return this.STDP(args[0], args[1]); + case 'VAR': + return this.VAR(args[0], args[1]); + case 'VARP': + return this.VARP(args[0], args[1]); + case 'UPNDAY': + return this.UPNDAY(args[0],args[1]); + case 'DOWNNDAY': + return this.DOWNNDAY(args[0],args[1]); + case 'NDAY': + return this.NDAY(args[0],args[1],args[2]); + case 'RELATE': + return this.RELATE(args[0],args[1],args[2]); + case 'COVAR': + return this.COVAR(args[0],args[1],args[2]); + case 'BETA': + return this.BETA(args[0]); + case 'BETA2': + return this.BETA2(args[0],args[1],args[2]); + case 'WMA': + return this.WMA(args[0], args[1]); + case 'MEMA': + return this.MEMA(args[0], args[1]); + case 'SUMBARS': + return this.SUMBARS(args[0], args[1]); + case 'REVERSE': + return this.REVERSE(args[0]); + case 'SAR': + return this.SAR(args[0], args[1], args[2]); + case 'SARTURN': + return this.SARTURN(args[0], args[1], args[2]); + case 'BACKSET': + return this.BACKSET(args[0], args[1]); + case 'BETWEEN': + return this.BETWEEN(args[0], args[1], args[2]); + case 'STRCAT': + return this.STRCAT(args[0], args[1]); + case 'CON2STR': + return this.CON2STR(args[0], args[1]); + case "VAR2STR": + return this.VAR2STR(args[0], args[1]); + case "VARCAT": + return this.VARCAT(args[0], args[1]); + case "STRSPACE": + return this.STRSPACE(args[0]); + case 'DTPRICE': + return this.DTPRICE(args[0], args[1]); + case 'ZTPRICE': + return this.ZTPRICE(args[0], args[1]); + case 'MOD': + return this.MOD(args[0],args[1]); + case 'POW': + return this.POW(args[0],args[1]); + case 'CEILING': + return this.CEILING(args[0]); + case 'FLOOR': + return this.FLOOR(args[0]); + case 'FRACPART': + return this.FRACPART(args[0]); + case 'BARSLASTCOUNT': + return this.BARSLASTCOUNT(args[0]); + case 'INTPART': + return this.INTPART(args[0]); + case "CONST": + return this.CONST(args[0]); + case "TOPRANGE": + return this.TOPRANGE(args[0]); + case "LOWRANGE": + return this.LOWRANGE(args[0]); + case "FINDLOW": + return this.FINDLOW(args[0],args[1],args[2],args[3]); + case "FINDLOWBARS": + return this.FINDLOWBARS(args[0],args[1],args[2],args[3]); + case "FINDHIGH": + return this.FINDHIGH(args[0],args[1],args[2],args[3]); + case "FINDHIGHBARS": + return this.FINDHIGHBARS(args[0],args[1],args[2],args[3]); + case "BARSNEXT": + return this.BARSNEXT(args[0]); + + case "HOD": + return this.HOD(args[0], args[1]); + case "LOD": + return this.LOD(args[0], args[1]); + case "AMA": + return this.AMA(args[0], args[1]); + case "TMA": + return this.TMA(args[0],args[1],args[2]); + case "ROUND": + return this.ROUND(args[0]); + case "ROUND2": + return this.ROUND2(args[0],args[1]); + case "TRMA": + return this.TRMA(args[0],args[1]); + case "VALUEWHEN": + return this.VALUEWHEN(args[0],args[1]); + case "HARMEAN": + return this.HARMEAN(args[0],args[1]); + case "DATETODAY": + return this.DATETODAY(args[0]); + case "DAYTODATE": + return this.DAYTODATE(args[0]); + + //三角函数 + case 'ATAN': + return this.Trigonometric(args[0],Math.atan); + case 'ACOS': + return this.ACOS(args[0]); + case 'ASIN': + return this.ASIN(args[0]); + case 'COS': + return this.Trigonometric(args[0],Math.cos); + case 'SIN': + return this.Trigonometric(args[0],Math.sin); + case 'TAN': + return this.Trigonometric(args[0],Math.tan); + case 'LN': + return this.Trigonometric(args[0],Math.log); + case 'LOG': + return this.Trigonometric(args[0],Math.log10); + case 'EXP': + return this.Trigonometric(args[0],Math.exp); + case 'SQRT': + return this.Trigonometric(args[0],Math.sqrt); + case "RAND": + return this.RAND(args[0]); + default: + this.ThrowUnexpectedNode(node,'函数'+name+'不存在', name); + } + } + + //调用自定义函数 返回数据格式{Out:输出数据, Draw:绘图数据(可选)} + this.CallCustomFunction=function(name, args, symbolData, node) + { + var functionInfo=g_JSComplierResource.CustomFunction.Data.get(name); + var dwonloadData=symbolData.GetStockCacheData({ CustomName:name, Node:node }); + if (!functionInfo.Invoke) + return { Out: dwonloadData } + + JSConsole.Complier.Log('[JSAlgorithm::CallCustomFunction] call custom function functionInfo=',functionInfo); + + var self=this; + var obj= + { + Name:name, + Args:args, + Symbol:symbolData.Symbol, Period:symbolData.Period, Right:symbolData.Right, + KData:symbolData.Data, //K线数据 + DownloadData:dwonloadData, + ThrowError:function(error) + { + self.ThrowUnexpectedNode(node, error); + } + }; + + return functionInfo.Invoke(obj); + } + + this.ThrowUnexpectedNode=function(node,message,word) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg,word); + + } +} + +//是否有是有效的数字 +JSAlgorithm.prototype.IsNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return true; +} + +//是否有是有效的除数 +JSAlgorithm.prototype.IsDivideNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + if (value==0) return false; + + return true; +} + +//取模 +JSAlgorithm.MOD=function(number,divisor) +{ + if( (number < 0 && divisor < 0) || (number >=0 && divisor >= 0)) //同号 + { + if(parseInt(number) == number && parseInt(divisor) == divisor) //全为整数 + { + return number%divisor; + } + else //被除数-(整商×除数)之后在第一位小数位进行四舍五入 + { + var value = parseFloat((number - (Math.floor(number/divisor) * divisor)).toFixed(1)); + return value; + } + } + else //异号 + { + var absNumber = Math.abs(number); //绝对值 + var absDivisor = Math.abs(divisor); //绝对值 + var value = Math.abs(Math.abs(divisor) * (Math.floor(absNumber/absDivisor) + 1) - Math.abs(number)); + if(divisor < 0) value = -value + return value; + } +} + +/* + 绘图函数 +*/ +function JSDraw(errorHandler,symbolData) +{ + this.ErrorHandler=errorHandler; + this.SymbolData=symbolData; + + this.DRAWTEXT=function(condition,price,text) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'DRAWTEXT',Text:text}; + + if (Array.isArray(condition)) + { + if (condition.length<=0) return result; + + var IsNumber=this.IsNumber(price); + + for(var i in condition) + { + drawData[i]=null; + + if (isNaN(condition[i]) || !condition[i]) continue; + + if (IsNumber) + { + drawData[i]=price; + } + else + { + if (this.IsNumber(price[i])) drawData[i]=price[i]; + } + } + } + else if (this.IsNumber(condition) && condition) + { + var IsNumber=this.IsNumber(price); + for(var i=0;iOPEN,CLOSE,OPEN,0.8,1)表示画K线中阳线的空心柱体部分。 + */ + this.STICKLINE=function(condition,data,data2,width,type) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'STICKLINE',Width:width, Type:type}; + if (result.Width<0) result.Width=3; //<0的宽度 使用K线宽度 + + var IsNumber=typeof(data)=="number"; + var IsNumber2=typeof(data2)=="number"; + + if (Array.isArray(condition)) //数组 + { + if(condition.length<=0) return result; + for(var i in condition) + { + drawData[i]=null; + + if (isNaN(condition[i]) || !condition[i]) continue; + + if (IsNumber && IsNumber2) + { + drawData[i]={Value:data,Value2:data2}; + } + else if (IsNumber && !IsNumber2) + { + if (isNaN(data2[i])) continue; + drawData[i]={Value:data,Value2:data2[i]}; + } + else if (!IsNumber && IsNumber2) + { + if (isNaN(data[i])) continue; + drawData[i]={Value:data[i],Value2:data2}; + } + else + { + if (isNaN(data[i]) || isNaN(data2[i])) continue; + drawData[i]={Value:data[i],Value2:data2[i]}; + } + } + } + else //单值 + { + if(!condition) return result; + + for(var i=0;i=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1) 表示在创20天新高与创20天新低之间画直线并且向右延长。 + */ + this.DRAWLINE=function(condition,data,condition2,data2,expand) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'DRAWLINE', Expand:expand}; + + if(condition.length<=0) return result; + let count=Math.max(condition.length,condition2.length); + + let bFirstPoint=false; + let bSecondPont=false; + let lineCache={Start:{ },End:{ }, List:new Array()}; + + for(let i=0;iVAL2时,在VAL1和VAL2之间填充COLOR1;当VAL1MA2, MA1,MA2),colorred + 表示MA1>MA2时以红色填充MA1和MA2之间的区域 + */ + this.FILLRGN=function(condition, data,data2) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'FILLRGN'}; + var isNumber=this.IsNumber(data); + var isNumber2=this.IsNumber(data2); + + if (Array.isArray(condition)) //数组 + { + for(var i in condition) + { + drawData[i]=null; + var condItem=condition[i]; + if (!condItem) continue; + + var value=this.GetDataByIndex(data, i); + if (!value.Result) continue; + + var value2=this.GetDataByIndex(data2, i); + if (!value2.Result) continue; + + var item={ Value:value.Data, Value2:value2.Data }; + drawData[i]=item; + } + } + else + { + if (condition) + { + for(var i=0;i=HHV(HIGH,20),HIGH)表示在创20天新高点之间画折线。 + */ + this.POLYLINE=function(condition,data) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'POLYLINE'}; + let isNumber=typeof(data)=='number'; + + let bFirstPoint=false; + let bSecondPont=false; + if (isNumber) + { + if (this.IsNumber(condition)) + { + if (condition) + { + var count=this.SymbolData.Data.Data.length; + for(var i=0;i=data.length || !this.IsNumber(data[i])) continue; + + bFirstPoint=true; + lineCache.Start={ID:parseInt(i), Value:data[i]}; //第1个点 + } + else if (bFirstPoint==true && bSecondPont==false) + { + if (condition[i]==null || !condition[i]) continue; + if (i>=data.length || !this.IsNumber(data[i])) continue; + + lineCache.End={ID:parseInt(i), Value:data[i]}; //第2个点 + //根据起始点和结束点 计算中间各个点的数据 + let lineData=this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值 + + for(let j in lineData) + { + let item=lineData[j]; + drawData[item.ID]=item.Value; + } + + let start={ ID:lineCache.End.ID,Value:lineCache.End.Value }; + lineCache={Start:start,End:{ } }; + } + } + + this.CalculateDrawDataExtendLine(drawData); + } + + return result + } + + this.CalculateDrawDataExtendLine=function(drawData, maxCount) + { + if (maxCount<0) return; + + var x2=null; + var count=drawData.length; + if (this.IsNumber(maxCount) && maxCount=0;--i) + { + if (this.IsNumber(drawData[i])) + { + x2=i; + break; + } + } + //y3=(y1-y2)*(x3-x1)/(x2-x1) + if (x2!=null && x2-1>=0) + { + var x1=x2-1; + for(var i=x2+1;i1.08,LOW,C)表示当日实体阳线大于8%时在最低价位置显示收盘价. + */ + this.DRAWNUMBER=function(condition,data,data2) + { + let drawData={ Value:new Array(), Text:new Array() }; + let result={DrawData:drawData, DrawType:'DRAWNUMBER'}; + let isNumber=typeof(data2)=='number'; + var isArrayData=Array.isArray(data); + let text; + if (isNumber) + { + if (IFrameSplitOperator.IsInteger(data2)) text=data2.toString(); + else text=data2.toFixed(2); + } + + for(let i in condition) + { + drawData.Value[i]=null; + if (!condition[i]) continue; + + if (isArrayData) + { + if (i>=data.length || !this.IsNumber(data[i])) continue; + + if (isNumber) + { + drawData.Value[i]=data[i]; + drawData.Text[i]=text; + } + else + { + if (i>=data2.length || !data2[i]) continue; + drawData.Value[i]=data[i]; + if (this.IsNumber(data2[i])) + { + if (IFrameSplitOperator.IsInteger(data2[i])) drawData.Text[i]=data2[i].toString(); + else drawData.Text[i] = data2[i].toFixed(2); + } + else + { + drawData.Text[i] = data2[i].toString(); + } + } + } + else if (this.IsNumber(data)) + { + if (isNumber) + { + drawData.Value[i]=data; + drawData.Text[i]=text; + } + else + { + if (i>=data2.length || !data2[i]) continue; + drawData.Value[i]=data; + if (this.IsNumber(data2[i])) + { + if (IFrameSplitOperator.IsInteger(data2[i])) drawData.Text[i]=data2[i].toString(); + else drawData.Text[i] = data2[i].toFixed(2); + } + else + { + drawData.Text[i] = data2[i].toString(); + } + + } + } + + } + + return result; + } + + /* + 固定位置显示数字. + 用法: DRAWNUMBER_FIX(COND,X,Y,TYPE,NUMBER),当COND条件满足时,在当前指标窗口内(X,Y)位置书写数字NUMBER,X,Y为书写点在窗口中相对于左上角的百分比,TYPE:0为左对齐,1为右对齐. + 例如: DRAWNUMBER_FIX(CURRBARSCOUNT=1 AND CLOSE/OPEN>1.08,0.5,0.5,0,C)表示最后一个交易日实体阳线大于8%时在窗口中间位置显示收盘价. + */ + this.DRAWNUMBER_FIX=function(condition,x,y,align,data) + { + var drawData={ Value:[], Text:[], }; + var result={ DrawData:drawData, DrawType:'DRAWNUMBER_FIX', Position:{ X:x, Y:y, Type:align } }; + var isNumber=IFrameSplitOperator.IsNumber(data); + + if (Array.isArray(condition)) + { + for(var i in condition) + { + drawData.Text[i]=null; + drawData.Value[i]=null; + if (!condition[i]) continue; + + if (isNumber) + { + drawData.Text[i]=this.RemoveZero(data.toFixed(2)); + drawData.Value[i]=data; + } + else + { + if (i>=data.length || !IFrameSplitOperator.IsNumber(data[i])) continue; + + var item=data[i]; + drawData.Text[i]=this.RemoveZero(item.toFixed(2)); + drawData.Value[i]=item; + } + } + } + else + { + if(!condition) + { + + } + else + { + for(var i=0;i=data.length || !IFrameSplitOperator.IsNumber(data[i])) continue; + + var item=data[i]; + drawData.Text[i]=this.RemoveZero(item.toFixed(2)); + drawData.Value[i]=item; + } + } + } + } + + return result; + } + + /* + 在图形上绘制小图标. + 用法: + DRAWICON(COND,PRICE,TYPE),当COND条件满足时,在PRICE位置画TYPE号图标(TYPE为1--41). + 例如: + DRAWICON(CLOSE>OPEN,LOW,1)表示当收阳时在最低价位置画1号图标. + */ + this.DRAWICON=function(condition,data,type) + { + var icon=g_JSComplierResource.GetDrawIcon(type); + if (!icon) g_JSComplierResource.GetDrawTextIcon(type); + if (!icon) icon={Symbol:'🚩'}; + + let drawData=[]; + let result={DrawData:drawData, DrawType:'DRAWICON',Icon:icon}; + if (condition.length<=0) return result; + + var IsNumber=typeof(data)=="number"; + if (typeof(condition)=='number') + { + if (!condition) return result; + + for(var i=0;i0) result.Border.Width=borderWidth; + if (dotted) + { + let ary=dotted.split(','); + result.Border.Dotted=[]; + for(var i in ary) + { + var item=ary[i]; + if (!item) continue; + var value=parseInt(ary[i]); + if (value<=0) continue; + result.Border.Dotted.push(value); + } + if (result.Border.Dotted.length<=0) result.Border.Dotted=null; + } + + var IsNumber=typeof(data)=="number"; + var IsNumber2=typeof(data2)=="number"; + if (typeof(condition)=='number') + { + if (!condition) return result; //条件是否 + + for(var i=0;iOPEN,RGB(255,0,0),CLOSEOPEN,RGB(255,0,0),CLOSE=price.length) continue; + if (!this.IsNumber(price[i])) continue; + drawItem.Value=price[i]; + } + else if (this.IsNumber(price)) + { + drawItem.Value=price; + } + else + { + continue; + } + + if (Array.isArray(price2)) + { + if (i>=price2.length) continue; + if (!this.IsNumber(price2[i])) continue; + drawItem.Value2=price2[i]; + } + else if (this.IsNumber(price2)) + { + drawItem.Value2=price2; + } + else + { + continue; + } + + drawData[i]=drawItem; + } + + return result; + } + + //用法:FLOATRGN(PRICE,WIDTH,COND1,COLOR1,COND2,COLOR2...),以PRICE为基础填充宽度为WIDTH像素的区域,WIDTH为负则向下填充,当COND1条件满足时,用COLOR1颜色,当COND2条件满足时,用COLOR2颜色,否则不填充,从COND1之后的参数均可以省略,最多可以有10组条件 + //例如:FLOATRGN(CLOSE,VOL/HHV(VOL,10)*15,CLOSE>OPEN,RGB(255,0,0),1,RGB(0,255,0))表示沿收盘价填充宽度为成交量的区域,区域最大宽度为15像素,阳线时用红色,阴线时用绿色。 + this.FLOATRGN=function(args) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:'FLOATRGN'}; + if (args.length<4) return result; + + var price=args[0]; + var width=args[1]; + var condition=[]; + for(var i=2;i=price.length) continue; + if (!this.IsNumber(price[i])) continue; + drawItem.Value=price[i]; + } + else if (this.IsNumber(price)) + { + drawItem.Value=price; + } + else + { + continue; + } + + if (Array.isArray(width)) + { + if (i>=width.length) continue; + if (!this.IsNumber(width[i])) continue; + drawItem.Value2=width[i]; + } + else if (this.IsNumber(width)) + { + drawItem.Value2=width; + } + else + { + continue; + } + + drawData[i]=drawItem; + } + + return result; + } + + //函数:FILLTOPRGN / FILLBOTTOMRGN 根据条件填充顶部或底部区域 + //用法 FILLTOPRGN(PRICE, COND1, COLOR, COND2, COLOR2) + this.FILLBGRGN=function(type,args) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:(type==1 ?'FILLTOPRGN':"FILLBOTTOMRGN")}; + if (args.length<3) return result; + + var price=args[0]; + var condition=[]; + for(var i=1;i=price.length) continue; + if (!this.IsNumber(price[i])) continue; + drawItem.Value=price[i]; + } + else if (this.IsNumber(price)) + { + drawItem.Value=price; + } + else + { + continue; + } + + drawData[i]=drawItem; + } + + return result; + } + + //函数:FILLVERTICALRGN 根据条件填充顶部到底部区域 + //用法 FILLVERTICALRGN(COND1, COLOR, COND2, COLOR2) + this.FILLVERTICALRGN=function(args) + { + let drawData=[]; + let result={DrawData:drawData, DrawType:"FILLVERTICALRGN"}; + if (args.length<2) return result; + + var condition=[]; + for(var i=0;iC,RGB(0,255,0),RGB(255,0,0),0); + this.DRAWGBK=function(condition, color, color2, colorAngle) + { + let drawData={ Color:[], Angle:colorAngle }; + if (color) drawData.Color.push(color); + if (color2) drawData.Color.push(color2); + + let result={DrawData:null, DrawType:'DRAWGBK'}; + if (Array.isArray(condition)) + { + for(var i in condition) + { + var item=condition[i]; + if (item) + { + result.DrawData=drawData; + break; + } + } + } + else + { + if (condition) result.DrawData=drawData; + } + + return result; + } + + this.DRAWGBK2=function(condition, color, color2, colorAngle) + { + let drawData={ Color:[], Angle:colorAngle }; + if (color) drawData.Color.push(color); + if (color2) drawData.Color.push(color2); + + let result={DrawData:null, DrawType:'DRAWGBK2'}; + if (Array.isArray(condition)) + { + drawData.Data=[]; + for(var i in condition) + { + var item=condition[i]; + drawData.Data[i]=item ? 1:0; + } + + result.DrawData=drawData; + } + else + { + if (condition) + { + result.DrawData=drawData; + result.DrawType="DRAWGBK"; + } + } + + return result; + } + + //画文字 及线段 + this.DRAWTEXT_LINE=function(condition, price, text, textcolor, fontSize, linetype, linecolor) + { + let drawData={ Text:{ Title:text, Color:textcolor }, Line:{ Type:linetype, Color:linecolor } }; + if (fontSize<=0) fontSize=12; + drawData.Text.Font=fontSize*GetDevicePixelRatio()+'px 微软雅黑'; + + let result={DrawData:null, DrawType:'DRAWTEXT_LINE'}; + + if (Array.isArray(condition)) + { + for(var i in condition) + { + var item=condition[i]; + if (item) + { + if (Array.isArray(price)) drawData.Price=price[i]; + else drawData.Price=price; + result.DrawData=drawData; + + break; + } + } + } + else + { + if (condition) + { + if (Array.isArray(price)) drawData.Price=price[0]; + else drawData.Price=price; + result.DrawData=drawData; + } + } + + return result; + } + + // 相对位置上画矩形. + //用法: DRAWRECTREL(LEFT,TOP,RIGHT,BOTTOM,COLOR),以图形窗口(LEFT,TOP)为左上角,(RIGHT,BOTTOM)为右下角绘制矩形,坐标单位是窗口沿水平和垂直方向的1/1000,取值范围是0—999,超出范围则可能显示在图形窗口外,矩形中间填充颜色COLOR,COLOR为0表示不填充. + //例如: DRAWRECTREL(0,0,500,500,RGB(255,255,0))表示在图形最左上部1/4位置用黄色绘制矩形 + this.DRAWRECTREL=function(left, top, right,bottom, color) + { + + let drawData={ Rect:{Left:Math.min(left,right), Top:Math.min(top,bottom), Right:Math.max(right,left), Bottom:Math.max(bottom,top) }, Color:color }; + if (color==0) drawData.Color=null; + let result={DrawData:drawData, DrawType:'DRAWRECTREL'}; + + return result; + } + + //DRAWTEXTREL(X,Y,TEXT),在图形窗口(X,Y)坐标位置书写文字TEXT,坐标单位是窗口沿水平和垂直方向的1/1000,X、Y取值范围是0—999,超出范围则可能显示在图形窗口外。 + //例如:DRAWTEXTREL(500,500,'注意')表示在图形中间位置显示'注意'字样。 + this.DRAWTEXTREL=function(x, y, text) + { + let drawData={ Point:{X:x, Y:y} }; + if (IFrameSplitOperator.IsString(text)) + drawData.Text=text; + else if (IFrameSplitOperator.IsNonEmptyArray(text)) + drawData.Text=text[0]; + + let result={DrawData:drawData, DrawType:'DRAWTEXTREL'}; + + return result; + } + + //DRAWTEXTABS(X,Y,TEXT),在图形窗口(X,Y)坐标位置书写文字TEXT,坐标单位是像素,图形窗口左上角坐标为(0,0)。 + //例如:DRAWTEXTABS(0,0,'注意')表示在图形最左上角位置显示'注意'字样。 + this.DRAWTEXTABS=function(x, y, text) + { + let drawData={ Point:{X:x, Y:y} }; + if (IFrameSplitOperator.IsString(text)) + drawData.Text=text; + else if (IFrameSplitOperator.IsNonEmptyArray(text)) + drawData.Text=text[0]; + + let result={DrawData:drawData, DrawType:'DRAWTEXTABS'}; + + return result; + } + + //画百分比叠加线 + this.DRAWOVERLAYLINE=function(data, mainData, title) + { + let drawData={ Data:data, MainData:mainData }; + if (title && typeof(title)=='string') drawData.Title=title; + let result={ DrawData:drawData, DrawType:'DRAWOVERLAYLINE' }; + + return result; + } + + //绘制斜线. + //用法:DRAWSL(COND,PRICE,SLOPE,LEN,DIRECT),当COND条件满足时,在PRICE位置画斜线,SLOPE为斜率,LEN为长度,DIRECT为0向右延伸,1向左延伸,2双向延伸. + //注意: + //1.K线间的纵向高度差为SLOPE; + //2.SLOPE为0时,为水平线; + //3.SLOPE为10000时,为垂直线,LEN为向上的像素高度,DIRECT表示向上或向下延伸; + //4.SLOPE和LEN支持变量; + this.DRAWSL=function(condition, data, slope, len, direct) + { + let drawData={ Data:[], Option:[] }; + let result={ DrawData:drawData, DrawType:'DRAWSL' }; + var isNumber=this.IsNumber(data); + + if (Array.isArray(condition)) + { + for(var i in condition) + { + drawData[i]=null; + if (!condition[i]) continue; + + if (isNumber) + { + drawData.Data[i]=data; + drawData.Option[i]={Slope:slope, Length:len, Direct:direct }; + } + else + { + if (i=HHV(HIGH,20),1)表示在创20天新高画垂直虚线。 + + this.VERTLINE=function(condition, type) + { + let drawData={ Data:[], LineType:type }; + let result={ DrawData:drawData, DrawType:'VERTLINE' }; + if (Array.isArray(condition)) + { + for(var i=0;i=HHV(HIGH,20),HIGH,1,2)表示在创20天新高时画水平虚线向右延伸。 + this.HORLINE=function(condition, data, type, extend) + { + let drawData={ Data:[], LineType:type, Extend:extend }; + let result={ DrawData:drawData, DrawType:'HORLINE' }; + if (Array.isArray(condition) && Array.isArray(data)) + { + for(var i=0;i=data.length) continue; + var value=data[i]; + if (IFrameSplitOperator.IsNumber(value)) drawData.Data[i]=value; + } + } + else if (Array.isArray(condition) && IFrameSplitOperator.IsNumber(data)) + { + for(var i=0;i=data.length) continue; + var value=data[i]; + if (IFrameSplitOperator.IsNumber(value)) drawData.Data[i]=value; + } + } + else if (IFrameSplitOperator.IsNumber(data)) + { + var count=this.SymbolData.Data.Data.length; + for(var i=0; ilineCache.End.Value) + { + for(let i=1;i0) + { + var index=strValue.length-1; + var ch=strValue[index]; + if (ch=="0") + { + strValue=strValue.substr(0,index); + } + else if (ch==".") + { + strValue=strValue.substr(0,index); + break; + } + else + { + break; + } + } + + return strValue; +} + +//http://www.newone.com.cn/helpcontroller/index?code=zszy_pc +var DYNAINFO_ARGUMENT_ID= +{ + YCLOSE:3, + OPEN:4, + HIGH:5, + LOW:6, + CLOSE:7, + VOL:8, + AMOUNT:10, + AMPLITUDE:13, //振幅 + INCREASE:14, //涨幅 + EXCHANGERATE:37, //换手率 +}; + +function JSSymbolData(ast,option,jsExecute) +{ + this.AST=ast; //语法树 + this.Execute=jsExecute; + + this.Symbol='600000.sh'; + this.Name; + this.Data=null; //个股数据 + this.PeriodData=new Map(); //跨周期数据 {Key=period, value=[histroydata]} + this.IsApiPeriod=false; //是否是后台api周期数据 + this.SourceData=null; //不复权的个股数据 + this.MarketValue=null; //总市值 + this.Period=0; //周期 + this.Right=0; //复权 + this.DataType=0; //默认K线数据 2=分钟走势图数据 3=多日分钟走势图 + this.IsBeforeData=false; //当日走势图数据是否包含开盘前数据 + this.DayCount; //多日分时图天数 + this.Arguments=[]; //指标参数 + + this.KLineApiUrl= g_JSComplierResource.Domain+"/API/KLine2"; //日线 + this.MinuteKLineApiUrl= g_JSComplierResource.Domain+'/API/KLine3'; //分钟K线 + this.RealtimeApiUrl= g_JSComplierResource.Domain+'/API/stock'; //实时行情 + this.HistoryMinuteApiUrl=g_JSChartResource.Domain+'/API/StockMinuteData'; //历史分钟数据(多日分时图) + this.StockHistoryDayApiUrl= g_JSComplierResource.Domain+'/API/StockHistoryDay'; //历史财务数据 + this.StockHistoryDay3ApiUrl= g_JSComplierResource.Domain+'/API/StockHistoryDay3'; //历史财务数据 + this.StockHistoryDay2ApiUrl= g_JSComplierResource.Domain+'/API/StockHistoryDay2'; //历史财务数据 + this.StockNewsAnalysisApiUrl= g_JSComplierResource.CacheDomain+'/cache/newsanalyze'; //新闻分析数据 + this.HKToSHSZApiUrl= //北上资金 !!顺序不要变 后面都是写死的 + [ + g_JSComplierResource.CacheDomain+'/cache/historyday/all/hk2shsz.json', //日线数据 + g_JSComplierResource.CacheDomain+'/cache/analyze/hk2shsz/hk2shsz.json', //最新分钟 + g_JSComplierResource.Domain+'/API/HKToSHSZMinuteData', //多日分时分钟 + g_JSComplierResource.CacheDomain+'/cache/analyze/hk2szshanalyze' //个股的北上 + ] ; + + this.MaxRequestDataCount=1000; + this.MaxRequestMinuteDayCount=5; + this.KLineDateTimeRange; //请求的K线日期范围 + + this.LatestData; //最新行情 + this.IndexData; //大盘指数 + this.LatestIndexData; //最新大盘数据 + this.MarginData=new Map(); //融资融券 + this.HKToSHSZData=new Map(); //北上资金 + this.NewsAnalysisData=new Map(); //新闻统计 + this.ExtendData=new Map(); //其他的扩展数据 + this.UserData=new Map(); //用户数据 + this.CustomAPIData=new Map(); //自定义API数据 + this.ScriptIndexOutData=new Map(); //调用脚本执行返回的数据 + this.OtherSymbolData=new Map(); //其他股票信息 key=symbol value=[historydata] + + //股票数据缓存 key=函数名(参数) { Data: value=拟合的数据 , Error: } + //FinValue(id) + this.StockData=new Map(); + + this.SectionFinanceData=new Map(); //截面报告数据 + this.ThrowSFPeirod=new Set(); //重新获取数据 + + this.NetworkFilter; //网络请求回调 function(data, callback); + + + + //使用option初始化 + if (option) + { + if (option.HQDataType) this.DataType=option.HQDataType; + if (option.Data) + { + this.Data=option.Data; + if (this.DataType!=HQ_DATA_TYPE.MINUTE_ID && this.DataType!=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID && this.DataType!=HQ_DATA_TYPE.HISTORY_MINUTE_ID) //分钟走势图数据 没有周期和复权 + { + this.Period=option.Data.Period; //周期 + this.Right=option.Data.Right; //复权 + } + //this.Data=null; + } + + if (option.SourceData) this.SourceData=option.SourceData; + if (option.Symbol) this.Symbol=option.Symbol; + if (option.Name) this.Name=option.Name; + if (option.MaxRequestDataCount>0) this.MaxRequestDataCount=option.MaxRequestDataCount; + if (option.MaxRequestMinuteDayCount>0) this.MaxRequestMinuteDayCount=option.MaxRequestMinuteDayCount; + if (option.KLineApiUrl) this.KLineApiUrl=option.KLineApiUrl; + if (option.Right) this.Right=option.Right; + if (option.Period) this.Period=option.Period; + if (option.IsBeforeData===true) this.IsBeforeData=option.IsBeforeData; + if (option.NetworkFilter) this.NetworkFilter=option.NetworkFilter; + if (option.DayCount>0) this.DayCount=option.DayCount; + if (option.Arguments) this.Arguments=option.Arguments; + if (option.KLineRange) this.KLineDateTimeRange=option.KLineRange; + if (option.IsApiPeriod) this.IsApiPeriod=option.IsApiPeriod; + } + + this.RecvError=function(request) + { + JSConsole.Complier.Log('[JSSymbolData::RecvError] ajax error.',request.status); + throw {FunctionName:'RecvError', Request:request}; + } + + //最新行情 + this.GetLatestData=function() + { + if (this.LatestData) return this.Execute.RunNextJob(); + + var self=this; + + if (this.NetworkFilter) + { + var obj= + { + Name:'JSSymbolData::GetLatestData', //类名:: + Explain:'DYNAINFO()', + Request:{ Url:self.RealtimeApiUrl, Type:'POST' , + Data: { symbol:[this.Symbol], field: ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"] } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvLatestData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"], + "symbol": [this.Symbol] + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvLatestData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + this.RecvLatestData=function(data) + { + if (!data.stock || data.stock.length!=1) return; + + let stock=data.stock[0]; + this.LatestData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time, + YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount, + Increase:stock.increase, Exchangerate:stock.exchangerate, Amplitude:stock.amplitude}; + + JSConsole.Complier.Log('[JSSymbolData::RecvLatestData]', this.LatestData); + } + + this.GetLatestCacheData=function(dataname) + { + if (!this.LatestData) return null; + + switch(dataname) + { + case DYNAINFO_ARGUMENT_ID.YCLOSE: + return this.LatestData.YClose; + case DYNAINFO_ARGUMENT_ID.OPEN: + return this.LatestData.Open; + case DYNAINFO_ARGUMENT_ID.HIGH: + return this.LatestData.High; + case DYNAINFO_ARGUMENT_ID.LOW: + return this.LatestData.Low; + case DYNAINFO_ARGUMENT_ID.VOL: + return this.LatestData.Vol; + case DYNAINFO_ARGUMENT_ID.AMOUNT: + return this.LatestData.Amount; + case DYNAINFO_ARGUMENT_ID.INCREASE: + return this.LatestData.Increase; + case DYNAINFO_ARGUMENT_ID.EXCHANGERATE: + return this.LatestData.Exchangerate; + case DYNAINFO_ARGUMENT_ID.AMPLITUDE: + return this.LatestData.Amplitude; + case DYNAINFO_ARGUMENT_ID.CLOSE: + return this.LatestData.Price; + default: + return null; + } + } + + this.GetLatestIndexData=function() + { + if (this.LatestIndexData) return this.Execute.RunNextJob(); + + var self=this; + + if (this.NetworkFilter) + { + var obj= + { + Name:'JSSymbolData::GetLatestIndexData', //类名:: 函数 + Explain:'最新大盘数据', + Request:{ Url:self.RealtimeApiUrl, Type:'POST' , + Data: { symbol:['000001.sh'], field: ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","amplitude"] } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvLatestIndexData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","amplitude"], + "symbol": ['000001.sh'] + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvLatestIndexData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + this.RecvLatestIndexData=function(data) + { + if (!data.stock || data.stock.length!=1) return; + + let stock=data.stock[0]; + this.LatestIndexData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time, + YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount, + Increase:stock.increase, Amplitude:stock.amplitude}; + + JSConsole.Complier.Log('[JSSymbolData::RecvLatestIndexData]', this.LatestIndexData); + } + + this.GetLatestIndexCacheData=function(dataname) + { + if (!this.LatestIndexData) return null; + + switch(dataname) + { + case 'FROMOPEN': + { + var value=parseInt(this.LatestIndexData.Time/100); + var time=parseInt(value/100)*60+(value%100); //转化为分钟个数 + var index=0; + if(time <= 9 * 60 + 25) index=0; + else if(time < 9 * 60 + 30) index=1; + else if(time <= 11 * 60 + 30) index=(time - (9 * 60 + 30) + 1); + else if(time < 13 * 60) index=121; + else if(time <= 15 * 60) index=(122 + time - 13 * 60); + else index=242; + return index; + } + default: + return null; + } + } + + + this.GetVolRateData=function(job,node) + { + var volrKey=job.ID.toString()+'-VolRate-'+this.Symbol; + if (this.ExtendData.has(volrKey)) return this.Execute.RunNextJob(); + + var self=this; + JSNetwork.HttpRequest({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name","symbol","avgvol5", 'date'], + "symbol": [this.Symbol] + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvVolRateData(recvData,volrKey); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + this.RecvVolRateData=function(data,key) + { + if (!data.stock || data.stock.length!=1) return; + var avgVol5=data.stock[0].avgvol5; + var date=data.stock[0].date; + var item={AvgVol5:avgVol5, Date:date}; + this.ExtendData.set(key,item); + + JSConsole.Complier.Log('[JSSymbolData::RecvVolRateData]', item); + } + + this.GetVolRateCacheData=function(node) + { + var key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA.toString()+'-VolRate-'+this.Symbol; + if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node,'不支持VOLR'); + + var result=[]; + var value=this.ExtendData.get(key); + var avgVol5=value.AvgVol5/241; + var totalVol=0; + //5日成交总量只取了最新一天的,历史的暂时没有取,所以数据计算的时候只计算最新的一天, 其他都空 + for(var i=0, j=0;i0) result[i]=totalVol/(j+1)/avgVol5*100; + ++j; + } + + return result; + } + + //获取大盘指数数据 + this.GetIndexData=function() + { + if (this.IndexData) return this.Execute.RunNextJob(); + + var self=this; + if (ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 9=季线 + { + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetIndexData', //类名:: + Explain:'大盘数据', + Period:self.Period, + Request: + { + Url:self.KLineApiUrl, Type:'POST' , + Data: + { + field:[ "name", "symbol","yclose","open","price","high","low","vol",'up','down','stop','unchanged'], + indexSymbol:"000001.sh", symbol: this.Symbol, period:this.Period, + dateRange:dateRange + } + }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvIndexHistroyData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.KLineApiUrl, + data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol",'up','down','stop','unchanged'], + "symbol": '000001.sh', + "start": -1, + "count": self.MaxRequestDataCount+500 //多请求2年的数据 确保股票剔除停牌日期以后可以对上 + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvIndexHistroyData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据 + { + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetIndexData', //类名:: + Explain:'大盘数据', + Period:self.Period, + Request:{ Url:self.MinuteKLineApiUrl, Type:'POST' , + Data: + { + field:["name","symbol","yclose","open","price","high","low","vol"], + indexSymbol:"000001.sh", symbol: this.Symbol, period:this.Period, + dateRange:dateRange + } + }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvIndexMinuteHistroyData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": '000001.sh', + "start": -1, + "count": self.MaxRequestMinuteDayCount+5 + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvIndexMinuteHistroyData(data); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + } + + this.RecvIndexHistroyData=function(data) + { + JSConsole.Complier.Log('[JSSymbolData::RecvIndexHistroyData] recv data' , data); + + let hisData=this.JsonDataToHistoryData(data); + this.IndexData=new ChartData(); + this.IndexData.DataType=0; /*日线数据 */ + this.IndexData.Data=hisData; + + if (this.IsApiPeriod==true) + { + this.IndexData.Period=this.Period; + this.IndexData.Data=this.Data.FixKData(hisData,this.Period); + } + else + { + var aryOverlayData=this.SourceData.GetOverlayData(this.IndexData.Data); //和主图数据拟合以后的数据 + this.IndexData.Data=aryOverlayData; + + if (ChartData.IsDayPeriod(this.Period,false)) //周期数据 + { + let periodData=this.IndexData.GetPeriodData(this.Period); + this.IndexData.Data=periodData; + } + } + } + + this.RecvIndexMinuteHistroyData=function(data) + { + JSConsole.Complier.Log('[JSSymbolData::RecvIndexMinuteHistroyData] recv data' , data); + + let hisData=this.JsonDataToMinuteHistoryData(data); + this.IndexData=new ChartData(); + this.IndexData.DataType=1; /*分钟线数据 */ + this.IndexData.Data=hisData; + + if (this.IsApiPeriod==true) + { + this.IndexData.Period=this.Period; + this.IndexData.Data=this.Data.FixKData(hisData,this.Period); + } + else + { + if (ChartData.IsMinutePeriod(this.Period,false)) //周期数据 + { + let periodData=this.IndexData.GetPeriodData(this.Period); + this.IndexData.Data=periodData; + } + } + } + + this.GetOtherSymbolParam=function(name) + { + var args=name.split("$"); + var setStockDataName=new Set(['CLOSE',"C",'VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT','AMO','VOLINSTK']); + if (!setStockDataName.has(args[1])) return null; + + var symbol=args[0]; + if (symbol.length==6) + { + if (symbol[0]=="6" || symbol[0]=="5" || symbol[0]=="8" || symbol[0]=="9") + symbol+=".SH"; + else if (symbol[0]=='0' || symbol[0]=='1' || symbol[0]=='2' || symbol[0]=='3') + symbol+='.SZ'; + } + else if (symbol.indexOf("SZ")==0) + { + symbol=symbol.substr(2)+".SZ"; + } + else if (symbol.indexOf("SH")==0) + { + symbol=symbol.substr(2)+".SH"; + } + else if (symbol.indexOf("_")>0) + { + var arySymbol=symbol.split("_"); + symbol=`${arySymbol[1]}.${arySymbol[0]}`; + } + else + return null; + + return { Symbol:symbol.toLowerCase(), DataName:args[1] }; + + } + + //获取其他股票数据 + this.GetOtherSymbolData=function(job) + { + var symbol=this.Symbol; + if (job.Literal) + { + var args=this.GetOtherSymbolParam(job.Literal.toUpperCase()); + if (!args) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.Literal} Error.`); + } + + symbol=args.Symbol; + } + else + { + var args=job.Args; + if (args.length>0) + { + var item=args[0]; + if (item.Type==Syntax.Literal) + { + symbol=item.Value; + } + else if (item.Type==Syntax.Identifier) //变量 !!只支持默认的变量值 + { + var isFind=false; + for(var j in this.Arguments) + { + const argItem=this.Arguments[j]; + if (argItem.Name==item.Name) + { + symbol=argItem.Value; + isFind=true; + break; + } + } + + if (!isFind) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName}() Error: can't read ${item.Name}`); + } + } + } + } + + + job.Symbol=symbol.toLowerCase(); + if (job.Symbol==this.Symbol) return this.Execute.RunNextJob(); + if (this.OtherSymbolData.has(job.Symbol)) return this.Execute.RunNextJob(); + + var self=this; + if (this.DataType==HQ_DATA_TYPE.KLINE_ID && ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 9=季线 + { + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetOtherSymbolData', //类名::函数名 + Explain:'指定个股数据', + Request: + { + Data: + { + symbol:job.Symbol, + right:self.Right, + period:self.Period, + dateRange:dateRange + } + }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvOtherSymbolKData(data,job); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.KLineApiUrl, + data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol"], + "symbol": job.Symbol, + "start": -1, + "count": self.MaxRequestDataCount+500 //多请求2年的数据 确保股票剔除停牌日期以后可以对上 + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvOtherSymbolKDayData(recvData,job); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (ChartData.IsMinutePeriod(this.Period, true) || this.DataType==HQ_DATA_TYPE.MINUTE_ID || this.DataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) //请求分钟数据 + { + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetOtherSymbolData', //类名::函数名 + Explain:'指定个股数据', + Request: + { + Data: + { + symbol:job.Symbol, + right:self.Right, + period:self.Period, + dateRange:dateRange + } + }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.RecvOtherSymbolKData(data,job); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": job.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount+5 + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvOtherSymbolKMinuteData(data,job); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + } + + //第3方数据对接 + this.RecvOtherSymbolKData=function(data,job) + { + JSConsole.Complier.Log('[JSSymbolData::RecvOtherSymbolKDayData2] recv data' , data); + + var kData=new ChartData(); + var hisData=null; + var period=this.Period; + if (this.DataType==HQ_DATA_TYPE.KLINE_ID && ChartData.IsDayPeriod(this.Period,true)) //日线数据 + { + hisData=this.JsonDataToHistoryData(data); + kData.DataType=0; + } + else //分钟线数据 + { + hisData=this.JsonDataToMinuteHistoryData(data); + kData.DataType=1; + //走势图使用1分钟K线模式 + if (this.DataType==HQ_DATA_TYPE.MINUTE_ID || this.DataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) + period=4; + } + + kData.Period=this.Period; + kData.Right=this.Right; + + kData.Data=this.Data.FixKData(hisData,period); + this.OtherSymbolData.set(job.Symbol, kData); + } + + this.RecvOtherSymbolKDayData=function(data,job) + { + JSConsole.Complier.Log('[JSSymbolData::RecvOtherSymbolKDayData] recv data' , data); + + let hisData=this.JsonDataToHistoryData(data); + var kData=new ChartData(); + kData.DataType=0; //日线数据 + kData.Data=hisData; + + var aryOverlayData=this.SourceData.GetOverlayData(kData.Data); //和主图数据拟合以后的数据 + kData.Data=aryOverlayData; + + if (ChartData.IsDayPeriod(this.Period,false)) //周期数据 + { + let periodData=kData.GetPeriodData(this.Period); + kData.Data=periodData; + } + + this.OtherSymbolData.set(job.Symbol, kData); + } + + this.RecvOtherSymbolKMinuteData=function(data, job) + { + JSConsole.Complier.Log('[JSSymbolData::RecvOtherSymbolKMinuteData] recv data' , data); + + let hisData=this.JsonDataToMinuteHistoryData(data); + var kData=new ChartData(); + kData.DataType=1; /*分钟线数据 */ + kData.Data=hisData; + + if (ChartData.IsMinutePeriod(this.Period,false)) //周期数据 + { + let periodData=kData.GetPeriodData(this.Period); + kData.Data=periodData; + } + + this.OtherSymbolData.set(job.Symbol, kData); + } + + this.GetOtherSymolCacheData=function(obj) + { + var symbol,dataName; + if (obj.FunctionName) + { + dataName=obj.FunctionName; + var args=obj.Args; + if (args.length<=0) return this.GetSymbolCacheData(dataName); + symbol=args[0].toString().toLowerCase(); + } + else if (obj.Literal) + { + var args=this.GetOtherSymbolParam(obj.Literal.toUpperCase()); + if (!args) return []; + symbol=args.Symbol; + dataName=args.DataName; + } + + if (symbol==this.Symbol) return this.GetSymbolCacheData(dataName); + if (!this.OtherSymbolData.has(symbol)) return []; + + var kData=this.OtherSymbolData.get(symbol); + var upperSymbol=symbol.toUpperCase(); + + switch(dataName) + { + case 'CLOSE': + case 'C': + return kData.GetClose(); + case 'VOL': + case 'V': + if (MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) + return kData.GetVol(100); //A股的 把股转成手 + return kData.GetVol(); + case 'OPEN': + case 'O': + return kData.GetOpen(); + case 'HIGH': + case 'H': + return kData.GetHigh(); + case 'LOW': + case 'L': + return kData.GetLow(); + case 'AMOUNT': + case 'AMO': + return kData.GetAmount(); + case 'VOLINSTK': + return kData.GetPosition(); + } + } + + //获取大盘指数缓存数据 + this.GetIndexCacheData=function(dataName) + { + if (!this.IndexData) return new Array(); + + switch(dataName) + { + case 'INDEXA': + return this.IndexData.GetAmount(); + case 'INDEXC': + return this.IndexData.GetClose(); + case 'INDEXH': + return this.IndexData.GetHigh(); + case 'INDEXL': + return this.IndexData.GetLow(); + case 'INDEXO': + return this.IndexData.GetOpen(); + case 'INDEXV': + return this.IndexData.GetVol(); + case 'INDEXADV': + return this.IndexData.GetUp(); + case 'INDEXDEC': + return this.IndexData.GetDown(); + } + } + + //指数转成对应的板块 + this.GetBlockSymbol=function(symbol) + { + //中文对应板块代码 + const BLOCK_CN_NAME_MAP=new Map([ ["沪深A股","CNA.ci"], ["创业板","GEM.ci"], ["沪市A股","SHA.ci"], ["中小板","SME.ci"], ["深市A股","SZA.ci"] ]); + if (BLOCK_CN_NAME_MAP.has(symbol)) return BLOCK_CN_NAME_MAP.get(symbol); + + if (!symbol) return null; + var blockSymbol=null; + var upperSymbol=symbol.toUpperCase(); + + if (upperSymbol.indexOf('.SH')>0 || upperSymbol.indexOf('.SZ')>0 ) + { + const INDEX_SYMBOL_SET=new Set(["000001.SH", "000003.SH", "000016.SH", "000300.SH", "000905.SH", "399001.SZ", " 399005.SZ", "399006.SZ"]); + if (!INDEX_SYMBOL_SET.has(upperSymbol)) return null; + + blockSymbol=symbol.replace('.SH','.sh'); + blockSymbol=symbol.replace('.SZ','.sz'); + } + else if (symbol.indexOf('.CI')>0) + { + blockSymbol=symbol.replace('.CI','.ci'); + } + + /* + const SYMBOL_TO_BLOCK_MAP=new Map([ + ["000001.SH","SME.ci"], + ["399001.SZ","SZA.ci"],["399001.SZ"," GEM.ci"],["399005.SZ","SME.ci"] + ]); + + if (SYMBOL_TO_BLOCK_MAP.has(upperSymbol)) return SYMBOL_TO_BLOCK_MAP.get(upperSymbol); + + if(upperSymbol.indexOf('.CI')<0) return null; + */ + + return blockSymbol; + } + + //分钟涨幅股票个数统计数据下载 + this.GetIndexIncreaseData=function(job) + { + var symbol=null; + if (job.IsSelfSymbol) + { + symbol=this.Symbol; + } + else + { + if (!job.Args || job.Args.length<=0) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName} Error: 参数不能为空`); + } + symbol=job.Args[0].Value; + } + + var blockSymbol=this.GetBlockSymbol(symbol); + if (!blockSymbol) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName} Error: can't support ${symbol}`); + } + + var upKey=job.ID.toString()+'-UpCount-'+blockSymbol; + var downKey=job.ID.toString()+'-DownCount-'+blockSymbol; + if (this.ExtendData.has(upKey) && this.ExtendData.has(downKey)) return this.Execute.RunNextJob(); + + var self=this; + if (this.DataType===HQ_DATA_TYPE.MINUTE_ID || this.DataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) //走势图数据 + { + var apiUrl=g_JSComplierResource.CacheDomain+'/cache/analyze/increaseanalyze/'+blockSymbol+'.json'; + JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] minute Get url=' , apiUrl); + JSNetwork.HttpRequest({ + url: apiUrl, + type:"get", + dataType: "json", + async:true, + success: function (data) + { + self.RecvMinuteIncreaseData(data, {UpKey:upKey,DownKey:downKey}); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (this.DataType===HQ_DATA_TYPE.KLINE_ID && ChartData.IsDayPeriod(this.Period,true)) //K线图 日线 + { + JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] K day Get url=' , self.KLineApiUrl); + JSNetwork.HttpRequest({ + url: self.KLineApiUrl, + data: + { + "symbol": blockSymbol, + "start": -1, + "count": self.MaxRequestDataCount, + "field":['up', 'down'] + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvHistoryIncreaseData(data, {UpKey:upKey,DownKey:downKey}); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else + { + this.Execute.RunNextJob(); + } + } + + this.RecvHistoryIncreaseData=function(data,key) + { + JSConsole.Complier.Log('[JSSymbolData::RecvHistoryIncreaseData] recv data' , data); + + var upData=[],downData=[]; + for(var i in data.data) + { + var item=data.data[i]; + let upItem=new SingleData(); + let downItem=new SingleData(); + upItem.Date=item[0]; + upItem.Value=item[8]; + upData[i]=upItem; + downItem.Date=item[0]; + downItem.Value=item[9]; + downData[i]=downItem; + } + + var upFixedData, downFixedData; + if (this.SourceData) upFixedData=this.SourceData.GetFittingData(upData); + else upFixedData=this.Data.GetFittingData(aryData); + + if (this.SourceData) downFixedData=this.SourceData.GetFittingData(downData); + else downFixedData=this.Data.GetFittingData(aryData); + + this.ExtendData.set(key.UpKey,upFixedData); + this.ExtendData.set(key.DownKey,downFixedData); + } + + this.RecvMinuteIncreaseData=function(data,key) + { + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteIncreaseData] recv data' , data); + if (!data.minute) return; + var minuteData=data.minute; + if (!minuteData.time || !minuteData.up || !minuteData.down) return; + var upData=[],downData=[]; + + if (this.IsBeforeData) + { + for(var i=0, j=0;i0) //周期数据 + { + var periodData=bindData.GetPeriodSingleData(bindData.Period); + bindData.Data=periodData; + } + + return bindData.GetValue(); + } + else + { + return null; + } + } + + + this.GetSymbolData=function() + { + if (this.Data) return this.Execute.RunNextJob(); + + let self=this; + + if (this.DataType===HQ_DATA_TYPE.MINUTE_ID) //当天分钟数据 + { + JSNetwork.HttpRequest({ + url: self.RealtimeApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","minute","time","minutecount"], + "symbol": [self.Symbol], + "start": -1 + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvMinuteData(recvData); + self.Execute.RunNextJob(); + } + }); + return; + } + + if (this.DataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) + { + JSNetwork.HttpRequest({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol": self.Symbol, + "daycount": self.DayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvMultiDayMinuteData(recvData); + self.Execute.RunNextJob(); + } + }); + return; + } + + if (ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 + { + if (this.NetworkFilter) + { + var obj= + { + Name:'JSSymbolData::GetSymbolData', + Explain:"日线数据", + Request: + { Url:self.RealtimeApiUrl, Type:'POST' , + Data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestDataCount + } + }, + Self:this, + PreventDefault:false + }; + + if (this.KLineDateTimeRange) + { + obj.Request.KLineDataTimeRange={Start:{ Date:this.KLineDateTimeRange.Start.Date}, End:{ Date:this.KLineDateTimeRange.End.Date} }; + if (this.IsNumber(this.KLineDateTimeRange.Start.Time)) obj.Request.KLineDataTimeRange.Start.Time=this.KLineDateTimeRange.Start.Time; + if (this.IsNumber(this.KLineDateTimeRange.End.Time)) obj.Request.KLineDataTimeRange.End.Time=this.KLineDateTimeRange.End.Time; + } + + this.NetworkFilter(obj, function(data) + { + self.RecvHistroyData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.KLineApiUrl, + data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestDataCount + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvHistroyData(recvData); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据 + { + if (this.NetworkFilter) + { + var obj= + { + Name:'JSSymbolData::GetSymbolData', + Explain:"分钟K线数据", + Request: + { Url:self.MinuteKLineApiUrl, Type:'POST' , + Data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount + } + }, + Self:this, + PreventDefault:false + }; + + if (this.KLineDateTimeRange) + { + obj.Request.KLineDataTimeRange={Start:{ Date:this.KLineDateTimeRange.Start.Date}, End:{ Date:this.KLineDateTimeRange.End.Date} }; + if (this.IsNumber(this.KLineDateTimeRange.Start.Time)) obj.Request.KLineDataTimeRange.Start.Time=this.KLineDateTimeRange.Start.Time; + if (this.IsNumber(this.KLineDateTimeRange.End.Time)) obj.Request.KLineDataTimeRange.End.Time=this.KLineDateTimeRange.End.Time; + } + + this.NetworkFilter(obj, function(data) + { + self.RecvMinuteHistroyData(data); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: this.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvMinuteHistroyData(data); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + } + + this.RecvHistroyData=function(data) + { + JSConsole.Complier.Log('[JSSymbolData::RecvHistroyData] recv data' , data); + + let hisData=this.JsonDataToHistoryData(data); + this.Data=new ChartData(); + this.Data.DataType=0; /*日线数据 */ + this.Data.Data=hisData; + this.SourceData=new ChartData; + this.SourceData.Data=hisData; + + if (this.Right>0) //复权 + { + let rightData=this.Data.GetRightData(this.Right); + this.Data.Data=rightData; + } + + if (ChartData.IsDayPeriod(this.Period,false)) //周期数据 + { + let periodData=this.Data.GetPeriodData(this.Period); + this.Data.Data=periodData; + } + + this.Data.Right=this.Right; + this.Data.Period=this.Period; + this.Name=data.name; + } + + this.RecvMinuteHistroyData=function(data) + { + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteHistroyData] recv data' , data); + + let hisData=this.JsonDataToMinuteHistoryData(data); + this.Data=new ChartData(); + this.Data.DataType=1; /*分钟线数据 */ + this.Data.Data=hisData; + this.SourceData=new ChartData; + this.SourceData.Data=hisData; + + if (ChartData.IsMinutePeriod(this.Period,false)) //周期数据 + { + let periodData=this.Data.GetPeriodData(this.Period); + this.Data.Data=periodData; + } + + this.Data.Period=this.Period; + this.Name=data.name; + } + + //最新的分钟数据走势图 + this.RecvMinuteData=function(data) + { + JSConsole.Complier.Log('[JSSymbolData::RecvMinuteData] recv data' , data); + + var aryMinuteData=this.JsonDataToMinuteData(data); + this.Data=new ChartData(); + this.Data.DataType=2; /*分钟走势图数据 */ + this.Data.Data=aryMinuteData; + + this.Name=data.stock[0].name; + } + + this.RecvMultiDayMinuteData=function(data) + { + var aryMinuteData=this.JsonDataToMultiDayMinuteData(data); + this.Data=new ChartData(); + this.Data.DataType=2; /*分钟走势图数据 */ + this.Data.Data=aryMinuteData; + + this.Name=data.name; + } + + this.GetSymbolCacheData=function(dataName) + { + if (!this.Data) return new Array(); + + var upperSymbol=this.Symbol.toUpperCase(); + + switch(dataName) + { + case 'CLOSE': + case 'C': + return this.Data.GetClose(); + case 'VOL': + case 'V': + if (MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) + return this.Data.GetVol(100); //A股的 把股转成手 + return this.Data.GetVol(); + case 'OPEN': + case 'O': + return this.Data.GetOpen(); + case 'HIGH': + case 'H': + return this.Data.GetHigh(); + case 'LOW': + case 'L': + return this.Data.GetLow(); + case 'AMOUNT': + case 'AMO': + return this.Data.GetAmount(); + + case "OPI": //文华 持仓量 + case 'VOLINSTK': //通达信 持仓量 + return this.Data.GetPosition(); + + + case "ZSTJJ": //均价 + return this.Data.GetAvPrice(); + + case "SETTLE": //文华 结算价 + case "QHJSJ": //通达信 结算价 + return this.Data.GetSettlementPrice(); //结算价 + + case "ISEQUAL": //平盘 + return this.Data.GetIsEqual(); + case "ISUP": //收阳 + return this.Data.GetIsUp(); + case "ISDOWN": //收阴 + return this.Data.GetIsDown(); + } + } + + this.GetSymbolPeriodData=function(job) + { + var periodID=JSComplierHelper.GetPeriodInfo({ Name:job.PeriodName }).Period; + var periodInfo={ PeriodID:periodID, PeriodName:job.PeriodName }; + //同周期不请求 + if (periodID==this.Period) this.Execute.RunNextJob(); + if (this.IsApiPeriod) + { + if (this.PeriodData.has(job.PeriodName)) return this.Execute.RunNextJob(); + } + else + { + if (ChartData.IsDayPeriod(periodID,true)) + { + if (ChartData.IsDayPeriod(this.Period,true)) return this.Execute.RunNextJob(); + periodInfo={PeriodID:0, PeriodName:'DAY' }; + } + else if (ChartData.IsMinutePeriod(periodID,true)) + { + if (ChartData.IsMinutePeriod(this.Period,true)) return this.Execute.RunNextJob(); + periodInfo={ PeriodID:4, PeriodName:"MIN1" }; + } + else + { + return this.Execute.RunNextJob(); + } + } + + var self=this; + if (this.NetworkFilter) + { + var obj= + { + Name:'JSSymbolData::GetSymbolPeriodData', + Explain:"跨周期数据", + Request: + { + Data: + { + "field": [ 'symbol','name', job.ValueName ], + "period":job.PeriodName, + "symbol": self.Symbol, + } + }, + Self:this, + PreventDefault:false + }; + + this.NetworkFilter(obj, function(data) + { + self.RecvSymbolPeriodData(data, periodInfo); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + if (ChartData.IsMinutePeriod(periodID,true)) + { + JSNetwork.HttpRequest({ + url: this.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvSymbolPeriodData(data,periodInfo); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else if (ChartData.IsDayPeriod(periodID,true)) + { + JSNetwork.HttpRequest({ + url: self.KLineApiUrl, + data: + { + "field": [ "name", "symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestDataCount + }, + type:"post", + dataType: "json", + async:true, + success: function (data) + { + self.RecvSymbolPeriodData(data, periodInfo); + self.Execute.RunNextJob(); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + else + { + return this.Execute.RunNextJob(); + } + + } + + this.RecvSymbolPeriodData=function(data, periodInfo) + { + JSConsole.Complier.Log('[JSSymbolData::RecvSymbolPeriodData] data, periodInfo ' , data, periodInfo); + + if (ChartData.IsDayPeriod(periodInfo.PeriodID,true)) + { + var hisData=this.JsonDataToHistoryData(data); + var dayData=new ChartData(); + dayData.DataType=0; //日线数据 + dayData.Data=hisData; //保存原始数据 不复权 + dayData.Right=0; + dayData.Period=periodInfo.PeriodID; + this.PeriodData.set(periodInfo.PeriodName, dayData); + } + else if (ChartData.IsMinutePeriod(periodInfo.PeriodID,true)) + { + var hisData=this.JsonDataToMinuteHistoryData(data); + var minData=new ChartData(); + minData.DataType=1; //分钟线数据 + minData.Data=hisData; + minData.Right=0; + minData.Period=periodInfo.PeriodID; + this.PeriodData.set(periodInfo.PeriodName, minData); + } + } + + this.GetSymbolPeriodCacheData=function(valueName,periodName) + { + var periodInfo=JSComplierHelper.GetPeriodInfo({Name:periodName}); + if (this.Period==periodInfo.Period) + return this.GetSymbolCacheData(valueName); + + var hisData=null; + if (this.IsApiPeriod) + { + var curPeriodInfo=JSComplierHelper.GetPeriodInfo({PeriodID:this.Period}); + if (!curPeriodInfo) return null; + if (curPeriodInfo.Order>periodInfo.Order) return null; //只能小周期转大周期 + + if (!this.PeriodData.has(periodName)) return null; + hisData=this.PeriodData.get(periodName); + hisData=hisData.Data; + } + else + { + var curPeriodInfo=JSComplierHelper.GetPeriodInfo({PeriodID:this.Period}); + if (!curPeriodInfo) return null; + + if (curPeriodInfo.Order>periodInfo.Order) return null; //只能小周期转大周期 + + if (ChartData.IsDayPeriod(periodInfo.Period,true) && ChartData.IsMinutePeriod(this.Period,true)) + { + var dayData=this.PeriodData.get('DAY'); //日线 + if (periodInfo.Period==0) hisData=dayData.Data; + else hisData=dayData.GetPeriodData(periodInfo.Period); //日线周期 + } + else + { + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + + if (ChartData.IsDayPeriod(periodInfo.Period,true) && bindData.Right>0) //日线数据才复权 + { + var rightData=bindData.GetRightData(bindData.Right); + bindData.Data=rightData; + } + + hisData=bindData.GetPeriodData(periodInfo.Period); + } + } + + var data=this.Data.ConverPeriod(hisData, this.Period, periodInfo.Period); + var result=new ChartData(); + result.Data=data; + + var upperSymbol=this.Symbol.toUpperCase(); + switch(valueName) + { + case 'C': + case "CLOSE": + return result.GetClose(); + case 'O': + case "OPEN": + return result.GetOpen(); + case 'H': + case "HIGH": + return result.GetHigh(); + case 'L': + case "LOW": + return result.GetLow(); + case 'AMO': + case "AMOUNT": + return result.GetAmount(); + case 'V': + case "VOL": + if (MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) + return result.GetVol(100); + return result.GetVol(); + case "VOLINSTK": + return result.GetPosition(); + default: + return null; + } + } + + this.GetSymbolPeriodCacheData2=function(valueName,periodName,n) + { + var periodInfo=JSComplierHelper.GetPeriodInfo({Name:periodName}); + if (!periodInfo) return null; + + var curPeriodInfo=JSComplierHelper.GetPeriodInfo({PeriodID:this.Period}); + if (!curPeriodInfo) return null; + + if (curPeriodInfo.Order>periodInfo.Order) return null; //只能小周期转大周期 + + var result; + if (curPeriodInfo.Period==periodInfo.Period) + { + result=this.Data; + } + else + { + var hisData; + if (this.IsApiPeriod) + { + if (!this.PeriodData.has(periodName)) return null; + hisData=this.PeriodData.get(periodName); + } + else + { + if (ChartData.IsMinutePeriod(curPeriodInfo.Period,true) && ChartData.IsDayPeriod(periodInfo.Period,true)) + { + var dayData=this.PeriodData.get('DAY'); //日线 + if (periodInfo.Period==0) hisData=dayData; + else hisData=dayData.GetPeriodData(periodInfo.Period); //日线周期 + } + else + { + var bindData=new ChartData(); + bindData.Data=this.SourceData.Data; + bindData.Period=this.Period; + bindData.Right=this.Right; + + if (ChartData.IsDayPeriod(periodInfo.Period,true) && bindData.Right>0) //日线数据才复权 + { + var rightData=bindData.GetRightData(bindData.Right); + bindData.Data=rightData; + } + + hisData=bindData.GetPeriodData(periodInfo.Period); + } + } + + var data=this.Data.ConverPeriod(hisData, this.Period, periodInfo.Period); + var result=new ChartData(); + result.Data=data; + } + + if (IFrameSplitOperator.IsPlusNumber(n)) + { + var refResult=new ChartData(); + var data=result.GetRef(n); + refResult.Data=data; + result=refResult; + } + + var upperSymbol=this.Symbol.toUpperCase(); + + switch(valueName) + { + case 'C': + case "CLOSE": + return result.GetClose(); + case 'O': + case "OPEN": + return result.GetOpen(); + case 'H': + case "HIGH": + return result.GetHigh(); + case 'L': + case "LOW": + return result.GetLow(); + case 'AMO': + case "AMOUNT": + return result.GetAmount(); + case 'V': + case "VOL": + if (MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) + return result.GetVol(100); + return result.GetVol(); + case "VOLINSTK": + return result.GetPosition(); + default: + return null; + } + } + + this.GetCurrBarsCount=function() + { + let result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + let lCount=this.Data.Data.length; + for(let i=lCount-1;i>=0;--i) + result.push(i+1); //数据从0开始 + + return result; + } + + this.GetTotalTradeMinuteCount=function() + { + var data=g_MinuteCoordinateData.GetCoordinateData(this.Symbol); + if (data && data.Count>0) return data.Count-1; + return 242; + } + + this.GetTotalBarsCount=function() + { + let lCount=this.Data.Data.length; + return lCount; + } + + this.GetIsLastBar=function() + { + let result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result + + let lCount=this.Data.Data.length; + for(let i=0;i0) + { + var item=args[0]; + if (item.Type==Syntax.Literal) + { + code=item.Value; + } + else if (item.Type==Syntax.Identifier) //变量 !!只支持默认的变量值 + { + var isFind=false; + for(var j in this.Arguments) + { + const argItem=this.Arguments[j]; + if (argItem.Name==item.Name) + { + code=argItem.Value; + isFind=true; + break; + } + } + + if (!isFind) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`HK2SHSZ() Error: can't read ${item.Name}`); + } + } + } + + job.Symbol=code; + + if (code==1 || code==2 || code==3) //下载全市场数据 + this.GetHKToSHSZMarketData(code,job); + else + this.GetHKToSHSZStockData(code,job); //下载个股数据 + } + + this.GetHKToSHSZStockData=function(symbol,job) + { + if (this.HKToSHSZData.has(symbol)) return this.Execute.RunNextJob(); + + var upperSymbol=symbol.toLowerCase(); //代码小写 + var self=this; + var url=`${this.HKToSHSZApiUrl[3]}/${upperSymbol}.json`; + //请求数据 + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvHKToSHSZStockData(recvData,job); + self.Execute.RunNextJob(); + }, + error:function(request) + { + console.warn(`[JSSymbolData::GetHKToSHSZStockData] request url=${url} failed. error=${request.status}`); + self.RecvHKToSHSZStockData(null,job); + self.Execute.RunNextJob(); + } + }); + } + + this.RecvHKToSHSZStockData=function(data,job) + { + if (!data) return; + + var symbol=data.symbol; + var upperSymbol=symbol.toUpperCase(); + var aryData=[]; + for(var i=0;i0) //周期数据 + { + var periodData=bindData.GetPeriodSingleData(bindData.Period); + bindData.Data=periodData; + } + + var data=bindData.GetValue(); + this.HKToSHSZData.set(upperSymbol,data); + } + + this.GetHKToSHSZMarketData=function(symbol,job) + { + if (this.HKToSHSZData.has(symbol)) return this.Execute.RunNextJob(); + + var url, dataType=0; + if (this.DataType===HQ_DATA_TYPE.MINUTE_ID) dataType=1; + else if (this.DataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) dataType=2; + else if (this.Period<=3) dataType=0; + + url=this.HKToSHSZApiUrl[dataType]; + var self=this; + JSConsole.Complier.Log(`[JSSymbolData::GetHKToSHSZMarketData] code=${symbol} url=${url}, dataType=${dataType}`); + + if (dataType===2) //多日分时数据 (取这个股票的多日日期对应的北上数据) + { + //请求数据 + JSNetwork.HttpRequest({ + url: url, + data: + { + "symbol": this.Symbol, + 'daycount': this.MaxRequestMinuteDayCount + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvMulitMinuteHKToSHSZData(recvData,job); + self.Execute.RunNextJob(); + } + }); + } + else + { + //请求数据 + JSNetwork.HttpRequest({ + url: url, + type:"get", + dataType: "json", + async:true, + success: function (recvData) + { + if (dataType==0) self.RecvHKToSHSZData(recvData,job); + else if (dataType==1) self.RecvMinuteHKToSHSZData(recvData,job); + self.Execute.RunNextJob(); + } + }); + } + + } + + this.RecvMinuteHKToSHSZData=function(data,job) + { + var arySHSZData=[], arySHData=[], arySZData=[]; + if (this.IsBeforeData) + { + for( i in this.SourceData.Data) + { + var item=this.SourceData.Data[i]; + if (item.Before) + { + arySHSZData.push(null); + arySHData.push(null); + arySZData.push(null); + } + else + { + break; + } + } + + for(var i=0;i0) //周期数据 + { + var periodData=bindData.GetPeriodSingleData(bindData.Period); + bindData.Data=periodData; + } + + let data=bindData.GetValue(); + this.HKToSHSZData.set(allData[i].ID,data); + } + } + + this.RecvMulitMinuteHKToSHSZData=function(data,job) //多日分时图北上资金 + { + if (!data.data || data.data.length<=0) return; + + var arySHSZData=[], arySHData=[], arySZData=[]; + for(var i=0 ,j=0;idate) + { + ++i; + continue; + } + else if (day.date0) + { + key+="("; + for(var i=0;i0) key+=","; + key+=aryArgs[i].toString(); + } + key+=")"; + } + + return key; + } + + this.GetFinValue=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 1); + var lID=aryArgs[0]; + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetFinValue', //类名:: + Explain:'财务数据FINVALUE(ID)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,0); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadFinValueData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetFinance=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 1); + var lID=aryArgs[0]; + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetFinance', //类名:: + Explain:'财务数据FINANCE(ID)', + JobID:jobItem.ID, + Request:{ Url:self.RealtimeApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,0); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadFinanceData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetGPJYValue=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 3); + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + //TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理 + var dataType=aryArgs[2]==1?0:2; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetGPJYValue', //类名:: + Explain:'股票交易类数据GPJYVALUE(ID,N,TYPE)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,dataType); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadGPJYValue( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,dataType); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetSCJYValue=function(jobItem) + { + var aryArgs=this.JobArgumentsToArray(jobItem, 3); + var key=this.GetStockDataKey(jobItem,aryArgs); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + //TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理 + var dataType=aryArgs[2]==1?0:2; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetSCJYValue', //类名:: + Explain:'股票交易类数据SCJYVALUE(ID,N,TYPE)', + JobID:jobItem.ID, + Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + self.RecvStockValue(recvData,jobItem,key,dataType); + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var apiDownload=new DownloadSCJYValue( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + Args:aryArgs, + DataKey:key, + Callback:function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,dataType); + self.Execute.RunNextJob(); + }, + ErrorCallback:function(strError) + { + self.AddStockValueError(key,strError); + } + }); + + apiDownload.Download(); + } + + this.GetVariantData=function(jobItem) + { + var key=jobItem.VariantName; + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetVariantData', //类名::函数名 + Explain:'变量数据下载', + JobID:jobItem.ID, + Request:{ Url:"www.121287.com", Type:'POST', Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) + { + self.AddStockValueError(key,recvData.Error); + } + else + { + var dataType=0; + if (IFrameSplitOperator.IsNumber(recvData.DataType)) dataType=recvData.DataType; + self.RecvStockValue(recvData.Data,jobItem,key,dataType); + } + + self.Execute.RunNextJob(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + var errorCallback=function(strError) + { + self.AddStockValueError(key,strError); + }; + + var apiDownload; + if (jobItem.VariantName=="CAPITAL" || jobItem.VariantName=="TOTALCAPITAL" || jobItem.VariantName=="EXCHANGE") + { + var callback=function(recvData, jobItem, key) + { + self.RecvStockValue(recvData, jobItem, key,0); + self.Execute.RunNextJob(); + }; + + apiDownload=new DownloadFinanceData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:[jobItem.VariantName], + DataKey:key, + Callback:callback, + ErrorCallback:errorCallback + }); + } + else if (jobItem.VariantName=="HYBLOCK" || jobItem.VariantName=="DYBLOCK" || jobItem.VariantName=="GNBLOCK") + { + var callback=function(recvData, jobItem, key, dataType) + { + self.RecvStockValue(recvData, jobItem, key, dataType); + self.Execute.RunNextJob(); + }; + + apiDownload=new DownloadGroupData( + { + Job:jobItem, + Symbol:this.Symbol, + Url:this.StockHistoryDayApiUrl, + RealtimeUrl:this.RealtimeApiUrl, + Args:[jobItem.VariantName], + DataKey:key, + Callback:callback, + ErrorCallback:errorCallback + }); + } + else if (jobItem.VariantName=="INBLOCK") + { + var errorMessage=`${jobItem.VariantName}, 请对接外部数据.`; + this.AddStockValueError(key,errorMessage); + this.Execute.RunNextJob(); + return; + } + else + { + var errorMessage=`不支持变量${jobItem.VariantName}, 请对接外部数据.`; + this.AddStockValueError(key,errorMessage); + this.Execute.RunNextJob(); + return; + } + + apiDownload.Download(); + } + + //自定义变量数据下载 + this.GetCustomVariantData=function(jobItem) + { + var key=jobItem.VariantName; + if (this.StockData.has(key)) return this.Execute.RunNextJob(); + + var variantInfo=g_JSComplierResource.CustomVariant.Data.get(key); + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetCustomVariantData', //类名::函数名 + Explain:'自定义变量数据下载', + JobID:jobItem.ID, + Request: + { + Url:"www.121287.com", Type:'POST', + Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange, period:this.Period } + }, + Self:this, + VariantInfo:variantInfo, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) self.AddStockValueError(key,recvData.Error); + else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType); + self.Execute.RunNextJob(); + }); + } + else + { + this.AddStockValueError(key, `自定义变量${key}下载失败`); + this.Execute.RunNextJob(); + } + } + + this.GetCustomFunctionData=function(jobItem) + { + var key=jobItem.FunctionName; + var functionInfo=g_JSComplierResource.CustomFunction.Data.get(key); + if (!functionInfo.IsDownload) return this.Execute.RunNextJob(); + if (this.StockData.has(key)) return this.Execute.RunNextJob(); //一个函数只能缓存一个数据, 保存多个外部自己保存 + + var self=this; + if (this.NetworkFilter) + { + var dateRange=this.Data.GetDateRange(); + var obj= + { + Name:'JSSymbolData::GetCustomFunctionData', //类名::函数名 + Explain:'自定义函数数据下载', + JobID:jobItem.ID, + Request: + { + Url:"www.121287.com", Type:'POST', + Data: + { + FunctionName:jobItem.FunctionName, + symbol: this.Symbol, daterange:dateRange, + JobItem:jobItem //函数编译信息 + } + }, + Self:this, + FunctionInfo:functionInfo, + PreventDefault:false + }; + this.NetworkFilter(obj, function(recvData) + { + if (recvData.Error) self.AddStockValueError(key,recvData.Error); + else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType); + self.Execute.RunNextJob(); + }); + } + else + { + this.AddStockValueError(key, `自定义函数${key}下载失败`); + this.Execute.RunNextJob(); + } + } + + this.RecvStockValue=function(recvData,jobItem,key,dataType) + { + if (!recvData) + { + JSConsole.Complier.Log(`[JSSymbolData::RecvStockValue] key=${key} data is null`); + return; + } + + if (dataType==0) + { + if (Array.isArray(recvData)) + { + var kdata=this.Data; //K线 + var aryFittingData; + if (ChartData.IsDayPeriod(this.Period,true)) + aryFittingData=kdata.GetFittingFinanceData(recvData); //数据和主图K线拟合 + else if (ChartData.IsMinutePeriod(this.Period,true)) + aryFittingData=kdata.GetMinuteFittingFinanceData(recvData); //数据和主图K线拟合 + else + return; + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + var result=bindData.GetValue(); + + if (key=="EXCHANGE") //计算换手率=成交量/流通股本*100 + { + for(var i in result) + { + var kitem=kdata.Data[i]; + if (IFrameSplitOperator.IsPlusNumber(result[i])) + result[i]=kitem.Vol/result[i] * 100; + } + } + + this.StockData.set(key,{ Data:result }); + } + else + { + this.StockData.set(key,{ Data:recvData.Value }); + } + } + else if (dataType==1) //单数值 + { + this.StockData.set(key,{ Data:recvData.Value }); + } + else if (dataType==2) + { + var kdata=this.Data; //K线 + var aryFittingData; + if (ChartData.IsDayPeriod(this.Period,true)) + aryFittingData=kdata.GetFittingTradeData(recvData, 0); //数据和主图K线拟合 + else if (ChartData.IsMinutePeriod(this.Period,true)) + aryFittingData=kdata.GetMinuteFittingTradeData(recvData, 0); //数据和主图K线拟合 + else if (ChartData.IsTickPeriod(this.Period)) + aryFittingData=kdata.GetMinuteFittingTradeData(recvData, 0); //数据和主图K线拟合 + else + return; + + var bindData=new ChartData(); + bindData.Data=aryFittingData; + var result=bindData.GetValue(); + + this.StockData.set(key,{ Data:result }); + } + } + + this.AddStockValueError=function(key, message) + { + this.StockData.set(key,{ Error:message }); + } + + this.GetStockCacheData=function(obj) + { + var key; + if (obj.FunctionName) + key=this.GetStockDataKey({FunctionName:obj.FunctionName}, obj.Args); + else if (obj.VariantName) + key=obj.VariantName; + else if (obj.CustomName) + key=obj.CustomName; //自定义名字 + else + return null; + + if (!this.StockData.has(key)) return null; + var data=this.StockData.get(key); + + if (data.Error) this.Execute.ThrowUnexpectedNode(obj.Node, data.Error); + return data.Data; + } + + this.IsInBlock=function(blockName, node) + { + var data=this.GetStockCacheData({ VariantName:"INBLOCK", Node:node }); + if (!data) return 0; + var aryBlock=data.split('|'); + for(var i=0; i0) key+=',' + key+=args[i]; + } + + return key; + } + + this.GetCustomApiData=function(args) + { + var key=this.GenerateCustomAPIKey(args); + + if (!this.CustomAPIData.has(key)) + { + JSConsole.Complier.Log(`[JSSymbolData::GetCustomApiData] can't find api data key=[${key}]`); + return null; + } + + return this.CustomAPIData.get(key); + } + + this.ReadArgumentValue=function(item, result) //读取变量值 + { + result.Name=item.Name; + if (item.Type==Syntax.Literal) + { + result.Value=item.Value; + return true; + } + + if (item.Type==Syntax.Identifier) + { + var isFind=false; + for(var i in this.Arguments) + { + const argItem=this.Arguments[i]; + if (argItem.Name==item.Name) + { + result.Value=argItem.Value; + isFind=true; + return true; + } + } + + if (!isFind) + { + result.Error=`can't read ${item.Name}` ; + return false; + } + } + + result.Error=`can't read ${item.Name}, type error.`; + return false; + } + + //MA.MA1#WEEK + this.ReadIndexFunctionValue=function(item, result) //返回 {Period:周期, Out:输出变量, Error:, Name:脚本名字 } + { + var indexParam={}; + if (typeof(item)=== 'object') + { + if (!this.ReadArgumentValue(item,indexParam)) + { + result.Error=indexParam.Error; + return false; + } + } + else + { + indexParam.Value=item; + } + + var pos=indexParam.Value.indexOf("\."); + if (pos!=-1) + { + result.Name=indexParam.Value.slice(0, pos); //名字 + var pos2=indexParam.Value.indexOf('#', pos+1); + if (pos2!=-1) + { + result.Out=indexParam.Value.slice(pos+1, pos2); //输出变量 + result.Period=indexParam.Value.slice(pos2+1); //周期 + } + else + { + result.Out=indexParam.Value.slice(pos+1); + } + } + else + { + var pos2=indexParam.Value.indexOf('#'); + if (pos2!=-1) + { + result.Name=indexParam.Value.slice(0,pos2); + result.Period=indexParam.Value.slice(pos2+1); //周期 + } + else + { + result.Name=indexParam.Value; + } + } + + const PERIOD_MAP=new Map([ + ["DAY",0 ], ["WEEK", 1 ], ["MONTH",2 ], ["SEASON",9 ], ["YEAR", 3], ["HALFYEAR",22], ["WEEK2",21], + ["MIN1", 4], ["MIN5", 5 ], ["MIN15", 6 ], ["MIN30",7 ], ["MIN60", 8 ], + + ["DAY2", 40002],["MULTIDAY",40002],["DAY3", 40003],["DAY4", 40004],["DAY5",40005], + ["DAY6", 40006],["DAY7", 40007],["DAY8", 40008],["DAY9", 40009],["DAY10",40010], + ["DAY11", 40011],["DAY12", 40012],["DAY13", 40013],["DAY14", 40014],["DAY15", 40015], + ]); + + if (result.Period) + { + if (!PERIOD_MAP.has(result.Period)) + { + result.Error=`${result.Period}, 周期错误`; + return false; + } + result.PeriodID=PERIOD_MAP.get(result.Period); + } + return true; + } + + this.ReadSymbolArgumentValue=function(item, result) //返回{ Value:股票代码, Error:错误信息} + { + var readArgument={}; + if (typeof(item)=== 'object') + { + if (!this.ReadArgumentValue(item,readArgument)) + { + result.Error=readArgument.Error; + return false; + } + } + else + { + readArgument.Value=item; + } + + if (readArgument.Value=='') readArgument.Value=this.Symbol; //缺省使用股票代码 + + //A股后缀小写 + if (readArgument.Value.indexOf('.SH')>0) result.Symbol=readArgument.Value.replace('.SH', ".sh"); + else if (readArgument.Value.indexOf('.SZ')>0) result.Symbol=readArgument.Value.replace('.SZ', ".sz"); + else result.Symbol=readArgument.Value; + + return true; + } + + this.ReadIndexArgumentValue=function(args, result) + { + result.Args=[]; + for(var i in result.SytemIndex.Args) //复制参数 + { + var item=result.SytemIndex.Args[i]; + result.Args.push({Value:item.Value, Name:item.Name}); + } + + if (args.length>2 && result.SytemIndex.Args && result.SytemIndex.Args.length>0) + { + for(var i=2, j=0; i 输出 + this.GenerateScriptIndexKey=function(indexInfo) + { + var indexParam=''; + var args=indexInfo.Args; + for(var i in args) + { + if (indexParam.length>0) indexParam+=','; + var item=args[i]; + indexParam+=item.Value.toString(); + } + + var out=indexInfo.Out ? indexInfo.Out :"ALL"; + var key=`(${indexInfo.Symbol},${indexInfo.PeriodID}), ${indexInfo.Name}(${indexParam})=>${out}`; + + return key; + } + + //TMP2:=KDJ.K#WEEK; + this.CallMemberScriptIndex=function(job) + { + if (job.Member.Object.Type!=Syntax.Identifier ||job.Member.Property.Type!=Syntax.Identifier) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallMemberScriptIndex() Error: 参数错误`); + } + + var objName=job.Member.Object.Name; + var PropertyName=job.Member.Property.Name; + if (PropertyName=="" || PropertyName==null) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallMemberScriptIndex() Error: ${objName}.${PropertyName} 指标输出变量错误`); + } + + if (this.Execute.VarTable.has(objName)) + { + var memberValue=this.Execute.VarTable.get(objName); + if (memberValue.hasOwnProperty(PropertyName)) + { + JSConsole.Complier.Log(`[JSSymbolData::CallMemberScriptIndex] index data ${objName}.${PropertyName} in cache.`); + return this.Execute.RunNextJob(); + } + } + + var callInfo=objName+"."+PropertyName; + var indexInfo={ Job:job, PeriodID:this.Period , Symbol:this.Symbol }; + if (!this.ReadIndexFunctionValue(callInfo,indexInfo)) //读取指标 + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallMemberScriptIndex() Error: '${callInfo}' ${indexInfo.Error}`); + } + + var systemIndex=new JSIndexScript(); + var systemItem=systemIndex.Get(indexInfo.Name); + if (!systemItem) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallMemberScriptIndex() Error: '${callInfo}' ${indexInfo.Name} 指标不存在`); + } + + if (Array.isArray(systemItem.Args) && systemItem.Args.length>0) + { + indexInfo.Args=[]; + for(var i in systemItem.Args) //复制参数 + { + var item=systemItem.Args[i]; + indexInfo.Args.push({Value:item.Value, Name:item.Name}); + } + } + + JSConsole.Complier.Log('[JSSymbolData::CallMemberScriptIndex] call script index', indexInfo); + + var dateTimeRange=this.Data.GetDateRange(); + + var option= + { + HQDataType:this.DataType, + Symbol:indexInfo.Symbol, + Name:'', + Right:this.Right, //复权 + Period:indexInfo.PeriodID, //周期 + Data:null, + SourceData:null, + Callback:(outVar,job, symbolData)=> { + this.RecvMemberScriptIndexData(outVar,job,symbolData); + this.Execute.RunNextJob(); + }, + CallbackParam:indexInfo, + Async:true, + MaxRequestDataCount:this.MaxRequestDataCount+30*2, + MaxRequestMinuteDayCount:this.MaxRequestMinuteDayCount+2, + Arguments:indexInfo.Args, + //Condition:this.Condition, + IsBeforeData:this.IsBeforeData, + NetworkFilter:this.NetworkFilter, + KLineRange:dateTimeRange //K线数据范围 + }; + + //执行脚本 + var run=JSComplier.Execute(systemItem.Script,option,(error, indexInfo)=>{this.ExecuteScriptIndexError(error,indexInfo)}); + + } + + //脚本调用 + //STKINDI('600000.sh','MA.MA1#WEEK',5,10,20,30,60,120); + //1=股票代码 2=指标名字.输出变量#周期, 3....参数 + this.CallScriptIndex=function(job) + { + if (job.Member) return this.CallMemberScriptIndex(job); + + if (!job.Args || !(job.Args.length>=2)) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() Error: ${job.FunctionName} 参数错误`); + } + + var indexInfo={ Job:job, PeriodID:this.Period }; + if (!this.ReadSymbolArgumentValue(job.Args[0],indexInfo)) //读取代码 + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() Error: ${indexInfo.Error}`); + } + + if (!this.ReadIndexFunctionValue(job.Args[1],indexInfo)) //读取指标 + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() Error: ${indexInfo.Error}`); + } + + var systemIndex=new JSIndexScript(); + var systemItem=systemIndex.Get(indexInfo.Name); + if (!systemItem) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() ${indexInfo.Name} 指标不存在`); + } + + indexInfo.SytemIndex=systemItem; //系统指标 + if (!this.ReadIndexArgumentValue(job.Args,indexInfo)) + { + var token=job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() ${indexInfo.Name} 指标参数错误 : ${indexInfo.Error} `); + } + + JSConsole.Complier.Log('[JSSymbolData::CallScriptIndex] call script index', indexInfo); + var DateTimeRange=null; + if (this.Data && this.Data.Data.length>0) + { + var start=this.Data.Data[0]; + var end=this.Data.Data[this.Data.Data.length-1]; + DateTimeRange= + { + Start:{Date:start.Date, Time: start.Time}, + End:{Date:end.Date, Time: end.Time}, + } + } + + var option= + { + HQDataType:this.DataType, + Symbol:indexInfo.Symbol, + Name:'', + Right:this.Right, //复权 + Period:indexInfo.PeriodID, //周期 + Data:null, + SourceData:null, + Callback:(outVar,job, symbolData)=> { + this.RecvScriptIndexData(outVar,job,symbolData); + this.Execute.RunNextJob(); + }, + CallbackParam:indexInfo, + Async:true, + MaxRequestDataCount:this.MaxRequestDataCount+30*2, + MaxRequestMinuteDayCount:this.MaxRequestMinuteDayCount+2, + Arguments:indexInfo.Args, + //Condition:this.Condition, + IsBeforeData:this.IsBeforeData, + NetworkFilter:this.NetworkFilter, + KLineRange:DateTimeRange //K线数据范围 + }; + + //执行脚本 + var run=JSComplier.Execute(indexInfo.SytemIndex.Script,option,(error, indexInfo)=>{this.ExecuteScriptIndexError(error,indexInfo)}); + } + + this.RecvMemberScriptIndexData=function(outVar,indexInfo,symbolData) + { + JSConsole.Complier.Log('[JSSymbolData::RecvMemberScriptIndexData] ', outVar, indexInfo, symbolData); + var kLine=symbolData.Data.Data; + var aryOutVar=outVar; + var data=this.Data.FitKLineIndex(kLine,aryOutVar,this.Period,indexInfo.PeriodID); + + var member=indexInfo.Job.Member; + var objName=member.Object.Name; + var propertyName=member.Property.Name; + + var memberValue={}; + if (this.Execute.VarTable.has(objName)) + memberValue=this.Execute.VarTable.get(objName); + else + this.Execute.VarTable.set(objName, memberValue); + + //保存所有的指标数据, 下面用到了就可以不用算了 + for(var i in data) + { + var key=outVar[i].Name; + if (indexInfo.Period) key+='#'+indexInfo.Period; //带周期的变量 + memberValue[key]=data[i].Data; + } + } + + this.RecvScriptIndexData=function(outVar,indexInfo,symbolData) + { + var key=this.GenerateScriptIndexKey(indexInfo); + JSConsole.Complier.Log('[JSSymbolData::RecvScriptIndexData] ', outVar, indexInfo, symbolData, key); + + var kLine=symbolData.Data.Data; + var aryOutVar=outVar; + if (indexInfo.Out) + { + for(var i in outVar) + { + var item=outVar[i]; + if (item.Name==indexInfo.Out) + { + aryOutVar=[item]; + break; + } + } + + var data=this.Data.FitKLineIndex(kLine,aryOutVar,this.Period,indexInfo.PeriodID); + this.ScriptIndexOutData.set(key,data[0].Data); + } + else + { + var data=this.Data.FitKLineIndex(kLine,aryOutVar,this.Period,indexInfo.PeriodID); + var result={ __Type__:"Object" }; + for(var i in data) + { + var item=data[i]; + result[item.Name]=item.Data; + } + this.ScriptIndexOutData.set(key,result); + } + } + + this.ExecuteScriptIndexError=function(error,indexInfo) + { + var token=indexInfo.Job.Token; + this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallScriptIndex() ${indexInfo.Name} 指标执行错误 : ${error} `); + } + + this.GetScriptIndexOutData=function(args,node) + { + var indexInfo={ PeriodID:this.Period }; + if (!this.ReadSymbolArgumentValue(args[0],indexInfo)) //读取代码 + this.Execute.ThrowUnexpectedNode(node,`STKINDI() 股票代码错误: ${indexInfo.Error}`); + + if (!this.ReadIndexFunctionValue(args[1],indexInfo)) //读取指标 + this.Execute.ThrowUnexpectedNode(node,`STKINDI() 指标错误: ${indexInfo.Error}`); + + var systemIndex=new JSIndexScript(); + var systemItem=systemIndex.Get(indexInfo.Name); + if (!systemItem) + this.Execute.ThrowUnexpectedNode(node,`STKINDI() 指标错误: ${indexInfo.Name} 指标不存在`); + + indexInfo.SytemIndex=systemItem; //系统指标 + if (!this.ReadIndexArgumentValue(args,indexInfo)) + this.Execute.ThrowUnexpectedNode(node,`STKINDI() 指标参数错误: ${indexInfo.Error}`); + + var key=this.GenerateScriptIndexKey(indexInfo); + if (!this.ScriptIndexOutData.has(key)) return null; + + return this.ScriptIndexOutData.get(key); + } + + this.JsonDataToHistoryData=function(data) + { + var list = data.data; + var aryDayData=new Array(); + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7; + var up=8,down=9,stop=10,unchanged=11; + for (var i = 0; i < list.length; ++i) + { + var item = new HistoryData(); + + item.Date = list[i][date]; + item.Open = list[i][open]; + item.YClose = list[i][yclose]; + item.Close = list[i][close]; + item.High = list[i][high]; + item.Low = list[i][low]; + item.Vol = list[i][vol]; //原始单位股 + item.Amount = list[i][amount]; + //上涨 下跌家数 + if (list[i].length>up) item.Up=list[i][up]; + if (list[i].length>down) item.Down=list[i][down]; + if (list[i].length>stop) item.Stop=list[i][stop]; + if (list[i].length>unchanged) item.Unchanged=list[i][unchanged]; + + if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除 + + aryDayData.push(item); + } + + return aryDayData; + } + + this.JsonDataToMinuteHistoryData=function(data) + { + var list = data.data; + var aryDayData=new Array(); + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8; + for (var i = 0; i < list.length; ++i) + { + let item = new HistoryData(); + + item.Date = list[i][date]; + item.Open = list[i][open]; + item.YClose = list[i][yclose]; + item.Close = list[i][close]; + item.High = list[i][high]; + item.Low = list[i][low]; + item.Vol = list[i][vol]; //原始单位股 + item.Amount = list[i][amount]; + item.Time=list[i][time]; + + // if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除 + aryDayData.push(item); + } + + // 无效数据处理 + for(let i = 0; i < aryDayData.length; ++i) + { + var minData = aryDayData[i]; + if (minData == null) coninue; + if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0 + || isNaN(minData.Close) || minData.Close <= 0 || isNaN(minData.YClose) || minData.YClose <= 0) + { + if (i == 0) + { + if (minData.YClose > 0) + { + minData.Open = minData.YClose; + minData.High = minData.YClose; + minData.Low = minData.YClose; + minData.Close = minData.YClose; + } + } + else // 用前一个有效数据填充 + { + for(let j = i-1; j >= 0; --j) + { + var minData2 = aryDayData[j]; + if (minData2 == null) coninue; + if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0) + { + if (minData.YClose <= 0) minData.YClose = minData2.Close; + minData.Open = minData2.Open; + minData.High = minData2.High; + minData.Low = minData2.Low; + minData.Close = minData2.Close; + break; + } + } + } + } + } + return aryDayData; + } + + //API 返回数据 转化为array[] + this.JsonDataToMinuteData=function(data) + { + var aryMinuteData=new Array(); + for(var i in data.stock[0].minute) + { + var jsData=data.stock[0].minute[i]; + var item=new MinuteData(); + + item.Close=jsData.price; + item.Open=jsData.open; + item.High=jsData.high; + item.Low=jsData.low; + item.Vol=jsData.vol; //股 + item.Amount=jsData.amount; + if (i==0) //第1个数据 写死9:25 + item.DateTime=data.stock[0].date.toString()+" 0925"; + else + item.DateTime=data.stock[0].date.toString()+" "+jsData.time.toString(); + item.Date=data.stock[0].date; + item.Time=jsData.time; + item.Increase=jsData.increase; + item.Risefall=jsData.risefall; + item.AvPrice=jsData.avprice; + + aryMinuteData[i]=item; + } + + return aryMinuteData; + } + + //多日日线数据API 转化成array[]; + this.JsonDataToMultiDayMinuteData=function(data) + { + var symbol=data.symbol; + var upperSymbol=symbol.toUpperCase(); + var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol); + var result=[]; + for(var i in data.data) + { + var minuteData=[]; + var dayData=data.data[i]; + var date=dayData.date; + var yClose=dayData.yclose; //前收盘 计算涨幅 + var preClose=yClose; //前一个数据价格 + var preAvPrice=null; //上一个均价 + //var preAvPrice=data.stock[0].yclose; //前一个均价 + for(var j in dayData.minute) + { + var jsData=dayData.minute[j]; + if (jsData[2]) preClose=jsData[2]; //保存上一个收盘数据 + var item=new MinuteData(); + item.Close=jsData[2]; + item.Open=jsData[1]; + item.High=jsData[3]; + item.Low=jsData[4]; + item.Vol=jsData[5]/100; //原始单位股 + item.Amount=jsData[6]; + if (70) + { + item.AvPrice=jsData[7]; //均价 + preAvPrice=jsData[7]; + } + item.DateTime=date.toString()+" "+jsData[0].toString(); + item.Date=date + item.Time=jsData[0]; + + if (!item.Close) //当前没有价格 使用上一个价格填充 + { + item.Close=preClose; + item.Open=item.High=item.Low=item.Close; + } + + if (!item.AvPrice && preAvPrice) item.AvPrice=preAvPrice; + + if (item.Close && yClose) item.Increase = (item.Close - yClose)/yClose*100; + else item.Increase=null; + if (j==0) //第1个数据 写死9:25 + { + if (isSHSZ) item.DateTime=date.toString()+" 0925"; + item.IsFristData=true; + } + + //价格是0的 都用空 + if (item.Open<=0) item.Open=null; + if (item.Close<=0) item.Close=null; + if (item.AvPrice<=0) item.AvPrice=null; + if (item.High<=0) item.High=null; + if (item.Low<=0) item.Low=null; + if (item.AvPrice<=0) item.AvPrice=null; + + minuteData[j]=item; + } + + var newData=new ChartData(); + newData.Data=minuteData; + newData.YClose=yClose; + newData.Close=dayData.close; + newData.Date=date; + + result.push(newData); + } + + var minuteData=[]; + for(var i=result.length-1; i>=0;--i) + { + var item=result[i]; + for(var j in item.Data) + { + minuteData.push(item.Data[j]); + } + } + + return minuteData; + } + + //CODELIKE 模糊股票代码 + this.CODELIKE=function(value) + { + if (this.Symbol && this.Symbol.indexOf(value)==0) return 1; + return 0; + } + + this.NAMELIKE=function(value) + { + if (this.Name && this.Name.indexOf(value)==0) return 1; + return 0; + } + + /* + SETCODE 市场类型 + 0:深圳 1:上海,47:中金所期货 28:郑州商品 29:大连商品 30:上海商品,27:香港指数 31:香港主板,48:香港创业板... + */ + this.SETCODE=function() + { + if (this.Symbol.indexOf('.sh')) return 1; + if (this.Symbol.indexOf('.sz')) return 0; + + return 0; + } + + this.GetSymbol=function() + { + return this.Symbol; + } + + this.GetName=function() + { + return this.Name; + } + + this.DATE=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + result[i]=item.Date-19000000; + } + + return result; + } + + this.YEAR=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + if (this.IsNumber(item.Date)) + result[i]=parseInt(item.Date/10000); + else + result[i]=null; + } + + return result; + } + + this.DAY=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + if (this.IsNumber(item.Date)) + result[i]=parseInt(item.Date%100); + else + result[i]=null; + } + + return result; + } + + this.MONTH=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + if (this.IsNumber(item.Date)) + result[i]=parseInt(item.Date%10000/100); + else + result[i]=null; + } + + return result; + } + + this.TIME=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + if (this.IsNumber(item.Time)) + result[i]=item.Time; + else + result[i]=0; + } + + return result; + } + + //星期 1-7 + this.WEEK=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + var tempDate=new Date(); + for(let i in this.Data.Data) + { + var item=this.Data.Data[i]; + result[i]=null; + if (!this.IsNumber(item.Date)) continue; + + var year=parseInt(item.Date/10000); + var month=parseInt(item.Date%10000/100); + var day=item.Date%100; + + tempDate.setFullYear(year); + tempDate.setMonth(month-1); + tempDate.setDate(day); + + result[i]=tempDate.getDay(); + } + + return result; + } + + this.REFDATE=function(data,date) + { + var result=null; + var findDate=null; + if (Array.isArray(date)) + { + if (date.length>0) findDate=date[date.length-1]; + } + else if (this.IsNumber(date)) + { + findDate=date; + } + + if (findDate==null) return null; + if (findDate<5000000) findDate+=19000000; + + var index=null; + for(let i in this.Data.Data) //查找日期对应的索引 + { + if (this.Data.Data[i].Date==findDate) + { + index=parseInt(i); + break; + } + } + + if (index==null || index>=data.length) return null; + + return data[index]; + } + + //用法:结果从0到11,依次分别是1/5/15/30/60分钟,日/周/月,多分钟,多日,季,年 + this.PERIOD=function() + { + //Period周期 0=日线 1=周线 2=月线 3=年线 9=季线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 + const PERIOD_MAP=[5,6,7,11, 0,1,2,3,4,5, 9]; + if (this.Period>=0 && this.Period<=PERIOD_MAP.length-1) + return PERIOD_MAP[this.Period]; + + return this.Period; + } + + this.GetDrawNull=function() + { + var result=[]; + if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; + + for(let i in this.Data.Data) + { + result[i]=null; + } + + return result; + } + +} + +//是否有是有效的数字 +JSSymbolData.prototype.IsNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + + return true; +} + +JSSymbolData.prototype.IsDivideNumber=function(value) +{ + if (value==null) return false; + if (isNaN(value)) return false; + if (value==0) return false; + + return true; +} + + +JSSymbolData.prototype.JsonDataToFinance=function(data) +{ + var financeData; + + for(let i=1;i<=4;++i) + { + switch(i) + { + case 1: + var finance=data.finance1; + var announcement=data.announcement1; + break; + case 2: + var finance=data.finance2; + var announcement=data.announcement2; + break; + case 3: + var finance=data.finance3; + var announcement=data.announcement3; + break; + case 4: + var finance=data.finance4; + var announcement=data.announcement4; + break; + default: + break; + } + + if (!finance || !announcement || !this.IsNumber(announcement.year) || !this.IsNumber(announcement.quarter)) continue; + if (financeData) //如果存在1天公布多个报告期数据 只取最新的一个公告期数据 + { + if (financeData.Announcement.year=2000 && quarter>=1 && quarter<=4) + return { Year:year, Quarter:quarter }; + return null; + } + +}; + +function JSExecute(ast,option) +{ + this.AST=ast; //语法树 + + this.ErrorHandler=new ErrorHandler(); + this.VarTable=new Map(); //变量表 + this.OutVarTable=[]; //输出变量 + this.Arguments=[]; + this.ErrorCallback; //执行错误回调 + + //脚本自动变量表, 只读 + this.ConstVarTable=new Map([ + //个股数据 + ['CLOSE',null],['VOL',null],['OPEN',null],['HIGH',null],['LOW',null],['AMOUNT',null], + ['C',null],['V',null],['O',null],['H',null],['L',null],['AMO',null], + ['VOLR',null], //量比 + ['VOLINSTK',null], ["OPI",null], //持仓量 + ["QHJSJ",null], ["SETTLE",null], //结算价 + ["ZSTJJ",null], //分时图均价线,对于分时图周期指标有效. + ["ISEQUAL",null], ["ISUP",null],["ISDOWN"], //ISUP=收阳 ISEQUAL=平盘 ISDOWN=收阴 + + //日期类 + ['DATE',null],['YEAR',null],['MONTH',null],['PERIOD', null],['WEEK',null],["TIME",null],["DAY",null], + + //大盘数据 + ['INDEXA',null],['INDEXC',null],['INDEXH',null],['INDEXL',null],['INDEXO',null],['INDEXV',null], + ['INDEXADV',null],['INDEXDEC',null], + + ["ADVANCE",null],['DECLINE', null], + + ['FROMOPEN',null], //已开盘有多长分钟 + ['TOTALFZNUM', null], //该品种的每天的总交易分钟数. + + ['CURRBARSCOUNT',null], //到最后交易日的周期数 + ['TOTALBARSCOUNT',null], + ['ISLASTBAR',null], //判断是否为最后一个周期 + ['BARSTATUS',null], //BARSTATUS返回数据位置信息,1表示第一根K线,2表示最后一个数据,0表示中间位置. + + ['CAPITAL',null], //流通股本(手) + ["TOTALCAPITAL",null], //TOTALCAPITAL 当前总股本 手 + ['EXCHANGE',null], //换手率 + ['SETCODE', null], //市场类型 + ['CODE',null], //品种代码 + ['STKNAME',null], //品种名称 + ["TQFLAG",null], //TQFLAG 当前的复权状态,0:无复权 1:前复权 2:后复权 + + ['HYBLOCK',null], //所属行业板块 + ['DYBLOCK',null], //所属地域板块 + ['GNBLOCK',null], //所属概念 + ["FGBLOCK",null], //所属风格板块 + ["ZSBLOCK",null], //所属指数板块 + ["ZHBLOCK",null], //所属组合板块 + ["ZDBLOCK",null], //所属自定义板块 + ["HYZSCODE",null], + + ["GNBLOCKNUM",null], //所属概念板块的个数 + ["FGBLOCKNUM",null], //所属风格板块的个数 + ["ZSBLOCKNUM",null], //所属指数板块的个数 + ["ZHBLOCKNUM",null], //所属组合板块的个数 + ["ZDBLOCKNUM",null], //所属自定义板块的个数 + + ["HYSYL",null], //指数市盈率或个股所属行业的市盈率 + ["HYSJL",null], //指数市净率或个股所属行业的市净率 + + ['DRAWNULL',null], + + ["MACHINEDATE",null],["MACHINETIME",null],["MACHINEWEEK",null] + + ]); + + this.SymbolData=new JSSymbolData(this.AST,option,this); + this.Algorithm=new JSAlgorithm(this.ErrorHandler,this.SymbolData); + this.Draw=new JSDraw(this.ErrorHandler,this.SymbolData); + + this.JobList=[]; //执行的任务队列 + + this.UpdateUICallback=null; //回调 + this.CallbackParam=null; + this.IsSectionMode=false; + + if (option) + { + if (option.Callback) this.UpdateUICallback=option.Callback; + if (option.CallbackParam) this.CallbackParam=option.CallbackParam; + if (option.Arguments) this.Arguments=option.Arguments; + if (option.IsSectionMode) this.IsSectionMode=option.IsSectionMode; + } + + this.Execute=function() + { + this.OutVarTable=[]; + this.VarTable=new Map(); + JSConsole.Complier.Log('[JSExecute::Execute] Load Arguments', this.Arguments); + for(let i in this.Arguments) //预定义的变量 + { + let item =this.Arguments[i]; + this.VarTable.set(item.Name,item.Value); + } + + this.RunNextJob(); + } + + this.RunNextJob=function() + { + if (this.JobList.length<=0) return; + + JSConsole.Complier.Log('[JSExecute::Execute] JobList', this.JobList); + var jobItem=this.JobList.shift(); + + switch(jobItem.ID) + { + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA: + return this.SymbolData.GetSymbolData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_PERIOD_DATA: + return this.SymbolData.GetSymbolPeriodData(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA: + return this.SymbolData.GetIndexData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_LATEST_INDEX_DATA: + return this.SymbolData.GetLatestIndexData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA: + return this.SymbolData.GetIndexIncreaseData(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA: + return this.SymbolData.GetLatestData(); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA: //量比 + return this.SymbolData.GetVolRateData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_OTHER_SYMBOL_DATA: //指定股票数据 + return this.SymbolData.GetOtherSymbolData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_RELEASE_DATE_DATA: + return this.SymbolData.GetCompanyReleaseDate(jobItem.ID); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE: + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE: + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出 + return this.SymbolData.GetMarginData(jobItem.ID); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: //负面新闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: //机构调研 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: //互动易 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: //股东增持 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: //股东减持 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: //信托持股 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: //大宗交易 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: //官网新闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: //高管要闻 + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: //股权质押 + return this.SymbolData.GetNewsAnalysisData(jobItem.ID); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SHSZ: + return this.SymbolData.GetHKToSHSZData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_SF: + return this.SymbolData.GetSectionFinanceData(jobItem); //财务截面报告数据 + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE: + return this.SymbolData.GetFinOne(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE: + return this.SymbolData.GetFinValue(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE: + return this.SymbolData.GetFinance(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE: + return this.SymbolData.GetGPJYValue(jobItem); + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SCJYVALUE: + return this.SymbolData.GetSCJYValue(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT: //CAPITAL, TOTALCAPITAL + return this.SymbolData.GetVariantData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA: + return this.SymbolData.GetCustomVariantData(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA: + return this.SymbolData.GetCustomFunctionData(jobItem); + + + case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA: + return this.SymbolData.DownloadCustomAPIData(jobItem); + case JS_EXECUTE_JOB_ID.JOB_EXECUTE_INDEX: + return this.SymbolData.CallScriptIndex(jobItem); + + case JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT: + return this.Run(); + } + } + + this.ReadSymbolData=function(name,node) + { + switch(name) + { + case 'CLOSE': + case 'C': + case 'VOL': + case 'V': + case 'OPEN': + case 'O': + case 'HIGH': + case 'H': + case 'LOW': + case 'L': + case 'AMOUNT': + case 'AMO': + case "OPI": + case 'VOLINSTK': + case "SETTLE": + case "QHJSJ": + case "ZSTJJ": + case "ISEQUAL": //平盘 + case "ISUP": //收阳 + case "ISDOWN": //收阴 + return this.SymbolData.GetSymbolCacheData(name); + case 'VOLR': + return this.SymbolData.GetVolRateCacheData(node); + + //大盘数据 + case 'INDEXA': + case 'INDEXC': + case 'INDEXH': + case 'INDEXH': + case 'INDEXO': + case 'INDEXV': + case 'INDEXL': + case 'INDEXADV': + case 'INDEXDEC': + return this.SymbolData.GetIndexCacheData(name); + case 'CURRBARSCOUNT': + return this.SymbolData.GetCurrBarsCount(); + case "TOTALBARSCOUNT": + return this.SymbolData.GetTotalBarsCount(); + case "TOTALFZNUM": + return this.SymbolData.GetTotalTradeMinuteCount(); + case 'ISLASTBAR': + return this.SymbolData.GetIsLastBar(); + case "BARSTATUS": + return this.SymbolData.GetBarStatus(); + + case "TOTALCAPITAL": + case 'CAPITAL': + case 'EXCHANGE': + + case "HYBLOCK": + case "DYBLOCK": + case "GNBLOCK": + case "FGBLOCK": + case "ZSBLOCK": + case "ZHBLOCK": + case "ZDBLOCK": + case "HYZSCODE": + + case "GNBLOCKNUM": + case "FGBLOCKNUM": + case "ZSBLOCKNUM": + case "ZHBLOCKNUM": + case "ZDBLOCKNUM": + + case "HYSYL": + case "HYSJL": + return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node }); + case 'SETCODE': + return this.SymbolData.SETCODE(); + case 'CODE': + return this.SymbolData.GetSymbol(); + case 'STKNAME': + return this.SymbolData.GetName(); + + case "TIME": + return this.SymbolData.TIME(); + case 'DATE': + return this.SymbolData.DATE(); + case 'YEAR': + return this.SymbolData.YEAR(); + case 'MONTH': + return this.SymbolData.MONTH(); + case 'WEEK': + return this.SymbolData.WEEK(); + case "DAY": + return this.SymbolData.DAY(); + case 'PERIOD': + return this.SymbolData.PERIOD(); + case 'FROMOPEN': + return this.SymbolData.GetLatestIndexCacheData('FROMOPEN'); + + case 'DRAWNULL': + return this.SymbolData.GetDrawNull(); + + case 'ADVANCE': + case 'DECLINE': + return this.SymbolData.GetIndexIncreaseCacheData(name,this.SymbolData.Symbol,node); + + case "TQFLAG": + return this.SymbolData.Right; + + case "MACHINEDATE": + { + var now=new Date(); + return (now.getFullYear()*10000+(now.getMonth()*1)*100+now.getDate())-19000000; + } + case "MACHINETIME": + { + var now=new Date(); + return now.getHours()*10000+(now.getMinutes()*1)*100+now.getSeconds(); + } + case "MACHINEWEEK": + { + var now=new Date(); + return now.getDay(); + } + } + } + + this.ReadCustomVariant=function(name,node) + { + return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node }); + } + + //读取变量 + this.ReadVariable=function(name,node) + { + if (this.ConstVarTable.has(name)) + { + let data=this.ConstVarTable.get(name); + + if (data==null) //动态加载,用到再加载 + { + data=this.ReadSymbolData(name,node); + this.ConstVarTable.set(name,data); + } + + return data; + } + + if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量 + + if (this.VarTable.has(name)) return this.VarTable.get(name); + + if (name.indexOf('#')>0) + { + var aryPeriod=name.split('#'); + return this.SymbolData.GetSymbolPeriodCacheData(aryPeriod[0],aryPeriod[1]); + } + + this.ThrowUnexpectedNode(node, '变量'+name+'不存在', name); + return null; + } + + this.ReadMemberVariable=function(node) + { + var obj=node.Object; + var member=node.Property; + + let maiObj; + if (obj.Type==Syntax.BinaryExpression || obj.Type==Syntax.LogicalExpression ) + maiObj=this.VisitBinaryExpression(obj); + else if (obj.Type==Syntax.CallExpression) + maiObj=this.VisitCallExpression(obj); + else + maiObj=this.GetNodeValue(obj); + + if (!maiObj) return null; + var value=maiObj[member.Name]; + if (value) return value; + + return null; + } + + //单数据转成数组 个数和历史数据一致 + this.SingleDataToArrayData=function(value) + { + let count=this.SymbolData.Data.Data.length; + let result=[]; + for(let i=0;i0) + outVar=this.SymbolData.GetOtherSymolCacheData({ Literal:outVar }); + varName="__temp_li_"+i+"__"; + var type=0; + this.OutVarTable.push({Name:varName, Data:outVar, Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.BinaryExpression) + { + var varName="__temp_b_"+i+"__"; + let outVar=item.Expression.Out; + var type=0; + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.LogicalExpression) //逻辑语句 如 T1 AND T2 + { + var varName="__temp_l_"+i+"__"; + let outVar=item.Expression.Out; + var type=0; + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true}); + } + else if (item.Expression.Type==Syntax.SequenceExpression) + { + let varName; + let draw; + let color; + let lineWidth; + let colorStick=false; + let pointDot=false; + let circleDot=false; + let lineStick=false; + let stick=false; + let volStick=false; + let isShow=true; + let isExData=false; + let isDotLine=false; + let isOverlayLine=false; //叠加线 + var isNoneName=false; + var isShowTitle=true; + //显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如: + //DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE; + var isDrawAbove=false; + var isDrawCenter=false; + var isDrawBelow=false; + for(let j in item.Expression.Expression) + { + let itemExpression=item.Expression.Expression[j]; + if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left) + { + varName=itemExpression.Left.Name; + let varValue=this.VarTable.get(varName); + if (!Array.isArray(varValue)) + { + varValue=this.SingleDataToArrayData(varValue); + this.VarTable.set(varName,varValue); //把常量放到变量表里 + } + } + else if (itemExpression.Type==Syntax.Identifier) + { + let value=itemExpression.Name; + if (value==='COLORSTICK') colorStick=true; + else if (value==='POINTDOT') pointDot=true; + else if (value==='CIRCLEDOT') circleDot=true; + else if (value==='DOTLINE') isDotLine=true; + else if (value==='LINESTICK') lineStick=true; + else if (value==='STICK') stick=true; + else if (value==='VOLSTICK') volStick=true; + else if (value==="DRAWABOVE") isDrawAbove=true; + else if (value==="DRAWCENTER") isDrawCenter=true; + else if (value=="DRAWBELOW") isDrawBelow=true; + else if (value.indexOf('COLOR')==0) color=value; + else if (value.indexOf('LINETHICK')==0) lineWidth=value; + else if (value.indexOf('NODRAW')==0) isShow=false; + else if (value.indexOf('EXDATA')==0) isExData=true; //扩展数据, 不显示再图形里面 + else if (value.indexOf('LINEOVERLAY')==0) isOverlayLine=true; + else if (value.indexOf("NOTEXT")==0 || value.indexOf("NOTITLE")==0) isShowTitle=false; //标题不显示 + else + { + varName=itemExpression.Name; + let varValue=this.ReadVariable(varName,itemExpression); + if (!Array.isArray(varValue)) varValue=this.SingleDataToArrayData(varValue); + varName="__temp_si_"+i+"__"; + isNoneName=true; + this.VarTable.set(varName,varValue); //放到变量表里 + } + } + else if(itemExpression.Type==Syntax.Literal) //常量 + { + let aryValue=this.SingleDataToArrayData(itemExpression.Value); + varName=itemExpression.Value.toString(); + this.VarTable.set(varName,aryValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.CallExpression) + { + if (this.Draw.IsDrawFunction(itemExpression.Callee.Name)) + { + draw=itemExpression.Draw; + draw.Name=itemExpression.Callee.Name; + } + else + { + let varValue=itemExpression.Out; + varName=`__temp_sc_${itemExpression.Callee.Name}_${i}__`; + isNoneName=true; + this.VarTable.set(varName,varValue); + } + } + else if (itemExpression.Type==Syntax.BinaryExpression) + { + varName="__temp_sb_"+i+"__"; + let aryValue=itemExpression.Out; + isNoneName=true; + this.VarTable.set(varName,aryValue); + } + } + + if (pointDot && varName) //圆点 + { + let outVar=this.VarTable.get(varName); + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + let value={Name:varName, Data:outVar, Radius:g_JSChartResource.POINTDOT.Radius, Type:3}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (circleDot && varName) //圆点 + { + let outVar=this.VarTable.get(varName); + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + let value={Name:varName, Data:outVar, Radius:g_JSChartResource.CIRCLEDOT.Radius, Type:3}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (lineStick && varName) //LINESTICK 同时画出柱状线和指标线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:4}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (stick && varName) //STICK 画柱状线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:5}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + this.OutVarTable.push(value); + } + else if (volStick && varName) //VOLSTICK 画彩色柱状线 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Type:6}; + if (color) value.Color=color; + this.OutVarTable.push(value); + } + else if (colorStick && varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子 + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar, Color:color, Type:2}; + if (lineWidth) value.LineWidth=lineWidth; + if (color) value.Color=color; + this.OutVarTable.push(value); + } + else if (varName && color) + { + let outVar=this.VarTable.get(varName); + if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); + let value={Name:varName, Data:outVar, Color:color, Type:0}; + if (lineWidth) value.LineWidth=lineWidth; + if (isShow == false) value.IsShow = false; + if (isExData==true) value.IsExData = true; + if (isDotLine==true) value.IsDotLine=true; + if (isOverlayLine==true) value.IsOverlayLine=true; + if (isNoneName==true) value.NoneName=true; + if (isShowTitle==false) value.IsShowTitle=false; + this.OutVarTable.push(value); + } + else if (draw) //画图函数 + { + var outVar={Name:draw.Name, Draw:draw, Type:1}; + if (color) outVar.Color=color; + if (isDotLine==true) outVar.IsDotLine=true; + if (lineWidth) outVar.LineWidth=lineWidth; + if (isDrawAbove) outVar.IsDrawAbove=true; + if (isDrawCenter) outVar.IsDrawCenter=true; + if (isDrawBelow) outVar.IsDrawBelow=true; + this.OutVarTable.push(outVar); + } + else if (varName) + { + let outVar=this.VarTable.get(varName); + let value={Name:varName, Data:outVar,Type:0}; + if (color) value.Color=color; + if (lineWidth) value.LineWidth=lineWidth; + if (isShow==false) value.IsShow=false; + if (isExData==true) value.IsExData = true; + if (isDotLine==true) value.IsDotLine=true; + if (isOverlayLine==true) value.IsOverlayLine=true; + if (isShowTitle==false) value.IsShowTitle=false; + this.OutVarTable.push(value); + } + } + } + } + + JSConsole.Complier.Log('[JSExecute::Run]', this.VarTable); + + return this.OutVarTable; + } + + this.Run=function() + { + try + { + let data=this.RunAST();//执行脚本 + JSConsole.Complier.Log('[JSComplier.Run] execute finish', data); + + if (this.UpdateUICallback) + { + JSConsole.Complier.Log('[JSComplier.Run] invoke UpdateUICallback.'); + if (this.CallbackParam && this.CallbackParam.Job && this.CallbackParam.Job.ID==JS_EXECUTE_JOB_ID.JOB_EXECUTE_INDEX) + { + this.UpdateUICallback(data,this.CallbackParam, this.SymbolData); + } + else + { + if (this.CallbackParam && this.CallbackParam.Self && this.CallbackParam.Self.ClassName==='ScriptIndexConsole') this.CallbackParam.JSExecute=this; + this.UpdateUICallback(data,this.CallbackParam); + } + } + } + catch(error) + { + JSConsole.Complier.Log(error); + if (error.Job) + { + JSConsole.Complier.Log('[JSComplier.Run] download job and reexectue', error.Job); + this.JobList.push(error.Job); + this.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT}); + this.Execute(); + } + else if (this.ErrorCallback) + { + this.ErrorCallback(error, this.CallbackParam); + } + } + } + + this.VisitNode=function(node) + { + switch(node.Type) + { + case Syntax.SequenceExpression: + this.VisitSequenceExpression(node); + break; + case Syntax.ExpressionStatement: + this.VisitNode(node.Expression); + break; + case Syntax.AssignmentExpression: + this.VisitAssignmentExpression(node); + break; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + this.VisitBinaryExpression(node); + break; + case Syntax.CallExpression: + this.VisitCallExpression(node); + break; + } + } + + this.VisitSequenceExpression=function(node) + { + for(let i in node.Expression) + { + let item =node.Expression[i]; + this.VisitNode(item); + } + } + + //函数调用 + this.VisitCallExpression=function(node) + { + let funcName=node.Callee.Name; + let args=[]; + for(let i in node.Arguments) + { + let item=node.Arguments[i]; + let value; + if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(item); + else if (item.Type==Syntax.CallExpression) + value=this.VisitCallExpression(item); + else + value=this.GetNodeValue(item); + args.push(value); + } + + //JSConsole.Complier.Log('[JSExecute::VisitCallExpression]' , funcName, '(', args.toString() ,')'); + + if (g_JSComplierResource.IsCustomFunction(funcName)) + { + var data=this.Algorithm.CallCustomFunction(funcName, args, this.SymbolData, node); + node.Out=[]; + node.Draw=null; + + if (data) + { + if (data.Out) node.Out=data.Out; + if (data.Draw) node.Draw=data.Draw; + } + + return node.Out; + } + + switch(funcName) + { + case 'DYNAINFO': //行情最新数据 + node.Out=this.SymbolData.GetLatestCacheData(args[0]); + break; + case 'STICKLINE': + node.Draw=this.Draw.STICKLINE(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'DRAWTEXT': + node.Draw=this.Draw.DRAWTEXT(args[0],args[1],args[2]); + node.Out=[]; + break; + case 'DRAWTEXT_FIX': + node.Draw=this.Draw.DRAWTEXT_FIX(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'SUPERDRAWTEXT': + node.Draw=this.Draw.SUPERDRAWTEXT(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'DRAWICON': + node.Draw=this.Draw.DRAWICON(args[0],args[1],args[2]); + node.Out=[]; + break; + case 'DRAWLINE': + node.Draw=this.Draw.DRAWLINE(args[0],args[1],args[2],args[3],args[4]); + node.Out=node.Draw.DrawData; + break; + case 'DRAWBAND': + node.Draw=this.Draw.DRAWBAND(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case "FILLRGN": + if (args.length>=4) node.Draw=this.Draw.FILLRGN2(args); + else node.Draw=this.Draw.FILLRGN(args[0],args[1],args[2]); + node.Out=[]; + break; + case "FLOATRGN": + node.Draw=this.Draw.FLOATRGN(args); + node.Out=[]; + break; + case "FILLTOPRGN": + node.Draw=this.Draw.FILLBGRGN(1, args); + node.Out=[]; + break; + case "FILLBOTTOMRGN": + node.Draw=this.Draw.FILLBGRGN(0, args); + node.Out=[]; + break; + case "FILLVERTICALRGN": + node.Draw=this.Draw.FILLVERTICALRGN(args); + node.Out=[]; + break; + case 'DRAWKLINE': + node.Draw=this.Draw.DRAWKLINE(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'DRAWKLINE_IF': + node.Draw=this.Draw.DRAWKLINE_IF(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case 'PLOYLINE': + case 'POLYLINE': + node.Draw=this.Draw.POLYLINE(args[0],args[1]); + node.Out=node.Draw.DrawData; + break; + case 'DRAWNUMBER': + node.Draw=this.Draw.DRAWNUMBER(args[0],args[1],args[2]); + node.Out=node.Draw.DrawData.Value; + break; + case "DRAWNUMBER_FIX": + node.Draw=this.Draw.DRAWNUMBER_FIX(args[0],args[1],args[2],args[3],args[4]); + node.Out=node.Draw.DrawData.Value; + break; + case "DRAWCHANNEL": + node.Draw=this.Draw.DRAWCHANNEL(args[0],args[1],args[2],args[3],args[4],args[5],args[6]); + node.Out=[]; + break; + case 'RGB': + node.Out=this.Draw.RGB(args[0],args[1],args[2]); + break; + case "RGBA": + node.Out=this.Draw.RGBA(args[0],args[1],args[2],args[3]); + break; + case 'PARTLINE': + node.Draw=this.Draw.PARTLINE(args); + node.Out=[]; + break; + case 'DRAWGBK': + node.Draw=this.Draw.DRAWGBK(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'DRAWGBK2': + node.Draw=this.Draw.DRAWGBK2(args[0],args[1],args[2],args[3]); + node.Out=[]; + break; + case 'DRAWTEXT_LINE': + node.Draw=this.Draw.DRAWTEXT_LINE(args[0],args[1],args[2],args[3],args[4],args[5],args[6]); + node.Out=[]; + break; + case 'DRAWRECTREL': + node.Draw=this.Draw.DRAWRECTREL(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case "DRAWTEXTREL": + node.Draw=this.Draw.DRAWTEXTREL(args[0],args[1],args[2]); + node.Out=[]; + break; + case "DRAWTEXTABS": + node.Draw=this.Draw.DRAWTEXTABS(args[0],args[1],args[2]); + node.Out=[]; + break; + case "DRAWOVERLAYLINE": + node.Draw=this.Draw.DRAWOVERLAYLINE(args[0],args[1],args[2]); + node.Out=node.Draw.DrawData.Data; + break; + case "DRAWSL": + node.Draw=this.Draw.DRAWSL(args[0],args[1],args[2],args[3],args[4]); + node.Out=[]; + break; + case "VERTLINE": + node.Draw=this.Draw.VERTLINE(args[0],args[1]); + node.Out=node.Draw.DrawData.Data; + break; + case "HORLINE": + node.Draw=this.Draw.HORLINE(args[0],args[1],args[2],args[3]); + node.Out=node.Draw.DrawData.Data; + break; + case 'CODELIKE': + node.Out=this.SymbolData.CODELIKE(args[0]); + break; + case 'NAMELIKE': + case "NAMEINCLUDE": + node.Out=this.SymbolData.NAMELIKE(args[1]); + break; + case 'REFDATE': + node.Out=this.SymbolData.REFDATE(args[0],args[1]); + break; + case 'FINANCE': + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } ); + break; + case "FINVALUE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } ); + break; + case "FINONE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); + break; + case "GPJYVALUE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); + break; + case "SCJYVALUE": + node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); + break; + case "MARGIN": + node.Out=this.SymbolData.GetMarginCacheData(args[0],node); + break; + case "HK2SHSZ": + node.Out=this.SymbolData.GetHKToSHSZCacheData(args[0],node); + break; + case "NEWS": + node.Out=this.SymbolData.GetNewsAnalysisCacheData(args[0],node); + break; + case 'UPCOUNT': + case 'DOWNCOUNT': + node.Out=this.SymbolData.GetIndexIncreaseCacheData(funcName,args[0],node); + break; + case 'SF': + node.Out=this.SymbolData.GetSectionFinanceCacheData(args[0],args[1],args[2],node); + break; + case 'LOADAPIDATA': + node.Out=this.SymbolData.GetCustomApiData(args); + break; + case "STKINDI": + node.Out=this.SymbolData.GetScriptIndexOutData(args,node); + break; + case 'CLOSE': + case 'C': + case 'VOL': + case 'V': + case 'OPEN': + case 'O': + case 'HIGH': + case 'H': + case 'LOW': + case 'L': + case 'AMOUNT': + case 'AMO': + node.Out=this.SymbolData.GetOtherSymolCacheData( {FunctionName:funcName, Args:args} ); + break; + case "INBLOCK": + node.Out=this.SymbolData.IsInBlock(args[0],node); + break; + + case 'COVER_C': + case 'COVER_O': + case 'COVER_H': + case 'COVER_L': + case 'COVER_A': + case 'COVER_V': + if (args.length==2) return this.SymbolData.GetSymbolPeriodCacheData2(JSComplierHelper.GetConvertValueName(funcName),args[0],args[1]); + return this.SymbolData.GetSymbolPeriodCacheData(JSComplierHelper.GetConvertValueName(funcName),args[0]); + + default: + node.Out=this.Algorithm.CallFunction(funcName, args, node, this.SymbolData); + break; + } + + return node.Out; + } + + //赋值 + this.VisitAssignmentExpression=function(node) + { + let left=node.Left; + if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node); + + let varName=left.Name; + + let right=node.Right; + let value=null; + if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(right); + else if (right.Type==Syntax.CallExpression) + value=this.VisitCallExpression(right); + else if (right.Type==Syntax.Literal) + { + value=right.Value; + if (IFrameSplitOperator.IsString(value) && right.Value.indexOf("$")>0) + value=this.SymbolData.GetOtherSymolCacheData( {Literal:value} ); + } + else if (right.Type==Syntax.Identifier) //右值是变量 + value=this.ReadVariable(right.Name,right); + else if (right.Type==Syntax.MemberExpression) + value=this.ReadMemberVariable(right); + else if (right.Type==Syntax.UnaryExpression) + { + if (right.Operator=='-') + { + var tempValue=this.GetNodeValue(right.Argument); + value=this.Algorithm.Subtract(0,tempValue); + } + else + { + value=right.Argument.Value; + } + } + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitAssignmentExpression]' , varName, ' = ',value); + this.VarTable.set(varName,value); + } + + //逻辑运算 + this.VisitBinaryExpression=function(node) + { + let stack=[]; + stack.push(node); + let temp=null; + + while(stack.length!=0) + { + temp=stack[stack.length-1]; + if (temp.Left && node!=temp.Left && node!=temp.Right) + { + stack.push(temp.Left); + } + else if (temp.Right && node!=temp.Right) + { + stack.push(temp.Right); + } + else + { + let value=stack.pop(); + if (value.Type==Syntax.BinaryExpression) //只遍历操作符就可以 + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '-': + value.Out=this.Algorithm.Subtract(leftValue,rightValue); + break; + case '*': + value.Out=this.Algorithm.Multiply(leftValue,rightValue); + break; + case '/': + value.Out=this.Algorithm.Divide(leftValue,rightValue) + break; + case '+': + value.Out=this.Algorithm.Add(leftValue,rightValue); + break; + case '>': + value.Out=this.Algorithm.GT(leftValue,rightValue); + break; + case '>=': + value.Out=this.Algorithm.GTE(leftValue,rightValue); + break; + case '<': + value.Out=this.Algorithm.LT(leftValue,rightValue); + break; + case '<=': + value.Out=this.Algorithm.LTE(leftValue,rightValue); + break; + case '==': + case '=': //= 比较 + value.Out=this.Algorithm.EQ(leftValue,rightValue); + break; + case '!=': + case '<>': + value.Out=this.Algorithm.NEQ(leftValue,rightValue); + break; + } + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value); + } + else if (value.Type==Syntax.LogicalExpression) + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '&&': + case 'AND': + value.Out=this.Algorithm.And(leftValue,rightValue); + break; + case '||': + case 'OR': + value.Out=this.Algorithm.Or(leftValue,rightValue); + break; + } + + if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value); + } + + node=temp; + } + } + + return node.Out; + + } + + this.GetNodeValue=function(node) + { + switch(node.Type) + { + case Syntax.Literal: //数字 + return node.Value; + case Syntax.UnaryExpression: + if (node.Operator=='-') + { + let value=this.GetNodeValue(node.Argument); + return this.Algorithm.Subtract(0,value); + } + return node.Argument.Value; + case Syntax.Identifier: + let value=this.ReadVariable(node.Name,node); + return value; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + return node.Out; + case Syntax.CallExpression: + return this.VisitCallExpression(node); + default: + this.ThrowUnexpectedNode(node); + } + } + + this.ThrowUnexpectedNode=function(node,message,word) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg, word); + + } + + this.ThrowDownloadSF=function(node,job,message) + { + let marker=node.Marker; + let msg=message; + + return this.ErrorHandler.ThrowDownloadJob(marker.Index,marker.Line,marker.Column,msg,job); + } + + this.ThrowError=function() + { + + } +} + +//脚本说明 +function JSExplainer(ast,option) +{ + this.AST=ast; + this.ErrorHandler=new ErrorHandler(); + this.ErrorCallback; //执行错误回调 + this.UpdateUICallback; + this.CallbackParam; + this.JobList=[]; //执行的任务队列 + this.VarTable=new Map(); //变量表 + this.OutVarTable=[]; //输出变量 + + //脚本自动变量表, 只读 + this.ConstVarTable=new Map( + [ + //个股数据 + ['CLOSE',"收盘价"],['VOL',"成交量"],['OPEN',"开盘价"],['HIGH',"最高价"],['LOW',"最低价"],['AMOUNT',"成交量"], + ['C',"收盘价"],['V',"成交量"],['O',"开盘价"],['H',"最高价"],['L',"最低价"],['AMO',"成交量"], + ['VOLR',"量比"], ['VOLINSTK',"持仓量"], ["OPI","持仓量"], ["ZSTJJ","均价"], ["QHJSJ","结算价"], ["SETTLE", "结算价"], + + //日期类 + ['DATE',"日期"],['YEAR',"年份"],['MONTH',"月份"],['PERIOD', "周期"],['WEEK',"星期"],["TIME","时间"], + + //大盘数据 + ['INDEXA',"大盘成交额"],['INDEXC',"大盘收盘价"],['INDEXH',"大盘最高价"],['INDEXL',"大盘最低价"],['INDEXO',"大盘开盘价"],['INDEXV',"大盘成交量"], + ['INDEXADV',"大盘上涨家数"],['INDEXDEC',"´大盘下跌家数"], + + ["ADVANCE","上涨家数"], ['DECLINE', "下跌家数"], + + ['FROMOPEN',"当前离开盘分钟数"], + ['TOTALFZNUM', "总分钟数"], + + ['CURRBARSCOUNT',"到最后交易的周期"], //到最后交易日的周期数 + ['TOTALBARSCOUNT',"总的周期数"], + ['ISLASTBAR',"是否是最后一个周期"], //判断是否为最后一个周期 + ['BARSTATUS',"数据位置状态"], //BARSTATUS返回数据位置信息,1表示第一根K线,2表示最后一个数据,0表示中间位置. + + ['CAPITAL',"当前流通股本(手)"], ["TOTALCAPITAL","当前总股本(手)"], + ['EXCHANGE',"换手率"], //换手率 + ['SETCODE', "市场类型"], //市场类型 + ['CODE',"品种代码"], //品种代码 + ['STKNAME',"品种名称"], //品种名称 + ["TQFLAG","当前复权状态"], //TQFLAG 当前的复权状态,0:无复权 1:前复权 2:后复权 + + ['HYBLOCK',"所属行业"], //所属行业板块 + ['DYBLOCK',"所属地域"], //所属地域板块 + ['GNBLOCK',"所属概念"], //所属概念 + ["FGBLOCK","所属风格板块"], + ["ZSBLOCK","所属指数板块"], + ["ZHBLOCK",'所属组合板块'], + ["ZDBLOCK",'所属自定义板块'], + ["HYZSCODE","所属行业的板块指数代码"], + + ["GNBLOCKNUM","所属概念板块的个数"], + ["FGBLOCKNUM","所属风格板块的个数"], + ["ZSBLOCKNUM","所属指数板块的个数"], + ["ZHBLOCKNUM","所属组合板块的个数"], + ["ZDBLOCKNUM","所属自定义板块的个数"], + + ["HYSYL","指数市盈率或个股所属行业的市盈率"], + ["HYSJL","指数市净率或个股所属行业的市净率"], + + ['DRAWNULL',"无效数据"] + + ]); + + if (option) + { + if (option.Callback) this.UpdateUICallback=option.Callback; + if (option.CallbackParam) this.CallbackParam=option.CallbackParam; + if (option.Arguments) this.Arguments=option.Arguments; + } + + this.Run=function() + { + try + { + this.OutVarTable=[]; + this.VarTable=new Map(); + JSConsole.Complier.Log('[JSExecute::JSExplainer] Load Arguments', this.Arguments); + for(let i in this.Arguments) //预定义的变量 + { + let item =this.Arguments[i]; + this.VarTable.set(item.Name,item.Value); + } + + let data=this.RunAST();//执行脚本 + JSConsole.Complier.Log('[JSExplainer.Run] explain finish', data); + if (this.UpdateUICallback) //回调发送结果, 可以支持异步 + { + JSConsole.Complier.Log('[JSExplainer.Run] invoke UpdateUICallback.'); + this.UpdateUICallback(data); + } + } + catch(error) + { + JSConsole.Complier.Log('[JSExplainer.Run] throw error ', error); + if (this.ErrorCallback) + { + this.ErrorCallback(error, this.OutVarTable); + } + } + } + + + this.RunAST=function() + { + if (!this.AST) this.ThrowError(); + if (!this.AST.Body) this.ThrowError(); + + for(let i in this.AST.Body) + { + let item =this.AST.Body[i]; + this.VisitNode(item); + + //输出变量 + if (item.Type==Syntax.ExpressionStatement && item.Expression) + { + if (item.Expression.Type==Syntax.AssignmentExpression) + { + if (item.Expression.Operator==':' && item.Expression.Left) + { + let assignmentItem=item.Expression; + let varName=assignmentItem.Left.Name; + let outVar=`输出${varName}: ${this.VarTable.get(varName)}`; + this.OutVarTable.push({ Name:varName, Data:outVar,Type:0}); + } + else if (item.Expression.Operator==':=' && item.Expression.Left) + { + let assignmentItem=item.Expression; + let varName=assignmentItem.Left.Name; + let outVar=`赋值${varName}: ${this.VarTable.get(varName)}`; + this.OutVarTable.push({ Name:varName, Data:outVar,Type:0, IsOut:false }); + } + } + else if (item.Expression.Type==Syntax.CallExpression) + { + let callItem=item.Expression; + if (this.IsDrawFunction(callItem.Callee.Name)) + { + let outVar=callItem.Out; + var drawName=callItem.Callee.Name; + this.OutVarTable.push({Name:drawName, Draw:`输出: ${outVar}`, Type:1}); + } + else + { + let outVar=callItem.Out; + varName=`__temp_c_${callItem.Callee.Name}_${i}__`; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + } + else if (item.Expression.Type==Syntax.Identifier) + { + let varName=item.Expression.Name; + let outVar=this.ReadVariable(varName,item.Expression); + varName="__temp_i_"+i+"__"; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`, Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.Literal) //常量 + { + let outVar=item.Expression.Value; + if (IFrameSplitOperator.IsString(outVar) && outVar.indexOf("$")>0) + outVar=this.GetOtherSymbolExplain({ Literal:outVar }, item); + varName="__temp_li_"+i+"__"; + var type=0; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`, Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.BinaryExpression) // CLOSE+OPEN; + { + var varName="__temp_b_"+i+"__"; + let outVar=item.Expression.Out; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.LogicalExpression) //逻辑语句 如 T1 AND T2 + { + var varName="__temp_l_"+i+"__"; + let outVar=item.Expression.Out; + this.OutVarTable.push({Name:varName, Data:`输出: ${outVar}`,Type:0, NoneName:true}); + } + else if (item.Expression.Type==Syntax.SequenceExpression) + { + let varName; + let drawName; + let draw; + let color; + let lineWidth; + let colorStick=false; + let pointDot=false; + let circleDot=false; + let lineStick=false; + let stick=false; + let volStick=false; + let isShow=true; + let isExData=false; + let isDotLine=false; + let isOverlayLine=false; //叠加线 + var isNoneName=false; + //显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如: + //DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE; + var isDrawAbove=false; + for(let j in item.Expression.Expression) + { + let itemExpression=item.Expression.Expression[j]; + if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left) + { + varName=itemExpression.Left.Name; + let varValue=this.VarTable.get(varName); + this.VarTable.set(varName,varValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.Identifier) + { + let value=itemExpression.Name; + if (value==='COLORSTICK') colorStick=true; + else if (value==='POINTDOT') pointDot=true; + else if (value==='CIRCLEDOT') circleDot=true; + else if (value==='DOTLINE') isDotLine=true; + else if (value==='LINESTICK') lineStick=true; + else if (value==='STICK') stick=true; + else if (value==='VOLSTICK') volStick=true; + else if (value==="DRAWABOVE") isDrawAbove=true; + else if (value.indexOf('COLOR')==0) color=value; + else if (value.indexOf('LINETHICK')==0) lineWidth=value; + else if (value.indexOf('NODRAW')==0) isShow=false; + else if (value.indexOf('EXDATA')==0) isExData=true; //扩展数据, 不显示再图形里面 + else if (value.indexOf('LINEOVERLAY')==0) isOverlayLine=true; + else + { + varName=itemExpression.Name; + let varValue=this.ReadVariable(varName,itemExpression); + varName="__temp_si_"+i+"__"; + isNoneName=true; + this.VarTable.set(varName,varValue); //放到变量表里 + } + } + else if(itemExpression.Type==Syntax.Literal) //常量 + { + let aryValue=itemExpression.Value; + varName=itemExpression.Value.toString(); + isNoneName=true; + this.VarTable.set(varName,aryValue); //把常量放到变量表里 + } + else if (itemExpression.Type==Syntax.CallExpression) + { + if (this.IsDrawFunction(itemExpression.Callee.Name)) + { + draw=itemExpression.Out; + drawName=itemExpression.Callee.Name; + } + else + { + let varValue=itemExpression.Out; + varName=`__temp_sc_${itemExpression.Callee.Name}_${i}__`; + isNoneName=true; + this.VarTable.set(varName,varValue); + } + } + else if (itemExpression.Type==Syntax.BinaryExpression) + { + varName="__temp_sb_"+i+"__"; + let aryValue=itemExpression.Out; + isNoneName=true; + this.VarTable.set(varName,aryValue); + } + } + + var outValue; + if (draw) outValue=`输出: ${draw}`; + else if (isNoneName) outValue=`输出: ${this.VarTable.get(varName)}`; + else outValue=`输出${varName}: ${this.VarTable.get(varName)}`; + + if (color) outValue+=`,颜色${this.GetColorExplain(color)}`; + if (lineWidth) outValue+=`,线段粗细${this.GetLineWidthExplain(lineWidth)}`; + if (isShow==false) outValue+=",不显示"; + if (isDotLine==true) outValue+=",画虚线"; + if (isDrawAbove==true) outValue+=',显示在位置之上'; + + if (pointDot && varName) //圆点 + { + outValue+=",画小圆点线"; + let value={Name:varName, Data:outValue, Radius:g_JSChartResource.POINTDOT.Radius, Type:3}; + this.OutVarTable.push(value); + } + else if (circleDot && varName) //圆点 + { + outValue+=",画小圆圈线"; + let value={Name:varName, Data:outValue, Radius:g_JSChartResource.CIRCLEDOT.Radius, Type:3}; + this.OutVarTable.push(value); + } + else if (lineStick && varName) //LINESTICK 同时画出柱状线和指标线 + { + outValue+=",画出柱状线和指标线"; + let value={Name:varName, Data:outValue, Type:4}; + this.OutVarTable.push(value); + } + else if (stick && varName) //STICK 画柱状线 + { + outValue+=",画柱状线"; + let value={Name:varName, Data:outValue, Type:5}; + this.OutVarTable.push(value); + } + else if (volStick && varName) //VOLSTICK 画彩色柱状线 + { + outValue+=",画成交量柱状线"; + let value={Name:varName, Data:outValue, Type:6}; + this.OutVarTable.push(value); + } + else if (varName && color) + { + let value={Name:varName, Data:outValue, Color:color, Type:0}; + this.OutVarTable.push(value); + } + else if (draw) //画图函数 + { + var outVar={ Name:drawName, Data:outValue, Type:1 }; + this.OutVarTable.push(outVar); + } + else if (colorStick && varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子 + { + outValue+=",画彩色柱状线"; + let value={Name:varName, Data:outValue, Color:color, Type:2}; + this.OutVarTable.push(value); + } + else if (varName) + { + let value={Name:varName, Data:outValue,Type:0}; + this.OutVarTable.push(value); + } + } + } + } + + JSConsole.Complier.Log('[JSExplainer::Run]', this.VarTable); + return this.OutVarTable; + } + + this.VisitNode=function(node) + { + switch(node.Type) + { + case Syntax.SequenceExpression: + this.VisitSequenceExpression(node); + break; + case Syntax.ExpressionStatement: + this.VisitNode(node.Expression); + break; + case Syntax.AssignmentExpression: + this.VisitAssignmentExpression(node); + break; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + this.VisitBinaryExpression(node); + break; + case Syntax.CallExpression: + this.VisitCallExpression(node); + break; + } + } + + this.VisitSequenceExpression=function(node) + { + for(let i in node.Expression) + { + let item =node.Expression[i]; + this.VisitNode(item); + } + } + + //函数调用 + this.VisitCallExpression=function(node) + { + let funcName=node.Callee.Name; + let args=[]; + for(let i in node.Arguments) + { + let item=node.Arguments[i]; + let value; + if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(item); + else if (item.Type==Syntax.CallExpression) + value=this.VisitCallExpression(item); + else + value=this.GetNodeValue(item); + args.push(value); + } + + JSConsole.Complier.Log('[JSExplainer::VisitCallExpression]' , funcName, '(', args.toString() ,')'); + + if (g_JSComplierResource.IsCustomFunction(funcName)) + { + var data=this.Algorithm.CallCustomFunction(funcName, args, this.SymbolData, node); + node.Out=[]; + node.Draw=null; + + if (data) + { + if (data.Out) node.Out=data.Out; + if (data.Draw) node.Draw=data.Draw; + } + + return node.Out; + } + + node.Out=this.CallFunctionExplain(funcName, args, node); + return node.Out; + } + + this.FUNCTION_INFO_LIST=new Map( + [ + ["REF", { Name:"REF", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日前的${args[0]}`; } } ], + ["REFX", { Name:"REFX", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日后的${args[0]}`; } } ], + ["REFV", { Name:"REFV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日前的(未作平滑处理)${args[0]}`; } } ], + ["REFXV", { Name:"REFXV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日后的(未作平滑处理)${args[0]}`; } } ], + + ["REFDATE", { Name:"REFDATE", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日${args[0]}`; } } ], + ["COUNT", { Name:"COUNT", Param:{ Count:2 }, ToString:function(args) { return `统计${args[1]}日中满足${args[0]}的天数`; } } ], + ["BARSLASTCOUNT", { Name:"BARSLASTCOUNT", Param:{ Count:1 }, ToString:function(args) { return `条件${args[0]}连续成立次数`; } } ], + ["BARSCOUNT", { Name:"BARSCOUNT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}有效数据周期数`; } } ], + ["BARSLAST", { Name:"BARSLAST", Param:{ Count:1 }, ToString:function(args) { return `上次${args[0]}不为0距今天数`; } } ], + ["BARSNEXT", { Name:"BARSNEXT", Param:{ Count:1 }, ToString:function(args) { return `下次${args[0]}不为0距今天数`; } } ], + ["BARSSINCEN", { Name:"BARSSINCEN", Param:{ Count:2 }, ToString:function(args) { return `在${args[1]}周期内首次${args[0]}距今天数`; } } ], + ["BARSSINCE", { Name:"BARSSINCE", Param:{ Count:1 }, ToString:function(args) { return `首次${args[0]}距今天数`; } } ], + ["HHV", { Name:"HHV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的最高值`; } } ], + ["LLV", { Name:"LLV", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的最低值`; } } ], + + ["HOD", { Name:"HOD", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的高值名次`; } } ], + ["LOD", { Name:"LOD", Param:{ Count:2 }, ToString:function(args) { return `${args[1]}日内${args[0]}的低值名次`; } } ], + ["REVERSE", { Name:"REVERSE", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的相反数`; } } ], + ["FILTER", { Name:"FILTER", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日过滤`; } } ], + ["FILTERX", { Name:"FILTERX", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日反向过滤`; } } ], + ["SUMBARS", { Name:"SUMBARS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}累加至${args[1]}的天数`; } } ], + ["MA", { Name:"MA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日简单移动平均`; } } ], + ["SMA", { Name:"SMA", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}的${args[1]}日[${args[2]}日权重]移动平均`; } } ], + ["MEMA", { Name:"MEMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日平滑移动平均`; } } ], + ["EMA", { Name:"EMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日指数移动平均`; } } ], + ["EXPMA", { Name:"EXPMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日指数移动平均`; } } ], + ["EXPMEMA", { Name:"EXPMEMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日指数平滑移动平均`; } }], + ["WMA", { Name:"WMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日加权移动平均`; } } ], + ["DMA", { Name:"DMA", Param:{ Count:2 }, ToString:function(args) { return `以${args[1]}为权重${args[0]}的动态移动平均`; } } ], + ["XMA", { Name:"XMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日偏移移动平均`; } } ], + + ["RANGE", { Name:"RANGE", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}位于${args[1]}和${args[2]}之间`; } } ], + ["CONST", { Name:"CONST", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的最后一日值`; } } ], + ["TOPRANGE", { Name:"TOPRANGE", Param:{ Count:1 }, ToString:function(args) { return `当前值是近${args[0]}周期的最大值`; } } ], + ["LOWRANGE", { Name:"LOWRANGE", Param:{ Count:1 }, ToString:function(args) { return `当前值是近${args[0]}周期的最小值`; } } ], + ["FINDHIGH", { Name:"FINDHIGH", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最高价`; } } ], + ["FINDHIGHBARS", { Name:"FINDHIGHBARS", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最高价到当前周期的周期数`; } } ], + ["FINDLOW", { Name:"FINDLOW", Param:{ Count:4 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最低价`; } } ], + ["FINDLOWBARS", { Name:"FINDLOWBARS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}在${args[1]}日前的${args[2]}天内第${args[3]}个最低价到当前周期的周期数`; } } ], + ["SUM", { Name:"SUM", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}${args[1]}日累加`; } } ], + ["MULAR", { Name:"MULAR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}日累乘`; } } ], + ["AMA", { Name:"AMA", Param:{ Count:2 }, ToString:function(args) { return `以${args[1]}为权重${args[0]}的自适应均线`; } } ], + ["TMA", { Name:"TMA", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}的${args[1]}日[${args[2]}日权重]移动平均`; } } ], + ["CROSS", { Name:"CROSS", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}上穿${args[1]}`; } } ], + ["LONGCROSS", { Name:"LONGCROSS", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}小于${args[1]}保持${args[2]}个交易日后交叉上穿`; } } ], + ["UPNDAY", { Name:"UPNDAY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日${args[0]}连涨`; } } ], + ["DOWNNDAY", { Name:"DOWNNDAY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日${args[0]}连跌`; } } ], + ["NDAY", { Name:"NDAY", Param:{ Count:3 }, ToString:function(args) { return `最近${args[2]}日${args[0]}一直大于${args[1]}`; } } ], + ["EXIST", { Name:"EXIST", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日存在${args[0]}`; } } ], + ["EXISTR", { Name:"EXISTR", Param:{ Count:3 }, ToString:function(args) { return `从前${args[1]}日到前${args[2]}日存在${args[0]}`; } } ], + ["EVERY", { Name:"EVERY", Param:{ Count:2 }, ToString:function(args) { return `最近${args[1]}日一直存在${args[0]}`; } } ], + ["LAST", { Name:"LAST", Param:{ Count:3 }, ToString:function(args) { return `从前${args[1]}日到前${args[2]}日持续${args[0]}`; } } ], + ["NOT", { Name:"NOT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}取反`; } } ], + ["IF", { Name:"IF", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + ["IFF", { Name:"IFF", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + ["IFN", { Name:"IFN", Param:{ Count:3 }, ToString:function(args) { return `如果${args[0]},返回${args[1]},否则返回${args[2]}`; } } ], + + ["MAX", { Name:"MAX", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}的较大值`; } } ], + ["MIN", { Name:"MIN", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}和${args[1]}的较小值`; } } ], + ["ACOS", { Name:"ACOS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反余弦`; } } ], + ["ASIN", { Name:"ASIN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反正弦`; } } ], + ["ATAN", { Name:"ATAN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的反正切`; } } ], + ["COS", { Name:"COS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的余弦`; } } ], + ["SIN", { Name:"SIN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的正弦`; } } ], + ["TAN", { Name:"TAN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的正切`; } } ], + ["EXP", { Name:"EXP", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的指数`; } } ], + ["LN", { Name:"LN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的自然对数`; } } ], + ["LOG", { Name:"LOG", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的对数`; } } ], + ["SQRT", { Name:"SQRT", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的开方`; } } ], + ["ABS", { Name:"ABS", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的绝对值`; } } ], + ["POW", { Name:"POW", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}乘幂`; } } ], + ["CEILING", { Name:"CEILING", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的向上舍入`; } } ], + ["FLOOR", { Name:"FLOOR", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的向上舍入`; } } ], + ["INTPART", { Name:"INTPART", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的整数部分`; } } ], + ["BETWEEN", { Name:"BETWEEN", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}位于${args[1]}和${args[2]}之间`; } } ], + ["FRACPART", { Name:"FRACPART", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的小数部分`; } } ], + ["ROUND", { Name:"ROUND", Param:{ Count:1 }, ToString:function(args) { return `对${args[0]}(进行)四舍五入`; } } ], + ["ROUND2", { Name:"ROUND2", Param:{ Count:2 }, ToString:function(args) { return `对${args[0]}(进行)四舍五入`; } } ], + ["SIGN", { Name:"SIGN", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}的符号`; } } ], + ["MOD", { Name:"MOD", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}关于${args[1]}的模`; } } ], + ["RAND", { Name:"RAND", Param:{ Count:1 }, ToString:function(args) { return `随机正整数`; } } ], + + ["AVEDEV", { Name:"AVEDEV", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日平均绝对偏差`; } } ], + ["DEVSQ", { Name:"DEVSQ", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日数据偏差平方和`; } } ], + ["FORCAST", { Name:"FORCAST", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日线性回归预测值`; } } ], + ["TSMA", { Name:"TSMA", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}在${args[1]}个周期内的时间序列三角移动平均`; } } ], + ["SLOPE", { Name:"SLOPE", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日线性回归斜率`; } } ], + ["STD", { Name:"STD", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日估算标准差`; } } ], + ["STDP", { Name:"STDP", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日总体标准差`; } } ], + ["STDDEV", { Name:"STDDEV", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日标准偏差`; } } ], + ["VAR", { Name:"VAR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日估算样本方差`; } } ], + ["VARP", { Name:"VARP", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}的${args[1]}日总体样本方差`; } } ], + ["COVAR", { Name:"COVAR", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[2]}周期的协方差`; } } ], + ["RELATE", { Name:"RELATE", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[0]}周期的相关系数`; } } ], + ["BETA", { Name:"BETA", Param:{ Count:1 }, ToString:function(args) { return `β(Beta)系数`; } } ], + ["BETAEX", { Name:"BETAEX", Param:{ Count:3 }, ToString:function(args) { return `${args[0]}和${args[1]}的${args[2]}周期的相关放大系数`; } } ], + + ["COST", { Name:"COST", Param:{ Count:1 }, ToString:function(args) { return `获利盘为${args[0]}%的成本分布`; } } ], + ["WINNER", { Name:"WINNER", Param:{ Count:1 }, ToString:function(args) { return `以${args[0]}计算的获利盘比例`; } } ], + ["LWINNER", { Name:"LWINNER", Param:{ Count:2 }, ToString:function(args) { return `最近${args[0]}日那部分成本以${args[1]}价格卖出的获利盘比例`; } } ], + ["PWINNER", { Name:"PWINNER", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}日前那部分成本以${args[1]}价格卖出的获利盘比例`; } } ], + ["COSTEX", { Name:"COSTEX", Param:{ Count:2 }, ToString:function(args) { return `位于价格${args[0]}和${args[1]}间的成本`; } } ], + ["PPART", { Name:"PPART", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}日前那部分成本占总成本的比例`; } } ], + + ["SAR", { Name:"SAR", Param:{ Count:3 }, ToString:function(args) { return `步长为${args[1]}极限值为${args[0]}的${args[2]}日抛物转向`; } } ], + ["SARTURN", { Name:"SARTURN", Param:{ Count:3 }, ToString:function(args) { return `步长为${args[1]}极限值为${args[0]}的${args[2]}日抛物转向点`; } } ], + + //字符串函数 + ["CON2STR", { Name:"CON2STR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}转为字符串`; } } ], + ["VAR2STR", { Name:"VAR2STR", Param:{ Count:2 }, ToString:function(args) { return `${args[0]}转为字符串`; } } ], + ["STR2CON", { Name:"STR2CON", Param:{ Count:1 }, ToString:function(args) { return `${args[0]}转为数字`; } } ], + ["STRLEN", { Name:"STRLEN", Param:{ Count:1 }, ToString:function(args) { return `得到${args[0]}字符串长度`; } } ], + ["STRCAT", { Name:"STRCAT", Param:{ Count:2 }, ToString:function(args) { return `字符串相加`; } } ], + ["VARCAT", { Name:"VARCAT", Param:{ Count:2 }, ToString:function(args) { return `字符串相加`; } } ], + ["STRSPACE", { Name:"STRSPACE", Param:{ Count:1 }, ToString:function(args) { return `字符串${args[0]}加一空格`; } } ], + ["SUBSTR", { Name:"SUBSTR", Param:{ Count:3 }, ToString:function(args) { return `字符串${args[0]}中取一部分`; } } ], + ["STRCMP", { Name:"STRCMP", Param:{ Count:2 }, ToString:function(args) { return `字符串${args[0]}和字符串${args[1]}比较`; } } ], + ["FINDSTR", { Name:"FINDSTR", Param:{ Count:2 }, ToString:function(args) { return `字符串${args[0]}中查找字符串${args[1]}`; } } ], + ["NAMEINCLUD", { Name:"NAMEINCLUD", Param:{ Count:1 }, ToString:function(args) { return `查找品种名称中包含${args[0]}`; } } ], + ["CODELIKE", { Name:"CODELIKE", Param:{ Count:1 }, ToString:function(args) { return `查找品种名称中包含${args[0]}`; } } ], + ["INBLOCK", { Name:"AVEDEV", Param:{ Count:1 }, ToString:function(args) { return `属于${args[0]}板块`; } } ], + + + [ + "HHVBARS", + { + Name:"HHVBARS", Param:{ Count:2 }, + ToString:function(args) + { + if (args[1]==0) return `历史${args[0]}新高距今天数`; + return `${args[1]}日内${args[0]}新高距今天数`; + } + } + ], + + [ + "LLVBARS", + { + Name:"LLVBARS", Param:{ Count:2 }, + ToString:function(args) + { + if (args[1]==0) return `历史${args[0]}新低距今天数`; + return `${args[1]}日内${args[0]}新低距今天数`; + } + } + ] + ] + ); + + this.CallFunctionExplain=function(funcName, args, node) + { + if (this.FUNCTION_INFO_LIST.has(funcName)) + { + var item=this.FUNCTION_INFO_LIST.get(funcName); + if (item.Param.Count!=args.length) + this.ThrowUnexpectedNode(node,`函数${funcName}参数个数不正确. 需要${item.Param.Count}个参数`); + return item.ToString(args); + } + + switch(funcName) + { + case "CALCSTOCKINDEX": + return `引用${args[0]}的${args[1]}指标第${args[2]}个输出值`; + + case "PEAK": + case "PEAKBARS": + case "ZIG": + case "ZIGA": + case "TROUGH": + case "TROUGHBARS": + return this.GetZIGExplain(funcName,args); + + case "FINANCE": + return this.GetFinanceExplain(args); + case "DYNAINFO": + return this.GetDynainfoExplain(args); + + + case 'CLOSE': + case 'C': + case 'VOL': + case 'V': + case 'OPEN': + case 'O': + case 'HIGH': + case 'H': + case 'LOW': + case 'L': + case 'AMOUNT': + case 'AMO': + return this.GetOtherSymbolExplain( {FunctionName:funcName, Args:args} ,node); + + + //绘图函数 + case "PLOYLINE": + return `当满足条件${args[0]}时以${args[1]}位置为顶点画折线连接`; + case "DRAWLINE": + return `当满足条件${args[0]}时,在${args[1]}位置画直线起点,当满足条件${args[2]}时,在${args[3]}位置画直线终点,${args[4]}表示是否延长`; + case "DRAWSL": + return `当满足条件${args[0]}时,在${args[1]}位置画斜线线性回归,${args[2]}斜率,${args[3]}长度,${args[4]}方向`; + case "DRAWKLINE": + return 'K线'; + case "DRAWICON": + return `当满足条件${args[0]}时,在${args[1]}位置画${args[2]}号图标`; + case "DRAWTEXT": + return `当满足条件${args[0]}时,在${args[1]}位置书写文字`; + case "DRAWTEXT_FIX": + return `当满足条件${args[0]}时,在横轴${args[1]}纵轴${args[2]}位置书写文字`; + case "DRAWNUMBER": + return `当满足条件${args[0]}时,在${args[1]}位置书写数字`; + case "DRAWNUMBER_FIX": + return `当满足条件${args[0]}时,在横轴${args[1]}纵轴${args[2]}位置书写数字`; + case "RGB": + return `自定色[${args[0]},${args[1]},${args[2]}]`; + case "DRAWBAND": + return '画带状线'; + case "DRAWRECTREL": + return "相对位置上画矩形."; + case "DRAWGBK": + return "填充背景"; + case "STICKLINE": + var barType=""; + if (args[4]==-1) barType="虚线空心柱"; + else if (args[4]==0) barType="实心柱"; + else barType="实线空心柱"; + return `当满足条件${args[0]}时, 在${args[1]}和${args[2]}位置之间画柱状线,宽度为${args[3]},${barType}`; + + default: + this.ThrowUnexpectedNode(node,`函数${funcName}不存在`); + } + } + + this.GetDynainfoExplain=function(args) + { + const DATA_NAME_MAP=new Map( + [ + [3,"前收盘价"], [4,"开盘价"], [5,"最高价"], [6,"最低价"], [7,"现价"], [8,'总量'], [9,"现量"], + [10,"总金额"], [11,"均价"], [12,"日涨跌"], [13,"振幅"], [14,"涨幅"], [15,"开盘时的成交金额"], + [16,"前5日每分钟均量"], [17,"量比"], [18,"上涨家数"], [19,"下跌家数"] + ]); + + var id=args[0]; + if (DATA_NAME_MAP.has(id)) return DATA_NAME_MAP.get(id); + + return `即时行情[${id}]`; + } + + this.GetFinanceExplain=function(args) + { + const DATA_NAME_MAP=new Map( + [ + [1,"总股本"], [2,"市场类型"], [3,"沪深品种类型"], [4,"沪深行业代码"], [5,"B股"], [6,"H股"], [7,"流通股本[股]"], [8,"股东人数[户]"], [9,"资产负债率%"], + [10,"总资产"], [11,"流动资产"], [12,"固定资产"], [13,"无形资产"], [15,"流动负债"], [16,"少数股东权益"] + + ]); + var id=args[0]; + + if (DATA_NAME_MAP.has(id)) return DATA_NAME_MAP.get(id); + + return `财务数据[${id}]`; + } + + this.GetZIGExplain=function(funcName,args) + { + var value=args[0]; + if (value==0) value="开盘价"; + else if (value==1) value="最高价"; + else if (value==2) value="最低价"; + else if (value==3) value="收盘价"; + + switch(funcName) + { + case "PEAK": + return `${value}的${args[1]}%之字转向的前${args[2]}个波峰值`; + case "PEAKBARS": + return `${value}的${args[1]}5%之字转向的前${args[2]}个波峰位置`; + case "ZIG": + return `${value}的${args[1]}的之字转向`; + case "ZIGA": + return `${value}变化${args[1]}的之字转向`; + case "TROUGH": + return `${value}的${args[1]}%之字转向的前${args[2]}个波谷值`; + case "TROUGHBARS": + return `${value}的${args[1]}%之字转向的前${args[2]}个波谷位置`; + } + } + + this.GetColorExplain=function(colorName) + { + const COLOR_MAP=new Map( + [ + ['COLORBLACK','黑色'],['COLORBLUE','蓝色'],['COLORGREEN','绿色'],['COLORCYAN','青色'],['COLORRED','红色'], + ['COLORMAGENTA','洋红色'],['COLORBROWN','棕色'],['COLORLIGRAY','淡灰色'],['COLORGRAY','深灰色'],['COLORLIBLUE','淡蓝色'], + ['COLORLIGREEN','淡绿色'],['COLORLICYAN','淡青色'],['COLORLIRED','淡红色'],['COLORLIMAGENTA','淡洋红色'],['COLORWHITE','白色'],['COLORYELLOW','黄色'] + ]); + + if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName); + + //COLOR 自定义色 + //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。 + //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。 + if (colorName.indexOf('COLOR')==0) return '#'+colorName.substr(5); + + return 'rgb(30,144,255)'; + } + + this.GetLineWidthExplain=function(lineWidth) + { + var width=parseInt(lineWidth.replace("LINETHICK","")); + if (IFrameSplitOperator.IsPlusNumber(width)) return width; + return 1; + } + + this.SymbolPeriodExplain=function(valueName,period) + { + const mapStockDataName=new Map( + [ + ['CLOSE',"收盘价"],["C","收盘价"],['VOL',"成交量"],['V',"成交量"], ['OPEN',"开盘价"], ['O',"开盘价"], + ['HIGH',"最高价"],['H',"最高价"], ['LOW',"最低价"],['L',"最低价"],['AMOUNT',"成交金额"],['AMO',"成交金额"], + ['VOLINSTK',"持仓量"] + ]); + //MIN1,MIN5,MIN15,MIN30,MIN60,DAY,WEEK,MONTH,SEASON,YEAR + const mapPeriodName=new Map( + [ + ["MIN1","1分钟"], ["MIN5", "5分钟"], ["MIN15", "15分钟"], ["MIN30","30分钟"],["MIN60","60分钟"], + ["DAY","日"],["WEEK","周"], ["MONTH", "月"], ['SEASON',"季"], ["YEAR", "年"],["WEEK2","双周"], ["HALFYEAR", "半年"] + ]); + + var dataName=valueName; + if (mapStockDataName.has(valueName)) dataName=mapStockDataName.get(valueName); + + var periodName=period; + if (mapPeriodName.has(period)) periodName=mapPeriodName.get(period); + + return `${dataName}[取${periodName}数据]`; + } + + this.GetOtherSymbolExplain=function(obj, node) + { + const mapStockDataName=new Map( + [ + ['CLOSE',"收盘价"],["C","收盘价"],['VOL',"成交量"],['V',"成交量"], ['OPEN',"开盘价"], ['O',"开盘价"], + ['HIGH',"最高价"],['H',"最高价"], ['LOW',"最低价"],['L',"最低价"],['AMOUNT',"成交金额"],['AMO',"成交金额"], + ['VOLINSTK',"持仓量"] + ]); + + if (obj.FunctionName) + { + var args=obj.Args; + var dataName=mapStockDataName.get(obj.FunctionName); + return `[${args[0]}]${dataName}`; + } + else if (obj.Literal) + { + var value=obj.Literal.toUpperCase(); + var args=value.split("$"); + if (!mapStockDataName.has(args[1])) return ""; + var symbol=args[0]; + var dataName=mapStockDataName.get(args[1]); + return `[${symbol}]${dataName}`; + } + } + + this.IsDrawFunction=function(name) + { + let setFunctionName=new Set( + [ + "STICKLINE","DRAWTEXT",'SUPERDRAWTEXT','DRAWLINE','DRAWBAND','DRAWKLINE','DRAWKLINE_IF','PLOYLINE', + 'POLYLINE','DRAWNUMBER',"DRAWNUMBER_FIX",'DRAWICON','DRAWCHANNEL','PARTLINE','DRAWTEXT_FIX','DRAWGBK','DRAWTEXT_LINE','DRAWRECTREL',"DRAWTEXTABS", + 'DRAWOVERLAYLINE',"FILLRGN", "FILLRGN2","FILLTOPRGN", "FILLBOTTOMRGN", "FILLVERTICALRGN","FLOATRGN","DRAWSL", "DRAWGBK2" + ]); + if (setFunctionName.has(name)) return true; + + return false; + } + + //赋值 + this.VisitAssignmentExpression=function(node) + { + let left=node.Left; + if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node); + + let varName=left.Name; + + let right=node.Right; + let value=null; + if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression) + value=this.VisitBinaryExpression(right); + else if (right.Type==Syntax.CallExpression) + value=this.VisitCallExpression(right); + else if (right.Type==Syntax.Literal) + { + value=right.Value; + if (IFrameSplitOperator.IsString(value) && right.Value.indexOf("$")>0) + value=this.GetOtherSymbolExplain({ Literal:value }, node); + } + else if (right.Type==Syntax.Identifier) //右值是变量 + value=this.ReadVariable(right.Name,right); + else if (right.Type==Syntax.MemberExpression) + value=this.ReadMemberVariable(right); + else if (right.Type==Syntax.UnaryExpression) + { + if (right.Operator=='-') + { + var tempValue=this.GetNodeValue(right.Argument); + value='-'+tempValue; + } + else + { + value=right.Argument.Value; + } + } + + JSConsole.Complier.Log('[JSExplainer::VisitAssignmentExpression]' , varName, ' = ',value); + this.VarTable.set(varName,value); + } + + //逻辑运算 + this.VisitBinaryExpression=function(node) + { + let stack=[]; + stack.push(node); + let temp=null; + + while(stack.length!=0) + { + temp=stack[stack.length-1]; + if (temp.Left && node!=temp.Left && node!=temp.Right) + { + stack.push(temp.Left); + } + else if (temp.Right && node!=temp.Right) + { + stack.push(temp.Right); + } + else + { + let value=stack.pop(); + if (value.Type==Syntax.BinaryExpression) //只遍历操作符就可以 + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + value.Out=`(${leftValue} ${value.Operator} ${rightValue})`; + if (leftValue=="收盘价" && rightValue=="开盘价") + { + if (value.Operator==">") value.Out='(收阳线)'; + else if (value.Operator=="<") value.Out='(收阴线)'; + else if (value.Operator=="=") value.Out='(平盘)'; + } + else if (leftValue=="开盘价" && rightValue=="收盘价") + { + if (value.Operator=="<") value.Out='(收阳线)'; + else if (value.Operator==">") value.Out='(收阴线)'; + else if (value.Operator=="=") value.Out='(平盘)'; + } + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] BinaryExpression',value); + } + else if (value.Type==Syntax.LogicalExpression) + { + let leftValue=this.GetNodeValue(value.Left); + let rightValue=this.GetNodeValue(value.Right); + + JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue); + value.Out=null; //保存中间值 + + switch(value.Operator) + { + case '&&': + case 'AND': + value.Out=`(${leftValue} 并且 ${rightValue})`; + break; + case '||': + case 'OR': + value.Out=`(${leftValue} 或者 ${rightValue})`; + break; + } + + JSConsole.Complier.Log('[JSExplainer::VisitBinaryExpression] LogicalExpression',value); + } + + node=temp; + } + } + + return node.Out; + + } + + this.GetNodeValue=function(node) + { + switch(node.Type) + { + case Syntax.Literal: //数字 + return node.Value; + case Syntax.UnaryExpression: + if (node.Operator=='-') + { + let value=this.GetNodeValue(node.Argument); + return '-'+value; + } + return node.Argument.Value; + case Syntax.Identifier: + let value=this.ReadVariable(node.Name,node); + return value; + case Syntax.BinaryExpression: + case Syntax.LogicalExpression: + return node.Out; + case Syntax.CallExpression: + return this.VisitCallExpression(node); + default: + this.ThrowUnexpectedNode(node); + } + } + + //读取变量 + this.ReadVariable=function(name,node) + { + if (this.ConstVarTable.has(name)) + { + let data=this.ConstVarTable.get(name); + return data; + } + + if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量 + + if (this.VarTable.has(name)) return this.VarTable.get(name); + + if (name.indexOf('#')>0) + { + var aryPeriod=name.split('#'); + return this.SymbolPeriodExplain(aryPeriod[0],aryPeriod[1]); + } + + this.ThrowUnexpectedNode(node, '变量'+name+'不存在'); + return name; + } + + this.ThrowUnexpectedNode=function(node,message) + { + let marker=node.Marker; + let msg=message || "执行异常"; + + return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg); + + } + + this.ThrowError=function() + { + + } +} + +//对外导出类 +function JSComplier() +{ + +} + + +//词法分析 +JSComplier.Tokenize=function(code) +{ + JSConsole.Complier.Log('[JSComplier.Tokenize]', code); + let tokenizer=new Tokenizer(code); + let tokens=[]; + try + { + while(true) + { + let token=tokenizer.GetNextToken(); + if (!token) break; + + tokens.push(token); + } + } + catch(e) + { + + } + + return tokens; +} + +//语法解析 生成抽象语法树(Abstract Syntax Tree) +JSComplier.Parse=function(code) +{ + JSConsole.Complier.Log('[JSComplier.Parse]',code); + + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + let ast=program; + return ast; +} + +/* + 执行 + option.Symbol=股票代码 + option.Name=股票名称 + option.Data=这个股票的ChartData + option.Right=复权 + option.MaxReqeustDataCount=请求数据的最大个数 +*/ + +function timeout(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + +JSComplier.Execute=function(code,option,errorCallback) +{ + //异步调用 + //var asyncExecute= async function() es5不能执行 去掉异步 + var asyncExecute= function() + { + try + { + JSConsole.Complier.Log('[JSComplier.Execute]',code,option); + + JSConsole.Complier.Log('[JSComplier.Execute] parser .....'); + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + + let ast=program; + JSConsole.Complier.Log('[JSComplier.Execute] parser finish.', ast); + + JSConsole.Complier.Log('[JSComplier.Execute] execute .....'); + let execute=new JSExecute(ast,option); + execute.ErrorCallback=errorCallback; //执行错误回调 + execute.JobList=parser.Node.GetDataJobList(); + if (option.ClassName=='ScriptIndexConsole') execute.JobList.unshift({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA}); + execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT}); + + let result=execute.Execute(); + + }catch(error) + { + JSConsole.Complier.Log(error); + if (errorCallback) errorCallback(error, option.CallbackParam); + } + } + + asyncExecute(); + + JSConsole.Complier.Log('[JSComplier.Execute] async execute.'); +} + +JSComplier.Explain=function(code,option, errorCallback) +{ + //异步调用 + //var asyncExecute= async function() es5不能执行 去掉异步 + var asyncExplain= function() + { + try + { + JSConsole.Complier.Log('[JSComplier.Explain]',code,option); + + JSConsole.Complier.Log('[JSComplier.Explain] parser .....'); + let parser=new JSParser(code); + parser.Initialize(); + let program=parser.ParseScript(); + + let ast=program; + JSConsole.Complier.Log('[JSComplier.Explain] parser finish.', ast); + + JSConsole.Complier.Log('[JSComplier.Explain] explain .....'); + let execute=new JSExplainer(ast,option); + execute.ErrorCallback=errorCallback; //执行错误回调 + execute.JobList=parser.Node.GetDataJobList(); + execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT}); + let result=execute.Run(); + + }catch(error) + { + JSConsole.Complier.Log(error); + + if (errorCallback) errorCallback(error, option.CallbackParam); + } + } + + asyncExplain(); + + JSConsole.Complier.Log('[JSComplier.Explain] async explain.'); +} + + +JSComplier.SetDomain = function (domain, cacheDomain) //修改API地址 +{ + if (domain) g_JSComplierResource.Domain = domain; + if (cacheDomain) g_JSComplierResource.CacheDomain = cacheDomain; +} + + +JSComplier.AddIcon=function(obj) //添加一个obj={ID:, Text:, Color, Family: } +{ + g_JSComplierResource.CustomDrawIcon.Data.set(obj.ID, obj); +} + +JSComplier.AddFunction=function(obj) //添加函数 { Name:函数名, Description:描述信息, IsDownload:是否需要下载数据, Invoke:函数执行(可选) } +{ + if (!obj || !obj.Name) return; + + var ID=obj.Name.toUpperCase(); + g_JSComplierResource.CustomFunction.Data.set(ID, obj); +} + +JSComplier.AddVariant=function(obj) //{ Name:变量名, Description:描述信息 } +{ + if (!obj || !obj.Name) return; + + var ID=obj.Name.toUpperCase(); + g_JSComplierResource.CustomVariant.Data.set(ID, obj); +} + +var HQ_DATA_TYPE= +{ + KLINE_ID:0, //K线 + MINUTE_ID:2, //当日走势图 + HISTORY_MINUTE_ID:3,//历史分钟走势图 + MULTIDAY_MINUTE_ID:4,//多日走势图 +}; + + +//脚本指标 +//name=指标名字 args=参数名字 参数值 +function ScriptIndex(name,script,args,option) +{ + this.newMethod=BaseIndex; //派生 + this.newMethod(name); + delete this.newMethod; + + this.ClassName="ScriptIndex"; + this.Script=script; + this.Arguments=[]; + this.OutVar=[]; + this.ID; //指标ID + this.FloatPrecision=2; //小数位数 + this.StringFormat; + this.KLineType=null; //K线显示类型 + this.InstructionType; //五彩K线, 交易指标 + this.YSpecificMaxMin=null; //最大最小值 + this.YSplitScale=null; //固定刻度 + this.Condition=null; //限制条件 + this.OutName=null; //动态输出指标名字 + + //指标上锁配置信息 + this.IsLocked=false; //是否锁住指标 + this.LockCallback=null; + this.LockID=null; + this.LockBG=null; //锁背景色 + this.LockTextColor=null; + this.LockText=null; + this.LockFont=null; + this.LockCount=20; + this.LockMinWidth=null; + this.TitleFont=g_JSChartResource.TitleFont; //标题字体 + this.IsShortTitle=false; //是否显示指标参数 + + if (option) + { + if (option.FloatPrecision>=0) this.FloatPrecision=option.FloatPrecision; + if (option.StringFormat>0) this.StringFormat=option.StringFormat; + if (option.ID) this.ID=option.ID; + if (option.KLineType>=0 || option.KLineType===-1) this.KLineType=option.KLineType; + if (option.InstructionType) this.InstructionType=option.InstructionType; + if (option.YSpecificMaxMin) this.YSpecificMaxMin=option.YSpecificMaxMin; + if (option.YSplitScale) this.YSplitScale=option.YSplitScale; + if (option.Condition) this.Condition=option.Condition; + if (option.TitleFont) this.TitleFont=option.TitleFont; + if (option.IsShortTitle) this.IsShortTitle=option.IsShortTitle; + if (option.OutName) this.OutName=option.OutName; + } + + if (option && option.Lock) + { + if (option.Lock.IsLocked==true) this.IsLocked=true; //指标上锁 + if (option.Lock.Callback) this.LockCallback=option.Lock.Callback; //锁回调 + if (option.Lock.ID) this.LockID=option.Lock.ID; //锁ID + if (option.Lock.BG) this.LockBG=option.Lock.BG; + if (option.Lock.TextColor) this.LockTextColor=option.Lock.TextColor; + if (option.Lock.Text) this.LockText=option.Lock.Text; + if (option.Lock.Font) this.LockFont=option.Lock.Font; + if (option.Lock.Count) this.LockCount=option.Lock.Count; + if (option.Lock.MinWidth) this.LockMinWidth=option.Lock.MinWidth*GetDevicePixelRatio(); + } + + if (args) this.Arguments=args; + + this.CopyTo=function(dest) //赋值到新实例出来 + { + dest.FloatPrecision=this.FloatPrecision; + dest.StringFormat=this.StringFormat; + dest.KLineType=this.KLineType; + dest.InstructionType=this.InstructionType; + dest.Condition=this.Condition; + dest.TitleFont=this.TitleFont; + dest.IsShortTitle=this.IsShortTitle; + dest.OutName=this.OutName; + + dest.Arguments=this.Arguments; + dest.Script=this.Script; + dest.Name=this.Name; + } + + this.SetLock=function(lockData) + { + if (lockData.IsLocked==true) + { + this.IsLocked=true; //指标上锁 + if (lockData.Callback) this.LockCallback=lockData.Callback; //锁回调 + if (lockData.ID) this.LockID=lockData.ID; //锁ID + if (lockData.BG) this.LockBG=lockData.BG; + if (lockData.TextColor) this.LockTextColor=lockData.TextColor; + if (lockData.Text) this.LockText=lockData.Text; + if (lockData.Font) this.LockFont=lockData.Font; + if (lockData.Count) this.LockCount=lockData.Count; + if (lockData.MinWidth) this.LockMinWidth=lockData.MinWidth*GetDevicePixelRatio(); + } + else + { //清空锁配置信息 + this.IsLocked=false; //是否锁住指标 + this.LockCallback=null; + this.LockID=null; + this.LockBG=null; //锁背景色 + this.LockTextColor=null; + this.LockText=null; + this.LockFont=null; + this.LockCount=20; + } + } + + this.ExecuteScript=function(hqChart,windowIndex,hisData) + { + this.OutVar=[]; + let self = this; + let param= + { + HQChart:hqChart, + WindowIndex:windowIndex, + HistoryData:hisData, + Self:this + }; + + //数据类型 + let hqDataType=HQ_DATA_TYPE.KLINE_ID; //默认K线 + if (hqChart.ClassName==='MinuteChartContainer' || hqChart.ClassName==='MinuteChartHScreenContainer') + { + if (hqChart.DayCount>1) hqDataType=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟 + else hqDataType=HQ_DATA_TYPE.MINUTE_ID; //分钟数据 + } + else if (hqChart.ClassName==='HistoryMinuteChartContainer') + { + hqDataType=HQ_DATA_TYPE.HISTORY_MINUTE_ID; //历史分钟 + } + let option= + { + HQDataType:hqDataType, + Symbol:hqChart.Symbol, + Name:hqChart.Name, + Data:hisData, + SourceData:hqChart.SourceData, + Callback:this.RecvResultData, CallbackParam:param, + Async:true, + MaxRequestDataCount:hqChart.MaxReqeustDataCount, + MaxRequestMinuteDayCount:hqChart.MaxRequestMinuteDayCount, + Arguments:this.Arguments, + Condition:this.Condition, + IsBeforeData:hqChart.IsBeforeData, + IsApiPeriod:hqChart.IsApiPeriod, + }; + + if (hqDataType===HQ_DATA_TYPE.HISTORY_MINUTE_ID) option.TrateDate=hqChart.TradeDate; + if (hqDataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) option.DayCount=hqChart.DayCount; + if (hqChart.NetworkFilter) option.NetworkFilter=hqChart.NetworkFilter; + + if (this.Condition && !this.IsMeetCondition(param,option)) + { + this.ShowConditionError(param); + return; + } + + let code=this.Script; + let run=JSComplier.Execute(code,option,hqChart.ScriptErrorCallback); + } + + //是否符合限制条件 + this.IsMeetCondition=function(param,option) + { + JSConsole.Complier.Log('[ScriptIndex::IsMeetCondition] ', this.Condition); + if (this.Condition.Period) //周期是否满足 + { + if (!this.IsMeetPeriodCondition(param,option)) return false; + if (!this.IsMeetIncludeCondition(param,option)) return false; + } + + return true; + } + + //周期是否满足条件 + this.IsMeetPeriodCondition=function(param,option) + { + if (!this.Condition.Period) return true; + + for(var i in this.Condition.Period) + { + var item=this.Condition.Period[i]; + switch(item) + { + case CONDITION_PERIOD.MINUTE_ID: + if (option.HQDataType==HQ_DATA_TYPE.MINUTE_ID) return true; + break; + case CONDITION_PERIOD.MULTIDAY_MINUTE_ID: + if (option.HQDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) return true; + break; + case CONDITION_PERIOD.KLINE_DAY_ID: + case CONDITION_PERIOD.KLINE_WEEK_ID: + case CONDITION_PERIOD.KLINE_MONTH_ID: + case CONDITION_PERIOD.KLINE_YEAR_ID: + case CONDITION_PERIOD.KLINE_TWOWEEK_ID: + case CONDITION_PERIOD.KLINE_QUARTER_ID: + + case CONDITION_PERIOD.KLINE_MINUTE_ID: + case CONDITION_PERIOD.KLINE_5_MINUTE_ID: + case CONDITION_PERIOD.KLINE_15_MINUTE_ID: + case CONDITION_PERIOD.KLINE_30_MINUTE_ID: + case CONDITION_PERIOD.KLINE_60_MINUTE_ID: + if (param.HQChart.Period==item) return true; + break; + } + } + + return false; + } + + this.IsMeetIncludeCondition=function(param,option) + { + if (!this.Condition.Include || this.Condition.Include.length<=0) return true; + + var symbol=param.HQChart.Symbol; + if (symbol) symbol=symbol.toUpperCase(); + for(var i in this.Condition.Include) + { + var item=this.Condition.Include[i]; + if (symbol==item) return true; + } + return false; + } + + //显示指标不符合条件 + this.ShowConditionError=function(param) + { + var hqChart=param.HQChart; + var windowIndex=param.WindowIndex; + + hqChart.DeleteIndexPaint(windowIndex); + if (windowIndex==0) hqChart.ShowKLine(true); + + var message='指标不支持当前品种或周期'; + + let line=new ChartLine(); + line.Canvas=hqChart.Canvas; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + line.NotSupportMessage=message; + hqChart.ChartPaint.push(line); + hqChart.Draw(); + } + + this.RecvResultData=function(outVar,param) + { + let hqChart=param.HQChart; + let windowIndex=param.WindowIndex; + let hisData=param.HistoryData; + param.Self.OutVar=outVar; + param.Self.BindData(hqChart,windowIndex,hisData); + + if (param.Self.IsLocked==false) //不上锁 + { + param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(null); + } + else //上锁 + { + let lockData={ IsLocked:true,Callback:param.Self.LockCallback,IndexName:param.Self.Name ,ID:param.Self.LockID, + BG:param.Self.LockBG,Text:param.Self.LockText,TextColor:param.Self.LockTextColor, Font:param.Self.LockFont, Count:param.Self.LockCount, MinWidth:param.Self.LockMinWidth }; + param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData); + } + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + if (hqChart.GetIndexEvent) + { + var event=hqChart.GetIndexEvent(); //指标计算完成回调 + if (event) + { + var self=param.Self; + var data={ OutVar:self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData, + Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} }; + event.Callback(event,data,self); + } + } + } + + this.CreateLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartLine(); + line.Canvas=hqChart.Canvas; + line.DrawType=1; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=this.GetColor(varItem.Color); + else line.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) line.LineWidth=width; + } + + if (varItem.IsDotLine) line.IsDotLine=true; //虚线 + if (varItem.IsShow==false) line.IsShow=false; + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Data; + if (varItem.IsShowTitle===false) //NOTEXT 不绘制标题 + { + + } + else if (IFrameSplitOperator.IsString(varItem.Name) && varItem.Name.indexOf("NOTEXT")==0) //标题中包含NOTEXT不绘制标题 + { + + } + else + { + if (varItem.NoneName) + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,null,line.Color); + else + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + } + + hqChart.ChartPaint.push(line); + } + + this.CreateOverlayLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartSubLine(); + line.Canvas=hqChart.Canvas; + line.DrawType=1; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=this.GetColor(varItem.Color); + else line.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) line.LineWidth=width; + } + + if (varItem.IsDotLine) line.IsDotLine=true; //虚线 + if (varItem.IsShow==false) line.IsShow=false; + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + + hqChart.ChartPaint.push(line); + } + + //创建柱子 + this.CreateBar=function(hqChart,windowIndex,varItem,id) + { + let bar=new ChartStickLine(); + bar.Canvas=hqChart.Canvas; + if (varItem.Draw.Width>0) bar.Width=varItem.Draw.Width; + else bar.Width=0; + + bar.Name=varItem.Name; + bar.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + bar.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) bar.Color=this.GetColor(varItem.Color); + else bar.Color=this.GetDefaultColor(id); + + let titleIndex=windowIndex+1; + bar.Data.Data=varItem.Draw.DrawData; + bar.BarType=varItem.Draw.Type; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(bar); + } + + //创建文本 + this.CreateText=function(hqChart,windowIndex,varItem,id,resource) + { + let chartText=new ChartSingleText(); + chartText.Canvas=hqChart.Canvas; + + chartText.Name=varItem.Name; + chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.ReloadResource(); + + if (varItem.Color) chartText.Color=this.GetColor(varItem.Color); + else chartText.Color=this.GetDefaultColor(id); + if (varItem.IsDrawCenter===true) chartText.TextAlign='center'; + if (varItem.IsDrawAbove===true) chartText.Direction=1; + if (varItem.IsDrawBelow===true) chartText.Direction=2; + + let titleIndex=windowIndex+1; + if (varItem.Draw.Position) chartText.Position=varItem.Draw.Position; //赋值坐标 + if (varItem.Draw.DrawData) chartText.Data.Data=varItem.Draw.DrawData; + chartText.Text=varItem.Draw.Text; + if (varItem.Draw.Direction>0) chartText.Direction=varItem.Draw.Direction; + if (varItem.Draw.YOffset>0) chartText.YOffset=varItem.Draw.YOffset; + if (varItem.Draw.TextAlign) chartText.TextAlign=varItem.Draw.TextAlign; + + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(chartText); + } + + //COLORSTICK + this.CreateMACD=function(hqChart,windowIndex,varItem,id) + { + let chartMACD=new ChartMACD(); + chartMACD.Canvas=hqChart.Canvas; + + chartMACD.Name=varItem.Name; + chartMACD.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartMACD.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.LineWidth) + { + var width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chartMACD.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chartMACD.Data.Data=varItem.Data; + var clrTitle=this.GetDefaultColor(id); + if (varItem.Color) clrTitle=this.GetColor(varItem.Color); + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chartMACD.Data,varItem.Name,clrTitle); + + hqChart.ChartPaint.push(chartMACD); + } + + this.CreatePointDot=function(hqChart,windowIndex,varItem,id) + { + let pointDot=new ChartPointDot(); + pointDot.Canvas=hqChart.Canvas; + pointDot.Name=varItem.Name; + pointDot.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + pointDot.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) pointDot.Color=this.GetColor(varItem.Color); + else pointDot.Color=this.GetDefaultColor(id); + + if (varItem.Radius) pointDot.Radius=varItem.Radius; + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) pointDot.Radius=width; + } + + let titleIndex=windowIndex+1; + pointDot.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(pointDot.Data,varItem.Name,pointDot.Color); + + hqChart.ChartPaint.push(pointDot); + } + + this.CreateStick=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateLineStick=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartLineStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateStraightLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartLine(); + line.DrawType=1; + line.Canvas=hqChart.Canvas; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=this.GetColor(varItem.Color); + else line.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) line.LineWidth=width; + } + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Draw.DrawData; + + if (varItem.Name=="DRAWLINE") + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,null,line.Color); + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + + hqChart.ChartPaint.push(line); + } + + this.CreateVolStick=function(hqChart,windowIndex,varItem,id,hisData) + { + let chart=new ChartVolStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.KLineDrawType=hqChart.KLineDrawType; //设置K线显示类型 + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + chart.HistoryData=hisData; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreateBand=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartBand(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.FirstColor = varItem.Draw.Color[0]; + chart.SecondColor = varItem.Draw.Color[1]; + chart.Data.Data=varItem.Draw.DrawData; + + hqChart.ChartPaint.push(chart); + } + + this.CreateFillRGN=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartLineArea(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.Data.Data=varItem.Draw.DrawData; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + hqChart.ChartPaint.push(chart); + } + + this.CreateFillRGN2=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartFillRGN(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.Data.Data=varItem.Draw.DrawData; + + hqChart.ChartPaint.push(chart); + } + + this.CreateFillBGRGN=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartFillBGRGN(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.Data.Data=varItem.Draw.DrawData; + + hqChart.ChartPaint.push(chart); + } + + this.CreateFLOATRGN=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartFLOATRGN(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.Data.Data=varItem.Draw.DrawData; + + hqChart.ChartPaint.push(chart); + } + + //创建K线图 + this.CreateKLine=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartKLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data.Data=varItem.Draw.DrawData; + chart.IsShowMaxMinPrice=false; + chart.IsShowKTooltip=false; + + if (varItem.Color) //如果设置了颜色,使用外面设置的颜色 + chart.UnchagneColor=chart.DownColor=chart.UpColor=this.GetColor(varItem.Color); + + hqChart.ChartPaint.push(chart); + } + + this.CreatePolyLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartLine(); + line.Canvas=hqChart.Canvas; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=this.GetColor(varItem.Color); + else line.Color=this.GetDefaultColor(id); + if (varItem.IsDotLine) line.IsDotLine=true; //虚线 + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) line.LineWidth=width; + } + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Draw.DrawData; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,' ',line.Color); //给一个空的标题 + + hqChart.ChartPaint.push(line); + } + + this.CreateChartSlopeLine=function(hqChart,windowIndex,varItem,id) + { + let line=new ChartSlopeLine(); + line.Canvas=hqChart.Canvas; + line.Name=varItem.Name; + line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) line.Color=this.GetColor(varItem.Color); + else line.Color=this.GetDefaultColor(id); + if (varItem.IsDotLine) line.IsDotLine=true; //虚线 + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) line.LineWidth=width; + } + + let titleIndex=windowIndex+1; + line.Data.Data=varItem.Draw.DrawData.Data; + line.Option=varItem.Draw.DrawData.Option; + hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,null,line.Color); //给一个空的标题 + + hqChart.ChartPaint.push(line); + } + + this.CreateChartVericaltLine=function(hqChart,windowIndex,varItem,id) + { + var chart=new ChartVericaltLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + this.SetChartLineDash(chart,varItem.Draw.DrawData); + chart.Data.Data=varItem.Draw.DrawData.Data; + + hqChart.ChartPaint.push(chart); + } + + this.SetChartLineDash=function(chart, option) + { + if (IFrameSplitOperator.IsNumber(option.LineType)) + { + var lineType=option.LineType; + var pixelRatio=GetDevicePixelRatio(); + switch(lineType) + { + case 1: + chart.LineType=[10*pixelRatio,10*pixelRatio]; + break; + case 2: + chart.LineType=[0,10*pixelRatio]; + chart.LineCap="square"; + break; + case 3: + chart.LineType=[10*pixelRatio, 3*pixelRatio, 3*pixelRatio, 3*pixelRatio]; + break; + case 4: + chart.LineType=[10*pixelRatio, 3*pixelRatio, 3*pixelRatio, 3*pixelRatio,3*pixelRatio,3*pixelRatio]; + break; + } + } + else if (IFrameSplitOperator.IsString(option.LineType)) + { + var aryString=option.LineType.split(','); + var aryLinType=[]; + for(var i=0;i0) chart.LineWidth=width; + } + + this.SetChartLineDash(chart,varItem.Draw.DrawData); + chart.ExtendType=varItem.Draw.DrawData.Extend; + chart.Data.Data=varItem.Draw.DrawData.Data; + + hqChart.ChartPaint.push(chart); + } + + + this.CreateBackgroud=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartBackground(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Draw && varItem.Draw.DrawData) + { + var drawData=varItem.Draw.DrawData; + chart.Color=drawData.Color; + chart.ColorAngle=drawData.Angle; + + if (drawData.Data) chart.Data.Data=drawData.Data; + } + + hqChart.ChartPaint.push(chart); + } + + this.CreateTextLine=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartTextLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Draw && varItem.Draw.DrawData) + { + var drawData=varItem.Draw.DrawData; + chart.Text=drawData.Text; + chart.Line=drawData.Line; + chart.Price=drawData.Price; + } + + hqChart.ChartPaint.push(chart); + } + + this.CreateNumberText=function(hqChart,windowIndex,varItem,id) + { + let chartText=new ChartSingleText(); + chartText.Canvas=hqChart.Canvas; + + chartText.Name=varItem.Name; + chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.ReloadResource(); + chartText.TextAlign="center"; + + if (varItem.Color) chartText.Color=this.GetColor(varItem.Color); + else chartText.Color=this.GetDefaultColor(id); + if (varItem.IsDrawAbove) chartText.Direction=1; + else chartText.Direction=2; + + if (varItem.Draw.Position) chartText.Position=varItem.Draw.Position; //赋值坐标 + + let titleIndex=windowIndex+1; + chartText.Data.Data=varItem.Draw.DrawData.Value; + chartText.Text=varItem.Draw.DrawData.Text; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(chartText); + } + + this.CreateDrawText=function(hqChart,windowIndex,varItem,id) + { + let chartText=new ChartSingleText(); + chartText.Canvas=hqChart.Canvas; + chartText.Name=varItem.Name; + chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.ReloadResource(); + + if (varItem.Color) chartText.Color=this.GetColor(varItem.Color); + else chartText.Color=this.GetDefaultColor(id); + if (varItem.IsDrawAbove) chartText.Direction=1; + else chartText.Direction=0; + + let titleIndex=windowIndex+1; + chartText.DrawData=varItem.Draw.DrawData; + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(chartText); + } + + //创建图标 + this.CreateIcon=function(hqChart,windowIndex,varItem,id) + { + let chartText=new ChartSingleText(); + chartText.Canvas=hqChart.Canvas; + chartText.TextAlign='center'; + + chartText.Name=varItem.Name; + chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chartText.Direction=2; + if (varItem.IsDrawAbove) chartText.Direction=1; + let titleIndex=windowIndex+1; + chartText.Data.Data=varItem.Draw.DrawData; + var icon=varItem.Draw.Icon; + if (icon.IconFont==true) + { + chartText.IconFont={ Family:icon.Family, Text:icon.Symbol, Color:icon.Color }; + if (varItem.Color) chartText.IconFont.Color=this.GetColor(varItem.Color); + } + else + { + chartText.Text=icon.Symbol; + if (varItem.Color) chartText.Color=this.GetColor(varItem.Color); + else if (icon.Color) chartText.Color=icon.Color; + else chartText.Color='rgb(0,0,0)'; + } + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + hqChart.ChartPaint.push(chartText); + } + + //创建通道 + this.CreateChannel=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartChannel(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + if(varItem.Draw.AreaColor) chart.AreaColor=varItem.Draw.AreaColor; + else if (varItem.Color) chart.AreaColor=this.GetColor(varItem.Color); + else chart.AreaColor=this.GetDefaultColor(id); + + if (varItem.Draw.Border.Color) chart.LineColor=varItem.Draw.Border.Color; + else chart.LineColor=null; + + if (varItem.Draw.Border.Dotted) chart.LineDotted=varItem.Draw.Border.Dotted; + if (varItem.Draw.Border.Width>0) chart.LineWidth=varItem.Draw.Border.Width; + + //let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + hqChart.ChartPaint.push(chart); + } + + this.CreatePartLine=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartPartLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data.Data=varItem.Draw.DrawData; + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiLine=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartMultiLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Lines=varItem.Draw.DrawData; + if (varItem.Draw.LineDash) chart.LineDash=varItem.Draw.LineDash; + if (IFrameSplitOperator.IsNumber(varItem.Draw.LineWidth)) chart.LineWidth=varItem.Draw.LineWidth; + if(varItem.Draw.Arrow) //箭头配置 + { + var item=varItem.Draw.Arrow; + if (item.Start==true) chart.Arrow.Start=true; + if (item.End==true) chart.Arrow.End=true; + if (IFrameSplitOperator.IsNumber(item.Angle)) chart.ArrawAngle=item.Angle; + if (IFrameSplitOperator.IsNumber(item.Length)) chart.ArrawLength=item.Length; + if (IFrameSplitOperator.IsNumber(item.LineWidth)) chart.ArrawLineWidth=item.LineWidth; + } + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiBar=function(hqChart,windowIndex,varItem,id) + { + let chart=new ChartMultiBar(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Bars=varItem.Draw.DrawData; + + var titleIndex=windowIndex+1; + var titleData=new DynamicTitleData({ KData:chart.Data, BarData:chart.Bars },varItem.Name,null); + titleData.IsShow=false; + titleData.DataType="MULTI_BAR"; + hqChart.TitlePaint[titleIndex].Data[id]=titleData; + + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiText=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartMultiText(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts=varItem.Draw.DrawData; + hqChart.ChartPaint.push(chart); + } + + this.CreateMultiSVGIcon=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartMultiSVGIcon(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Family=varItem.Draw.DrawData.Family; + chart.Icon= varItem.Draw.DrawData.Icon; + hqChart.ChartPaint.push(chart); + } + + this.CreateMulitHtmlDom=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartMultiHtmlDom(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + chart.HQChart=hqChart; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts=varItem.Draw.DrawData; + chart.DrawCallback= varItem.Draw.Callback; + hqChart.ChartPaint.push(chart); + } + + this.CreateColorKLine=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartColorKline(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.KLineColor= varItem.Draw.DrawData.KLine; + if (varItem.Color) chart.Color=varItem.Color; + hqChart.ChartPaint.push(chart); + } + + this.CreateRectangle=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartRectangle(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Color=[varItem.Draw.DrawData.Color]; + chart.Rect=varItem.Draw.DrawData.Rect; + if (varItem.Color) chart.BorderColor=this.GetColor(varItem.Color); + hqChart.ChartPaint.push(chart); + } + + this.CreateScriptOverlayLine=function(hqChart,windowIndex,varItem,i) + { + let chart=new ChartOverlayLine(); + chart.Canvas=hqChart.Canvas; + chart.DrawType=1; + chart.Name=varItem.Name; + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(i); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + if (varItem.IsDotLine) chart.IsDotLine=true; //虚线 + if (varItem.IsShow==false) chart.IsShow=false; + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData.Data; + chart.MainData.Data=varItem.Draw.DrawData.MainData; + + if (varItem.Draw.DrawData.Title) + hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(chart.Data,varItem.Draw.DrawData.Title,chart.Color); + + hqChart.ChartPaint.push(chart); + } + + //创建K线 + this.CreateSelfKLine=function(hqChart,windowIndex,hisData) + { + let chart=new ChartKLine(); + chart.Canvas=hqChart.Canvas; + chart.Name="Self Kline" + chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame; + + chart.Data=hisData + chart.IsShowMaxMinPrice=false; + chart.IsShowKTooltip=false; + chart.DrawType=this.KLineType; + + hqChart.ChartPaint.push(chart); + } + + this.BindInstructionData=function(hqChart,windowIndex,hisData) //绑定指示指标 + { + if (this.OutVar==null || this.OutVar.length<0) return; + + //参数 + var indexParam=''; + for(let i in this.Arguments) + { + let item=this.Arguments[i]; + if (indexParam.length>0) indexParam+=','; + indexParam+=item.Value.toString(); + } + if (indexParam.length>0) indexParam='('+indexParam+')'; + + if (this.InstructionType==2) //五彩K线 + { + let varItem=this.OutVar[this.OutVar.length-1]; //取最后一组数据作为指示数据 + hqChart.SetInstructionData(this.InstructionType, {Data:varItem.Data, Name:this.Name, Param:indexParam, ID:this.ID }); //设置指示数据 + return true; + } + else if (this.InstructionType==1) //交易系统 + { + var buyData, sellData; + for(var i in this.OutVar) + { + let item=this.OutVar[i]; + if (item.Name=='ENTERLONG') buyData=item.Data; + else if (item.Name=='EXITLONG') sellData=item.Data; + } + + hqChart.SetInstructionData(this.InstructionType, {Buy:buyData, Sell:sellData, Name:this.Name, Param:indexParam, ID:this.ID}); //设置指示数据 + return true; + } + } + + this.BindData=function(hqChart,windowIndex,hisData) + { + if (windowIndex==0 && this.InstructionType) + { + this.BindInstructionData(hqChart,windowIndex,hisData); + return; + } + + //清空主指标图形 + hqChart.DeleteIndexPaint(windowIndex); + if (windowIndex==0) hqChart.ShowKLine(true); + + if (this.OutVar==null || this.OutVar.length<0) return; + + //叠加一个K线背景 + if (this.KLineType!=null) + { + if (this.KLineType===0 || this.KLineType===1 || this.KLineType===2) this.CreateSelfKLine(hqChart,windowIndex,hisData); + else if (this.KLineType===-1 && windowIndex==0) hqChart.ShowKLine(false); + } + + if (windowIndex>=1 && hqChart.Frame) + { + hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision=this.FloatPrecision; + if (this.YSpecificMaxMin) hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=this.YSpecificMaxMin; //最大最小值 + if (this.YSplitScale) hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale=this.YSplitScale; //固定刻度 + } + + /* + if (this.Name=='MA') //测试多线段 + { + var point1={Point:[{Index:300, Value:15.5}, {Index:301, Value:14.2} , {Index:304, Value:14.05}], Color:'rgb(244,55,50)'}; + var point2={Point:[{Index:307, Value:14.5}, {Index:308, Value:14.2} , {Index:309, Value:14.15}], Color:'rgb(0,55,50)'}; + var testData={ Name:'MULTI_LINE', Type:1,Draw:{ DrawType:'MULTI_LINE', DrawData:[point1,point2] } }; + this.OutVar.push(testData); + } + */ + + for(let i in this.OutVar) + { + let item=this.OutVar[i]; + if (item.IsExData===true) continue; //扩展数据不显示图形 + if (item.Type==1000 || item.Type==1001) continue; //数据集合, 字符串 + + if (item.Type==0) + { + if (item.IsOverlayLine) this.CreateOverlayLine(hqChart,windowIndex,item,i); + else this.CreateLine(hqChart,windowIndex,item,i); + } + else if (item.Type==1) + { + switch(item.Draw.DrawType) + { + case 'STICKLINE': + this.CreateBar(hqChart,windowIndex,item,i); + break; + case 'DRAWTEXT': + case 'SUPERDRAWTEXT': + this.CreateText(hqChart,windowIndex,item,i); + break; + case 'DRAWLINE': + this.CreateStraightLine(hqChart,windowIndex,item,i); + break; + case 'DRAWBAND': + this.CreateBand(hqChart,windowIndex,item,i); + break; + case "FILLRGN": + this.CreateFillRGN(hqChart,windowIndex,item,i); + break; + case "FILLRGN2": + this.CreateFillRGN2(hqChart,windowIndex,item,i); + break; + case "FILLTOPRGN": + case "FILLBOTTOMRGN": + case "FILLVERTICALRGN": + this.CreateFillBGRGN(hqChart,windowIndex,item,i); + break; + case "FLOATRGN": + this.CreateFLOATRGN(hqChart,windowIndex,item,i); + break; + case 'DRAWKLINE': + this.CreateKLine(hqChart,windowIndex,item,i); + break; + case 'DRAWKLINE_IF': + this.CreateKLine(hqChart,windowIndex,item,i); + break; + case 'POLYLINE': + this.CreatePolyLine(hqChart,windowIndex,item,i); + break; + case 'DRAWGBK': + case "DRAWGBK2": + this.CreateBackgroud(hqChart,windowIndex,item,i); + break; + case 'DRAWTEXT_LINE': + this.CreateTextLine(hqChart,windowIndex,item,i); + break; + case 'DRAWNUMBER': + case "DRAWNUMBER_FIX": + case 'DRAWTEXT_FIX': + this.CreateNumberText(hqChart,windowIndex,item,i); + break; + case 'DRAWICON': + this.CreateIcon(hqChart,windowIndex,item,i); + break; + case 'DRAWCHANNEL': + this.CreateChannel(hqChart,windowIndex,item,i); + break; + case 'PARTLINE': + this.CreatePartLine(hqChart,windowIndex,item,i); + break; + case 'DRAWRECTREL': + this.CreateRectangle(hqChart,windowIndex,item,i); + break; + case "DRAWTEXTABS": + case "DRAWTEXTREL": + this.CreateDrawText(hqChart,windowIndex,item,i); + break; + case "DRAWOVERLAYLINE": + this.CreateScriptOverlayLine(hqChart,windowIndex,item,i); + break; + case "DRAWSL": + this.CreateChartSlopeLine(hqChart,windowIndex,item,i); + break; + case "VERTLINE": + this.CreateChartVericaltLine(hqChart,windowIndex,item,i); + break; + case "HORLINE": + this.CreateChartHorizontalLine(hqChart,windowIndex,item,i); + break; + case 'MULTI_LINE': + this.CreateMultiLine(hqChart,windowIndex,item,i); + break; + case 'MULTI_BAR': + this.CreateMultiBar(hqChart,windowIndex,item,i); + break; + case 'MULTI_TEXT': + this.CreateMultiText(hqChart,windowIndex,item,i); + break; + case 'MULTI_SVGICON': + this.CreateMultiSVGIcon(hqChart,windowIndex,item,i); + break; + case "MULTI_HTMLDOM": + this.CreateMulitHtmlDom(hqChart,windowIndex,item,i); + break; + case "COLOR_KLINE": + this.CreateColorKLine(hqChart,windowIndex,item,i); + break; + case "KLINE_BG": + this.CreateBackgroud(hqChart,windowIndex,item,i); + break; + } + } + else if (item.Type==2) + { + this.CreateMACD(hqChart,windowIndex,item,i); + } + else if (item.Type==3) + { + this.CreatePointDot(hqChart,windowIndex,item,i); + } + else if (item.Type==4) + { + this.CreateLineStick(hqChart,windowIndex,item,i); + } + else if (item.Type==5) + { + this.CreateStick(hqChart,windowIndex,item,i); + } + else if (item.Type==6) + { + this.CreateVolStick(hqChart,windowIndex,item,i,hisData); + } + + var titlePaint=hqChart.TitlePaint[windowIndex+1]; + if (titlePaint && titlePaint.Data && i0) titlePaint.Data[i].StringFormat=this.StringFormat; + if (this.FloatPrecision>=0) titlePaint.Data[i].FloatPrecision=this.FloatPrecision; + + if (this.OutName && this.OutName.length>0 && this.Arguments && this.Arguments.length>0) + { + titlePaint.SetDynamicOutName(this.OutName,this.Arguments); + } + } + + + } + + let titleIndex=windowIndex+1; + hqChart.TitlePaint[titleIndex].Title=this.Name; + + if (!this.IsShortTitle) + { + let indexParam=''; + for(let i in this.Arguments) + { + let item=this.Arguments[i]; + if (indexParam.length>0) indexParam+=','; + indexParam+=item.Value.toString(); + } + + if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')'; + } + + if (this.TitleFont) hqChart.TitlePaint[titleIndex].Font=this.TitleFont; + + return true; + } + + + //给一个默认的颜色 + this.GetDefaultColor=function(id) + { + let COLOR_ARRAY=null; + //使用全局线段配置 + if (g_JSChartResource && g_JSChartResource.Index && g_JSChartResource.Index.LineColor) + { + COLOR_ARRAY=g_JSChartResource.Index.LineColor; + } + + if (!COLOR_ARRAY || !Array.isArray(COLOR_ARRAY)) + { + COLOR_ARRAY= + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ]; + } + + let number=parseInt(id); + return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)]; + } + + //获取颜色 + this.GetColor=function(colorName) + { + let COLOR_MAP=new Map([ + ['COLORBLACK','rgb(0,0,0)'], + ['COLORBLUE','rgb(18,95,216)'], + ['COLORGREEN','rgb(25,158,0)'], + ['COLORCYAN','rgb(0,255,198)'], + ['COLORRED','rgb(238,21,21)'], + ['COLORMAGENTA','rgb(255,0,222)'], + ['COLORBROWN','rgb(149,94,15)'], + ['COLORLIGRAY','rgb(218,218,218)'], //画淡灰色 + ['COLORGRAY','rgb(133,133,133)'], //画深灰色 + ['COLORLIBLUE','rgb(94,204,255)'], //淡蓝色 + ['COLORLIGREEN','rgb(183,255,190)'], //淡绿色 + ['COLORLICYAN','rgb(154,255,242)'], //淡青色 + ['COLORLIRED','rgb(255,172,172)'], //淡红色 + ['COLORLIMAGENTA','rgb(255,145,241)'], //淡洋红色 + ['COLORWHITE','rgb(255,255,255)'], //白色 + ['COLORYELLOW','rgb(255,198,0)'] + ]); + + if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName); + + //COLOR 自定义色 + //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。 + //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。 + if (colorName.indexOf('COLOR')==0) return '#'+colorName.substr(5); + + return 'rgb(30,144,255)'; + } +} + +function OverlayScriptIndex(name,script,args,option) +{ + this.newMethod=ScriptIndex; //派生 + this.newMethod(name,script,args,option); + delete this.newMethod; + + this.ClassName="OverlayScriptIndex"; + //叠加指标 + this.OverlayIndex=null; // { IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame } + + this.BindData=function(hqChart,windowIndex,hisData) + { + if (!this.OverlayIndex || this.OverlayIndex.IsOverlay!=true) return; + + this.OverlayIndex.Frame.ChartPaint=[]; + if (this.OutVar==null || this.OutVar.length<0) return; + + /*叠加一个K线背景 + if (this.KLineType!=null) + { + if (this.KLineType===0 || this.KLineType===1 || this.KLineType===2) this.CreateSelfKLine(hqChart,windowIndex,hisData); + else if (this.KLineType===-1 && windowIndex==0) hqChart.ShowKLine(false); + } + + if (windowIndex>=1 && hqChart.Frame) + { + hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision=this.FloatPrecision; + if (this.YSpecificMaxMin) hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=this.YSpecificMaxMin; //最大最小值 + if (this.YSplitScale) hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale=this.YSplitScale; //固定刻度 + } + */ + + //指标名字 + var titleInfo={ Data:[], Title:this.Name }; + let indexParam=''; + for(var i in this.Arguments) + { + let item=this.Arguments[i]; + if (indexParam.length>0) indexParam+=','; + indexParam+=item.Value.toString(); + } + if (indexParam.length>0) titleInfo.Title=this.Name+'('+indexParam+')'; + + var titleIndex=windowIndex+1; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo); + this.OverlayIndex.Frame.Frame.Title=titleInfo.Title; //给子框架设置标题 + + for(var i in this.OutVar) + { + let item=this.OutVar[i]; + if (item.IsExData===true) continue; //扩展数据不显示图形 + + if (item.Type==0) + { + this.CreateLine(hqChart,windowIndex,item,i); + } + else if (item.Type==1) + { + switch(item.Draw.DrawType) + { + case 'STICKLINE': + this.CreateBar(hqChart,windowIndex,item,i); + break; + case 'DRAWTEXT': + case 'SUPERDRAWTEXT': + this.CreateText(hqChart,windowIndex,item,i); + break; + case 'DRAWLINE': + this.CreateStraightLine(hqChart,windowIndex,item,i); + break; + case 'DRAWBAND': + this.CreateBand(hqChart,windowIndex,item,i); + break; + case 'DRAWKLINE': + this.CreateKLine(hqChart,windowIndex,item,i); + break; + case 'DRAWKLINE_IF': + this.CreateKLine(hqChart,windowIndex,item,i); + break; + case 'POLYLINE': + this.CreatePolyLine(hqChart,windowIndex,item,i); + break; + case 'DRAWNUMBER': + case "DRAWNUMBER_FIX": + case 'DRAWTEXT_FIX': + this.CreateNumberText(hqChart,windowIndex,item,i); + break; + case 'DRAWICON': + this.CreateIcon(hqChart,windowIndex,item,i); + break; + case 'DRAWCHANNEL': + this.CreateChannel(hqChart,windowIndex,item,i); + break; + case 'DRAWTEXT_LINE': + this.CreateTextLine(hqChart,windowIndex,item,i); + break; + + case 'MULTI_LINE': + this.CreateMultiLine(hqChart,windowIndex,item,i); + break; + case 'MULTI_BAR': + this.CreateMultiBar(hqChart,windowIndex,item,i); + break; + case 'MULTI_TEXT': + this.CreateMultiText(hqChart,windowIndex,item,i); + break; + case 'MULTI_SVGICON': + this.CreateMultiSVGIcon(hqChart,windowIndex,item,i); + break; + case "MULTI_HTMLDOM": + this.CreateMulitHtmlDom(hqChart,windowIndex,item,i); + break; + } + } + else if (item.Type==2) + { + this.CreateMACD(hqChart,windowIndex,item,i); + } + else if (item.Type==3) + { + this.CreatePointDot(hqChart,windowIndex,item,i); + } + else if (item.Type==4) + { + this.CreateLineStick(hqChart,windowIndex,item,i); + } + else if (item.Type==5) + { + this.CreateStick(hqChart,windowIndex,item,i); + } + else if (item.Type==6) + { + this.CreateVolStick(hqChart,windowIndex,item,i,hisData); + } + } + + + /* + hqChart.TitlePaint[titleIndex].Title=this.Name; + + let indexParam=''; + for(let i in this.Arguments) + { + let item=this.Arguments[i]; + if (indexParam.length>0) indexParam+=','; + indexParam+=item.Value.toString(); + } + + if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')'; + */ + + return true; + } + + //指标执行完成 + this.RecvResultData=function(outVar,param) + { + let hqChart=param.HQChart; + let windowIndex=param.WindowIndex; + let hisData=param.HistoryData; + param.Self.OutVar=outVar; + param.Self.BindData(hqChart,windowIndex,hisData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + var event=hqChart.GetOverlayIndexEvent(); //指标计算完成回调 + if (event) + { + var self=param.Self; + var data={ OutVar:self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData, + Identify:self.OverlayIndex.Identify, + Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} }; + event.Callback(event,data,self); + } + } + + ////////////////////////////////////////////////////////////////////////////////////// + // 图形创建 + ///////////////////////////////////////////////////////////////////////////////////// + + this.CreateLine=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartLine(); + chart.Canvas=hqChart.Canvas; + chart.DrawType=1; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + if (varItem.IsShow==false) chart.IsShow=false; + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + frame.ChartPaint.push(chart); + } + + //创建柱子 + this.CreateBar=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartStickLine(); + chart.Canvas=hqChart.Canvas; + if (varItem.Draw.Width>0) chart.Width=varItem.Draw.Width; + else chart.Width=1; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + frame.ChartPaint.push(chart); + } + + //创建文本 + this.CreateText=function(hqChart,windowIndex,varItem,id, drawName) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartSingleText(); + chart.Canvas=hqChart.Canvas; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + chart.ReloadResource(); + + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + let titleIndex=windowIndex+1; + if (varItem.Draw.Position) chart.Position=varItem.Draw.Position; //赋值坐标 + if (varItem.Draw.DrawData) chart.Data.Data=varItem.Draw.DrawData; + chart.Text=varItem.Draw.Text; + if (varItem.Draw.Direction>0) chart.Direction=varItem.Draw.Direction; + if (varItem.Draw.YOffset>0) chart.YOffset=varItem.Draw.YOffset; + if (varItem.Draw.TextAlign) chart.TextAlign=varItem.Draw.TextAlign; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + frame.ChartPaint.push(chart); + } + + //COLORSTICK + this.CreateMACD=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMACD(); + chart.Canvas=hqChart.Canvas; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + var titlePaint=hqChart.TitlePaint[titleIndex]; + var clrTitle=this.GetDefaultColor(id); + if (varItem.Color) clrTitle=this.GetColor(varItem.Color); + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,clrTitle); + + frame.ChartPaint.push(chart); + } + + this.CreatePointDot=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartPointDot(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.Radius) chart.Radius=varItem.Radius; + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.Radius=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateStick=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateLineStick=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartLineStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateStraightLine=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartLine(); + chart.DrawType=1; + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateVolStick=function(hqChart,windowIndex,varItem,id,hisData) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartVolStick(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + chart.KLineDrawType=hqChart.KLineDrawType; //设置K线显示类型 + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Data; + chart.HistoryData=hisData; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateBand=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartBand(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.FirstColor = varItem.Draw.Color[0]; + chart.SecondColor = varItem.Draw.Color[1]; + chart.Data.Data=varItem.Draw.DrawData; + + frame.ChartPaint.push(chart); + } + + //创建K线图 + this.CreateKLine=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartKLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data.Data=varItem.Draw.DrawData; + chart.IsShowMaxMinPrice=false; + chart.IsShowKTooltip=false; + + if (varItem.Color) //如果设置了颜色,使用外面设置的颜色 + chart.UnchagneColor=chart.DownColor=chart.UpColor=this.GetColor(varItem.Color); + + frame.ChartPaint.push(chart); + } + + this.CreatePolyLine=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + + if (varItem.LineWidth) + { + let width=parseInt(varItem.LineWidth.replace("LINETHICK","")); + if (!isNaN(width) && width>0) chart.LineWidth=width; + } + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + var titlePaint=hqChart.TitlePaint[titleIndex]; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(line.Data,' ',line.Color); //给一个空的标题 + + frame.ChartPaint.push(chart); + } + + this.CreateNumberText=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartSingleText(); + chart.Canvas=hqChart.Canvas; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + chart.ReloadResource(); + + chart.TextAlign="center"; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else chart.Color=this.GetDefaultColor(id); + if (varItem.IsDrawAbove) chart.Direction=1; + else chart.Direction=2; + + if (varItem.Draw.Position) chart.Position=varItem.Draw.Position; //赋值坐标 + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData.Value; + chart.Text=varItem.Draw.DrawData.Text; + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + frame.ChartPaint.push(chart); + } + + this.CreateTextLine=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartTextLine(); + chart.Canvas=hqChart.Canvas; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + if (varItem.Draw && varItem.Draw.DrawData) + { + var drawData=varItem.Draw.DrawData; + chart.Text=drawData.Text; + chart.Line=drawData.Line; + chart.Price=drawData.Price; + } + + frame.ChartPaint.push(chart); + } + + //创建图标 + this.CreateIcon=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartSingleText(); + chart.Canvas=hqChart.Canvas; + chart.TextAlign='center'; + + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + var icon=varItem.Draw.Icon; + if (icon.IconFont==true) + { + chart.IconFont={ Family:icon.Family, Text:icon.Symbol, Color:icon.Color }; + } + else + { + chart.Text=icon.Symbol; + if (varItem.Color) chart.Color=this.GetColor(varItem.Color); + else if (icon.Color) chart.Color=icon.Color; + else chart.Color='rgb(0,0,0)'; + } + + //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color); + + frame.ChartPaint.push(chart); + } + + //创建通道 + this.CreateChannel=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartChannel(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + if(varItem.Draw.AreaColor) chart.AreaColor=varItem.Draw.AreaColor; + else if (varItem.Color) chart.AreaColor=this.GetColor(varItem.Color); + else chart.AreaColor=this.GetDefaultColor(id); + + if (varItem.Draw.Border.Color) chart.LineColor=varItem.Draw.Border.Color; + else chart.LineColor=null; + + if (varItem.Draw.Border.Dotted) chart.LineDotted=varItem.Draw.Border.Dotted; + if (varItem.Draw.Border.Width>0) chart.LineWidth=varItem.Draw.Border.Width; + + //let titleIndex=windowIndex+1; + chart.Data.Data=varItem.Draw.DrawData; + frame.ChartPaint.push(chart); + } + + // + this.CreatePartLine=function(hqChart,windowIndex,varItem,i) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartPartLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data.Data=varItem.Draw.DrawData; + frame.ChartPaint.push(chart); + } + + this.CreateMultiLine=function(hqChart,windowIndex,varItem,i) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMultiLine(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Lines=varItem.Draw.DrawData; + if (varItem.Draw.LineDash) chart.LineDash=varItem.Draw.LineDash; + if (IFrameSplitOperator.IsNumber(varItem.Draw.LineWidth)) chart.LineWidth=varItem.Draw.LineWidth; + if(varItem.Draw.Arrow) //箭头配置 + { + var item=varItem.Draw.Arrow; + if (item.Start==true) chart.Arrow.Start=true; + if (item.End==true) chart.Arrow.End=true; + if (IFrameSplitOperator.IsNumber(item.Angle)) chart.ArrawAngle=item.Angle; + if (IFrameSplitOperator.IsNumber(item.Length)) chart.ArrawLength=item.Length; + if (IFrameSplitOperator.IsNumber(item.LineWidth)) chart.ArrawLineWidth=item.LineWidth; + } + frame.ChartPaint.push(chart); + } + + this.CreateMultiBar=function(hqChart,windowIndex,varItem,id) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMultiBar(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Bars=varItem.Draw.DrawData; + + var titleIndex=windowIndex+1; + var titlePaint=hqChart.TitlePaint[titleIndex]; + var titleData=new DynamicTitleData({ KData:chart.Data, BarData:chart.Bars },varItem.Name,null); + titleData.IsShow=false; + titleData.DataType="MULTI_BAR"; + titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=titleData; + + frame.ChartPaint.push(chart); + } + + this.CreateMultiText=function(hqChart,windowIndex,varItem,i) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMultiText(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts=varItem.Draw.DrawData; + frame.ChartPaint.push(chart); + } + + this.CreateMultiSVGIcon=function(hqChart,windowIndex,varItem,i) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMultiSVGIcon(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Family=varItem.Draw.DrawData.Family; + chart.Icon= varItem.Draw.DrawData.Icon; + frame.ChartPaint.push(chart); + } + + this.CreateMulitHtmlDom=function(hqChart,windowIndex,varItem,i) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartMultiHtmlDom(); + chart.Canvas=hqChart.Canvas; + chart.Name=varItem.Name; + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + chart.HQChart=hqChart; + + chart.Data=hqChart.ChartPaint[0].Data;//绑定K线 + chart.Texts=varItem.Draw.DrawData; + chart.DrawCallback= varItem.Draw.Callback; + frame.ChartPaint.push(chart); + } + + //创建K线 + this.CreateSelfKLine=function(hqChart,windowIndex,hisData) + { + var overlayIndex=this.OverlayIndex; + var frame=overlayIndex.Frame; + let chart=new ChartKLine(); + chart.Canvas=hqChart.Canvas; + chart.Name="Self Kline" + chart.ChartBorder=frame.Frame.ChartBorder; + chart.ChartFrame=frame.Frame; + chart.Identify=overlayIndex.Identify; + + chart.Data=hisData + chart.IsShowMaxMinPrice=false; + chart.IsShowKTooltip=false; + chart.DrawType=this.KLineType; + + frame.ChartPaint.push(chart); + } + + //给一个默认的颜色 + this.GetDefaultColor=function(id) + { + let COLOR_ARRAY= + [ + "rgb(24,71,178)", + "rgb(42,230,215)", + "rgb(252,96,154)", + "rgb(0,128,255)", + "rgb(229,0,79)", + "rgb(68,114,196)", + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + ]; + + let number=parseInt(id); + return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)]; + } +} + +//后台执行指标 +function APIScriptIndex(name,script,args,option, isOverlay) +{ + if (isOverlay) this.newMethod=OverlayScriptIndex; + else this.newMethod=ScriptIndex; + this.newMethod(name,script,args,option); + delete this.newMethod; + + this.ClassName="APIScriptIndex"; + this.IsOverlayIndex=(isOverlay==true); //是否是叠加指标 + this.ApiUrl; //指标执行api地址 + this.HQDataType; + this.Version=1; //1=默认数据格式 1=新的.net数据格式 + + if (option && option.API) + { + if (option.API.Url) this.ApiUrl=option.API.Url; + if (option.API.Name) this.Name=this.ID=option.API.Name; + if (option.API.ID) this.ID=option.API.ID; + if (option.API.Version>0) this.Version=option.API.Version; + } + + this.Super_CopyTo=this.CopyTo; //父类方法 + this.CopyTo=function(dest) //赋值到新实例出来 + { + this.Super_CopyTo(dest); + + dest.ApiUrl=this.ApiUrl; + dest.Version=this.Version; + dest.IsOverlayIndex=this.IsOverlayIndex; + } + + this.ExecuteScript=function(hqChart,windowIndex,hisData) + { + JSConsole.Complier.Log('[APIScriptIndex::ExecuteScript] name, Arguments ', this.Name,this.Arguments ); + + //数据类型 + let hqDataType=HQ_DATA_TYPE.KLINE_ID; //默认K线 + var dateRange=null; + if (hqChart.ClassName==='MinuteChartContainer' || hqChart.ClassName==='MinuteChartHScreenContainer') + { + if (hqChart.DayCount>1) hqDataType=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟 + else hqDataType=HQ_DATA_TYPE.MINUTE_ID; //分钟数据 + + dateRange=hisData.GetDateRange(); + } + else if (hqChart.ClassName==='HistoryMinuteChartContainer') + { + hqDataType=HQ_DATA_TYPE.HISTORY_MINUTE_ID; //历史分钟 + } + else + { + dateRange=hisData.GetDateRange(); + } + + var args=[]; + if (this.Arguments) + { + for(var i in this.Arguments) + { + var item=this.Arguments[i]; + args.push({name:item.Name, value:item.Value}); + } + } + + var requestCount; + if (hqChart.GetRequestDataCount) requestCount=hqChart.GetRequestDataCount(); + var self=this; + var postData = + { + indexname:this.ID, symbol: hqChart.Symbol, script:this.Script, args:args, + period:hqChart.Period, right:hqChart.Right, hqdatatype: hqDataType + }; + + if (dateRange) postData.DateRange=dateRange; + + if (requestCount) + { + postData.maxdatacount=requestCount.MaxRequestDataCount; + postData.maxminutedaycount=requestCount.MaxRequestMinuteDayCount; + } + + if (hqDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID || hqDataType==HQ_DATA_TYPE.MINUTE_ID) postData.daycount=hqChart.DayCount; + this.HQDataType=hqDataType; + + if (hqChart.NetworkFilter) + { + var obj= + { + Name:'APIScriptIndex::ExecuteScript', //类名:: + Explain:'指标计算', + Request:{ Url:self.ApiUrl, Type:'POST', Data: postData }, + Self:this, + HQChart:hqChart, + PreventDefault:false + }; + + hqChart.NetworkFilter(obj, function(data) + { + if (self.Version==2) + self.RecvAPIData2(data,hqChart,windowIndex,hisData); + else + self.RecvAPIData(data,hqChart,windowIndex,hisData); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + + JSNetwork.HttpRequest({ + url: self.ApiUrl, + data: postData, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + self.RecvAPIData(recvData,hqChart,windowIndex,hisData); + }, + error: function(request) + { + self.RecvError(request); + } + }); + } + + //.net 新版后台指标计算格式 + this.RecvAPIData2=function(data,hqChart,windowIndex,hisData) + { + JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData2] recv data ', this.Name,data ); + //if (data.code!=0) return; + + if (this.HQDataType==HQ_DATA_TYPE.KLINE_ID) + { + this.OutVar=this.ConvertToLocalData(data,hqChart); + JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData2] conver to OutVar ', this.OutVar); + } + else if (this.HQDataType==HQ_DATA_TYPE.MINUTE_ID) + { + this.OutVar=this.ConvertToLocalData(data,hqChart); + JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData2] conver to OutVar ', this.OutVar); + } + else if (this.HQDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) + { + this.OutVar=this.ConvertToLocalData(data,hqChart); + JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData2] conver to OutVar ', this.OutVar); + } + + this.BindData(hqChart,windowIndex,hisData); + + hqChart.UpdataDataoffset(); //更新数据偏移 + hqChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + hqChart.Draw(); + + if (hqChart.GetIndexEvent) + { + var event=hqChart.GetIndexEvent(); //指标计算完成回调 + if (event) + { + var data={ OutVar:this.OutVar, WindowIndex: windowIndex, Name: this.Name, Arguments: this.Arguments, HistoryData: hisData, + Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} }; + event.Callback(event,data,this); + } + } + } + + this.ConvertToLocalData=function(jsonData, hqChart) + { + var outVar=jsonData.OutVar; + if (hqChart.ClassName=="MinuteChartContainer" || hqChart.ClassName=="MinuteChartHScreenContainer") + { + var kdata=hqChart.SourceData; + var aryDataIndex=kdata.GetAPIDataIndex(jsonData.Date,jsonData.Time); + } + else + { + var kdata=hqChart.ChartPaint[0].Data; + if (ChartData.IsDayPeriod(jsonData.Period,true)) + var aryDataIndex=kdata.GetAPIDataIndex(jsonData.Date); + else + var aryDataIndex=kdata.GetAPIDataIndex(jsonData.Date,jsonData.Time); + } + + var localOutVar=[]; + for(var i in outVar) + { + var item=outVar[i]; + var outItem={ Type:item.Type, Name:item.Name }; + + if (item.Type==0) + { + outItem.Data=[]; + for(var j=0;j=0) + outItem.Name=outItem.Name.replace("@NODRAW@",""); + + } + else if (item.Type==1) //绘图函数 + { + outItem.Name=item.DrawType; + outItem.Draw={ DrawType:item.DrawType }; + var draw=item.Draw; + switch(item.DrawType) + { + case "STICKLINE": + { + var drawData=[]; + for(var j=0;j=kData.Data.length) continue; + var valueName=item.Value.toUpperCase(); + var kItem=kData.Data[item.Index]; + switch(valueName) + { + case "HIGH": + case "H": + item.Value=kItem.High; + break; + case "L": + case "LOW": + item.Value=kItem.Low; + break; + case "C": + case "CLOSE": + item.Value=kItem.Close; + break; + } + } + } + + this.FittingKLineBG=function(data, hqChart) + { + var kData=hqChart.ChartPaint[0].Data; //K线 + var result=[]; + if (ChartData.IsDayPeriod(hqChart.Period,true)) //日线 + { + var bFill=false; + for(var i=0,j=0;i=data.length) + { + ++i; + continue; + } + var dataItem=data[j]; + + if (dataItem.DatekItem.Date) + { + ++i; + } + else + { + bFill=true; + result[i]=1; + ++j; + ++i; + } + } + + if (bFill) return result; + } + else if (ChartData.IsMinutePeriod(hqChart.Period,true)) //分钟线 + { + var bFill=false; + for(var i=0,j=0;i=data.length) + { + ++i; + continue; + } + var dataItem=data[j]; + + if (dataItem.DatekItem.Date || (dataItem.Date==kItem.Date && dataItem.Time>kItem.Time)) + { + ++i; + } + else + { + bFill=true; + result[i]=1; + ++j; + ++i; + } + } + + if (bFill) return result; + } + + return null; + } + + this.FittingMinuteData=function(jsonData, hqChart) + { + var outVar=jsonData.outvar; + var date=jsonData.date; + var time=jsonData.time; + var result=[]; + + for(var i in outVar) + { + var item=outVar[i]; + var outVarItem={Name:item.name,Type:item.type} + if (item.data) + { + outVarItem.Data=this.FittingMinuteArray(item.data,date,time,hqChart); + if (item.color) outVarItem.Color=item.color; + if (item.linewidth>=1) outVarItem.LineWidth=item.linewidth; + if (item.isshow==false) outVarItem.IsShow = false; + if (item.isexdata==true) outVarItem.IsExData = true; + + result.push(outVarItem); + } + else if (item.Draw) + { + var draw=item.Draw; + var drawItem={}; + if (draw.DrawType=='DRAWICON') //图标 + { + drawItem.Icon=draw.Icon; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData=this.FittingMinuteArray(draw.DrawData,date,time,hqChart); + outVarItem.Draw=drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType=='DRAWTEXT') //文本 + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData=this.FittingMinuteArray(draw.DrawData,date,time,hqChart); + outVarItem.Draw=drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType=='STICKLINE') //柱子 + { + drawItem.Name=draw.Name; + drawItem.Type=draw.Type; + drawItem.Width=draw.Width; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData=this.FittingMinuteArray(draw.DrawData,date,time,hqChart,1); + outVarItem.Draw=drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType=='MULTI_LINE') + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData=this.FittingMultiLine(draw.DrawData,date,time,hqChart); + for(var k in drawItem.DrawData) + { + this.GetKLineData(drawItem.DrawData[k].Point, hqChart); + } + + outVarItem.Draw=drawItem; + if (draw.LineDash) drawItem.LineDash=draw.LineDash; + if (draw.Arrow) drawItem.Arrow=draw.Arrow; + + result.push(outVarItem); + } + else if (draw.DrawType=='MULTI_TEXT') + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData=this.FittingMultiText(draw.DrawData,date,time,hqChart); + this.GetKLineData(drawItem.DrawData, hqChart); + outVarItem.Draw=drawItem; + result.push(outVarItem); + } + else if (draw.DrawType=='MULTI_SVGICON') + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData={ Icon:this.FittingMultiText(draw.DrawData.Icon,date,time,hqChart), Family:draw.DrawData.Family }; + this.GetKLineData(drawItem.DrawData.Icon, hqChart); + outVarItem.Draw=drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType=="MULTI_HTMLDOM") //外部自己创建dom + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.Callback=draw.Callback; + drawItem.DrawData=this.FittingMultiText(draw.DrawData,date,time,hqChart); + this.GetKLineData(drawItem.DrawData, hqChart); + outVarItem.Draw=drawItem; + + result.push(outVarItem); + } + } + } + + return result; + } + + this.FittingMinuteArray=function(sourceData,date,time,hqChart) + { + var minutedata=hqChart.SourceData;; //分钟线 + + var arySingleData=[]; + for(var i in sourceData) + { + var value=sourceData[i]; + var indexItem=new SingleData(); //单列指标数据 + indexItem.Date=date[i]; + if (time && i1909) + { + if (month==331) + { + aryCondition= + [ + {"item":["announcement1.year","int32","eq",year]}, + {"item":["finance1","doc","exists","true"]} + ]; + + fieldList.push("announcement1.year"); + fieldList.push("announcement1.quarter"); + } + else if (month==630) + { + aryCondition= + [ + {"item":["announcement2.year","int32","eq",year]}, + {"item":["finance2","doc","exists","true"]} + ]; + + fieldList.push("announcement2.year"); + fieldList.push("announcement2.quarter"); + } + else if (month==930) + { + aryCondition= + [ + {"item":["announcement3.year","int32","eq",year]}, + {"item":["finance3","doc","exists","true"]} + ]; + + fieldList.push("announcement4.year"); + fieldList.push("announcement4.quarter"); + } + else + { + aryCondition= + [ + {"item":["announcement4.year","int32","eq",year]}, + {"item":["finance4","doc","exists","true"]} + ]; + + fieldList.push("announcement4.year"); + fieldList.push("announcement4.quarter"); + } + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.Url, + data: + { + "field": fieldList, + "symbol": [this.Symbol], + "condition":aryCondition, + "start": 0, + "end": dataEnd + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.ToHQChartData(recvData); + var result=null; + if (data && data.length>0) + { + data.sort(function (a, b) { return (b.Date-a.Date) }); + if (preYear==null) + result=data[dataIndex]; //返回一个数据 + else + result=self.GetPreYearData(data, preYear); + } + self.RecvCallback(result, self.Job, self.DataKey); + } + }); + } + + this.GetPreYearData=function(data, preYear) + { + //331,630,930,1231这些,表示最近一期的对应季报的数据; + if (preYear==331 || preYear==630|| preYear==930 || preYear==1231) + { + for(var i in data) + { + var item=data[i]; + if (item.Date%10000==preYear) return item; + } + } + else + { + //如果MMDD为0,Y为一数字,表示最近一期向前推Y年的同期数据; + var month=data[0].Date%1000; + for(var i=1, j=0; i0) + { + data.sort(function (a, b) { return (a.Date-b.Date) }); + } + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + } + + this.GetFieldList=function() + { + var id=this.Args[0]; + switch(id) + { + case 1: //1--股东人数 股东户数(户) + return ["shareholder", "date", "symbol"]; + case 2: //2--龙虎榜 买入总计(万元) 卖出总计(万元) + return ["tradedetail.buy","tradedetail.sell", "date", "symbol"]; + case 3: //3--融资融券1 融资余额(万元) 融券余量(股) + return ["margin","date", "symbol"]; + case 4: //4--大宗交易 成交均价(元) 成交额(万元) + return ["blocktrading.amount","blocktrading.price","date", "symbol"] + default: + return null; + } + } + + this.ToHQChartData=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + + var aryData=[]; + var setDate=new Set(); //有重复数据 去掉 + var stock=recvData.stock[0]; + var id=this.Args[0]; + var subID=this.Args[1]; + for(var i in stock.stockday) + { + var item=stock.stockday[i]; + + var hqchartItem=this.ToHQChartItemData(item,id,subID); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + return aryData; + } + + this.ToHQChartItemData=function(item, id, subID) + { + if (!item) return null; + var date=item.date; + switch(id) + { + case 1: + if (!item.shareholder) return null; + return { Date:date, Value:item.shareholder.count }; + case 2: + if (!item.tradedetail && item.tradedetail[0]) return null; + if (subID==0) + return { Date:date, Value:item.tradedetail[0].buy }; + else + return { Date:date, Value:item.tradedetail[0].sell }; + case 3: + if (!item.margin) return null; + if (subID==0) + { + if (item.margin.buy) + return { Date:date, Value:item.margin.buy.balance }; + } + else + { + if (item.margin.sell) + return { Date:date, Value:item.margin.sell.balance }; + } + return null; + case 4: + if (!item.blocktrading) return null; + if (subID==0) + return { Date:date, Value:item.blocktrading.price }; + else + return { Date:date, Value:item.blocktrading.amount }; + default: + return null; + } + } +} + + + +/* +引用市场总的交易类数据.如果指标支持云数据函数,则不需要[专业财务数据]下载. +SCJYVALUE(ID,N,TYPE),ID为数据编号,N表示第几个数据,TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理 + +市场交易类数据函数,数据编号如下: +1--融资融券 沪深融资余额(万元) 沪深融券余额(万元) +2--陆股通资金流入 沪股通流入金额(亿元) 深股通流入金额(亿元) +3--沪深涨停股个数 涨停股个数 曾涨停股个数 [注:该指标展示20160926日之后的数据] +4--沪深跌停股个数 跌停股个数 曾跌停股个数 +5--上证50股指期货 净持仓(手)[注:该指标展示20171009日之后的数据] +6--沪深300股指期货 净持仓(手) [注:该指标展示20171009日之后的数据] +7--中证500股指期货 净持仓(手) [注:该指标展示20171009日之后的数据] +8--ETF基金规模数据 ETF基金规模(亿) ETF净申赎(亿) +9--每周新增投资者 新增投资者(万户) +10--增减持统计 增持额(万元) 减持额(万元)[注:部分公司公告滞后,造成每天查看的数据可能会不一样] +11--大宗交易 溢价的大宗交易额(万元) 折价的大宗交易额(万元) +12--限售解禁 限售解禁计划额(亿元) 限售解禁股份实际上市金额(亿元)[注:该指标展示201802月之后的数据;部分股票的解禁日期延后,造成不同日期提取的某天的计划额可能不同] +13--分红 市场总分红额(亿元)[注:除权派息日的A股市场总分红额] +14--募资 市场总募资额(亿元)[注:发行日期/除权日期的首发、配股和增发的总募资额] +15--打板资金 封板成功资金(亿元) 封板失败资金(亿元) [注:该指标展示20160926日之后的数据] +*/ +function DownloadSCJYValue(obj) +{ + this.Url=obj.Url; + //this.RealtimeUrl=obj.RealtimeUrl; + this.Job=obj.Job; + this.Symbol=obj.Symbol; + this.Args=obj.Args; + this.DataKey=obj.DataKey; + this.RecvCallback=obj.Callback; + this.ErrorCallback=obj.ErrorCallback; + + this.Download=function() + { + var self=this; + var query=this.GetQueryCondtion(); + if (!query) + { + message=`${this.Job.FunctionName}(${this.Args[0]}, ${this.Args[1]}, ${this.Args[2]}) can't support.`; + this.ErrorCallback(message); + self.RecvCallback(null, self.Job, self.DataKey, true); + return; + } + + //请求数据 + JSNetwork.HttpRequest({ + url: this.Url, + data: + { + "field": query.Field, + "symbol": query.Symbol, + "condition":query.Cond , + "start": 0, + "end": 2000 + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.ToHQChartData(recvData); + if (data) //排序 + data.sort(function (a, b) { return (a.Date - b.Date) }); + + self.RecvCallback(data, self.Job, self.DataKey); + } + }); + + } + + this.GetQueryCondtion=function() + { + var day=new Date(); + var endDate=day.getFullYear()*10000+(day.getMonth()+1)*100+day.getDate(); + day.setDate(day.getDate()-100); //取最近1000条数据 + var startDate=day.getFullYear()*10000+(day.getMonth()+1)*100+day.getDate() + var id=this.Args[0]; + switch(id) + { + case 1: + var data= + { + Symbol:["CNA.ci"], + Field:["margin"], + Cond: + [ + {"item":["date","int32","gte",startDate.toString(),"lte",endDate.toString()]}, + {"item":["margin","doc","exists","true"]} + ] + } + return data; + } + } + + this.ToHQChartData=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + + var aryData=[]; + var setDate=new Set(); //有重复数据 去掉 + var stock=recvData.stock[0]; + var id=this.Args[0]; + var subID=this.Args[1]; + for(var i in stock.stockday) + { + var item=stock.stockday[i]; + + var hqchartItem=this.ToHQChartItemData(item,id,subID); + if (hqchartItem && !setDate.has(hqchartItem.Date)) + { + aryData.push(hqchartItem); + setDate.add(hqchartItem.Date); + } + } + + return aryData; + } + + this.ToHQChartItemData=function(item, id, subID) + { + if (!item) return null; + var date=item.date; + var subItem=null; + switch(id) + { + case 1: + if (!item.margin) return null; + subItem=item.margin; + if (subID==1) + { + if (!subItem.buy || !IFrameSplitOperator.IsNumber(subItem.buy.balance)) return null; + return { Date:date, Value:subItem.buy.balance } + } + else + { + if (!IFrameSplitOperator.IsNumber(subItem.sell.balance)) return null; + return { Date:date, Value:subItem.sell.balance } + } + + default: + return null; + } + } +} + +function DownloadGroupData(obj) +{ + this.Url=obj.Url; + this.RealtimeUrl=obj.RealtimeUrl; + this.Job=obj.Job; + this.Symbol=obj.Symbol; + this.Args=obj.Args; + this.DataKey=obj.DataKey; + this.RecvCallback=obj.Callback; + this.ErrorCallback=obj.ErrorCallback; + + this.Download=function() + { + var varName=this.Args[0]; + switch(varName) + { + case "HYBLOCK": + case "DYBLOCK": + case "GNBLOCK": + this.DownloadGroupName(varName); + break; + } + } + + this.DownloadGroupName=function(blockType) + { + var self=this; + var field=["name","symbol"]; + if (blockType=="HYBLOCK") field.push("industry"); + else if (blockType=="DYBLOCK") field.push("region"); + else if (blockType=="GNBLOCK") field.push("concept"); + + JSNetwork.HttpRequest({ + url: self.RealtimeUrl, + data: + { + "field": field, + "symbol": [this.Symbol] + }, + type:"post", + dataType: "json", + async:true, + success: function (recvData) + { + var data=self.RecvGroupName(recvData); + self.RecvCallback(data, self.Job, self.DataKey, 1); + }, + error: function(request) + { + self.ErrorCallback(request); + } + }); + } + + this.RecvGroupName=function(recvData) + { + if (!recvData.stock || recvData.stock.length!=1) return null; + var stock=recvData.stock[0]; + var varName=this.Args[0]; + var value=null; + if (varName=="HYBLOCK") + { + var industry=stock.industry; + if (!industry) return null; + var value; + for(var i in industry) + { + var item=industry[i]; + value=item.name; + } + } + else if (varName=="DYBLOCK") + { + var region=stock.region; + if (!region) return null; + for(var i in region) + { + var item=region[i]; + value=item.name; + } + } + else if (varName=="GNBLOCK") + { + var concept=stock.concept; + if (!concept) return null; + value=""; + for(var i in concept) + { + var item=concept[i]; + if (value.length>0) value+=' '; + value+=item.name; + } + + } + + return { Value:value }; + } +} + +/* 测试例子 +var code1='VARHIGH:IF(VAR1<=REF(HH,-1),REF(H,BARSLAST(VAR1>=REF(HH,1))),DRAWNULL),COLORYELLOW;'; +var code2='VAR1=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);'; +var code3='mm1=1-2*-9+20;'; + +JSConsole.Complier.Log(code1+code2) +var tokens=JSComplier.Tokenize(code1+code2); +var ast=JSComplier.Parse(code2+code1); + +JSConsole.Complier.Log(ast); +*//* + Copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 配色 +*/ + +/* + 不同风格行情配置文件 + !!手机上字体大小需要*分辨率比 +*/ + +/* umychart.js 里面已经有了 +function GetDevicePixelRatio() +{ + return window.devicePixelRatio || 1; +} +*/ + +//黑色风格 +var BLACK_STYLE= +{ + BGColor:'rgb(0,0,0)', //背景色 + TooltipBGColor: "rgb(255, 255, 255)", //背景色 + TooltipAlpha: 0.92, //透明度 + + SelectRectBGColor: "rgba(1,130,212,0.06)", //背景色 + // SelectRectAlpha: 0.06; //透明度 + + //K线颜色 + UpBarColor: "rgb(238,21,21)", //上涨 + DownBarColor: "rgb(25,158,0)", //下跌 + UnchagneBarColor: "rgb(228,228,228)", //平盘 + EmptyBarBGColor:'rgb(0,0,0)', //空心柱子背景色 + + Minute: + { + VolBarColor: null, + PriceColor: "rgb(25,180,231)", + AreaPriceColor:"rgba(63,158,255,.3)", + AvPriceColor: "rgb(255,236,0)", + PositionColor:'rgb(218,165,32)', + VolTitleColor:"rgb(190,190,190)", + Before: + { + BGColor:"rgba(105,105,105,0.5)", + AvPriceColor:'rgb(248,248,255)' //均线 + } + }, + + + DefaultTextColor: "rgb(101,104,112)", + DefaultTextFont: 14*GetDevicePixelRatio() +'px 微软雅黑', + TitleFont: 13*GetDevicePixelRatio() +'px 微软雅黑', //标题字体(动态标题 K线及指标的动态信息字体) + + UpTextColor: "rgb(238,21,21)", + DownTextColor: "rgb(25,158,0)", + UnchagneTextColor: "rgb(101,104,112)", + CloseLineColor: 'rgb(178,34,34)', + + Title: + { + TradeIndexColor:'rgb(105,105,105)', //交易指标颜色 + ColorIndexColor:'rgb(112,128,144)', //五彩K线颜色 + + VolColor:"rgb(101,104,112)", //标题成交量 + AmountColor:"rgb(101,104,112)", //成交金额 + DateTimeColor:"rgb(101,104,112)", //时间,日期 + SettingColor:"rgb(101,104,112)", //周期,复权 + NameColor:"rgb(101,104,112)" , //股票名称 + TurnoverRateColor:'rgb(101,104,112)', //换手率 + PositionColor:"rgb(101,104,112)" //持仓 + }, + + FrameBorderPen: "rgba(236,236,236,0.13)", //边框 + FrameSplitPen: "rgba(236,236,236,0.13)", //分割线 + FrameSplitTextColor: "rgb(101,104,112)", //刻度文字颜色 + FrameSplitTextFont: 12*GetDevicePixelRatio() +"px 微软雅黑", //坐标刻度文字字体 + FrameTitleBGColor: "rgb(0,0,0)", //标题栏背景色 + OverlayIndexTitleBGColor:'rgba(0,0,0,0.7)', //叠加指标背景色 + + Frame: + { + XBottomOffset:1*GetDevicePixelRatio(), //X轴文字向下偏移 + + PercentageText: //百分比坐标文字颜色 + { + PriceColor:'rgb(101,104,112)', + PercentageColor:"rgb(101,104,112)", + SplitColor:"rgb(101,104,112)", + Font:14*GetDevicePixelRatio() +"px 微软雅黑" + } + }, + + + FrameLatestPrice : { + TextColor:'rgb(255,255,255)', //最新价格文字颜色 + UpBarColor:"rgb(238,21,21)", //上涨 + DownBarColor:"rgb(25,158,0)", //下跌 + UnchagneBarColor:"rgb(190,190,190)", //平盘 + BGAlpha:0.6 + }, + + CorssCursorBGColor: "rgb(43,54,69)", //十字光标背景 + CorssCursorTextColor: "rgb(255,255,255)", + CorssCursorTextFont: 12*GetDevicePixelRatio() +"px 微软雅黑", + CorssCursorHPenColor: "rgb(130,130,130)", //十字光标线段颜色 + CorssCursorVPenColor: "rgb(130,130,130)", //十字光标线段颜色 + + KLine: + { + MaxMin: { Font: 12*GetDevicePixelRatio() +'px 微软雅黑', Color: 'rgb(255,250,240)', RightArrow:"→", LeftArrow:"←", HighYOffset:0, LowYOffset:0 }, //K线最大最小值显示 + Info: //信息地雷 + { + Investor: + { + ApiUrl:'/API/NewsInteract', //互动易 + IconFont: { Family:'iconfont', Text:'\ue631' , HScreenText:'\ue684', Color:'#1c65db'} //SVG 文本 + }, + Announcement: //公告 + { + ApiUrl:'/API/ReportList', + IconFont: { Family:'iconfont', Text:'\ue633', HScreenText:'\ue685', Color:'#f5a521' }, //SVG 文本 + IconFont2: { Family:'iconfont', Text:'\ue634', HScreenText:'\ue686', Color:'#ed7520' } //SVG 文本 //季报 + }, + Pforecast: //业绩预告 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue62e', HScreenText:'\ue687', Color:'#986cad' } //SVG 文本 + }, + Research: //调研 + { + ApiUrl:'/API/InvestorRelationsList', + IconFont: { Family:'iconfont', Text:'\ue632', HScreenText:'\ue688', Color:'#19b1b7' } //SVG 文本 + }, + BlockTrading: //大宗交易 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue630', HScreenText:'\ue689', Color:'#f39f7c' } //SVG 文本 + }, + TradeDetail: //龙虎榜 + { + ApiUrl:'/API/StockHistoryDay', + IconFont: { Family:'iconfont', Text:'\ue62f', HScreenText:'\ue68a' ,Color:'#b22626' } //SVG 文本 + } + + }, + NumIcon: + { + Color:'rgb(251,80,80)',Family:'iconfont', + Text:[ '\ue649', + '\ue63b','\ue640','\ue63d','\ue63f','\ue645','\ue641','\ue647','\ue648','\ue646','\ue636', + '\ue635','\ue637','\ue638','\ue639','\ue63a','\ue63c','\ue63e','\ue642','\ue644','\ue643' + ] + }, + TradeIcon: //交易指标 图标 + { + Family:'iconfont', + Buy: { Color:'rgb(255,15,4)', Text:'\ue683', HScreenText:'\ue682'}, + Sell: { Color:'rgb(64,122,22)', Text:'\ue681',HScreenText:'\ue680'}, + } + }, + + Index: + { + LineColor: //指标线段颜色 + [ + "rgb(255,189,09)", + "rgb(22,198,255)", + "rgb(174,35,161)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ], + NotSupport: { Font: "14px 微软雅黑", TextColor: "rgb(52,52,52)" } + }, + + ColorArray: //自定义指标默认颜色 + [ + "rgb(255,174,0)", + "rgb(25,199,255)", + "rgb(175,95,162)", + "rgb(236,105,65)", + "rgb(68,114,196)", + "rgb(229,0,79)", + "rgb(0,128,255)", + "rgb(252,96,154)", + "rgb(42,230,215)", + "rgb(24,71,178)", + ], + + DrawPicture: //画图工具 + { + LineColor: "rgb(30,144,255)", + PointColor: "rgb(105,105,105)", + }, + + TooltipPaint : + { + BGColor:'rgba(20,20,20,0.8)', //背景色 + BorderColor:'rgb(210,210,210)', //边框颜色 + TitleColor:'rgb(210,210,210)', //标题颜色 + TitleFont:13*GetDevicePixelRatio() +'px 微软雅黑', //字体 + DateTimeColor:'rgb(210,210,210)', + VolColor:"rgb(210,210,210)", //标题成交量 + AmountColor:"rgb(210,210,210)", //成交金额 + }, + + //走势图 信息地雷 + MinuteInfo: + { + TextColor: 'rgb(84,143,255)', + Font: 14*GetDevicePixelRatio() +'px 微软雅黑', + PointColor:'rgb(38,113,254)', + LineColor:'rgb(120,167,255)', + TextBGColor:'rgba(255,255,255,1)' + }, + + //筹码分布图 + StockChip: + { + InfoColor:'rgb(255,255,255)', //文字颜色 + DayInfoColor:'rgb(0,0,0)' //周期颜色内文字颜色 + }, + + //深度图 + DepthChart: + { + BidColor: { Line:"rgb(82,176,123)", Area:"rgba(82,176,123,0.5)"}, //卖 + AskColor: { Line:"rgb(207,76,89)", Area:"rgba(207,76,89, 0.5)"}, //买 + LineWidth:4 + }, + + DepthCorss: + { + BidColor: { Line:"rgb(82,176,123)" }, //卖 + AskColor: { Line:"rgb(207,76,89)" }, //买 + LineWidth:2, //线段宽度 + LineDash:[3,3], + Tooltip: + { + BGColor:'rgba(54,54,54, 0.8)', TextColor:"rgb(203,215,224)", + Border:{ Top:5, Left:20, Bottom:5, Center: 5}, + Font:14*GetDevicePixelRatio() +"px 微软雅黑", + LineHeight:16 //单行高度 + } + }, + + //区间选择 + RectSelect: + { + LineColor:"rgb(115,83,64)", //竖线 + LineWidth:1*GetDevicePixelRatio(), + LineDotted:[3,3], + AreaColor:"rgba(26,13,7,0.5)", //面积 + } + +}; + +var STYLE_TYPE_ID= +{ + BLACK_ID:1, //黑色风格 +} + +function HQChartStyle() +{ + +} + +HQChartStyle.GetStyleConfig=function(styleid) //获取一个风格的配置变量 +{ + switch (styleid) + { + case STYLE_TYPE_ID.BLACK_ID: + return BLACK_STYLE; + default: + return null; + } +} + + + + + +/* + 单独的网络接口 +*/ +function JSNetwork() { } + +JSNetwork.HttpRequest=function(obj) //使用uniapp 网络接口接口 !!每家平台都喜欢自定义接口, 低层不都是一样的嘛 +{ + let method=obj.type.toUpperCase(); + uni.request({ + url: obj.url, + data: obj.data, + method:method, + success: (res)=> + { + obj.success(res.data); + }, + fail: obj.error, + }); +} + +//把给外界调用的方法暴露出来 +export default { + + //类导出 + JSChart:JSChart, //行情图形库 + ChartData:ChartData, + MARKET_SUFFIX_NAME:MARKET_SUFFIX_NAME, //判断股票属性 + IFrameSplitOperator:IFrameSplitOperator, //格式化字符串方法 + FrameSplitKLinePriceY:FrameSplitKLinePriceY, + FrameSplitKLineX:FrameSplitKLineX, + JSKLineInfoMap:JSKLineInfoMap, + JSCHART_EVENT_ID:JSCHART_EVENT_ID, //可以订阅的事件类型 + JSCHART_OPERATOR_ID:JSCHART_OPERATOR_ID, //图形控制类型 + JSAlgorithm:JSAlgorithm, //算法类 + JSComplier:JSComplier, //指标编译器 + JSIndexScript:JSIndexScript, //系统指标库 + GetDevicePixelRatio,GetDevicePixelRatio, + + ScriptIndexConsole:ScriptIndexConsole, //指标执行 无UI + //style.js相关 + STYLE_TYPE_ID:STYLE_TYPE_ID, + HQChartStyle:HQChartStyle, //预定义全局的配色 黑 + + JSConsole:JSConsole, //日志输出 + + KLineTooltipPaint:KLineTooltipPaint, //K线tooltip + MinuteTooltipPaint:MinuteTooltipPaint, //走势图tooltip + + CoordinateInfo:CoordinateInfo, +} + diff --git a/uni_modules/jones-hqchart2/js_sdk/umychart.wechat.3.0.js b/uni_modules/jones-hqchart2/js_sdk/umychart.wechat.3.0.js new file mode 100644 index 0000000..9f5c586 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/umychart.wechat.3.0.js @@ -0,0 +1,14245 @@ +/* + copyright (c) 2018 jones + + http://www.apache.org/licenses/LICENSE-2.0 + + 开源项目 https://github.com/jones2000/HQChart + + jones_2000@163.com + + 封装图形控件 (微信小程序版本) +*/ + +//日志 +import { JSConsole } from "./umychart.console.wechat.js" + +//行情数据结构体 及涉及到的行情算法(复权,周期等) +import { + JSCommon_ChartData as ChartData, JSCommon_HistoryData as HistoryData, + JSCommon_SingleData as SingleData, JSCommon_MinuteData as MinuteData, + JSCommon_CUSTOM_DAY_PERIOD_START as CUSTOM_DAY_PERIOD_START, + JSCommon_CUSTOM_DAY_PERIOD_END as CUSTOM_DAY_PERIOD_END, + JSCommon_CUSTOM_MINUTE_PERIOD_START as CUSTOM_MINUTE_PERIOD_START, + JSCommon_CUSTOM_MINUTE_PERIOD_END as CUSTOM_MINUTE_PERIOD_END, + JSCommon_CUSTOM_SECOND_PERIOD_START as CUSTOM_SECOND_PERIOD_START, + JSCommon_CUSTOM_SECOND_PERIOD_END as CUSTOM_SECOND_PERIOD_END, + JSCommon_Rect as Rect, + JSCommon_DataPlus as DataPlus, + JSCommon_JSCHART_EVENT_ID as JSCHART_EVENT_ID, + JSCommon_PhoneDBClick as PhoneDBClick, +} from "./umychart.data.wechat.js"; + +import { + JSCommon_JSKLineInfoMap as JSKLineInfoMap, + JSCommon_KLINE_INFO_TYPE as KLINE_INFO_TYPE, + JSCommon_JSMinuteInfoMap as JSMinuteInfoMap, +} from "./umychart.klineinfo.wechat.js"; + +import +{ + JSCommonCoordinateData as JSCommonCoordinateData, + JSCommonCoordinateData_MARKET_SUFFIX_NAME as MARKET_SUFFIX_NAME , + JSCommonCoordinateData_Global_FuturesTimeData as g_FuturesTimeData, + JSCommonCoordinateData_Global_NYMEXTimeData as g_NYMEXTimeData, + JSCommonCoordinateData_Global_COMEXTimeData as g_COMEXTimeData, + JSCommonCoordinateData_Global_NYBOTTimeData as g_NYBOTTimeData, + JSCommonCoordinateData_Global_LMETimeData as g_LMETimeData, + JSCommonCoordinateData_Global_CBOTTimeData as g_CBOTTimeData, +} from "./umychart.coordinatedata.wechat.js"; + +import { JSCommonComplier } from "./umychart.complier.wechat.js"; //通达信编译器 +import { JSCommonIndexScript } from "./umychart.index.data.wechat.js"; //系统指标定义 +import { JSCommon_HQIndexFormula as HQIndexFormula } from "./umychart.hqIndexformula.wechat.js"; //通达信编译器 + +//图形库 +import { + JSCommonChartPaint_IChartPainting as IChartPainting, + JSCommonChartPaint_ChartSingleText as ChartSingleText, + JSCommonChartPaint_ChartKLine as ChartKLine, + JSCommonChartPaint_ChartLine as ChartLine, + JSCommonChartPaint_ChartSubLine as ChartSubLine, + JSCommonChartPaint_ChartPointDot as ChartPointDot, + JSCommonChartPaint_ChartStick as ChartStick, + JSCommonChartPaint_ChartLineStick as ChartLineStick, + JSCommonChartPaint_ChartStickLine as ChartStickLine, + JSCommonChartPaint_ChartOverlayKLine as ChartOverlayKLine, + JSCommonChartPaint_ChartMinuteInfo as ChartMinuteInfo, + JSCommonChartPaint_ChartRectangle as ChartRectangle, + JSCommonChartPaint_ChartMultiText as ChartMultiText, + JSCommonChartPaint_ChartMultiLine as ChartMultiLine, + JSCommonChartPaint_ChartMultiBar as ChartMultiBar, + JSCommonChartPaint_ChartPie as ChartPie, + JSCommonChartPaint_ChartCircle as ChartCircle, + JSCommonChartPaint_ChartChinaMap as ChartChinaMap, + JSCommonChartPaint_ChartRadar as ChartRadar, + JSCommonChartPaint_ChartCorssCursor as ChartCorssCursor, + JSCommonChartPaint_ChartBuySell as ChartBuySell, + JSCommonChartPaint_ChartMACD as ChartMACD, + JSCommonChartPaint_ChartSplashPaint as ChartSplashPaint, + JSCommonChartPaint_ChartBackground as ChartBackground, + JSCommonChartPaint_ChartMinuteVolumBar as ChartMinuteVolumBar, + JSCommonChartPaint_ChartMultiHtmlDom as ChartMultiHtmlDom, + JSCommonChartPaint_ChartLock as ChartLock, + JSCommonChartPaint_ChartVolStick as ChartVolStick, + JSCommonChartPaint_ChartBand as ChartBand, + JSCommonChartPaint_DepthChartCorssCursor as DepthChartCorssCursor, + JSCommonChartPaint_ChartOrderbookDepth as ChartOrderbookDepth, + JSCommonChartPaint_ChartMinutePriceLine as ChartMinutePriceLine, + JSCommonChartPaint_ChartText as ChartText , + JSCommonChartPaint_ChartStraightArea as ChartStraightArea, +} from "./umychart.chartpaint.wechat.js"; + +//扩展画法图形库 +import { + JSCommonExtendChartPaint_IExtendChartPainting as IExtendChartPainting, + JSCommonExtendChartPaint_KLineTooltipPaint as KLineTooltipPaint, + JSCommonExtendChartPaint_BarragePaint as BarragePaint, + JSCommonExtendChartPaint_MinuteTooltipPaint as MinuteTooltipPaint, + JSCommonExtendChartPaint_BackgroundPaint as BackgroundPaint, +} from "./umychart.extendchart.wechat.js"; + +import { + JSCommonIndex_IndexInfo as IndexInfo, + JSCommonIndex_BaseIndex as BaseIndex, + JSCommonIndex_ScriptIndex as ScriptIndex, +} from './umychart.index.wechat.js' + +import{ + JSCommonResource_Global_JSChartResource as g_JSChartResource, + JSCommonResource_JSCHART_LANGUAGE_ID as JSCHART_LANGUAGE_ID, + JSCommonResource_Global_JSChartLocalization as g_JSChartLocalization, +} from './umychart.resource.wechat.js' + +import { JSCommonUniApp } from './umychart.uniapp.canvas.helper.js' + +import +{ + JSCommonSplit_CoordinateInfo as CoordinateInfo, + JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator, + JSCommonSplit_FrameSplitKLinePriceY as FrameSplitKLinePriceY, + JSCommonSplit_FrameSplitY as FrameSplitY, + JSCommonSplit_FrameSplitKLineX as FrameSplitKLineX, + JSCommonSplit_FrameSplitMinutePriceY as FrameSplitMinutePriceY, + JSCommonSplit_FrameSplitMinuteX as FrameSplitMinuteX, + JSCommonSplit_FrameSplitXData as FrameSplitXData, + JSCommonSplit_SplitData as SplitData, + JSCommonSplit_PriceSplitData as PriceSplitData, + JSCommonSplit_FrameSplitXDepth as FrameSplitXDepth, + + JSCommonFormat_IChangeStringFormat as IChangeStringFormat, + JSCommonFormat_HQPriceStringFormat as HQPriceStringFormat, + JSCommonFormat_HQDateStringFormat as HQDateStringFormat, + JSCommonFormat_HQMinuteTimeStringFormat as HQMinuteTimeStringFormat, + JSCommonFormat_Global_DataFormat as g_DivTooltipDataForamt, +} from './umychart.framesplit.wechat.js' + +import +{ + JSCommonChartTitle_IChartTitlePainting as IChartTitlePainting, + JSCommonChartTitle_DynamicKLineTitlePainting as DynamicKLineTitlePainting, + JSCommonChartTitle_DynamicMinuteTitlePainting as DynamicMinuteTitlePainting, + JSCommonChartTitle_DynamicChartTitlePainting as DynamicChartTitlePainting, + JSCommonChartTitle_DynamicTitleData as DynamicTitleData, + JSCommonChartTitle_STRING_FORMAT_TYPE as STRING_FORMAT_TYPE, +} from './umychart.charttitle.wechat.js' + + +function JSCanvasElement() +{ + this.Height; + this.Width; + this.ID; + this.WebGLCanvas; + this.IsUniApp=false; + this.CanvasNode=null; + this.ComponentObject=null; //在自定义组件下,当前组件实例的this,表示在这个自定义组件下查找拥有 canvas-id 的 canvas ,如果省略则不在任何自定义组件内查找 + + //获取画布 + this.GetContext = function () + { + var canvas; + if (this.CanvasNode && this.CanvasNode.node) + { + const width = this.CanvasNode.width; + const height = this.CanvasNode.height; + + var node = this.CanvasNode.node; + node._height = height; + node._width = width; + JSConsole.Chart.Log("[JSCanvasElement::GetContext] create by getContext('2d')"); + canvas = node.getContext('2d'); + const dpr = wx.getSystemInfoSync().pixelRatio; + node.width = width * dpr; + node.height = height * dpr; + canvas.restore(); + canvas.save(); + canvas.scale(dpr, dpr); + canvas.draw = (bDraw, callback) => { if (callback) callback(); }; + canvas.DomNode = node; + } + else + { + if (this.ComponentObject) //小程序自定义组件 + canvas=wx.createCanvasContext(this.ID,this.ComponentObject); + else + canvas=wx.createCanvasContext(this.ID); + } + + if (this.IsUniApp) + { + JSConsole.Chart.Log('[JSCanvasElement::GetContext] measureText() => JSUniAppCanvasHelper.MeasureText()'); + canvas.measureText = function (text) //uniapp 计算宽度需要自己计算 + { + var width = JSCommonUniApp.JSUniAppCanvasHelper.MeasureText(text, canvas); + return { width: width }; + } + + canvas.fillText_backup=canvas.fillText; //uniapp fillText 填了最大长度就会失真, 所以去掉 + canvas.fillText=function(text,x,y,maxWidth) + { + canvas.fillText_backup(text,x,y); + } + } + + return canvas; + } + + this.GetWebGLCanvas=function(id) + { + var self=this; + const query = wx.createSelectorQuery(); + query.select(id).node().exec((res) => { + JSConsole.Chart.Log('[JSCanvasElement::GetWebGLCanvas] res ', res) + self.WebGLCanvas = res[0].node; + }) + } +} + +function JSChart(element) +{ + this.JSChartContainer; //画图控件 + this.CanvasElement = element; + + this.AddEventCallback = function (obj) //事件回调 {event:事件id, callback:回调函数} + { + if (this.JSChartContainer && typeof (this.JSChartContainer.AddEventCallback) == 'function') + { + JSConsole.Chart.Log('[JSChart:AddEventCallback] ', obj); + this.JSChartContainer.AddEventCallback(obj); + } + } + + this.OnSize = function (option) + { + if (option) + { + if (IFrameSplitOperator.IsNumber(option.Width)) this.CanvasElement.Width=option.Width; + if (IFrameSplitOperator.IsNumber(option.Height)) this.CanvasElement.Height=option.Height; + } + + if (option && option.Redraw==false) return; + + if (this.JSChartContainer) + { + if (option && option.Type==1 && this.JSChartContainer.OnSize) + { + this.JSChartContainer.OnSize(); + } + else + { + if (this.JSChartContainer.Frame) this.JSChartContainer.Frame.SetSizeChage(true); + this.JSChartContainer.Draw(); + } + } + } + + //历史K线图 + this.CreateKLineChartContainer = function (option) + { + var chart = null; + if (option.Type === "历史K线图横屏") chart = new KLineChartHScreenContainer(this.CanvasElement); + else chart = new KLineChartContainer(this.CanvasElement); + + if (option.NetworkFilter) chart.NetworkFilter = option.NetworkFilter; + + if (option.KLine) //k线图的属性设置 + { + if (option.KLine.DragMode >= 0) chart.DragMode = option.KLine.DragMode; + if (option.KLine.Right >= 0) chart.Right = option.KLine.Right; + if (option.KLine.Period >= 0) chart.Period = option.KLine.Period; + if (option.KLine.MaxReqeustDataCount > 0) chart.MaxReqeustDataCount = option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length > 0) chart.SetKLineInfo(option.KLine.Info, false); + if (option.KLine.Policy && option.KLine.Policy.length > 0) chart.SetPolicyInfo(option.KLine.Policy, false); + if (option.KLine.KLineDoubleClick == false) chart.MinuteDialog = this.MinuteDialog = null; + if (option.KLine.MaxRequestMinuteDayCount > 0) chart.MaxRequestMinuteDayCount = option.KLine.MaxRequestMinuteDayCount; + if (option.KLine.DrawType) chart.KLineDrawType = option.KLine.DrawType; + if (option.KLine.RightSpaceCount >= 0) chart.RightSpaceCount = option.KLine.RightSpaceCount; + if (option.KLine.DataWidth>=1) chart.KLineSize={ DataWidth:option.KLine.DataWidth }; + } + + if (IFrameSplitOperator.IsString(option.SplashTitle)) chart.LoadDataSplashTitle = option.SplashTitle; //设置提示信息内容 + if (IFrameSplitOperator.IsBool(option.EnableZoomIndexWindow)) chart.EnableZoomIndexWindow=option.EnableZoomIndexWindow; //双击缩放附图 + if (!option.Windows || option.Windows.length <= 0) return null; + + if (option.Language) + { + if (option.Language === 'CN') chart.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + else if (option.Language === 'EN') chart.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID; + } + + if (option.SourceDatatLimit) chart.SetSourceDatatLimit(option.SourceDatatLimit); + if (option.EnableZoomUpDown) chart.EnableZoomUpDown=option.EnableZoomUpDown; + if (option.ZoomStepPixel>0) chart.ZoomStepPixel=option.ZoomStepPixel; + if (IFrameSplitOperator.IsNumber(option.DrawMoveWaitTime)) chart.DrawMoveWaitTime=option.DrawMoveWaitTime; + //创建子窗口 + chart.Create(option.Windows.length); + + if (option.Border) + { + var item=option.Border; + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + + if (item.AutoLeft) chart.Frame.AutoLeftBorder=item.AutoLeft; + if (item.AutoRight) chart.Frame.AutoRightBorder=item.AutoRight; + } + + if (option.KLine) + { + if (option.KLine.PageSize > 0) + { + let maxPageSize = chart.GetMaxPageSize(); + if (maxPageSize < option.KLine.PageSize) chart.PageSize = maxPageSize; + else chart.PageSize = option.KLine.PageSize; + } + if (option.KLine.InfoDrawType) chart.ChartPaint[0].InfoDrawType = option.KLine.InfoDrawType; + } + + if (option.DragDownload) + { + if (option.DragDownload.Day && option.DragDownload.Day.Enable == true) chart.DragDownload.Day.Enable = true; + if (option.DragDownload.Minute && option.DragDownload.Minute.Enable == true) chart.DragDownload.Minute.Enable = true; + } + + if (option.IsApiPeriod == true) chart.IsApiPeriod = option.IsApiPeriod; + + if (option.CorssCursorTouchEnd == true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsClickShowCorssCursor == true) chart.IsClickShowCorssCursor = option.IsClickShowCorssCursor; + if (option.IsFullDraw == true) chart.IsFullDraw = option.IsFullDraw; + if (option.CorssCursorInfo) + { + var item=option.CorssCursorInfo; + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left = option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right = option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom = option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss === false) chart.ChartCorssCursor.IsShowCorss = option.CorssCursorInfo.IsShowCorss; + if (option.CorssCursorInfo.IsShowClose == true) chart.ChartCorssCursor.IsShowClose = option.CorssCursorInfo.IsShowClose; //Y轴显示收盘价 + if (IFrameSplitOperator.IsNumber(option.CorssCursorInfo.HPenType)) chart.ChartCorssCursor.HPenType = option.CorssCursorInfo.HPenType; + if (option.CorssCursorInfo.VPenType > 0) chart.ChartCorssCursor.VPenType = option.CorssCursorInfo.VPenType; + if (IFrameSplitOperator.IsNumber(item.DateFormatType)) chart.ChartCorssCursor.StringFormatX.DateFormatType=item.DateFormatType; + } + + if (typeof (option.UpdateUICallback) == 'function') //数据到达回调 + chart.UpdateUICallback = option.UpdateUICallback; + + if (option.Frame) + { + for (var i in option.Frame) + { + var item = option.Frame[i]; + if (!chart.Frame.SubFrame[i]) continue; + if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = item.SplitCount; + if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat = item.StringFormat; + if (!isNaN(item.Height)) chart.Frame.SubFrame[i].Height = item.Height; + if (item.IsShowBorder == false) chart.Frame.SubFrame[i].Frame.IsShowBorder = item.IsShowBorder; + if (item.IsShowXLine == false) chart.Frame.SubFrame[i].Frame.IsShowXLine = item.IsShowXLine; + if (item.IsShowYLine==false) chart.Frame.SubFrame[i].Frame.IsShowYLine=item.IsShowYLine; + if (item.XMessageAlign == 'bottom') chart.Frame.SubFrame[i].Frame.XMessageAlign = item.XMessageAlign; + if (item.IsShowTitle == false) chart.Frame.SubFrame[i].Frame.IsShowTitle = false; + if (IFrameSplitOperator.IsBool(item.IsShowIndexTitle)) chart.Frame.SubFrame[i].Frame.IsShowTitle=item.IsShowIndexTitle; + if (item.UpdateTitleUICallback && chart.Frame.SubFrame[i].Frame.TitlePaint) chart.Frame.SubFrame[i].Frame.TitlePaint.UpdateUICallback = item.UpdateTitleUICallback; + if (item.IsShowLeftText === false || item.IsShowLeftText === true) chart.Frame.SubFrame[i].Frame.IsShowYText[0] = item.IsShowLeftText; //显示左边刻度 + if (item.IsShowRightText === false || item.IsShowRightText === true) chart.Frame.SubFrame[i].Frame.IsShowYText[1] = item.IsShowRightText; //显示右边刻度 + if (item.TopSpace >= 0) chart.Frame.SubFrame[i].Frame.ChartBorder.TopSpace = item.TopSpace; + if (item.BottomSpace >= 0) chart.Frame.SubFrame[i].Frame.ChartBorder.BottomSpace = item.BottomSpace; + if (item.Custom) chart.Frame.SubFrame[i].Frame.YSplitOperator.Custom = item.Custom; + if (IFrameSplitOperator.IsNumber(item.SplitType)) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitType = item.SplitType; + if (IFrameSplitOperator.IsNumber(item.FloatPrecision)) chart.Frame.SubFrame[i].Frame.YSplitOperator.FloatPrecision = item.FloatPrecision; //强制指定小数位数(主图有效) + if (IFrameSplitOperator.IsNumber(item.BorderLine)) chart.Frame.SubFrame[i].Frame.BorderLine=item.BorderLine; + if (IFrameSplitOperator.IsNumber(item.YTextBaseline)) chart.Frame.SubFrame[i].Frame.YTextBaseline=item.YTextBaseline; + } + } + + if (option.KLine) + { + if (option.KLine.ShowKLine == false) chart.ChartPaint[0].IsShow = false; + if (option.KLine.IsShowMaxMinPrice == false) chart.ChartPaint[0].IsShowMaxMinPrice = false; + } + + if (option.KLineTitle) + { + if (option.KLineTitle.IsShowName == false) chart.TitlePaint[0].IsShowName = false; + if (option.KLineTitle.IsShowSettingInfo == false) chart.TitlePaint[0].IsShowSettingInfo = false; + if (option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false; + if (option.KLineTitle.UpdateUICallback) chart.TitlePaint[0].UpdateUICallback = option.KLineTitle.UpdateUICallback + if (option.KLineTitle.LineCount > 1) chart.TitlePaint[0].LineCount = option.KLineTitle.LineCount; + } + + //叠加股票 只支持叠加1个股票 + for (var i in option.Overlay) + { + var item = option.Overlay[i]; + if (item.Symbol) + { + chart.OverlayChartPaint[0].Symbol = item.Symbol; + if (item.Color) chart.OverlayChartPaint[0].Color=item.Color; + chart.OverlayChartPaint[0].SetOption(item); + break; + } + } + + if (option.ExtendChart) //创建扩展画法 + { + for (var i in option.ExtendChart) + { + var item = option.ExtendChart[i]; + chart.CreateExtendChart(item.Name, item); + } + } + + let scriptData = new JSCommonIndexScript.JSIndexScript(); //系统指标 + + if (option.ColorIndex) //五彩K线 + { + var item = option.ColorIndex; + let indexInfo = scriptData.Get(item.Index); + if (indexInfo) chart.ColorIndex = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args, indexInfo); //脚本执行 + } + + if (option.TradeIndex) //交易指标 + { + var item = option.TradeIndex; + let indexInfo = scriptData.Get(item.Index); + if (indexInfo) chart.TradeIndex = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args, indexInfo); //脚本执行 + } + + for (var i in option.Windows) //创建子窗口的指标 + { + var item = option.Windows[i]; + if (item.Script) + { + chart.WindowIndex[i] = new ScriptIndex(item.Name, item.Script, item.Args, item); //脚本执行 + } + else if (item.API) //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 } + { + var apiItem = item.API; + chart.WindowIndex[i] = new APIScriptIndex(apiItem.Name, apiItem.Script, apiItem.Args, item); + } + else + { + var indexItem = JSIndexMap.Get(item.Index); //自定义指标 + if (indexItem) + { + chart.WindowIndex[i] = indexItem.Create(); + chart.CreateWindowIndex(i); + } + else //系统指标里查找 + { + let indexInfo = scriptData.Get(item.Index); + if (!indexInfo) continue; + + if (item.Lock) indexInfo.Lock = item.Lock; + indexInfo.ID = item.Index; + var args = indexInfo.Args; + if (item.Args) args = item.Args; + chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, args, indexInfo); //脚本执行 + if (item.StringFormat > 0) chart.WindowIndex[i].StringFormat = item.StringFormat; + if (item.FloatPrecision >= 0) chart.WindowIndex[i].FloatPrecision = item.FloatPrecision; + } + } + + if (item.Modify != null) chart.Frame.SubFrame[i].Frame.ModifyIndex = item.Modify; + if (item.Change != null) chart.Frame.SubFrame[i].Frame.ChangeIndex = item.Change; + if (item.IsDrawTitleBG==true) chart.Frame.SubFrame[i].Frame.IsDrawTitleBG=item.IsDrawTitleBG; + if (typeof (item.UpdateUICallback) == 'function') chart.WindowIndex[i].UpdateUICallback = item.UpdateUICallback; + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight = item.TitleHeight; + if (item.IsShowIndexName == false) chart.Frame.SubFrame[i].Frame.IsShowIndexName = false; + if (item.IndexParamSpace >= 0) chart.Frame.SubFrame[i].Frame.IndexParamSpace = item.IndexParamSpace; + } + + return chart; + } + + //自定义指数历史K线图 + this.CreateCustomKLineChartContainer = function (option) { + var chart = new CustomKLineChartContainer(this.CanvasElement); + + if (option.KLine) //k线图的属性设置 + { + if (option.KLine.DragMode >= 0) chart.DragMode = option.KLine.DragMode; + if (option.KLine.Right >= 0) chart.Right = option.KLine.Right; + if (option.KLine.Period >= 0) chart.Period = option.KLine.Period; + if (option.KLine.MaxReqeustDataCount > 0) chart.MaxReqeustDataCount = option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length > 0) chart.SetKLineInfo(option.KLine.Info, false); + if (option.KLine.KLineDoubleClick == false) chart.MinuteDialog = this.MinuteDialog = null; + if (option.KLine.PageSize > 0) chart.PageSize = option.KLine.PageSize; + if (option.KLine.IsShowTooltip == false) chart.IsShowTooltip = false; + } + + if (option.CustomStock) chart.CustomStock = option.CustomStock; + if (option.QueryDate) chart.QueryDate = option.QueryDate; + if (typeof (option.UpdateUICallback) == 'function') chart.UpdateUICallback = option.UpdateUICallback; + + if (!option.Windows || option.Windows.length <= 0) return null; + + //创建子窗口 + chart.Create(option.Windows.length); + + if (option.Border) { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + + } + + if (option.IsShowCorssCursorInfo == false) //取消显示十字光标刻度信息 + { + chart.ChartCorssCursor.IsShowText = option.IsShowCorssCursorInfo; + } + + if (option.Frame) { + for (var i in option.Frame) { + var item = option.Frame[i]; + if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = item.SplitCount; + if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat = item.StringFormat; + if (item.XMessageAlign == 'bottom') chart.Frame.SubFrame[i].Frame.XMessageAlign = item.XMessageAlign; + } + } + + if (option.KLineTitle) { + if (option.KLineTitle.IsShowName == false) chart.TitlePaint[0].IsShowName = false; + if (option.KLineTitle.IsShowSettingInfo == false) chart.TitlePaint[0].IsShowSettingInfo = false; + } + + //创建子窗口的指标 + let scriptData = new JSCommonIndexScript.JSIndexScript(); + for (var i in option.Windows) { + var item = option.Windows[i]; + if (item.Script) { + chart.WindowIndex[i] = new ScriptIndex(item.Name, item.Script, item.Args, item); //脚本执行 + } + else { + var indexItem = JSIndexMap.Get(item.Index); + if (indexItem) { + chart.WindowIndex[i] = indexItem.Create(); + chart.CreateWindowIndex(i); + } + else //系统指标里查找 + { + let indexInfo = scriptData.Get(item.Index); + if (!indexInfo) continue; + + if (item.Lock) indexInfo.Lock = item.Lock; + chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args, indexInfo); //脚本执行 + } + } + + if (item.Modify != null) + chart.Frame.SubFrame[i].Frame.ModifyIndex = item.Modify; + if (item.Change != null) + chart.Frame.SubFrame[i].Frame.ChangeIndex = item.Change; + + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight = item.TitleHeight; + } + + return chart; + } + + //分钟走势图 + this.CreateMinuteChartContainer = function (option) + { + var chart = null; + if (option.Type === "分钟走势图横屏") chart = new MinuteChartHScreenContainer(this.CanvasElement); + else chart = new MinuteChartContainer(this.CanvasElement); + if (option.NetworkFilter) chart.NetworkFilter = option.NetworkFilter; + + var windowsCount = 2; + if (option.Windows && option.Windows.length > 0) windowsCount += option.Windows.length; //指标窗口从第3个窗口开始 + if (option.EnableScrollUpDown==true) chart.EnableScrollUpDown=option.EnableScrollUpDown; + + if (option.Info && option.Info.length > 0) chart.SetMinuteInfo(option.Info, false); + if (IFrameSplitOperator.IsString(option.SplashTitle)) chart.LoadDataSplashTitle = option.SplashTitle; //设置提示信息内容 + if (IFrameSplitOperator.IsBool(option.EnableZoomIndexWindow)) chart.EnableZoomIndexWindow=option.EnableZoomIndexWindow; //双击缩放附图 + if (IFrameSplitOperator.IsNumber(option.DrawMoveWaitTime)) chart.DrawMoveWaitTime=option.DrawMoveWaitTime; + + if (option.Language) + { + if (option.Language === 'CN') chart.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + else if (option.Language === 'EN') chart.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID; + } + + chart.Create(windowsCount); //创建子窗口 + + if (option.CorssCursorTouchEnd == true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsFullDraw == true) chart.IsFullDraw = option.IsFullDraw; + if (option.CorssCursorInfo) //十字光标设置 + { + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left = option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right = option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom = option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss === false) chart.ChartCorssCursor.IsShowCorss = option.CorssCursorInfo.IsShowCorss; + if (IFrameSplitOperator.IsBool(option.CorssCursorInfo.IsFixXLastTime)) chart.ChartCorssCursor.IsFixXLastTime=option.CorssCursorInfo.IsFixXLastTime; + } + + if (option.MinuteInfo) chart.CreateMinuteInfo(option.MinuteInfo); + if (option.DayCount > 1) chart.DayCount = option.DayCount; + + if (option.Border) + { + var item=option.Border; + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + + if (item.AutoLeft) chart.Frame.AutoLeftBorder=item.AutoLeft; + if (item.AutoRight) chart.Frame.AutoRightBorder=item.AutoRight; + } + + if (option.Frame) + { + for (var i in option.Frame) + { + var item = option.Frame[i]; + if (!chart.Frame.SubFrame[i]) continue; + if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = item.SplitCount; + if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat = item.StringFormat; + if (item.XMessageAlign == 'bottom') chart.Frame.SubFrame[i].Frame.XMessageAlign = item.XMessageAlign; + if (item.IsShowLeftText === false || item.IsShowLeftText===true ) chart.Frame.SubFrame[i].Frame.IsShowYText[0] = item.IsShowLeftText; //显示左边刻度 + if (item.IsShowRightText === false || item.IsShowRightText===true) chart.Frame.SubFrame[i].Frame.IsShowYText[1] = item.IsShowRightText; //显示右边刻度 + if (item.Height >= 0) chart.Frame.SubFrame[i].Height = item.Height; + if (item.Custom) chart.Frame.SubFrame[i].Frame.YSplitOperator.Custom = item.Custom; + if (IFrameSplitOperator.IsNumber(item.BorderLine)) chart.Frame.SubFrame[i].Frame.BorderLine=item.BorderLine; + if (IFrameSplitOperator.IsNumber(item.YTextBaseline)) chart.Frame.SubFrame[i].Frame.YTextBaseline=item.YTextBaseline; + } + + chart.UpdateXShowText(); + } + + if (option.MinuteTitle) + { + if (option.MinuteTitle.IsShowName == false) chart.TitlePaint[0].IsShowName = false; + if (option.MinuteTitle.IsShow == false) chart.TitlePaint[0].IsShow = false; + if (option.MinuteTitle.UpdateUICallback) chart.TitlePaint[0].UpdateUICallback = option.MinuteTitle.UpdateUICallback + if (option.MinuteTitle.LineCount > 1) chart.TitlePaint[0].LineCount = option.MinuteTitle.LineCount; + } + + if (typeof (option.UpdateUICallback) == 'function') //数据到达回调 + chart.UpdateUICallback = option.UpdateUICallback; + + if (option.ExtendChart) //创建扩展画法 + { + for (var i in option.ExtendChart) { + var item = option.ExtendChart[i]; + chart.CreateExtendChart(item.Name, item); + } + } + + //叠加股票 只支持1只股票 + for (var i in option.Overlay) + { + var item = option.Overlay[i]; + if (item.Symbol) + { + chart.OverlayChartPaint[0].Symbol = item.Symbol; + if (item.Color) chart.OverlayChartPaint[0].Color = item.Color; + break; + } + } + if (option.Overlay && option.Overlay.length) + { + chart.OverlayChartPaint[0].Symbol = option.Overlay[0].Symbol; + } + + if (option.MinuteLine) + { + if (option.MinuteLine.IsDrawAreaPrice == false) chart.ChartPaint[0].IsDrawArea = false; + if (option.MinuteLine.IsShowAveragePrice == false) + { + chart.ChartPaint[1].IsShow = false; + chart.TitlePaint[0].IsShowAveragePrice=false; //标题栏均线也不显示 + for(var i in chart.ExtendChartPaint) + { + var item=chart.ExtendChartPaint[i]; + if (item.ClassName=="MinuteTooltipPaint") item.IsShowAveragePrice=false; + } + } + + if (option.MinuteLine.SplitType>0) chart.Frame.SubFrame[0].Frame.YSplitOperator.SplitType=option.MinuteLine.SplitType; + } + + let scriptData = new JSCommonIndexScript.JSIndexScript(); + for (var i in option.Windows) //分钟数据指标从第3个指标窗口设置 + { + var item = option.Windows[i]; + if (item.Script) + { + chart.WindowIndex[2 + parseInt(i)] = new ScriptIndex(item.Name, item.Script, item.Args, item); //脚本执行 + } + else + { + var indexItem = JSIndexMap.Get(item.Index); + if (indexItem) + { + chart.WindowIndex[2 + parseInt(i)] = indexItem.Create(); //创建子窗口的指标 + chart.CreateWindowIndex(2 + parseInt(i)); + } + else + { + let indexInfo = scriptData.Get(item.Index); + if (!indexInfo) continue; + var args = indexInfo.Args; + if (item.Args) args = item.Args; + if (item.Lock) indexInfo.Lock = item.Lock; + chart.WindowIndex[2 + parseInt(i)] = new ScriptIndex(indexInfo.Name, indexInfo.Script, args, indexInfo); //脚本执行 + if (item.StringFormat > 0) chart.WindowIndex[2 + parseInt(i)].StringFormat = item.StringFormat; + if (item.FloatPrecision >= 0) chart.WindowIndex[2 + parseInt(i)].FloatPrecision = item.FloatPrecision; + } + } + + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[2 + parseInt(i)].Frame.ChartBorder.TitleHeight = item.TitleHeight; //指标标题高度 + } + + return chart; + } + + //历史分钟走势图 + this.CreateHistoryMinuteChartContainer = function (option) { + var chart = new HistoryMinuteChartContainer(this.CanvasElement); + + var windowsCount = 2; + if (option.Windows && option.Windows.length > 0) windowsCount += option.Windows.length; //指标窗口从第3个窗口开始 + + chart.Create(windowsCount); //创建子窗口 + + if (option.IsShowCorssCursorInfo == false) //取消显示十字光标刻度信息 + { + chart.ChartCorssCursor.IsShowText = option.IsShowCorssCursorInfo; + } + + if (option.Border) { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + } + + if (option.Frame) { + for (var i in option.Frame) { + var item = option.Frame[i]; + if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = item.SplitCount; + if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat = item.StringFormat; + if (item.XMessageAlign == 'bottom') chart.Frame.SubFrame[i].Frame.XMessageAlign = item.XMessageAlign; + } + } + + let scriptData = new JSCommonIndexScript.JSIndexScript(); + for (var i in option.Windows) //分钟数据指标从第3个指标窗口设置 + { + var item = option.Windows[i]; + if (item.Script) { + chart.WindowIndex[2 + parseInt(i)] = new ScriptIndex(item.Name, item.Script, item.Args, item); //脚本执行 + } + else { + var indexItem = JSIndexMap.Get(item.Index); + if (indexItem) { + chart.WindowIndex[2 + parseInt(i)] = indexItem.Create(); //创建子窗口的指标 + chart.CreateWindowIndex(2 + parseInt(i)); + } + else { + let indexInfo = scriptData.Get(item.Index); + if (!indexInfo) continue; + + if (item.Lock) indexInfo.Lock = item.Lock; + chart.WindowIndex[2 + parseInt(i)] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args, indexInfo); //脚本执行 + } + } + + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[2 + parseInt(i)].Frame.ChartBorder.TitleHeight = item.TitleHeight; + } + + chart.TradeDate = 20181009; + if (option.HistoryMinute) { + if (option.HistoryMinute.TradeDate) chart.TradeDate = option.HistoryMinute.TradeDate; + if (option.HistoryMinute.IsShowName != null) chart.TitlePaint[0].IsShowName = option.HistoryMinute.IsShowName; //动态标题是否显示股票名称 + if (option.HistoryMinute.IsShowDate != null) chart.TitlePaint[0].IsShowDate = option.HistoryMinute.IsShowDate; //动态标题是否显示日期 + } + + return chart; + } + + this.CreateKLineTrainChartContainer = function (option) + { + var bHScreen = (option.Type == 'K线训练横屏' ? true : false); + var chart = new KLineTrainChartContainer(this.CanvasElement, bHScreen); + + if (option.NetworkFilter) chart.NetworkFilter = option.NetworkFilter; + if (option.IsApiPeriod == true) chart.IsApiPeriod = option.IsApiPeriod; + + if (option.KLine) //k线图的属性设置 + { + if (option.KLine.Right >= 0) chart.Right = option.KLine.Right; + if (option.KLine.Period >= 0) chart.Period = option.KLine.Period; + if (option.KLine.MaxReqeustDataCount > 0) chart.MaxReqeustDataCount = option.KLine.MaxReqeustDataCount; + if (option.KLine.Info && option.KLine.Info.length > 0) chart.SetKLineInfo(option.KLine.Info, false); + if (option.KLine.PageSize > 0) chart.PageSize = option.KLine.PageSize; + if (option.KLine.IsShowTooltip == false) chart.IsShowTooltip = false; + if (option.KLine.MaxRequestMinuteDayCount > 0) chart.MaxRequestMinuteDayCount = option.KLine.MaxRequestMinuteDayCount; + if (option.KLine.DrawType) chart.KLineDrawType = option.KLine.DrawType; + } + + if (option.Train) + { + if (option.Train.DataCount) chart.TrainDataCount = option.Train.DataCount; + if (option.Train.Callback) chart.TrainCallback = option.Train.Callback; + if (option.Train.StartDate) chart.TrainStartDate = option.Train.StartDate; + } + + if (!option.Windows || option.Windows.length <= 0) return null; + + //创建子窗口 + chart.Create(option.Windows.length); + + if (option.Border) + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + } + + if (option.ExtendChart) //创建扩展画法 + { + for (var i in option.ExtendChart) + { + var item = option.ExtendChart[i]; + chart.CreateExtendChart(item.Name, item); + } + } + + if (option.IsShowCorssCursorInfo == false) chart.ChartCorssCursor.IsShowText = option.IsShowCorssCursorInfo;//取消显示十字光标刻度信息 + if (option.CorssCursorTouchEnd == true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + if (option.IsClickShowCorssCursor == true) chart.IsClickShowCorssCursor = option.IsClickShowCorssCursor; + if (option.IsFullDraw == true) chart.IsFullDraw = option.IsFullDraw; + + if (option.CorssCursorInfo) + { + if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left = option.CorssCursorInfo.Left; + if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right = option.CorssCursorInfo.Right; + if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom = option.CorssCursorInfo.Bottom; + if (option.CorssCursorInfo.IsShowCorss === false) chart.ChartCorssCursor.IsShowCorss = option.CorssCursorInfo.IsShowCorss; + if (option.CorssCursorInfo.IsShowClose == true) chart.ChartCorssCursor.IsShowClose = option.CorssCursorInfo.IsShowClose; //Y轴显示收盘价 + if (option.CorssCursorInfo.HPenType > 0) chart.ChartCorssCursor.HPenType = option.CorssCursorInfo.HPenType; + if (option.CorssCursorInfo.VPenType > 0) chart.ChartCorssCursor.VPenType = option.CorssCursorInfo.VPenType; + if (IFrameSplitOperator.IsBool(option.CorssCursorInfo.IsFixXLastTime)) chart.ChartCorssCursor.IsFixXLastTime=option.CorssCursorInfo.IsFixXLastTime; + } + + if (option.Frame) + { + for (var i in option.Frame) + { + if (!chart.Frame.SubFrame[i]) continue; + var item = option.Frame[i]; + if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = item.SplitCount; + if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat = item.StringFormat; + if (item.Height>0) chart.Frame.SubFrame[i].Height = item.Height; + if (item.IsShowLeftText === false || item.IsShowLeftText === true) chart.Frame.SubFrame[i].Frame.IsShowYText[0] = item.IsShowLeftText; //显示左边刻度 + if (item.IsShowRightText === false || item.IsShowRightText === true) chart.Frame.SubFrame[i].Frame.IsShowYText[1] = item.IsShowRightText; //显示右边刻度 + } + } + + if (option.KLine) + { + if (option.KLine.ShowKLine == false) chart.ChartPaint[0].IsShow = false; + if (option.KLine.IsShowMaxMinPrice == false) chart.ChartPaint[0].IsShowMaxMinPrice = false; + } + + //股票名称 日期 周期都不显示 + chart.TitlePaint[0].IsShowName = false; + chart.TitlePaint[0].IsShowSettingInfo = false; + chart.TitlePaint[0].IsShowDateTime = false; + + //创建子窗口的指标 + let scriptData = new JSCommonIndexScript.JSIndexScript(); + for (var i in option.Windows) + { + var item = option.Windows[i]; + if (item.Script) + { + chart.WindowIndex[i] = new ScriptIndex(item.Name, item.Script, item.Args, item); //脚本执行 + } + else + { + let indexItem = JSIndexMap.Get(item.Index); + if (indexItem) + { + chart.WindowIndex[i] = indexItem.Create(); + chart.CreateWindowIndex(i); + } + else + { + let indexInfo = scriptData.Get(item.Index); + if (!indexInfo) continue; + + if (item.Lock) indexInfo.Lock = item.Lock; + chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args, indexInfo); //脚本执行 + } + + } + + if (item.Modify != null) chart.Frame.SubFrame[i].Frame.ModifyIndex = item.Modify; + if (item.Change != null) chart.Frame.SubFrame[i].Frame.ChangeIndex = item.Change; + if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight = item.TitleHeight; + } + + return chart; + } + + //深度图 + this.CreateDepthChartContainer=function(option) + { + var chart=null; + chart=new DepthChartContainer(this.CanvasElement); + if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter; + + if (option.EnableScrollUpDown==true) chart.EnableScrollUpDown=option.EnableScrollUpDown; + if (IFrameSplitOperator.IsPlusNumber(option.MaxVolRate)) chart.MaxVolRate=option.MaxVolRate; + if (option.ZoomStepPixel>0) chart.ZoomStepPixel=option.ZoomStepPixel; + + chart.Create(option.Listener); + + if (option.Border) + { + if (IFrameSplitOperator.IsNumber(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left; + else option.Border.Left=chart.Frame.ChartBorder.Left; + if (IFrameSplitOperator.IsNumber(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right; + else option.Border.Right=chart.Frame.ChartBorder.Right; + if (IFrameSplitOperator.IsNumber(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top; + else option.Border.Top=chart.Frame.ChartBorder.Top; + if (IFrameSplitOperator.IsNumber(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom; + else option.Border.Bottom=chart.Frame.ChartBorder.Bottom; + } + if (option.IsFullDraw == true) chart.IsFullDraw = option.IsFullDraw; + + if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd; + + if (option.CorssCursorInfo) + { + var item=option.CorssCursorInfo; + if (IFrameSplitOperator.IsNumber(item.HPenType)) chart.ChartCorssCursor.HPenType=item.HPenType; + if (IFrameSplitOperator.IsNumber(item.VPenType)) chart.ChartCorssCursor.VPenType=item.VPenType; + if (IFrameSplitOperator.IsBool(item.IsShowTooltip)) chart.ChartCorssCursor.IsShowTooltip=item.IsShowTooltip; + } + + if (option.Frame) + { + var item=option.Frame + if (item.SplitCount) chart.Frame.YSplitOperator.SplitCount=item.SplitCount; + if (IFrameSplitOperator.IsNumber(item.SplitType)) chart.Frame.YSplitOperator.SplitType=item.SplitType; + if (IFrameSplitOperator.IsNumber(item.Height)) chart.Frame.Height = item.Height; + if (IFrameSplitOperator.IsNumber(item.LineType)) chart.Frame.YSplitOperator.LineType=item.LineType; + if (Array.isArray(item.IgnoreYValue)) chart.Frame.YSplitOperator.IgnoreYValue=item.IgnoreYValue; + if (item.IsShowLeftText===false || item.IsShowLeftText===true) + { + chart.Frame.IsShowYText[0]=item.IsShowLeftText; + chart.Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText; //显示左边刻度 + } + if (item.IsShowRightText===false || item.IsShowRightText===true) + { + chart.Frame.IsShowYText[1]=item.IsShowRightText; + chart.Frame.YSplitOperator.IsShowRightText=item.IsShowRightText; //显示右边刻度 + } + + if (item.IsShowXLine==false) chart.Frame.IsShowXLine=item.IsShowXLine; + if (item.IsShowYLine==false) chart.Frame.IsShowYLine=item.IsShowYLine; + } + + return chart; + } + + //根据option内容绘制图形 + this.SetOption = function (option) + { + JSConsole.Chart.Log('[JSChart::SetOption]', option) + var chart = null; + switch (option.Type) + { + case "历史K线图": + case '历史K线图横屏': + chart = this.CreateKLineChartContainer(option); + break; + case "自定义指数历史K线图": + chart = this.CreateCustomKLineChartContainer(option); + break; + case "分钟走势图": + case "分钟走势图横屏": + chart = this.CreateMinuteChartContainer(option); + break; + case "历史分钟走势图": + chart = this.CreateHistoryMinuteChartContainer(option); + break; + case 'K线训练': + case 'K线训练横屏': + chart = this.CreateKLineTrainChartContainer(option); + break; + case "深度图": + chart=this.CreateDepthChartContainer(option); + break; + case "简单图形": + return this.CreateSimpleChart(option); + case '雷达图': + case "饼图": + return this.CreatePieChart(option); + case '地图': + return this.CreateMapChart(option); + default: + return false; + } + + if (!chart) return false; + + if (option.OnCreatedCallback) option.OnCreatedCallback(chart); + + //是否自动更新 + if (option.IsAutoUpdate == true || option.IsAutoUpate == true) chart.IsAutoUpdate = true; + if (option.AutoUpdateFrequency > 0) chart.AutoUpdateFrequency = option.AutoUpdateFrequency; + + //注册事件 + for(var i in option.EventCallback) + { + var item=option.EventCallback[i]; + chart.AddEventCallback(item); + } + + //设置股票代码 + if (!option.Symbol) return false; + this.JSChartContainer = chart; + + chart.Draw(); + chart.ChangeSymbol(option.Symbol); + + this.JSChartContainer.Draw(); + + if (!IFrameSplitOperator.IsBool(option.CheckLatestVerion) || !(option.CheckLatestVerion==false)) + { + if (chart && this.CanvasElement.IsUniApp && typeof(chart.GetLatestVersion)=='function') chart.GetLatestVersion(); + } + } + + //切换股票代码接口 + this.ChangeSymbol = function (symbol) + { + if (this.JSChartContainer) this.JSChartContainer.ChangeSymbol(symbol); + } + + //K线切换指标 + this.ChangeIndex = function (windowIndex, indexName, option) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeIndex) == 'function') + this.JSChartContainer.ChangeIndex(windowIndex, indexName, option); + } + + //切换K线指标 + this.ChangeScriptIndex = function (windowIndex, indexData) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeScriptIndex) == 'function') + this.JSChartContainer.ChangeScriptIndex(windowIndex, indexData); + } + + //获取当前显示的指标信息 + this.GetIndexInfo = function () + { + if (this.JSChartContainer && typeof (this.JSChartContainer.GetIndexInfo) == 'function') + return this.JSChartContainer.GetIndexInfo(); + else + return []; + } + + //K线周期切换 + this.ChangePeriod = function (period, option) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangePeriod) == 'function') + this.JSChartContainer.ChangePeriod(period, option); + } + + //切换系统指示 + this.ChangeInstructionIndex = function (indexName) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeInstructionIndex) == 'function') + this.JSChartContainer.ChangeInstructionIndex(indexName); + } + + //切换自定义指示 + this.ChangeInstructionScriptIndex = function (indexData) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeInstructionIndex) == 'function') + this.JSChartContainer.ChangeInstructionScriptIndex(indexData); + } + + //增加一个指标窗口 + this.AddIndexWindow=function(indexName,option) + { + if (this.JSChartContainer && typeof(this.JSChartContainer.AddIndexWindow)=='function') + this.JSChartContainer.AddIndexWindow(indexName,option); + } + + //删除一个指标窗口 + this.RemoveIndexWindow=function(id) + { + if (this.JSChartContainer && typeof(this.JSChartContainer.RemoveIndexWindow)=='function') + this.JSChartContainer.RemoveIndexWindow(id); + } + + //取消指示 + this.CancelInstructionIndex = function () + { + if (this.JSChartContainer && typeof (this.JSChartContainer.CancelInstructionIndex) == 'function') + this.JSChartContainer.CancelInstructionIndex(); + } + + //切换指标模板 + this.ChangeIndexTemplate = function (option) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeIndexTemplate) == 'function') { + JSConsole.Chart.Log('[JSChart:ChangeIndexTemplate] ', option); + this.JSChartContainer.ChangeIndexTemplate(option); + } + } + + //K线复权切换 + this.ChangeRight = function (right) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeRight) == 'function') + this.JSChartContainer.ChangeRight(right); + } + + //K线切换类型 0=实心K线 1=收盘价线 2=美国线 3=空心K线 + this.ChangeKLineDrawType = function (drawType) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeKLineDrawType) == 'function') + this.JSChartContainer.ChangeKLineDrawType(drawType); + } + + //切换数据类 + this.ChangMainDataControl = function (dataControl) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.SetMainDataConotrl) == 'function') + this.JSChartContainer.SetMainDataConotrl(dataControl); + } + + //叠加股票 + this.OverlaySymbol = function (symbol,option) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.OverlaySymbol) == 'function') + this.JSChartContainer.OverlaySymbol(symbol, option); + } + + //设置强制横屏 + this.ForceLandscape = function (bForceLandscape) { + if (this.JSChartContainer) { + JSConsole.Chart.Log("[JSChart::ForceLandscape] bForceLandscape=" + bForceLandscape); + this.JSChartContainer.IsForceLandscape = bForceLandscape; + } + } + + //锁|解锁指标 + this.LockIndex = function (lockData) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.LockIndex) == 'function') + { + JSConsole.Chart.Log('[JSChart:LockIndex] lockData', lockData); + this.JSChartContainer.LockIndex(lockData); + } + } + + //历史分钟数据 更改日期 + this.ChangeTradeDate = function (tradeDate) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeTradeDate) == 'function') + { + JSConsole.Chart.Log('[JSChart:ChangeTradeDate] date', tradeDate); + this.JSChartContainer.ChangeTradeDate(tradeDate); + } + } + + //多日走势图 + this.ChangeDayCount = function (count) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChangeDayCount) == 'function') + { + JSConsole.Chart.Log('[JSChart:ChangeDayCount] count', count); + this.JSChartContainer.ChangeDayCount(count); + } + } + + this.StopAutoUpdate = function () + { + if (this.JSChartContainer && typeof (this.JSChartContainer.StopAutoUpdate) == 'function') { + JSConsole.Chart.Log("[JSChart::StopAutoUpdate] Stop."); + this.JSChartContainer.StopAutoUpdate(); + } + } + this.StopAutoUpdata = this.StopAutoUpdate; + + this.ChartDestory=function() + { + if (this.JSChartContainer && typeof (this.JSChartContainer.ChartDestory) == 'function') { + JSConsole.Chart.Log("[JSChart::ChartDestory]"); + this.JSChartContainer.ChartDestory(); + } + } + + this.CreateSimpleChart = function (option) + { + var chart = new SimlpleChartContainer(this.CanvasElement); + if (option.MainDataControl) chart.MainDataControl = option.MainDataControl; + if (option.FrameType > 0) chart.FrameType = option.FrameType; + if (!isNaN(option.SplitCount)) chart.YSplitCount = option.SplitCount; + + chart.Create(); + + if (option.Border) //边框设置 + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + if (!isNaN(option.Border.TitleHeight)) chart.Frame.ChartBorder.TitleHeight = option.Border.TitleHeight; + } + + if (option.XFontType) chart.Frame.XFontType = option.XFontType; + + if (option.Frame) + { + if (option.Frame[0].MaxDistanceWidth) chart.Frame.MaxDistanceWidth = option.Frame[0].MaxDistanceWidth; + if (option.Frame[0].IsShowBorder != null) chart.Frame.IsShowBorder = option.Frame[0].IsShowBorder; + if (option.Frame[0].StringFormat) chart.Frame.YSplitOperator.StringFormat = option.Frame[0].StringFormat; + if (option.Frame[0].FloatPrecision >= 0) chart.Frame.YSplitOperator.FloatPrecision = option.Frame[0].FloatPrecision; + if (option.Frame[0].IgnoreYValue) chart.Frame.YSplitOperator.IgnoreYValue = option.Frame[0].IgnoreYValue; + } + + chart.Draw(); + chart.RequestData(); + + this.JSChartContainer = chart; + this.JSChartContainer.Draw(); + } + + //创建饼图 + this.CreatePieChart = function (option) { + var chart = new PieChartContainer(this.CanvasElement); + if (option.MainDataControl) chart.MainDataControl = option.MainDataControl; + + if (option.Radius) chart.Radius = option.Radius; + + chart.Create(); + + if (option.Border) //边框设置 + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + } + + if (option.Frame) { + if (option.Frame[0].IsShowBorder == false) chart.Frame.IsShowBorder = option.Frame[0].IsShowBorder; + } + + chart.Draw(); + chart.RequestData(); + + this.JSChartContainer = chart; + this.JSChartContainer.Draw(); + + } + + //中国地图 + this.CreateMapChart = function (option) { + var chart = new MapChartContainer(this.CanvasElement); + if (option.MainDataControl) chart.MainDataControl = option.MainDataControl; + + chart.Create(); + + if (option.Border) //边框设置 + { + if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left = option.Border.Left; + if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right = option.Border.Right; + if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top = option.Border.Top; + if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom = option.Border.Bottom; + } + + if (option.Frame) { + if (option.Frame[0].IsShowBorder == false) chart.Frame.IsShowBorder = option.Frame[0].IsShowBorder; + } + + chart.Draw(); + chart.RequestData(); + + this.JSChartContainer = chart; + this.JSChartContainer.Draw(); + } + + this.OnTouchStart = function (e) { + if (this.JSChartContainer) this.JSChartContainer.ontouchstart(e); + } + + this.OnTouchMove = function (e) { + if (this.JSChartContainer) this.JSChartContainer.ontouchmove(e); + } + + this.OnTouchEnd = function (e) + { + if (this.JSChartContainer) this.JSChartContainer.ontouchend(e); + } + + this.SaveToImage = function (callback) + { + if (this.JSChartContainer && typeof (this.JSChartContainer.SaveToImage) == 'function') + this.JSChartContainer.SaveToImage(callback); + } + + this.EnableSplashScreen=function(option) + { + if(this.JSChartContainer && typeof(this.JSChartContainer.EnableSplashScreen)=='function') + this.JSChartContainer.EnableSplashScreen(option); + } + +} + +//初始化 +JSChart.Init = function (uielement) { + JSConsole.Chart.Log('[JSChart.Init] uielement', uielement); + var jsChartControl = new JSChart(uielement); + jsChartControl.OnSize(); + + return jsChartControl; +} + +JSChart.SetDomain = function (domain, cacheDomain) { + if (domain) { + g_JSChartResource.Domain = domain; + + g_JSChartResource.Index.StockHistoryDayApiUrl = domain + "/API/StockHistoryDay"; //历史数据api + g_JSChartResource.Index.MarketLongShortApiUrl = domain + "/API/FactorTiming"; //市场多空 + g_JSChartResource.Index.MarketAttentionApiUrl = domain + "/API/MarketAttention"; //市场关注度 + g_JSChartResource.Index.MarketHeatApiUrl = domain + "/API/MarketHeat"; //行业,指数热度 + + } + + if (cacheDomain) g_JSChartResource.CacheDomain = cacheDomain; + + JSCommonComplier.JSComplier.SetDomain(domain, cacheDomain); //编译器数据api域名修改 +} + +//自定义风格 +JSChart.SetStyle = function (style) { + if (style) g_JSChartResource.SetStyle(style); +} + +JSChart.GetResource = function () //获取颜色配置 (设置配必须啊在JSChart.Init()之前) +{ + return g_JSChartResource; +} + +JSChart.GetKLineZoom = function () //K线缩放配置 +{ + return ZOOM_SEED; +} + +JSChart.GetChinaFuturesTimeData=function() //获取国内期货交易时间配置 +{ + return g_FuturesTimeData; +} + +JSChart.GetInternalTimeData=function(name) //内置品种交易时间 +{ + switch(name) + { + case "NYMEXTimeData": //纽约商业交易所 + return g_NYMEXTimeData; + case "COMEXTimeData": //纽约商品交易所 + return g_COMEXTimeData; + case "NYBOTTimeData": //纽约期货交易所 + return g_NYBOTTimeData; + case "CBOTTimeData": //芝加哥期货交易所 + return g_CBOTTimeData; + case "LMETimeData": //伦敦金属交易所 + return g_LMETimeData; + case "FuturesTimeData": //国内期货 + return g_FuturesTimeData; + default: + return null; + } +} + +JSChart.GetDivTooltipDataFormat=function() //div tooltip数据格式化 +{ + return g_DivTooltipDataForamt; +} + +var JSCHART_OPERATOR_ID = +{ + OP_SCROLL_LEFT: 1, + OP_SCROLL_RIGHT: 2, + OP_ZOOM_OUT: 3, //缩小 + OP_ZOOM_IN: 4, //放大 + OP_GOTO_HOME: 5, //第1页数据 +} + +/* + 图形控件 +*/ +function JSChartContainer(uielement) +{ + this.ClassName = 'JSChartContainer'; + var _self = this; + this.Frame; //框架画法 + this.ChartPaint = new Array(); //图形画法 + this.ChartPaintEx = []; //图形扩展画法 + this.ChartInfo = new Array(); //K线上信息地雷 + this.ChartInfoPaint; //信息地理 + this.ExtendChartPaint = new Array(); //扩展画法 + this.TitlePaint = new Array(); //标题画法 + this.OverlayChartPaint = new Array(); //叠加信息画法 + this.ChartDrawPicture = new Array(); //画图工具 + this.CurrentChartDrawPicture = null; //当前的画图工具 + this.SelectChartDrawPicture = null; //当前选中的画图 + this.ChartCorssCursor; //十字光标 + this.IsClickShowCorssCursor = false; //手势点击显示十字光标 + this.ChartSplashPaint = null; //等待提示 + this.LoadDataSplashTitle = '数据加载中'; + this.Canvas = uielement.GetContext("2d"); //画布 + this.UIElement = uielement; + this.MouseDrag; + this.DragMode = 1; //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择 + this.PhoneTouchInfo; //手机手势信息 {Start:起始点, End:结束点} + this.EnableScrollUpDown=false; //是否可以上下滚动图形(手机端才有) + + this.TouchTimer = null; //触屏定时器 + this.LastDrawStatus; //最后一次画的状态 + this.LastDrawID=1; //最后一次画的ID + this.SnapshotType = 0; + + this.CursorIndex = 0; //十字光标X轴索引 + this.LastPoint = new Point(); //鼠标位置 + this.IsForceLandscape = false; //是否强制横屏 + this.CorssCursorTouchEnd = false; //手离开屏幕自动隐藏十字光标 + this.EnableAnimation = false; //是否开启动画 + + //坐标轴风格方法 double-更加数值型分割 price-更加股票价格分割 + this.FrameSplitData = new Map(); + this.FrameSplitData.set("double", new SplitData()); + this.FrameSplitData.set("price", new PriceSplitData()); + + this.UpdateUICallback; //数据到达通知前端 + this.IsOnTouch = false; //当前是否正式手势操作 + this.IsFullDraw=false; //是否使用重绘模式 (可能会卡) + + this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID; + + //公共函数转发,不然要导出麻烦 + this.FormatDateString = IFrameSplitOperator.FormatDateString; + this.FormatValueString = IFrameSplitOperator.FormatValueString; + this.ToFixedPoint = ToFixedPoint; + this.ToFixedRect = ToFixedRect; + this.FormatTimeString = IFrameSplitOperator.FormatTimeString; + + //this.JSCHART_EVENT_ID = JSCHART_EVENT_ID; + + //事件回调 + this.mapEvent = new Map(); //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,} + this.NetworkFilter; //网络请求回调 function(data, callback); + this.IsDestroy=false; //是否已经销毁了 + + //手势移动 十字光标 + this.LastMovePoint; + this.DrawMoveTimer=null; + this.DrawMoveWaitTime=60; + + //双击缩放附图窗口 + this.EnableZoomIndexWindow=false; //是否支持双击缩放附图窗口 + this.PhoneDBClick=new PhoneDBClick(); + + this.ChartDestory=function() //销毁 + { + this.IsDestroy=true; + this.StopAutoUpdate(); + + if (this.GetLatestVersionTimer!=null) + { + clearTimeout(this.GetLatestVersionTimer); + this.GetLatestVersionTimer=null; + } + } + + this.GetLatestVersionTimer=null; //获取最新版本 + this.GetLatestVersion=function() + { + var waittimer=1000*60*3.5; + var value="aHR0cHM6Ly9ocWNoYXJ0LnplYWxpbmsuY29tL2FwaS9HZXRWZXJzaW9u"; + + this.GetLatestVersionTimer = setTimeout(()=> + { + var width=0, height=0; + if (this.Frame && this.Frame.ChartBorder) + { + width=this.Frame.ChartBorder.GetChartWidth(); + height=this.Frame.ChartBorder.GetChartHeight(); + } + + var url=`${atob(value)}?width=${width}&height=${height}&type=uniapp`; + + wx.request({ + url: url, + method:"get", + dataType: "json", + async:true, + success:function(data) + { + //TODO:判断是否是最新版本 + }, + fail:function(request, textStatus, errorThrown) + { + JSConsole.Chart.Log("[JSChartContainer::GetLatestVersion] Get HQChart latest version failed.", request); + } + }); + + }, waittimer); + } + + this.AddEventCallback = function (object) //设置事件回调 {event:事件id, callback:回调函数} + { + if (!object || !object.event || !object.callback) return; + + var data = { Callback: object.callback, Source: object }; + this.mapEvent.set(object.event, data); + } + + this.RemoveEventCallback = function (eventid) + { + if (!this.mapEvent.has(eventid)) return; + this.mapEvent.delete(eventid); + } + + this.GetEvent=function(eventId) + { + if (!this.mapEvent.has(eventId)) return null; + var item = this.mapEvent.get(eventId); + return item; + } + + this.GetEventCallback=this.GetEvent; + + this.GetIndexEvent = function () { return this.GetEvent(JSCHART_EVENT_ID.RECV_INDEX_DATA); } //接收指标数据 + this.GetBarrageEvent=function() { return this.GetEvent(JSCHART_EVENT_ID.BARRAGE_PLAY_END);} //获取弹幕事件 + this.GetEnableSplashEvent=function() { return this.GetEvent(JSCHART_EVENT_ID.ON_ENABLE_SPLASH_DRAW); } //开启/关闭文字提示信息事件 + + //判断是单个手指 + this.IsPhoneDragging = function (e) + { + // JSConsole.Chart.Log(e); + var changed = e.changedTouches.length; + var touching = e.touches.length; + + return changed == 1 && touching == 1; + } + + //是否是2个手指操所 + this.IsPhonePinching = function (e) + { + var changed = e.changedTouches.length; + var touching = e.touches.length; + + return (changed == 1 || changed == 2) && touching == 2; + } + + this.IsSingleTouch=function(e) //是否是单点触屏 + { + var touchCount=e.touches.length; + return touchCount==1; + } + + this.GetToucheData = function (e, isForceLandscape) + { + var touches = new Array(); + for (var i = 0; i < e.touches.length; ++i) + { + var item = e.touches[i]; + if (isForceLandscape) + { + touches.push( { clientX: item.y, clientY: item.x, pageX: item.y, pageY: item.x }); + } + else + { + touches.push( {clientX: item.x, clientY: item.y, pageX: item.x, pageY: item.y }); + } + } + + return touches; + } + + this.ClearDrawMoveTimer=function() + { + if (this.DrawMoveTimer != null) + { + clearTimeout(this.DrawMoveTimer); + this.DrawMoveTimer=null; + } + } + + this.ClearTouchTimer=function() + { + if (this.TouchTimer != null) + { + clearTimeout(this.TouchTimer); + this.TouchTimer=null; + } + } + + //手机拖拽 + this.ontouchstart = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var jsChart = this; + //if (jsChart.DragMode == 0) return; + + jsChart.IsOnTouch=true; + jsChart.PhonePinch = null; + + if (this.IsPhoneDragging(e)) + { + if (jsChart.TryClickLock || this.TryClickIndexTitle) + { + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + var x = touches[0].clientX; + var y = touches[0].clientY; + if (jsChart.TryClickLock && jsChart.TryClickLock(x, y)) return; + if (jsChart.TryClickIndexTitle && jsChart.TryClickIndexTitle(x, y)) return; + } + + //长按2秒,十字光标 + if (this.TouchTimer != null) clearTimeout(this.TouchTimer); + if (this.ChartCorssCursor.IsShow == true) + { + this.TouchTimer = setTimeout(function () { + if (drag.Click.X == drag.LastMove.X && drag.Click.Y == drag.LastMove.Y) //手指没有移动,出现十字光标 + { + var mouseDrag = jsChart.MouseDrag; + jsChart.MouseDrag = null; + //移动十字光标 + var x = drag.Click.X; + var y = drag.Click.Y; + if (jsChart.IsForceLandscape) y = jsChart.UIElement.Height - drag.Click.Y; //强制横屏Y计算 + jsChart.OnMouseMove(x, y, e); + } + + }, 800); + } + + var drag = + { + "Click": {}, + "LastMove": {}, //最后移动的位置 + }; + + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + + drag.Click.X = touches[0].clientX; + drag.Click.Y = touches[0].clientY; + drag.LastMove.X = touches[0].clientX; + drag.LastMove.Y = touches[0].clientY; + + if (jsChart.DragMode == 1) jsChart.MouseDrag = drag; + + this.PhoneTouchInfo={ Start:{X:touches[0].clientX, Y:touches[0].clientY }, End:{ X:touches[0].clientX, Y:touches[0].clientY } }; + + if (this.EnableZoomIndexWindow) + { + this.PhoneDBClick.AddTouchStart(touches[0].clientX, touches[0].clientY, Date.now()); + JSConsole.Chart.Log("[JSChartContainer::OnTouchStart] PhoneDBClick ", this.PhoneDBClick); + } + + if (jsChart.IsClickShowCorssCursor) + { + var x = drag.Click.X; + var y = drag.Click.Y; + jsChart.OnMouseMove(x, y, e,true); + } + + this.TouchEvent({ EventID:JSCHART_EVENT_ID.ON_PHONE_TOUCH, FunctionName:"OnTouchStart"}, e); + } + else if (this.IsPhonePinching(e)) + { + var phonePinch = { "Start": {}, "Last": {} }; + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + phonePinch.Start = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + phonePinch.Last = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + jsChart.PhonePinch = phonePinch; + } + } + + + this.ontouchmove = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var jsChart = this; + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + + if (this.IsPhoneDragging(e)) + { + var drag = jsChart.MouseDrag; + if (drag == null) + { + var x = touches[0].clientX; + var y = touches[0].clientY; + this.LastMovePoint={ X:x, Y:y }; + if (this.DrawMoveTimer) return; + this.DrawMoveTimer=setTimeout(()=> + { + if (!this.LastMovePoint) return; + this.OnMouseMove(this.LastMovePoint.X, this.LastMovePoint.Y, e); + this.DrawMoveTimer=null; + }, this.DrawMoveWaitTime); + + } + else + { + var moveSetp = Math.abs(drag.LastMove.X - touches[0].clientX); + moveSetp = parseInt(moveSetp); + if (jsChart.DragMode == 1) //数据左右拖拽 + { + if (moveSetp < 5) return; + var isLeft = true; + if (drag.LastMove.X < touches[0].clientX) isLeft = false;//右移数据 + + if (jsChart.DataMove(moveSetp, isLeft)) + { + jsChart.UpdataDataoffset(); + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + else + { + if (jsChart.DragDownloadData) jsChart.DragDownloadData(); + } + + drag.LastMove.X = touches[0].clientX; + drag.LastMove.Y = touches[0].clientY; + } + } + + if (this.PhoneTouchInfo) + { + this.PhoneTouchInfo.End.X=touches[0].clientX; + this.PhoneTouchInfo.End.Y=touches[0].clientY; + } + } + else if (this.IsPhonePinching(e)) + { + var phonePinch = jsChart.PhonePinch; + if (!phonePinch) return; + + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Touch===false) return; + + var yHeight = Math.abs(touches[0].pageY - touches[1].pageY); + var yLastHeight = Math.abs(phonePinch.Last.Y - phonePinch.Last.Y2); + var yStep = yHeight - yLastHeight; + var xHeight = Math.abs(touches[0].pageX - touches[1].pageX); + var xLastHeight = Math.abs(phonePinch.Last.X - phonePinch.Last.X2); + var xStep = xHeight - xLastHeight; + var minStep=this.ZoomStepPixel; + if (Math.abs(yStep) < minStep && Math.abs(xStep) < minStep) return; + + var step = yStep; + if (Math.abs(yStep) < minStep) step = xStep; + + if (step > 0) //放大 + { + var cursorIndex = {}; + cursorIndex.Index = parseInt(Math.abs(jsChart.CursorIndex - 0.5).toFixed(0)); + if (!jsChart.Frame.ZoomUp(cursorIndex)) return; + jsChart.CursorIndex = cursorIndex.Index; + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdataDataoffset(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + else //缩小 + { + var cursorIndex = {}; + cursorIndex.Index = parseInt(Math.abs(jsChart.CursorIndex - 0.5).toFixed(0)); + if (!jsChart.Frame.ZoomDown(cursorIndex)) return; + jsChart.CursorIndex = cursorIndex.Index; + jsChart.UpdataDataoffset(); + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + + phonePinch.Last = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + } + } + + this.ontouchend = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + if (this.EnableZoomIndexWindow) + { + var time=Date.now(); + this.PhoneDBClick.AddTouchEnd(time); + if (this.PhoneDBClick.IsVaildDBClick()) + { + this.OnTouchDBClick(this.PhoneDBClick.Start); + this.PhoneDBClick.Clear(); + } + } + + this.IsOnTouch = false; + this.LastMovePoint=null; + JSConsole.Chart.Log('[JSChartContainer:ontouchend] IsOnTouch=' + this.IsOnTouch +' LastDrawStatus=' + this.LastDrawStatus); + this.ClearDrawMoveTimer(); + this.ClearTouchTimer(); + this.TouchEvent({ EventID:JSCHART_EVENT_ID.ON_PHONE_TOUCH, FunctionName:"OnTouchEnd"}, e); + this.Draw();//手放开 重新绘制 + } + + this.OnTouchDBClick=function(points) + { + var x=points[0].X, y=points[0].Y; + JSConsole.Chart.Log('[JSChartContainer:OnTouchDBClick] Phone dbclick', x, y); + + var frameId=this.Frame.PtInFrame(x,y); + JSConsole.Chart.Log("[JSChartContainer::OnTouchDBClick] frameId",frameId); + if (frameId>=this.Frame.ZoomStartWindowIndex) + { + if (this.ZoomIndexWindow(frameId, {X:x, Y:y})) + { + this.Frame.SetSizeChage(true); + this.Draw(); + return true; + } + } + + return false; + } + + this.ZoomIndexWindow=function(frameID, option) //最大化/最小化指标窗口 + { + if (frameID<0 || frameID>=this.Frame.SubFrame.length) return false; + + return this.Frame.ZoomIndexWindow(frameID, option); + } + + this.TouchEvent=function(obj,e) + { + var eventID=obj.EventID; + var event=this.GetEvent(eventID); + if (!event || !event.Callback) return false; + + var drag=this.PhoneTouchInfo + if (!drag || !drag.Start || !drag.End ) return false; + + var clientX=drag.End.X; + var clientY=drag.End.Y; + var x=drag.End.X; + var y=drag.End.Y; + + var data= + { + X:clientX, Y:clientY, FrameID:-1, FunctionName:obj.FunctionName, + Drag: + { + Start:{ X:drag.Start.X, Y:drag.Start.Y }, + End:{ X:drag.End.X, Y:drag.End.Y } + } + }; + + var isInClient=false; + var rtClient = new Rect(this.Frame.ChartBorder.GetLeft(), this.Frame.ChartBorder.GetTop(), this.Frame.ChartBorder.GetWidth(), this.Frame.ChartBorder.GetHeight()); + isInClient = rtClient.IsPointIn(x, y); + + if (isInClient) + { + var yValueExtend={}; + var yValue=this.Frame.GetYData(y,yValueExtend); + + if (IFrameSplitOperator.IsNumber(yValueExtend.FrameID) && yValueExtend.FrameID>=0) + { + var xValue=this.Frame.GetXData(x); + data.FrameID=yValueExtend.FrameID; + data.Data={ X:xValue, Y:yValue } ; + } + } + + event.Callback(event, data, this); + return true; + } + + this.FullDraw=function(drawType) + { + var self = this; + this.Canvas.clearRect(0, 0, this.UIElement.Width, this.UIElement.Height); + + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) //动画 + { + this.Frame.ClearCoordinateText(); + this.Frame.Draw(); //框架 + this.ChartSplashPaint.Draw(); + this.LastDrawStatus = 'FullDraw'; + this.Canvas.draw(); + return; + } + + this.Frame.SetDrawOtherChart(() => + { + for (var i in this.ExtendChartPaint) + { + var item = this.ExtendChartPaint[i]; + if (item.IsCallbackDraw) item.Draw(); + } + }); + + this.Frame.Draw(); //框架 + + if (this.Frame.DrawCustomVertical) + { + var eventCVericalDraw = this.GetEvent(JSCHART_EVENT_ID.ON_CUSTOM_VERTICAL_DRAW); + this.Frame.DrawCustomVertical(eventCVericalDraw); + } + + for (var i in this.ChartPaint) //图形 + { + var item = this.ChartPaint[i]; + if (item.IsDrawFirst) item.Draw(); + } + + for (var i in this.ChartPaint) //图形2 框架内图形 + { + var item = this.ChartPaint[i]; + if (!item.IsDrawFirst) item.Draw(); + } + + for (var i in this.ChartPaintEx) //扩展图形 + { + var item = this.ChartPaintEx[i]; + item.Draw(); + } + + for (var i in this.OverlayChartPaint) //叠加股票 + { + var item = this.OverlayChartPaint[i]; + item.Draw(); + } + + if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal(); //框架内部坐标 + if (this.Frame.DrawCustomHorizontal) this.Frame.DrawCustomHorizontal(); + if (this.ChartInfoPaint) this.ChartInfoPaint.Draw(); + this.Frame.DrawLock(); + + var bOnTouchDraw=drawType == 'DrawDynamicInfo' || this.IsOnTouch; + if (bOnTouchDraw) + { + if (self.ChartCorssCursor) //十字光标 + { + self.ChartCorssCursor.LastPoint = self.LastPoint; + self.ChartCorssCursor.CursorIndex = self.CursorIndex; + self.ChartCorssCursor.Draw(); + } + } + + var eventTitleDraw = this.GetEvent(JSCHART_EVENT_ID.ON_TITLE_DRAW); + var eventIndexTitleDraw = this.GetEvent(JSCHART_EVENT_ID.ON_INDEXTITLE_DRAW); + for (var i in self.TitlePaint) //标题 + { + var item = self.TitlePaint[i]; + if (!item.IsDynamic) continue; + + if (item.ClassName == 'DynamicChartTitlePainting') item.OnDrawEvent = eventIndexTitleDraw + else item.OnDrawEvent = eventTitleDraw; + + item.CursorIndex = self.CursorIndex; + if (!bOnTouchDraw) //手势离开屏幕 去最后一个数据 + { + if (this.ChartPaint[0] && this.ChartPaint[0].Data && this.ChartPaint[0].Data.Data) + { + var hisData=this.ChartPaint[0].Data; + var dataCount=hisData.Data.length; + if (dataCount>0) item.CursorIndex=((dataCount-1)-hisData.DataOffset); + } + } + + if (item.FullDraw) item.FullDraw(); + } + + if (bOnTouchDraw) + { + for (var i in this.ExtendChartPaint) //动态扩展图形 在动态标题以后画 + { + var item = this.ExtendChartPaint[i]; + if (item.IsCallbackDraw) continue; + item.LatestPoint={ X:this.LastPoint.X, Y:this.LastPoint.Y }; + if (item.IsDynamic && item.DrawAfterTitle) item.Draw(); + } + } + + this.LastDrawStatus = 'FullDraw'; + this.Canvas.draw(false); + } + + this.Draw = function () + { + if (this.IsFullDraw) + { + this.FullDraw('Draw'); + return; + } + + if (this.IsOnTouch == true && (this.ClassName == 'MinuteChartContainer' || this.ClassName =='MinuteChartHScreenContainer')) return; + + var self = this; + this.Canvas.clearRect(0, 0, this.UIElement.Width, this.UIElement.Height); + + this.Frame.SetDrawOtherChart(() => + { + for (var i in this.ExtendChartPaint) + { + var item = this.ExtendChartPaint[i]; + if (item.IsCallbackDraw) item.Draw(); + } + }); + + //框架 + this.Frame.Draw(); + + if (this.Frame.DrawCustomVertical) + { + var eventCVericalDraw = this.GetEvent(JSCHART_EVENT_ID.ON_CUSTOM_VERTICAL_DRAW); + this.Frame.DrawCustomVertical(eventCVericalDraw); + } + + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) + { + this.Frame.DrawInsideHorizontal(); + this.ChartSplashPaint.Draw(); + this.LastDrawStatus = 'Draw'; + this.Canvas.draw(); + return; + } + + for (var i in this.ChartPaint) + { + var item = this.ChartPaint[i]; + if (item.IsDrawFirst) item.Draw(); + } + + //框架内图形 + for (var i in this.ChartPaint) + { + var item = this.ChartPaint[i]; + if (!item.IsDrawFirst) item.Draw(); + } + + for (var i in this.ChartPaintEx) + { + var item = this.ChartPaintEx[i]; + item.Draw(); + } + + //叠加股票 + for (var i in this.OverlayChartPaint) + { + var item = this.OverlayChartPaint[i]; + item.Draw(); + } + + //框架外图形 + for (var i in this.ExtendChartPaint) + { + var item = this.ExtendChartPaint[i]; + if (item.IsCallbackDraw) continue; + if (!item.IsDynamic && !item.IsAnimation) item.Draw(); + } + + //框架内部坐标 + if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal(); + if (this.Frame.DrawCustomHorizontal) this.Frame.DrawCustomHorizontal(); + if (this.ChartInfoPaint) this.ChartInfoPaint.Draw(); + this.Frame.DrawLock(); + + var eventTitleDraw = this.GetEvent(JSCHART_EVENT_ID.ON_TITLE_DRAW); + var eventIndexTitleDraw = this.GetEvent(JSCHART_EVENT_ID.ON_INDEXTITLE_DRAW); + for (var i in this.TitlePaint) + { + var item = this.TitlePaint[i]; + if (!item.IsDynamic) continue; + + if (item.ClassName == 'DynamicChartTitlePainting') item.OnDrawEvent = eventIndexTitleDraw + else item.OnDrawEvent = eventTitleDraw; + + if (typeof (item.DrawTitle) == 'function') item.DrawTitle(); + } + + this.LastDrawStatus='Draw'; + + if (this.IsOnTouch) //手势移动的时候不保存图片 + { + this.Canvas.draw(false); + } + else + { + ++this.LastDrawID; + //坑!!.画图是异步, 保存当前屏图放在回调里面 + //JSConsole.Chart.Log('[JSChartContainer:Draw][ID=' + this.UIElement.ID + '] draw and save snapshot. DrawID=' + this.LastDrawID + ' .....'); + var lastDrawID=this.LastDrawID; + this.Canvas.draw(false, function () + { + //if (lastDrawID == self.LastDrawID) + self.Frame.Snapshot(self.SnapshotType); //只保存最后一次的截图 + //JSConsole.Chart.Log('[JSChartContainer:Draw] finish. DrawID('+ lastDrawID +','+ self.LastDrawID +')'); + }); + } + + //JSConsole.Chart.Log('[JSChartContainer:Draw][ID=' + this.UIElement.ID + '] draw dynamic info ......'); + //动态标题都不画了(Canvas.draw 异步画的,如果下面再画会被截屏进去) 只有数据移动的时候在画 + } + + //画动态信息 + this.TempImage=null; + this.DrawDynamicInfo = function () + { + if (this.IsFullDraw) + { + this.FullDraw('DrawDynamicInfo'); + return; + } + + var self = this; + var width = this.Frame.ChartBorder.GetChartWidth(); + var height = this.Frame.ChartBorder.GetChartHeight(); + + if (self.SnapshotType==1) + { + if (this.Frame.ScreenImageData == null) return; + wx.canvasPutImageData({ + canvasId: this.UIElement.ID, + x: 0,y: 0,width: width,height: height, + data: this.Frame.ScreenImageData, + success(res) { self.DrawDynamicChart(true); } + }) + } + else + { + if (this.Frame.ScreenImagePath == null) return; + if (self.Canvas && self.Canvas.DomNode) //新版本2D画布 + { + //JSConsole.Chart.Log("[DrawDynamicInfo] ScreenImagePath ", this.Frame.ScreenImagePath); + if (!this.TempImage) this.TempImage= self.Canvas.DomNode.createImage(); //新版本的必须要装成image类 比较坑 + this.TempImage.src = this.Frame.ScreenImagePath; + //JSConsole.Chart.Log("[DrawDynamicInfo] tempImage ", this.TempImage); + this.TempImage.onload=()=> + { + //JSConsole.Chart.Log("[DrawDynamicInfo] onload ", self.TempImage); + self.Canvas.clearRect(0, 0, width, height); + self.Canvas.drawImage(self.TempImage, 0, 0, width, height); + self.DrawDynamicChart(false); + } + } + else + { + self.Canvas.drawImage(this.Frame.ScreenImagePath, 0, 0, width, height); + self.DrawDynamicChart(false); + } + + } + } + + this.DrawDynamicChart = function (bReserve) + { + var self = this; + if (self.ChartCorssCursor) + { + self.ChartCorssCursor.LastPoint = self.LastPoint; + self.ChartCorssCursor.CursorIndex = self.CursorIndex; + self.ChartCorssCursor.Draw(); + } + + for (var i in self.TitlePaint) + { + var item = self.TitlePaint[i]; + if (!item.IsDynamic) continue; + + item.CursorIndex = self.CursorIndex; + item.Draw(); + } + + for (var i in this.ExtendChartPaint) //动态扩展图形 在动态标题以后画 + { + var item = this.ExtendChartPaint[i]; + if (item.IsDynamic && item.DrawAfterTitle) item.Draw(); + } + + if (this.EnableAnimation) + { + for (var i in this.ExtendChartPaint) //动画 + { + var item = this.ExtendChartPaint[i]; + if (item.IsAnimation === true) item.Draw(); + } + } + + this.LastDrawStatus = 'DrawDynamicInfo'; + JSConsole.Chart.Log('[JSChartContainer:DrawDynamicChart][ID=' + this.UIElement.ID + '] draw .....'); + self.Canvas.draw(bReserve, function () { + JSConsole.Chart.Log('[JSChartContainer:DrawDynamicChart] finish.'); + }); + } + + this.DrawAnimation = function () //绘制动画 如弹幕 + { + if (!this.EnableAnimation) return; + + if (this.Frame.ScreenImagePath && !this.IsOnTouch) + { + for (var i in this.ExtendChartPaint) + { + var item = this.ExtendChartPaint[i]; + if (item.IsAnimation === true) item.IsMoveStep = true; //移动弹幕 + } + + this.DrawDynamicInfo(); + } + + var self = this; + this.UIElement.WebGLCanvas.requestAnimationFrame(() => { this.DrawAnimation(); }); + } + + this.StartAnimation = function (option) + { + JSConsole.Chart.Log('[JSChartContainer::StartAnimation] ', this.UIElement.WebGLCanvas); + if (!this.UIElement.WebGLCanvas) return; + + var bCreated = false; //是否已经创建了弹幕画法 + var barrageData = null; + for (var i in this.ExtendChartPaint) + { + var item = this.ExtendChartPaint[i]; + if (item.ClassName === 'BarragePaint') + { + bCreated = true; + barrageData = item.BarrageList; + break; + } + } + + if (!bCreated) + { + var chart = new BarragePaint(); + chart.Canvas = this.Canvas; + chart.ChartBorder = this.Frame.ChartBorder; + chart.ChartFrame = this.Frame; + chart.HQChart = this; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + barrageData = chart.BarrageList; + } + + this.EnableAnimation = true; + var self = this; + + this.UIElement.WebGLCanvas.requestAnimationFrame(()=> { this.DrawAnimation(); }); + return barrageData; + } + + this.StopAnimation = function () + { + this.EnableAnimation = false; + this.DrawDynamicInfo(); + } + + this.OnMouseMove = function (x, y, e, bFullDraw) + { + var lastY = this.LastPoint.Y; + this.LastPoint.X = x; + this.LastPoint.Y = y; + var lastCursorIndex = this.CursorIndex; + this.CursorIndex = this.Frame.GetXData(x); + if ( this.ClassName=="KLineChartContainer" || this.ClassName=="KLineChartHScreenContainer" ) + { + if (lastCursorIndex == this.CursorIndex && Math.abs(lastY - y) < 1) return; //一个一个数据移动 + } + else + { + if (parseInt(lastCursorIndex - 0.5) == parseInt(this.CursorIndex - 0.5) && Math.abs(lastY - y) < 1) return; //一个一个数据移动 + } + + if (bFullDraw) + { + this.FullDraw(); + } + else + { + if (this.IsForceLandscape) this.Draw();//横屏图片太大不让贴,分两张图贴,多次截图的函数是坏的, 直接重画了 + else this.DrawDynamicInfo(); + } + } + + + this.OnDoubleClick = function (x, y, e) + { + //JSConsole.Chart.Log(e); + } + + this.UpdatePointByCursorIndex = function () + { + this.LastPoint.X = this.Frame.GetXFromIndex(this.CursorIndex); + + var index = Math.abs(this.CursorIndex - 0.5); + index = parseInt(index.toFixed(0)); + if (this.ClassName == 'KLineChartContainer') index = this.CursorIndex; + var data = this.Frame.Data; + if (data.DataOffset + index >= data.Data.length) + { + return; + } + var close = data.Data[data.DataOffset + index].Close; + + this.LastPoint.Y = this.Frame.GetYFromData(close); + } + + this.ResetFrameXYSplit = function () + { + if (typeof (this.Frame.ResetXYSplit) == 'function') this.Frame.ResetXYSplit(); + } + + this.UpdateFrameMaxMin = function () + { + var frameMaxMinData = new Array(); + var chartPaint = new Array(); + + for (var i in this.ChartPaint) + { + var item=this.ChartPaint[i]; + if (item.IsShow==false) continue; //隐藏的图形不计算 + chartPaint.push(item); + } + for (var i in this.OverlayChartPaint) + { + chartPaint.push(this.OverlayChartPaint[i]); + } + + for (var i in chartPaint) + { + var paint = chartPaint[i]; + var range = paint.GetMaxMin(); + if (range == null || range.Max == null || range.Min == null) continue; + var frameItem = null; + for (var j in frameMaxMinData) + { + if (frameMaxMinData[j].Frame == paint.ChartFrame) + { + frameItem = frameMaxMinData[j]; + break; + } + } + + if (frameItem) + { + if (frameItem.Range.Max < range.Max) frameItem.Range.Max = range.Max; + if (frameItem.Range.Min > range.Min) frameItem.Range.Min = range.Min; + } + else + { + frameItem = {}; + frameItem.Frame = paint.ChartFrame; + frameItem.Range = range; + frameMaxMinData.push(frameItem); + } + } + + for (var i in frameMaxMinData) + { + var item = frameMaxMinData[i]; + if (!item.Frame || !item.Range) continue; + if (item.Range.Max == null || item.Range.Min == null) continue; + if (item.Frame.YSpecificMaxMin) + { + item.Frame.HorizontalMax = item.Frame.YSpecificMaxMin.Max; + item.Frame.HorizontalMin = item.Frame.YSpecificMaxMin.Min; + } + else + { + item.Frame.HorizontalMax = item.Range.Max; + item.Frame.HorizontalMin = item.Range.Min; + } + item.Frame.XYSplit = true; + } + } + + this.DataMoveLeft = function () { + var data = null; + if (!this.Frame.Data) data = this.Frame.Data; + else data = this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + if (data.DataOffset <= 0) return false; + --data.DataOffset; + return true; + } + + this.DataMoveRight = function () { + var data = null; + if (!this.Frame.Data) data = this.Frame.Data; + else data = this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + + var xPointcount = 0; + if (this.Frame.XPointCount) xPointcount = this.Frame.XPointCount; + else xPointcount = this.Frame.SubFrame[0].Frame.XPointCount; + if (!xPointcount) return false; + + if (xPointcount + data.DataOffset >= data.Data.length) return false; + + ++data.DataOffset; + return true; + } + + this.UpdataDataoffset = function () { + var data = null; + if (this.Frame.Data) + data = this.Frame.Data; + else + data = this.Frame.SubFrame[0].Frame.Data; + + if (!data) return; + + for (var i in this.ChartPaint) { + var item = this.ChartPaint[i]; + if (!item.Data) continue; + item.Data.DataOffset = data.DataOffset; + } + + for (var i in this.OverlayChartPaint) { + var item = this.OverlayChartPaint[i]; + if (!item.Data) continue; + item.Data.DataOffset = data.DataOffset; + } + } + + this.DataMove = function (step, isLeft) + { + step=parseInt(step/this.StepPixel); + if (step<=0) return false; + + var data = null; + if (!this.Frame.Data) data = this.Frame.Data; + else data = this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + + var xPointcount = 0; + if (this.Frame.XPointCount) xPointcount = this.Frame.XPointCount; + else xPointcount = this.Frame.SubFrame[0].Frame.XPointCount; + if (!xPointcount) return false; + + if (isLeft) //--> + { + if (this.RightSpaceCount > 0) + { + if (xPointcount + data.DataOffset >= data.Data.length + this.RightSpaceCount - 1) return false; + + data.DataOffset += step; + + if (data.DataOffset + xPointcount >= data.Data.length + this.RightSpaceCount) + data.DataOffset = data.Data.length - (xPointcount - this.RightSpaceCount); + } + else + { + if (xPointcount + data.DataOffset >= data.Data.length) return false; + + data.DataOffset += step; + + if (data.DataOffset + xPointcount >= data.Data.length) + data.DataOffset = data.Data.length - xPointcount; + } + return true; + } + else //<-- + { + if (data.DataOffset <= 0) return false; + + data.DataOffset -= step; + if (data.DataOffset < 0) data.DataOffset = 0; + + return true; + } + } + + //获取鼠标在当前子窗口id + this.GetSubFrameIndex = function (x, y) { + if (!this.Frame.SubFrame || this.Frame.SubFrame.length <= 0) return -1; + + for (var i in this.Frame.SubFrame) { + var frame = this.Frame.SubFrame[i].Frame; + var left = frame.ChartBorder.GetLeft(); + var top = frame.ChartBorder.GetTop(); + var height = frame.ChartBorder.GetHeight(); + var width = frame.ChartBorder.GetWidth(); + + this.Canvas.rect(left, top, width, height); + if (this.Canvas.isPointInPath(x, y)) return parseInt(i); + + } + return 0; + } + + //根据X坐标获取数据索引 + this.GetDataIndexByPoint = function (x) { + var frame = this.Frame; + if (this.Frame.SubFrame && this.Frame.SubFrame.length > 0) frame = this.Frame.SubFrame[0].Frame; + + var data = null; + if (this.Frame.Data) + data = this.Frame.Data; + else + data = this.Frame.SubFrame[0].Frame.Data; + + if (!data || !frame) return; + + var index = parseInt(frame.GetXData(x)); + + //JSConsole.Chart.Log('x='+ x +' date='+data.Data[data.DataOffset+index].Date); + return data.DataOffset + index; + } + + this.SaveToImage = function (callback) + { + let width = this.UIElement.Width; + let height = this.UIElement.Height;; + JSConsole.Chart.Log('[JSChartContainer::SaveToImage]', this.UIElement); + + wx.canvasToTempFilePath({ + x: 0, + y: 0, + width: width, + height: height, + canvasId: this.UIElement.ID, + success: function (res) + { + let data = { ImagePath: res.tempFilePath, Width: width, Height: height }; + if (typeof (callback) == 'function') callback(data); + } + }) + } + + //全屏提示信息 { Title:提示信息, Draw:false/true 是否立即重绘, } + this.EnableSplashScreen=function(option) + { + if (!this.ChartSplashPaint) return; + if (!option) return; + + if (IFrameSplitOperator.IsString(option.Title)) this.ChartSplashPaint.SetTitle(option.Title); + this.ChartSplashPaint.EnableSplash(true); + + if (option.Draw===false) return; + this.Draw(); + } + + //增加一个指标窗口 + this.AddIndexWindow=function(indexName,option) + { + //查找系统指标 + let scriptData = new JSCommonIndexScript.JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + + var index=this.Frame.SubFrame.length; + var subFrame=this.CreateSubFrameItem(index); + this.Frame.SubFrame[index]=subFrame; + var titlePaint=new DynamicChartTitlePainting(); + titlePaint.Frame=this.Frame.SubFrame[index].Frame; + titlePaint.Canvas=this.Canvas; + titlePaint.LanguageID=this.LanguageID; + titlePaint.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + this.TitlePaint[index+1]=titlePaint; + + if (option) + { + if (option.Window) + { + var item=option.Window; + if (item.IsDrawTitleBG==true) subFrame.Frame.IsDrawTitleBG=item.IsDrawTitleBG; + } + + if (IFrameSplitOperator.IsNumber(option.SplitCount)) subFrame.Frame.YSplitOperator.SplitCount=option.SplitCount; + if (IFrameSplitOperator.IsNumber(option.TitleHeight)) subFrame.Frame.ChartBorder.TitleHeight=option.TitleHeight; + if (IFrameSplitOperator.IsBool(option.IsShowTitleArraw)) subFrame.Frame.IsShowTitleArraw=option.IsShowTitleArraw; + if (IFrameSplitOperator.IsBool(option.IsShowIndexName)) subFrame.Frame.IsShowIndexName=option.IsShowIndexName; + if (IFrameSplitOperator.IsBool(option.IsShowOverlayIndexName)) subFrame.Frame.IsShowOverlayIndexName=option.IsShowOverlayIndexName; + if (IFrameSplitOperator.IsNumber(option.IndexParamSpace)) subFrame.Frame.IndexParamSpace=option.IndexParamSpace; + if (IFrameSplitOperator.IsBool(option.IsShowXLine)) subFrame.Frame.IsShowXLine=option.IsShowXLine; + if (IFrameSplitOperator.IsBool(option.IsShowYLine)) subFrame.Frame.IsShowYLine=option.IsShowYLine; + if (IFrameSplitOperator.IsBool(option.IsShowIndexTitle)) subFrame.Frame.IsShowTitle=option.IsShowIndexTitle; + + if (IFrameSplitOperator.IsBool(option.IsShowLeftText)) subFrame.Frame.IsShowYText[0] = option.IsShowLeftText; //显示左边刻度 + if (IFrameSplitOperator.IsBool(option.IsShowRightText)) subFrame.Frame.IsShowYText[1] = option.IsShowRightText; //显示右边刻度 + } + + //最后一个显示X轴坐标 + for(var i=0;i=0) indexData.FloatPrecision=option.FloatPrecision; + if (option.StringFormat>0) indexData.StringFormat=option.StringFormat; + if (option.Args) indexData.Args=option.Args; + } + + this.WindowIndex[index] = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args,indexData); //脚本执行 + if (this.ClassName=="MinuteChartContainer" || this.ClassName=="MinuteChartHScreenContainer") + var bindData=this.SourceData; + else + var bindData=this.ChartPaint[0].Data; + + this.BindIndexData(index,bindData); //执行脚本 + } + +} + +function ToFixed(number, precision) +{ + var b = 1; + if (isNaN(number)) return number; + if (number < 0) b = -1; + var multiplier = Math.pow(10, precision); + var value = Math.round(Math.abs(number) * multiplier) / multiplier * b; + + if (/^(\d+(?:\.\d+)?)(e)([\-]?\d+)$/.test(value)) + var s = value.toFixed2(precision); + else + var s = value.toString(); + var rs = s.indexOf('.'); + if (rs < 0 && precision > 0) + { + rs = s.length; + s += '.'; + } + + while (s.length <= rs + precision) + { + s += '0'; + } + + return s; +} + +Number.prototype.toFixed2 = Number.prototype.toFixed; //备份下老的 + +Number.prototype.toFixed = function (precision) { + return ToFixed(this, precision) +} + +function Guid() +{ + function S4() + { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + } + return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); +} + +function GetScrollPosition() { + var scrollPos = {}; + var scrollTop = 0; + var scrollLeft = 0; + if (document.documentElement && document.documentElement.scrollTop) { + scrollTop = document.documentElement.scrollTop; + scrollLeft = document.documentElement.scrollLeft; + } else if (document.body) { + scrollTop = document.body.scrollTop; + scrollLeft = document.body.scrollLeft; + } + + scrollPos.Top = scrollTop; + scrollPos.Left = scrollLeft; + return scrollPos; +} + +//修正线段有毛刺 +function ToFixedPoint(value) { + //return value; + return parseInt(value) + 0.5; +} + +function ToFixedRect(value) { + //return value; + // With a bitwise or. + //rounded = (0.5 + somenum) | 0; + // A double bitwise not. + //rounded = ~~ (0.5 + somenum); + // Finally, a left bitwise shift. + var rounded; + return rounded = (0.5 + value) << 0; +} + + + +function Point() { + this.X; + this.Y; +} + +function SelectRectData() { + this.Data; //主数据 + this.JSChartContainer; //行情控件 + + this.Start; //数据起始位子 + this.End; //数据结束位置 + + this.XStart;//X坐标起始位置 + this.XEnd; //X位置结束为止 +} + +//边框信息 +function ChartBorder() +{ + this.UIElement; + + //四周间距 + this.Left = 50; + this.Right = 80; + this.Top = 50; + this.Bottom = 50; + this.TitleHeight = 15; //标题高度 + //上下间距 + this.TopSpace = 0; + this.BottomSpace = 0; + + this.LeftExtendWidth=0; //左边扩展图形宽度 + this.RightExtendWidth=0; + + this.GetBorder=function() + { + var data= + { + Left:this.Left, + LeftEx:this.Left+this.LeftExtendWidth, + Right:this.UIElement.width-this.Right, + RightEx:this.UIElement.width-this.Right-this.RightExtendWidth, + + Top:this.Top, + TopEx:this.Top+this.TitleHeight+this.TopSpace, + TopTitle:this.Top+this.TitleHeight, + Bottom:this.UIElement.height-this.Bottom, + BottomEx:this.UIElement.height-this.Bottom-this.BottomSpace, + + ChartWidth:this.UIElement.width, + ChartHeight:this.UIElement.height + }; + + return data; + } + + this.GetHScreenBorder=function() + { + var data= + { + Left:this.Left, + LeftEx:this.Left+this.BottomSpace, + + Right:this.UIElement.width-this.Right, + RightEx:this.UIElement.width-this.Right-this.TitleHeight- this.TopSpace, + RightTitle:this.UIElement.width-this.Right-this.TitleHeight, + + Top:this.Top, + TopEx:this.Top+this.LeftExtendWidth, + Bottom:this.UIElement.height-this.Bottom, + BottomEx:this.UIElement.height-this.Bottom-this.RightExtendWidth, + + ChartWidth:this.UIElement.width, + ChartHeight:this.UIElement.height + }; + + return data; + } + + this.GetChartWidth = function () { + return this.UIElement.Width; + } + + this.GetChartHeight = function () { + return this.UIElement.Height; + } + + this.GetLeft = function () { + return this.Left; + } + + this.GetRight = function () { + return this.UIElement.Width - this.Right; + } + + this.GetTop = function () { + return this.Top; + } + + this.GetTopEx = function () //去掉标题,上面间距 + { + return this.Top + this.TitleHeight + this.TopSpace; + } + + this.GetTopTitle = function () //去掉标题 + { + return this.Top + this.TitleHeight; + } + + this.GetBottom = function () { + return this.UIElement.Height - this.Bottom; + } + + this.GetBottomEx = function () { + return this.UIElement.Height - this.Bottom - this.BottomSpace; + } + + this.GetWidth = function () { + return this.UIElement.Width - this.Left - this.Right; + } + + this.GetHeight = function () { + return this.UIElement.Height - this.Top - this.Bottom; + } + + this.GetHeightEx = function () //去掉标题的高度 上下间距 + { + return this.UIElement.Height - this.Top - this.Bottom - this.TitleHeight - this.TopSpace - this.BottomSpace; + } + + this.GetRightEx = function () //横屏去掉标题高度的 上面间距 + { + return this.UIElement.Width - this.Right - this.TitleHeight - this.TopSpace; + } + + this.GetWidthEx = function () //横屏去掉标题宽度 上下间距 + { + return this.UIElement.Width - this.Left - this.Right - this.TitleHeight - this.TopSpace - this.BottomSpace; + } + + this.GetLeftEx = function () //横屏 + { + return this.Left + this.BottomSpace; + } + + this.GetRightTitle = function ()//横屏 + { + return this.UIElement.Width - this.Right - this.TitleHeight; + } + + this.GetTitleHeight = function () { + return this.TitleHeight; + } +} + +function IChartFramePainting() +{ + this.HorizontalInfo = new Array(); //Y轴 + this.VerticalInfo = new Array(); //X轴 + this.ClassName='IChartFramePainting'; + this.Canvas; //画布 + + this.Identify; //窗口标识 + + this.ChartBorder; + this.PenBorder = g_JSChartResource.FrameBorderPen; //边框颜色 + this.TitleBGColor = g_JSChartResource.FrameTitleBGColor; //标题背景色 + this.IsShow = true; //是否显示 + this.SizeChange = true; //大小是否改变 + this.XYSplit = true; //XY轴坐标信息改变 + + this.HorizontalMax; //Y轴最大值 + this.HorizontalMin; //Y轴最小值 + this.XPointCount = 10; //X轴数据个数 + + this.YSplitOperator; //Y轴分割 + this.XSplitOperator; //X轴分割 + this.Data; //主数据 + + this.YSpecificMaxMin = null; //指定Y轴最大最小值 + this.YSplitScale=null; //固定分割刻度数组 [2,5,8] + + this.IsShowBorder = true; //是否显示边框 + this.IsShowIndexName = true; //是否显示指标名字 + this.IndexParamSpace = 2; //指标参数数值显示间距 + + //上锁信息 + this.IsLocked = false; //是否上锁 + this.LockPaint = null; + + this.BorderLine=null; //1=上 2=下 4=左 8=右 + this.IsMinSize=false; //窗口是否最小化 + + this.Draw = function () + { + this.DrawFrame(); + this.DrawBorder(); + + this.SizeChange = false; + this.XYSplit = false; + } + + this.DrawFrame = function () { } + + this.ClearCoordinateText=function(option) + { + if (IFrameSplitOperator.IsNonEmptyArray(this.HorizontalInfo)) + { + for(var i=0;i0) //上 + { + this.Canvas.moveTo(left,top); + this.Canvas.lineTo(right,top); + } + + if ((this.BorderLine&2)>0) //下 + { + this.Canvas.moveTo(left,bottom); + this.Canvas.lineTo(right,bottom); + } + + if ((this.BorderLine&4)>0) //左 + { + this.Canvas.moveTo(left,top); + this.Canvas.lineTo(left,bottom); + } + + if ((this.BorderLine&8)>0) //右 + { + this.Canvas.moveTo(right,top); + this.Canvas.lineTo(right,bottom); + } + + this.Canvas.stroke(); + } + } + + //左右刻度文字宽度 + this.GetScaleTextWidth=function() { } + + //画标题背景色 + this.DrawTitleBG = function () + { + /* 指标信息背景色不画,画了感觉框架变小了 + if (this.ChartBorder.TitleHeight<=0) return; + + var left=ToFixedPoint(this.ChartBorder.GetLeft()); + var top=ToFixedPoint(this.ChartBorder.GetTop()); + var right=ToFixedPoint(this.ChartBorder.GetRight()); + var bottom=ToFixedPoint(this.ChartBorder.GetTopEx()); + var width=right-left; + var height=bottom-top; + + this.Canvas.fillStyle=this.TitleBGColor; + this.Canvas.fillRect(left,top,width,height); + */ + } + + this.DrawLock = function () { + if (this.IsLocked) { + if (this.LockPaint == null) this.LockPaint = new ChartLock(); + + this.LockPaint.Canvas = this.Canvas; + this.LockPaint.ChartBorder = this.ChartBorder; + this.LockPaint.ChartFrame = this; + this.LockPaint.Draw(); + } + } + + //设施上锁 + this.SetLock = function (lockData) { + if (!lockData) //空数据不上锁 + { + this.IsLocked = false; + return; + } + + this.IsLocked = true; + if (!this.LockPaint) this.LockPaint = new ChartLock(); //创建锁 + + if (lockData.Callback) this.LockPaint.Callback = lockData.Callback; //回调 + if (lockData.IndexName) this.LockPaint.IndexName = lockData.IndexName; //指标名字 + if (lockData.ID) this.LockPaint.LockID = lockData.ID; //锁ID + if (lockData.BG) this.LockPaint.BGColor = lockData.BG; //背景色 + if (lockData.Text) this.LockPaint.Title = lockData.Text; + if (lockData.TextColor) this.LockPaint.TextColor = lockData.TextColor; + if (lockData.Font) this.LockPaint.Font = lockData.Font; + if (lockData.Count) this.LockPaint.LockCount = lockData.Count; + } +} + + +//空框架只画边框 +function NoneFrame() +{ + this.newMethod = IChartFramePainting; //派生 + this.newMethod(); + delete this.newMethod; + this.ClassName='NoneFrame'; + + this.Snapshot = function () { } + + this.DrawInsideHorizontal = function () { } + + this.SetSizeChage = function (sizeChange) + { + this.SizeChange = sizeChange; + + //画布的位置 + this.Position = + { + X: this.ChartBorder.UIElement.offsetLeft, + Y: this.ChartBorder.UIElement.offsetTop, + W: this.ChartBorder.UIElement.clientWidth, + H: this.ChartBorder.UIElement.clientHeight + }; + } +} + +function AverageWidthFrame() +{ + this.newMethod = IChartFramePainting; //派生 + this.newMethod(); + delete this.newMethod; + this.ClassName='AverageWidthFrame'; + + this.DataWidth = 50; + this.DistanceWidth = 10; + this.MinXDistance = 30; //X轴刻度最小间距 + this.MinYDistance=10; + this.XMessageAlign = 'top'; //X轴刻度文字上下对齐方式 + this.IsShowTitle = true; //是否显示动态标题 + this.IsShowYText = [true, true]; //是否显示Y轴坐标坐标 [0=左侧] [1=右侧] + this.XBottomOffset = g_JSChartResource.Frame.XBottomOffset; //X轴文字显示向下偏移 + this.YTextTopOffset=g_JSChartResource.Frame.YTopOffset; //Y轴顶部文字向下偏移 + this.YTextPosition=[0,0], //是坐标否强制画在内部 [0=左侧] [1=右侧] 1=OUT" , 2=INSIDE + this.IsShowXLine=true; //是否显示X轴刻度线 + this.IsShowYLine=true; + this.YTextBaseline=0; //0=居中 1=上部 (目前就支持内部刻度) + + this.ShortYLineLength=5; + this.ShortXLineLength=5; + + this.DrawOtherChart; //其他画法调用 + + this.DrawFrame = function () + { + if (this.XPointCount > 0) + { + this.DistanceWidth = this.ChartBorder.GetWidth() / (4 * this.XPointCount); + this.DataWidth = 2 * this.DistanceWidth; + } + + this.DrawHorizontal(); + this.DrawVertical(); + } + + this.GetYFromData = function (value) + { + if (value <= this.HorizontalMin) return this.ChartBorder.GetBottomEx(); + if (value >= this.HorizontalMax) return this.ChartBorder.GetTopEx(); + + var height = this.ChartBorder.GetHeightEx() * (value - this.HorizontalMin) / (this.HorizontalMax - this.HorizontalMin); + return this.ChartBorder.GetBottomEx() - height; + } + + //Y刻度画在内部 + this.DrawInsideHorizontal = function () + { + if (this.IsHScreen === true) return; //横屏不画 + if (this.IsMinSize) return; + if (this.IsShowYText[0] === false && this.IsShowYText[1] === false) return; + + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRight(); + var bottom = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetTopTitle(); + var borderRight = this.ChartBorder.Right; + var borderLeft = this.ChartBorder.Left; + var titleHeight = this.ChartBorder.TitleHeight; + if (borderLeft >= 10) return; + + if ((borderLeft < 10 && this.IsShowYText[0] === true) || (borderRight < 10 && this.IsShowYText[1] === true)) + { + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + if (!item) continue; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev !=null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + //坐标信息 左边 间距小于10 画在内部 + if (item.Message[0] != null && borderLeft < 10 && this.IsShowYText[0] === true) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + if (this.YTextBaseline==1) this.Canvas.textBaseline = "bottom"; + else this.Canvas.textBaseline = "middle"; + } + var textObj = { X: left, Y: yText, Text: { BaseLine: this.Canvas.textBaseline, Font: this.Canvas.font, Value: item.Message[0] } }; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) this.Canvas.fillText(item.Message[0], left + 1, yText); + } + + if (item.Message[1] != null && borderRight < 10 && this.IsShowYText[1] === true) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + if (this.YTextBaseline==1) this.Canvas.textBaseline = "bottom"; + else this.Canvas.textBaseline = "middle"; + } + var textWidth = this.Canvas.measureText(item.Message[1]).width; + var textObj = { X: right - textWidth, Y: yText, Text: { BaseLine: this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font: this.Canvas.font, Value: item.Message[1] } }; + if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj)) + this.Canvas.fillText(item.Message[1], right - 1, yText); + } + + yPrev = y; + } + } + } + + //画Y轴 + this.DrawHorizontal = function () + { + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRight(); + var bottom = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetTopTitle(); + var borderRight = this.ChartBorder.Right; + var borderLeft = this.ChartBorder.Left; + var titleHeight = this.ChartBorder.TitleHeight; + + this.Canvas.save(); + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev != null && Math.abs(y - yPrev) 0) + { + this.Canvas.strokeStyle = item.LineColor; + if(g_JSChartResource.FrameYLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameYLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(left, yFixed); + this.Canvas.lineTo(right, yFixed); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.beginPath(); + this.Canvas.moveTo(left, yFixed); + this.Canvas.lineTo(right, yFixed); + this.Canvas.stroke(); + } + } + } + var yText=y; + if (y >= bottom - 2) + { + this.Canvas.textBaseline = 'bottom'; + } + else if (y <= top + 2) + { + this.Canvas.textBaseline = 'top'; + yText+=this.YTextTopOffset; + } + else + { + this.Canvas.textBaseline = "middle"; + } + + //坐标信息 左边 间距小于10 不画坐标 + this.Canvas.fillStyle = item.TextColor; + this.Canvas.strokeStyle = item.TextColor; + if (item.Message[0] != null && borderLeft > 10 && this.IsShowYText[0] === true) + { + if (item.Font != null) this.Canvas.font = item.Font; + + this.Canvas.textAlign = "right"; + this.Canvas.fillText(item.Message[0], left - 2, yText); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1] != null && borderRight > 10 && this.IsShowYText[1] === true) + { + if (item.Font != null) this.Canvas.font = item.Font; + + var xText=right; + if (item.LineType==3) + { + var lineLength=this.ShortYLineLength; + this.Canvas.beginPath(); + this.Canvas.moveTo(xText,yFixed); + this.Canvas.lineTo(xText+lineLength,yFixed); + this.Canvas.stroke(); + + xText+=lineLength; + } + + this.Canvas.textAlign = "left"; + this.Canvas.fillText(item.Message[1], xText + 2, yText); + } + + yPrev = y; + } + + this.Canvas.restore(); + } + + this.GetXFromIndex = function (index) + { + var count = this.XPointCount; + + if (count == 1) + { + if (index == 0) return this.ChartBorder.GetLeft(); + else return this.ChartBorder.GetRight(); + } + else if (count <= 0) + { + return this.ChartBorder.GetLeft(); + } + else if (index >= count) + { + return this.ChartBorder.GetRight(); + } + else + { + var offset = this.ChartBorder.GetLeft() + this.ChartBorder.GetWidth() * index / count; + return offset; + } + } + + //画X轴 + this.DrawVertical = function () + { + var top = this.ChartBorder.GetTopTitle(); + var bottom = this.ChartBorder.GetBottom(); + var right = this.ChartBorder.GetRight(); + + if (this.ChartBorder.Bottom<=5) return; //高度不够 不显示 + + var yText = bottom; + if (this.XMessageAlign == 'bottom') yText = this.ChartBorder.GetChartHeight(); + else this.XMessageAlign = 'top'; + + var xPrev = null; //上一个坐标x的值 + let xPrevTextRight = null; + for (var i in this.VerticalInfo) + { + var x = this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x > right) break; + if (xPrev != null && Math.abs(x - xPrev) < this.MinXDistance) continue; + + var item=this.VerticalInfo[i]; + var xFixed=ToFixedPoint(x); + if (this.IsShowXLine && item.LineType > 0) + { + if (item.LineType==2) + { + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.setLineDash([5,5]); + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed, top); + this.Canvas.lineTo(xFixed, bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else if (item.LineType==3) + { + + } + else if (item.LineType>0) + { + if(g_JSChartResource.FrameXLineDash) + { + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.beginPath(); + this.Canvas.setLineDash(g_JSChartResource.FrameXLineDash); + this.Canvas.moveTo(xFixed, top); + this.Canvas.lineTo(xFixed, bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed, top); + this.Canvas.lineTo(xFixed, bottom); + this.Canvas.stroke(); + } + } + + } + + if (this.VerticalInfo[i].Message[0] != null && this.ChartBorder.Bottom > 5) + { + let xTextRight = null; + let xTextLeft = null; + if (this.VerticalInfo[i].Font != null) + this.Canvas.font = this.VerticalInfo[i].Font; + this.Canvas.fillStyle=item.TextColor; + this.Canvas.strokeStyle=item.TextColor; + + var testWidth = this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (x < testWidth / 2) + { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = this.XMessageAlign; + xTextRight = x + testWidth; + xTextLeft = x; + } + else if ((x + testWidth / 2) >= this.ChartBorder.GetChartWidth()) + { + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = this.XMessageAlign; + xTextRight = x + testWidth; + xTextLeft = x; + } + else + { + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = this.XMessageAlign; + xTextRight = x + testWidth / 2; + xTextLeft = x - testWidth / 2; + } + + if (xPrevTextRight != null && xPrevTextRight > xTextLeft) continue; + + var yText=bottom; + if (item.LineType==3) + { + var lineLength=this.ShortXLineLength; + this.Canvas.beginPath(); + this.Canvas.moveTo(xFixed,yText); + this.Canvas.lineTo(xFixed,yText+lineLength); + this.Canvas.stroke(); + + yText+=lineLength+2; + } + + this.Canvas.fillText(this.VerticalInfo[i].Message[0], x, yText + this.XBottomOffset); + xPrevTextRight = xTextRight; + } + + xPrev = x; + } + } + + this.GetYData = function (y) //Y坐标转y轴数值 + { + if (y < this.ChartBorder.GetTopEx()) return this.HorizontalMax; + if (y > this.ChartBorder.GetBottomEx()) return this.HorizontalMin; + + return (this.ChartBorder.GetBottomEx() - y) / this.ChartBorder.GetHeightEx() * (this.HorizontalMax - this.HorizontalMin) + this.HorizontalMin; + } + + this.GetXData = function (x) //X坐标转x轴数值 + { + if (x <= this.ChartBorder.GetLeft()) return 0; + if (x >= this.ChartBorder.GetRight()) return this.XPointCount; + + return (x - this.ChartBorder.GetLeft()) * (this.XPointCount * 1.0 / this.ChartBorder.GetWidth()); + } + + this.DrawCustomItem = function (item) //显示自定义刻度 + { + //if (this.IsHScreen === true) return; //横屏不画 + if (!item.Message[1] && !item.Message[0]) return; + if (item.Value > this.HorizontalMax || item.Value < this.HorizontalMin) return; + + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRight(); + var bottom = this.ChartBorder.GetBottom(); + var top = this.ChartBorder.GetTopTitle(); + var borderRight = this.ChartBorder.Right; + var borderLeft = this.ChartBorder.Left; + var titleHeight = this.ChartBorder.TitleHeight; + + if (this.IsHScreen) + { + borderLeft = this.ChartBorder.Top; + borderRight = this.ChartBorder.Bottom; + top = this.ChartBorder.GetTop(); + bottom = this.ChartBorder.GetBottom(); + } + + var textHeight = 18; + var y = this.GetYFromData(item.Value); + + if (item.Message[0]) + { + if (borderLeft < 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[0]).width + 2; + var bgColor = item.LineColor; + var rgb = this.RGBToStruct(item.LineColor); + if (rgb) bgColor = `rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`; //内部刻度 背景增加透明度 + this.Canvas.fillStyle = bgColor; + if (this.IsHScreen) + { + var bgTop = top; + var textLeft = y - textHeight / 2 - 1; + this.Canvas.fillRect(textLeft, bgTop, textHeight, textWidth); + this.DrawHScreenText({ X: y, Y: bgTop }, { Text: item.Message[0], Color: item.TextColor, XOffset: 1, YOffset: 2 }); + this.DrawLine(bgTop + textWidth, bottom, y, item.LineColor,item.LineType); + } + else + { + var bgTop = y - textHeight / 2 - 1; + var textLeft = left + 1; + this.Canvas.fillRect(textLeft, bgTop, textWidth, textHeight); + this.Canvas.fillStyle = item.TextColor; + this.Canvas.fillText(item.Message[0], textLeft + 1, y); + this.DrawLine(textLeft + textWidth, right, y, item.LineColor,item.LineType); + } + } + else + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[0]).width + 2; + this.Canvas.fillStyle = item.LineColor; + if (this.IsHScreen) + { + var bgTop = top - textWidth; + var textLeft = y - textHeight / 2 - 1 ; + this.Canvas.fillRect(textLeft, bgTop, textHeight, textWidth); + this.DrawHScreenText({ X: y, Y: bgTop }, { Text: item.Message[0], Color: item.TextColor, XOffset: 1, YOffset: 2 }); + this.DrawLine(bgTop + textWidth, bottom, y, item.LineColor,item.LineType); + } + else + { + var bgTop = y - textHeight / 2 - 1; + this.Canvas.fillRect(left - textWidth, bgTop, textWidth, textHeight); + this.Canvas.fillStyle = item.TextColor; + this.Canvas.fillText(item.Message[0], left - 1, y); + this.DrawLine(left, right, y, item.LineColor,item.LineType); + } + } + } + else if (item.Message[1]) + { + if (borderRight < 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[1]).width + 2; + var bgColor = item.LineColor; + var rgb = this.RGBToStruct(item.LineColor); + if (rgb) bgColor = `rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`; //内部刻度 背景增加透明度 + this.Canvas.fillStyle = bgColor; + if (this.IsHScreen) + { + var bgTop = bottom - textWidth; + var textLeft = y - textHeight / 2 - 1; + this.Canvas.fillRect(textLeft, bgTop, textHeight, textWidth); + this.DrawHScreenText({ X: y, Y: bgTop }, { Text: item.Message[1], Color: item.TextColor, XOffset: 1, YOffset: 2 }); + this.DrawLine(top, bgTop, y, item.LineColor,item.LineType); + } + else + { + var bgTop = y - textHeight / 2 - 1; + var textLeft = right - textWidth; + this.Canvas.fillRect(textLeft, bgTop, textWidth, textHeight); + this.Canvas.fillStyle = item.TextColor; + this.Canvas.fillText(item.Message[1], textLeft + 1, y); + this.DrawLine(left, textLeft, y, item.LineColor,item.LineType); + } + + } + else + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[1]).width + 2; + this.Canvas.fillStyle = item.LineColor; + if (this.IsHScreen) + { + var bgTop = bottom; + var textLeft = y - textHeight / 2 - 1 ; + this.Canvas.fillRect(textLeft, bgTop, textHeight, textWidth); + this.DrawHScreenText({ X: y, Y: bgTop }, { Text: item.Message[1], Color: item.TextColor, XOffset: 1 , YOffset: 2 }); + this.DrawLine(top, bgTop, y, item.LineColor,item.LineType); + } + else + { + var bgTop = y - textHeight / 2 - 1; + this.Canvas.fillRect(right, bgTop, textWidth, textHeight); + this.Canvas.fillStyle = item.TextColor; + this.Canvas.fillText(item.Message[1], right + 1, y); + this.DrawLine(left, right, y, item.LineColor,item.LineType); + } + } + } + } + + this.DrawLine=function(left, right, y, color, lineType) + { + if (lineType==-1) return; + + if (lineType==0) + { + this.Canvas.strokeStyle = color; + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), left); + this.Canvas.lineTo(ToFixedPoint(y), right); + } + else + { + this.Canvas.moveTo(left, ToFixedPoint(y)); + this.Canvas.lineTo(right, ToFixedPoint(y)); + } + this.Canvas.stroke(); + } + else + { + this.DrawDotLine(left, right, y, color); + } + } + + this.DrawDotLine = function (left, right, y, color) + { + this.Canvas.save(); + this.Canvas.strokeStyle = color; + this.Canvas.setLineDash([5, 5]); //虚线 + this.Canvas.beginPath(); + if (this.IsHScreen) + { + this.Canvas.moveTo(ToFixedPoint(y), left); + this.Canvas.lineTo(ToFixedPoint(y), right); + } + else + { + this.Canvas.moveTo(left, ToFixedPoint(y)); + this.Canvas.lineTo(right, ToFixedPoint(y)); + } + this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.RGBToStruct = function (rgb) + { + if (/^(rgb|RGB)/.test(rgb)) + { + var aColor = rgb.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(","); + var result = {}; + if (aColor.length != 3) return null; + + result.R = Number(aColor[0]); + result.G = Number(aColor[1]); + result.B = Number(aColor[2]); + return result; + } + return null; + } + + this.DrawHScreenText = function (center, data) + { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillStyle = data.Color; + + this.Canvas.save(); + this.Canvas.translate(center.X, center.Y); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(data.Text, data.XOffset, data.YOffset); + this.Canvas.restore(); + } + + this.GetScaleTextWidth=function() + { + var border=this.ChartBorder.GetBorder(); + if (this.IsHScreen) + { + var borderTop = this.ChartBorder.Top; + var borderBottom = this.ChartBorder.Bottom; + var isDrawLeft=borderTop>10 && this.IsShowYText[0]===true; + var isDrawRight=borderBottom>10 && this.IsShowYText[1]===true; + } + else + { + var borderRight=this.ChartBorder.Right; + var borderLeft=this.ChartBorder.Left; + var isDrawLeft=borderLeft>10 && this.IsShowYText[0]===true; + var isDrawRight=borderRight>10 && this.IsShowYText[1]===true; + } + + var width={ Left:null, Right:null }; + if (!isDrawRight && !isDrawLeft) return width; + + for(var i=0;i= count) + { + return this.ChartBorder.GetRight(); + } + else + { + var offset = this.ChartBorder.GetLeft() + this.ChartBorder.GetWidth() * index / count; + return offset; + } + } + + //X坐标转x轴数值 + this.GetXData = function (x) + { + if (x <= this.ChartBorder.GetLeft()) return 0; + if (x >= this.ChartBorder.GetRight()) return this.XPointCount; + + return (x - this.ChartBorder.GetLeft()) * (this.XPointCount * 1.0 / this.ChartBorder.GetWidth()); + } + + this.DrawCustomHorizontal = function () //Y轴刻度定制显示 + { + if (this.IsMinSize) return; + for (var i in this.CustomHorizontalInfo) + { + var item = this.CustomHorizontalInfo[i]; + switch (item.Type) + { + case 0: + case 1: + this.DrawCustomItem(item); //自定义刻度 + break; + } + } + } +} + +function MinuteHScreenFrame() +{ + this.newMethod = MinuteFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='MinuteHScreenFrame'; + this.IsHScreen = true; //是否是横屏 + + //画标题背景色 + this.DrawTitleBG = function () + { + /* + if (this.ChartBorder.TitleHeight <= 0) return; + + var left = ToFixedPoint(this.ChartBorder.GetRightEx()); + var top = ToFixedPoint(this.ChartBorder.GetTop()); + var bottom = ToFixedPoint(this.ChartBorder.GetBottom()); + var width = this.ChartBorder.TitleHeight; + var height = bottom - top; + + this.Canvas.fillStyle = this.TitleBGColor; + this.Canvas.fillRect(left, top, width, height); + */ + } + + this.DrawInsideHorizontal = function () + { + } + + //Y坐标转y轴数值 + this.GetYData = function (x) + { + if (x < this.ChartBorder.GetLeftEx()) return this.HorizontalMin; + if (x > this.ChartBorder.GetRightEx()) return this.HorizontalMax; + + return (x - this.ChartBorder.GetLeftEx()) / this.ChartBorder.GetWidthEx() * (this.HorizontalMax - this.HorizontalMin) + this.HorizontalMin; + } + + //X坐标转x轴数值 + this.GetXData = function (y) + { + if (y <= this.ChartBorder.GetTop()) return 0; + if (y >= this.ChartBorder.GetBottom()) return this.XPointCount; + + var count=this.XPointCount-1; + return (y - this.ChartBorder.GetTop()) * (count * 1.0 / this.ChartBorder.GetHeight()); + } + + this.GetXFromIndex = function (index) + { + var count = this.XPointCount - 1; + + if (count == 1) + { + if (index == 0) return this.ChartBorder.GetTop(); + else return this.ChartBorder.GetBottom(); + } + else if (count <= 0) + { + return this.ChartBorder.GetTop(); + } + else if (index >= count) + { + return this.ChartBorder.GetBottom(); + } + else + { + var offset = this.ChartBorder.GetTop() + this.ChartBorder.GetHeight() * index / count; + return offset; + } + } + + + this.GetYFromData = function (value) + { + if (value <= this.HorizontalMin) return this.ChartBorder.GetLeft(); + if (value >= this.HorizontalMax) return this.ChartBorder.GetRightEx(); + + var width = this.ChartBorder.GetWidthEx() * (value - this.HorizontalMin) / (this.HorizontalMax - this.HorizontalMin); + return this.ChartBorder.GetLeft() + width; + } + + //画Y轴 + this.DrawHorizontal = function () + { + var top = this.ChartBorder.GetTop(); + var bottom = this.ChartBorder.GetBottom(); + var left=this.ChartBorder.GetLeft(); + var right=this.ChartBorder.GetRight(); + var borderTop = this.ChartBorder.Top; + var borderBottom = this.ChartBorder.Bottom; + + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从左往右画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y), top); + this.Canvas.lineTo(ToFixedPoint(y), bottom); + this.Canvas.stroke(); + + if (y >= right - 2) + { + this.Canvas.textBaseline = 'top'; + y = right; + } + else if (y <= left + 2) + { + this.Canvas.textBaseline = 'bottom'; + y=left; + if (y != null && Math.abs(y - yPrev) < 2*this.MinYDistance) continue; //两个坐标在近了 就不画了 + } + else + { + this.Canvas.textBaseline = "middle"; + } + + //坐标信息 左边 间距小于10 不画坐标 + if (item.Message[0] != null && borderTop > 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + //this.Canvas.textBaseline = "middle"; + + var xText = y, yText = top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1] != null && borderBottom > 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + //this.Canvas.textBaseline = "middle"; + var xText = y, yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], 2, 0); + this.Canvas.restore(); + } + + yPrev = y; + } + } + + //画X轴 + this.DrawVertical = function () { + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRightEx(); + var bottom = this.ChartBorder.GetBottom(); + + var xPrev = null; //上一个坐标x的值 + for (var i in this.VerticalInfo) { + var x = this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x > bottom) break; + if (xPrev != null && Math.abs(x - xPrev) < this.MinXDistance) continue; + + this.Canvas.strokeStyle = this.VerticalInfo[i].LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(left, ToFixedPoint(x)); + this.Canvas.lineTo(right, ToFixedPoint(x)); + this.Canvas.stroke(); + + if (this.VerticalInfo[i].Message[0] != null) { + if (this.VerticalInfo[i].Font != null) + this.Canvas.font = this.VerticalInfo[i].Font; + + this.Canvas.fillStyle = this.VerticalInfo[i].TextColor; + var testWidth = this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (x < testWidth / 2) { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "top"; + } + else if ((x + testWidth / 2) >= this.ChartBorder.GetChartHeight()) { + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "top"; + } + else { + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + } + + var xText = left, yText = x; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.VerticalInfo[i].Message[0], 0, 0); + this.Canvas.restore(); + } + + xPrev = x; + } + } +} + +//K线框架 +function KLineFrame() +{ + this.newMethod = AverageWidthFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='KLineFrame'; + this.ToolbarID = Guid(); //工具条Div id + this.ModifyIndex = true; //是否显示'改参数'菜单 + this.ChangeIndex = true; //是否显示'换指标'菜单 + this.CustomHorizontalInfo = []; + + this.LastCalculateStatus = { Width: 0, XPointCount: 0 }; //最后一次计算宽度的状态 + + //定制X轴刻度 + //Type:0, Date:, Time: , Name:名字, Line:{ Color:线段颜色, Type:线段类型 0 直线 1 虚线 } + //Type: 1, Space: 第几个空白间距, Name:名字, Line: { Color: 线段颜色, Type: 线段类型 0 直线 1 虚线 } + this.CustomVerticalInfo = []; + this.DrawCustomVerticalEvent; + this.RightSpaceCount = 0; + + this.DrawFrame = function () + { + if (this.IsMinSize) return; + + this.SplitXYCoordinate(); + if (this.SizeChange == true) this.CalculateDataWidth(); + if (this.DrawOtherChart) this.DrawOtherChart(); + + this.DrawTitleBG(); + this.DrawHorizontal(); + this.DrawVertical(); + } + + this.GetXFromIndex = function (index) + { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var offset = this.ChartBorder.GetLeft() + g_JSChartResource.FrameLeftMargin + this.DistanceWidth / 2 + this.DataWidth / 2; + for (var i = 1; i <= index; ++i) { offset += this.DistanceWidth + this.DataWidth; } + return offset; + } + + //X坐标转x轴数值 + this.GetXData = function (x) + { + if (x <= this.ChartBorder.GetLeft()) return 0; + if (x >= this.ChartBorder.GetRight()) return this.XPointCount-1; + + var left=this.ChartBorder.GetLeft()+g_JSChartResource.FrameLeftMargin; + var right=this.ChartBorder.GetRight()-g_JSChartResource.FrameRightMargin; + var distanceWidth = this.DistanceWidth; + var dataWidth = this.DataWidth; + + var index = 0; + var xPoint = left + distanceWidth/2 + dataWidth + distanceWidth; + while (xPoint < right && index < 10000 && index+1 x) break; + xPoint += (dataWidth + distanceWidth); + ++index; + } + + //var test=(x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth()); + return index; + } + + this.DrawCustomHorizontal = function () //Y轴刻度定制显示 + { + if (this.IsMinSize) return; + for (var i in this.CustomHorizontalInfo) + { + var item = this.CustomHorizontalInfo[i]; + switch (item.Type) + { + case 0: //最新价格刻度 + case 1: //固定价格刻度 + this.DrawCustomItem(item); + break; + } + } + } + + this.DrawCustomVerticalItem = function (item) { + this.Canvas.save(); + if (item.Data.Line.Type == 1) this.Canvas.setLineDash([5, 5]); //虚线 + this.Canvas.strokeStyle = item.Data.Line.Color; + this.Canvas.beginPath(); + if (item.IsHScreen) { + this.Canvas.moveTo(item.Top, ToFixedPoint(item.X)); + this.Canvas.lineTo(item.Bottom, ToFixedPoint(item.X)); + } + else { + this.Canvas.moveTo(ToFixedPoint(item.X), item.Top); + this.Canvas.lineTo(ToFixedPoint(item.X), item.Bottom); + } + this.Canvas.stroke(); + this.Canvas.restore(); + } + + this.DrawCustomVertical = function () //X轴定制刻度显示 + { + if (!this.CustomVerticalInfo) return; + if (this.CustomVerticalInfo.length <= 0) return; + if (!this.Data) return; + + var isHScreen = this.IsHScreen; + var top = this.ChartBorder.GetTopEx(); + var bottom = this.ChartBorder.GetBottomEx(); + + var dataWidth = this.DataWidth; + var distanceWidth = this.DistanceWidth; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + + if (isHScreen) + { + xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + top = this.ChartBorder.GetLeftEx(); + bottom = this.ChartBorder.GetRightEx(); + } + + var j = 0; + for (var i = this.Data.DataOffset; i < this.Data.Data.length && j < this.XPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + var kItem = this.Data.Data[i]; + for (var k in this.CustomVerticalInfo) + { + var item = this.CustomVerticalInfo[k]; + if (item.Type != 0) continue; + + if (IFrameSplitOperator.IsNumber(item.Time)) + { + if (kItem.Date != item.Date || kItem.Time != item.Time) continue; + } + else + { + if (kItem.Date != item.Date) continue; + } + + var left = xOffset; + var right = xOffset + dataWidth; + var x = left + (right - left) / 2; + + var DrawData = { X: x, Top: top, Bottom: bottom, Data: item, IsHScreen: isHScreen }; + this.DrawCustomVerticalItem(DrawData); + if (this.DrawCustomVerticalEvent) + this.DrawCustomVerticalEvent.Callback(this.DrawCustomVerticalEvent, DrawData, this); + + break; + } + } + + for (var i = 1; j < this.XPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) + { + for (var k in this.CustomVerticalInfo) + { + var item = this.CustomVerticalInfo[k]; + if (item.Type != 1) continue; + if (item.Space != i) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + var x = left + (right - left) / 2; + + var DrawData = { X: x, Top: top, Bottom: bottom, Data: item, IsHScreen: isHScreen }; + this.DrawCustomVerticalItem(DrawData); + if (this.DrawCustomVerticalEvent) + this.DrawCustomVerticalEvent.Callback(this.DrawCustomVerticalEvent, DrawData, this); + + break; + } + } + } + + this.CalculateDataWidth = function () //计算数据宽度 + { + if (this.XPointCount < 2) return; + var width = this.GetFrameWidth() - g_JSChartResource.FrameMargin; //预留4个像素 防止最后1个柱子不够画 + + if (this.ZoomIndex>=0 && this.LastCalculateStatus.Width==width && this.LastCalculateStatus.XPointCount==this.XPointCount) //宽度没变 尝试使用原来的柱子宽度 + { + var caclWidth=(this.DistanceWidth/2+g_JSChartResource.FrameLeftMargin)+(this.DataWidth + this.DistanceWidth)*(this.XPointCount-1); + var caclWidth2=(this.DataWidth + this.DistanceWidth) * this.XPointCount; + if (caclWidth<= width) //当前的柱子宽度够用 就不调整了 + return; + } + + this.LastCalculateStatus.Width=width; + this.LastCalculateStatus.XPointCount=this.XPointCount; + + for (var i = 0; i < ZOOM_SEED.length; ++i) + { + let barWidth = ZOOM_SEED[i][0]; //数据宽度 + let distanceWidth = ZOOM_SEED[i][1]; //间距宽度 + if ((ZOOM_SEED[i][0] + ZOOM_SEED[i][1]) * this.XPointCount < width) + { + this.ZoomIndex = i; + this.DataWidth = ZOOM_SEED[i][0]; + this.DistanceWidth = ZOOM_SEED[i][1]; + this.TrimKLineDataWidth(width); + + JSConsole.Chart.Log(`[KLineFrame::CalculateDataWidth] ZoomIndex=${this.ZoomIndex} DataWidth=${this.DataWidth} DistanceWidth=${this.DistanceWidth}` ); + return; + } + } + + //太多了 就平均分了 + this.ZoomIndex = ZOOM_SEED.length - 1; + this.DataWidth = width / this.XPointCount; + this.DistanceWidth = 0; + } + + this.OnSize=function(obj) + { + var width=this.GetFrameWidth()-g_JSChartResource.FrameMargin; + var xPointCount=0; + var y=this.DistanceWidth/2+g_JSChartResource.FrameLeftMargin+(this.DataWidth+this.DistanceWidth); + for(;y width) + { + this.DistanceWidth -= 0.01; + break; + } + this.DistanceWidth += 0.01; + } + } + else + { + while (true) + { + if ((this.DistanceWidth + this.DataWidth) * this.XPointCount + this.DistanceWidth > width) + { + this.DataWidth -= 0.01; + break; + } + this.DataWidth += 0.01; + } + } + } + + this.IsOverlayMaxMin = function (obj) //当前坐标信息 是否覆盖最大 最小值输出 + { + if (!this.ChartKLine) return false; + if (!this.ChartKLine.Max || !this.ChartKLine.Min) return false; + + var textWidth = this.Canvas.measureText(obj.Text.Value).width + 4; //刻度文字宽度 + var max = this.ChartKLine.Max, min = this.ChartKLine.Min; + var isOverlayMax = false, isOverlayMin = false; + const textHeight = 20; //字体高度 + if (max.X >= obj.X && max.X <= obj.X + textWidth) + { + var y1 = max.Y + textHeight, y2 = max.Y - textHeight; + if ((y1 >= obj.Y - textHeight && y1 <= obj.Y + textHeight) || (y2 >= obj.Y - textHeight && y2 <= obj.Y + textHeight)) + isOverlayMax = true; + } + + if (isOverlayMax == true) return true; + + if (min.X >= obj.X && min.X <= obj.X + textWidth) //最小值X 坐标不在 刻度文字范围内 + { + var y1 = min.Y + textHeight, y2 = min.Y - textHeight; + if ((y1 >= obj.Y - textHeight && y1 <= obj.Y + textHeight) || (y2 >= obj.Y - textHeight && y2 <= obj.Y + textHeight)) + isOverlayMin = true; + } + + return isOverlayMax || isOverlayMin; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate = function () + { + if (this.XYSplit == false) return; + if (this.YSplitOperator != null) this.YSplitOperator.Operator(); + if (this.XSplitOperator != null) this.XSplitOperator.Operator(); + } + + this.CalculateCount = function (zoomIndex) + { + var width = this.GetFrameWidth() - g_JSChartResource.FrameMargin; + return parseInt(width / (ZOOM_SEED[zoomIndex][0] + ZOOM_SEED[zoomIndex][1])); + } + + this.ZoomUp = function (cursorIndex) + { + if (this.ZoomIndex <= 0) return false; + if (this.Data.DataOffset < 0) return false; + var dataCount = this.Data.Data.length; + var maxDataCount = dataCount + this.RightSpaceCount; + + var rightSpaceCount = 0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + var lastCursorIndex = this.Data.DataOffset + cursorIndex.Index; + + if (lastDataIndex >= dataCount) + { + rightSpaceCount = lastDataIndex - (this.Data.Data.length - 1); //计算右边预留空间 + lastDataIndex = this.Data.Data.length - 1; + if (rightSpaceCount > this.RightSpaceCount) rightSpaceCount = this.RightSpaceCount; + } + + var xPointCount = this.CalculateCount(this.ZoomIndex-1); + + --this.ZoomIndex; + this.XPointCount = xPointCount; + if (xPointCount >= maxDataCount) + { + xPointCount = maxDataCount; + this.XPointCount = xPointCount; + this.Data.DataOffset = 0; + } + else + { + this.XPointCount = xPointCount; + this.Data.DataOffset = lastDataIndex - (this.XPointCount - rightSpaceCount) + 1; + } + + this.DataWidth = ZOOM_SEED[this.ZoomIndex][0]; + this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1]; + var width = this.GetFrameWidth() - g_JSChartResource.FrameMargin; + this.TrimKLineDataWidth(width); + + this.LastCalculateStatus.XPointCount = this.XPointCount; + cursorIndex.Index = lastCursorIndex - this.Data.DataOffset; + + return true; + } + + this.ZoomDown = function (cursorIndex) + { + if (this.ZoomIndex + 1 >= ZOOM_SEED.length) return false; + if (this.Data.DataOffset < 0) return false; + var dataCount = this.Data.Data.length; + var maxDataCount = dataCount + this.RightSpaceCount; + if (this.XPointCount >= dataCount) return false; + + var rightSpaceCount = 0; + var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1; //最右边的数据索引 + if (lastDataIndex >= this.Data.Data.length) + { + rightSpaceCount = lastDataIndex - (this.Data.Data.length - 1); //计算右边预留空间 + lastDataIndex = this.Data.Data.length - 1; + if (rightSpaceCount > this.RightSpaceCount) rightSpaceCount = this.RightSpaceCount; + } + + var xPointCount = this.CalculateCount(this.ZoomIndex + 1); + var lastCursorIndex = this.Data.DataOffset + cursorIndex.Index; + + ++this.ZoomIndex; + if (xPointCount >= maxDataCount) + { + xPointCount = maxDataCount; + this.XPointCount = xPointCount; + this.Data.DataOffset = 0; + } + else + { + this.XPointCount = xPointCount; + this.Data.DataOffset = lastDataIndex - (this.XPointCount - rightSpaceCount) + 1; + } + + this.DataWidth = ZOOM_SEED[this.ZoomIndex][0]; + this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1]; + var width = this.GetFrameWidth() - g_JSChartResource.FrameMargin; + this.TrimKLineDataWidth(width); + + this.LastCalculateStatus.XPointCount = this.XPointCount; + cursorIndex.Index = lastCursorIndex - this.Data.DataOffset; + + return true; + } + + this.GetFrameWidth = function () + { + if (this.IsHScreen) return this.ChartBorder.GetHeight(); + return this.ChartBorder.GetWidth(); + } +} + +//K线横屏框架 +function KLineHScreenFrame() +{ + this.newMethod = KLineFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ClassName='KLineHScreenFrame'; + this.IsHScreen = true; //是否是横屏 + + this.DrawInsideHorizontal = function () + { + if (this.IsMinSize) return; + if (this.IsShowYText[0] === false && this.IsShowYText[1] === false) return; + + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRightEx(); + var top = this.ChartBorder.GetTop(); + var bottom = this.ChartBorder.GetBottom(); + var borderTop = this.ChartBorder.Top; + var borderBottom = this.ChartBorder.Bottom; + var titleHeight = this.ChartBorder.TitleHeight; + var pixelTatio = 1; + + var isDrawLeft = (borderTop < 10 * pixelTatio || this.YTextPosition[0] == 2) && this.IsShowYText[0] === true; + var isDrawRight = (borderBottom < 10 * pixelTatio || this.YTextPosition[1] == 2) && this.IsShowYText[1] === true; + + if (isDrawLeft || isDrawRight) + { + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从上往下画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev != null && Math.abs(y - yPrev) < this.MinYDistance) continue; //两个坐标在近了 就不画了 + + //坐标信息 左边 间距小于10 画在内部 + if (item.Message[0] != null && isDrawLeft) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + if (y >= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + + var textObj = { X: left, Y: y, Text: { BaseLine: this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font: this.Canvas.font, Value: item.Message[0] } }; + var xText = y, yText = top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + if (item.Message[1] != null && isDrawRight) + { + if (item.Font != null) this.Canvas.font = item.Font; + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + if (y >= right - 2) this.Canvas.textBaseline = 'top'; + else if (y <= left + 2) this.Canvas.textBaseline = 'bottom'; + else this.Canvas.textBaseline = "middle"; + var textWidth = this.Canvas.measureText(item.Message[1]).width; + var textObj = { X: right - textWidth, Y: y, Text: { BaseLine: this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font: this.Canvas.font, Value: item.Message[1] } }; + + var xText = y, yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], 2, 0); + this.Canvas.restore(); + } + yPrev = y; + } + } + } + + //画标题背景色 + this.DrawTitleBG = function () + { + /* + if (this.ChartBorder.TitleHeight <= 0) return; + + var left = ToFixedPoint(this.ChartBorder.GetRightEx()); + var top = ToFixedPoint(this.ChartBorder.GetTop()); + var bottom = ToFixedPoint(this.ChartBorder.GetBottom()); + var width = this.ChartBorder.TitleHeight; + var height = bottom - top; + + this.Canvas.fillStyle = this.TitleBGColor; + this.Canvas.fillRect(left, top, width, height); + */ + } + + this.GetYFromData = function (value) + { + if (value <= this.HorizontalMin) return this.ChartBorder.GetLeftEx(); + if (value >= this.HorizontalMax) return this.ChartBorder.GetRightEx(); + + var width = this.ChartBorder.GetWidthEx() * (value - this.HorizontalMin) / (this.HorizontalMax - this.HorizontalMin); + return this.ChartBorder.GetLeftEx() + width; + } + + //画Y轴 + this.DrawHorizontal = function () + { + var top = this.ChartBorder.GetTop(); + var bottom = this.ChartBorder.GetBottom(); + var borderTop = this.ChartBorder.Top; + var borderBottom = this.ChartBorder.Bottom; + var left=this.ChartBorder.GetLeft(); + + var yPrev = null; //上一个坐标y的值 + for (var i = this.HorizontalInfo.length - 1; i >= 0; --i) //从左往右画分割线 + { + var item = this.HorizontalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && Math.abs(y - yPrev) < 15) continue; //两个坐标在近了 就不画了 + + if (y!=left) + { + if (item.LineType==2) + { + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.setLineDash([5,5]); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y),top); + this.Canvas.lineTo(ToFixedPoint(y),bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else if (item.LineType>0) + { + this.Canvas.strokeStyle = item.LineColor; + if (g_JSChartResource.FrameYLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameYLineDash); //虚线 + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y),top); + this.Canvas.lineTo(ToFixedPoint(y),bottom); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(y), top); + this.Canvas.lineTo(ToFixedPoint(y), bottom); + this.Canvas.stroke(); + } + } + } + + //坐标信息 左边 间距小于10 不画坐标 + if (item.Message[0] != null && borderTop > 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + + var xText = y, yText = top; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[0], -2, 0); + this.Canvas.restore(); + } + + //坐标信息 右边 间距小于10 不画坐标 + if (item.Message[1] != null && borderBottom > 10) + { + if (item.Font != null) this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + var xText = y, yText = bottom; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(item.Message[1], 2, 0); + this.Canvas.restore(); + } + + yPrev = y; + } + } + + this.GetXFromIndex = function (index) + { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var offset = this.ChartBorder.GetTop() + g_JSChartResource.FrameLeftMargin + this.DistanceWidth / 2 + this.DataWidth / 2; + for (var i = 1; i <= index; ++i) + { + offset += this.DistanceWidth + this.DataWidth; + } + + return offset; + } + + //画X轴 + this.DrawVertical = function () + { + var left = this.ChartBorder.GetLeft(); + var right = this.ChartBorder.GetRightTitle(); + var bottom = this.ChartBorder.GetBottom(); + + var xPrev = null; //上一个坐标x的值 + for (var i in this.VerticalInfo) + { + var x = this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x >= bottom) break; + if (xPrev != null && Math.abs(x - xPrev) < 80) continue; + var item=this.VerticalInfo[i]; + if (item.LineType==2) + { + this.Canvas.setLineDash([5,5]); + this.Canvas.beginPath(); + this.Canvas.moveTo(left, ToFixedPoint(x)); + this.Canvas.lineTo(right, ToFixedPoint(x)); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else if (item.LineType>0) + { + this.Canvas.strokeStyle = item.LineColor; + if (g_JSChartResource.FrameXLineDash) + { + this.Canvas.setLineDash(g_JSChartResource.FrameXLineDash); + this.Canvas.beginPath(); + this.Canvas.moveTo(left, ToFixedPoint(x)); + this.Canvas.lineTo(right, ToFixedPoint(x)); + this.Canvas.stroke(); + this.Canvas.setLineDash([]); + } + else + { + this.Canvas.beginPath(); + this.Canvas.moveTo(left, ToFixedPoint(x)); + this.Canvas.lineTo(right, ToFixedPoint(x)); + this.Canvas.stroke(); + } + } + + if (this.VerticalInfo[i].Message[0] != null) + { + if (this.VerticalInfo[i].Font != null) + this.Canvas.font = this.VerticalInfo[i].Font; + + this.Canvas.fillStyle = this.VerticalInfo[i].TextColor; + var testWidth = this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (x < testWidth / 2) + { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "top"; + } + else + { + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + } + + var xText = left, yText = x; + this.Canvas.save(); + this.Canvas.translate(xText, yText); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.VerticalInfo[i].Message[0], 0, this.XBottomOffset); + this.Canvas.restore(); + } + + xPrev = x; + } + } + + //Y坐标转y轴数值 + this.GetYData = function (x) + { + if (x < this.ChartBorder.GetLeftEx()) return this.HorizontalMin; + if (x > this.ChartBorder.GetRightEx()) return this.HorizontalMax; + + return (x - this.ChartBorder.GetLeftEx()) / this.ChartBorder.GetWidthEx() * (this.HorizontalMax - this.HorizontalMin) + this.HorizontalMin; + } + + //X坐标转x轴数值 + this.GetXData = function (y) + { + if (y <= this.ChartBorder.GetTop()) return 0; + if (y >= this.ChartBorder.GetBottom()) return this.XPointCount-1; + + var distanceWidth=this.DistanceWidth; + var dataWidth=this.DataWidth; + var left=this.ChartBorder.GetTop()+g_JSChartResource.FrameLeftMargin; + var right=this.ChartBorder.GetBottom()-g_JSChartResource.FrameRightMargin; + + var index=0; + var xPoint=left+distanceWidth/2+dataWidth+distanceWidth; + while(xPoint=y) break; + xPoint+=(dataWidth+distanceWidth); + ++index; + } + + return index; + } + +} + +function SubFrameItem() +{ + this.Frame; + this.Height; +} + +//行情框架 +function HQTradeFrame() +{ + this.ClassName='HQTradeFrame'; + this.SubFrame = new Array(); //SubFrameItem 数组 + this.SizeChange = true; //大小是否改变 + this.ChartBorder; + this.Canvas; //画布 + this.ScreenImagePath; //截图路径 + this.ScreenImageData=null; //截图数据 + this.Data; //主数据 + this.Position; //画布的位置 + this.SizeChange = true; + this.SnapshotID=0; + this.CurrentSnapshotID=0; + this.SnapshotStatus=0; //0空闲 1工作 + this.AutoLeftBorder=null; //{ Blank:10 留白宽度, MinWidth:最小宽度 } + this.AutoRightBorder=null; //{ Blank:10 留白宽度, MinWidth:最小宽度 } + + this.ZoomWindowsInfo=null; //附图指标缩放,备份信息 + this.ZoomStartWindowIndex=1; //允许缩放窗口起始位置 + + this.CalculateChartBorder = function () //计算每个子框架的边框信息 + { + if (this.SubFrame.length <= 0) return; + + var top = this.ChartBorder.GetTop(); + var height = this.ChartBorder.GetHeight(); + var totalHeight = 0; + + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + totalHeight += item.Height; + } + + for (var i in this.SubFrame) + { + var item = this.SubFrame[i]; + item.Frame.ChartBorder.Top = top; + item.Frame.ChartBorder.Left = this.ChartBorder.Left; + item.Frame.ChartBorder.Right = this.ChartBorder.Right; + var frameHeight = height * (item.Height / totalHeight) + top; + item.Frame.ChartBorder.Bottom = this.ChartBorder.GetChartHeight() - frameHeight; + top = frameHeight; + } + } + + this.GetScaleTextWidth=function() + { + var width={ Left:null, Right:null }; + for(var i in this.SubFrame) + { + var item=this.SubFrame[i]; + if (item.Height<=0) continue; + var frame=item.Frame; + if (!frame) continue; + if (!frame.XSplitOperator) continue; + + var maxValue=frame.HorizontalMax; //最大最小要还原 + var minValue=frame.HorizontalMin; + + frame.YSplitOperator.Operator(); + var value=frame.GetScaleTextWidth(); + + frame.HorizontalMax=maxValue; + frame.HorizontalMin=minValue; + + if (value && value.TextWidth) + { + var widthItem=value.TextWidth; + if (IFrameSplitOperator.IsNumber(widthItem.Left)) + { + if (width.Left==null || width.Leftvalue) value=this.AutoLeftBorder.MinWidth; + } + if (this.IsHScreen) this.ChartBorder.Top=value; + else this.ChartBorder.Left=value; + for(var i=0; ivalue) value=this.AutoRightBorder.MinWidth; + } + if (this.IsHScreen) this.ChartBorder.Bottom=value; + else this.ChartBorder.Right=value; + for(var i=0; i0) + { + var minPrice=this.BidPrice[0]; + if (price0) + { + var maxPrice=this.AskPrice[this.AskPrice.length-1]; + if (price>maxPrice) + { + isAskPrice=true; + find=maxPrice; + } + } + } + + if (find==null) return null; + + var x=this.GetXFromIndex(find); + + return { X:x, Price:find, IsAsk:isAskPrice }; + } + + this.GetPrice=function(aryPrice, price) + { + if (!aryPrice || !Array.isArray(aryPrice) || aryPrice.length<=0) return null; + + if (pricearyPrice[aryPrice.length-1]) return null; + + var lastPrice=null; + for(var i in aryPrice) + { + var item=aryPrice[i]; + if (price==item) + { + return item; + } + + if (pricexRange.MaxDiffer) differ=xRange.MaxDiffer; + + xRange.Differ=differ; + xRange.Min=xRange.Center-xRange.Differ; + xRange.Max=xRange.Center+xRange.Differ; + + return true; + } +} + +//一般的图形框架 +function SimpleChartFrame() { + this.newMethod = AverageWidthFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.ScreenImageData; //截图 + this.Position; //画布的位置 + + this.IsShowBorder = false; //是否显示边框 + this.IsShowVertical = false; //是否显示X轴 + this.XFontType = 0; //X轴文本文字类型 + + this.MaxDistanceWidth = 4; + + this.BarCount = 0; //多柱子个数 + + this.Draw = function () { + this.DrawFrame(); + if (this.IsShowBorder) this.DrawBorder(); + + this.SizeChange = false; + this.XYSplit = false; + } + + this.DrawFrame = function () { + if (this.XPointCount > 0) { + let dInterval = this.ChartBorder.GetWidth() / (10 * this.XPointCount); //分6份, 数据4 间距2 + this.DistanceWidth = 4 * dInterval; + this.DataWidth = 6 * dInterval; + + if (this.DistanceWidth > this.MaxDistanceWidth) { + this.DistanceWidth = this.MaxDistanceWidth; + dInterval = this.ChartBorder.GetWidth() / this.XPointCount; + this.DataWidth = dInterval - this.MaxDistanceWidth; + } + } + + this.SplitXYCoordinate(); + this.DrawHorizontal(); + this.DrawVertical(); + } + + this.GetXFromIndex = function (index) { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var offset = this.ChartBorder.GetLeft() + 2 + this.DistanceWidth / 2 + this.DataWidth / 2; + for (var i = 1; i <= index; ++i) { + offset += this.DistanceWidth + this.DataWidth; + } + + return offset; + } + + //分割x,y轴坐标信息 + this.SplitXYCoordinate = function () { + if (this.XYSplit == false) return; + if (this.YSplitOperator != null) this.YSplitOperator.Operator(); + if (this.XSplitOperator != null) this.XSplitOperator.Operator(); + } + + //画X轴 + this.DrawVertical = function () { + var top = this.ChartBorder.GetTopEx(); + var bottom = this.ChartBorder.GetBottom(); + var right = this.ChartBorder.GetRight(); + + var xPrev = null; //上一个坐标x的值 + for (var i in this.VerticalInfo) { + var x = this.GetXFromIndex(this.VerticalInfo[i].Value); + if (x >= right) break; + if (this.XFontType == 1) { + if (xPrev != null && Math.abs(x - xPrev) < 20) continue; + } + else { + if (xPrev != null && Math.abs(x - xPrev) < 60) continue; + } + + if (this.IsShowVertical) { + this.Canvas.strokeStyle = this.VerticalInfo[i].LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), top); + this.Canvas.lineTo(ToFixedPoint(x), bottom); + this.Canvas.stroke(); + } + + if (this.VerticalInfo[i].Message[0] != null) { + if (this.VerticalInfo[i].Font != null) + this.Canvas.font = this.VerticalInfo[i].Font; + + this.Canvas.fillStyle = this.VerticalInfo[i].TextColor; + var testWidth = this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width; + if (x < testWidth / 2) { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "top"; + } + else { + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = "top"; + } + + if (this.XFontType == 1) { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = "middle"; + this.Canvas.save(); + this.Canvas.translate(x, bottom); + this.Canvas.rotate(90 * Math.PI / 180); + this.Canvas.fillText(this.VerticalInfo[i].Message[0], 2, 0); + this.Canvas.restore(); + } + else + this.Canvas.fillText(this.VerticalInfo[i].Message[0], x, bottom); + } + + xPrev = x; + } + } + + //图形快照 + this.Snapshot = function () { + var self = this; + var width = this.ChartBorder.GetChartWidth(); + var height = this.ChartBorder.GetChartHeight(); + + JSConsole.Chart.Log('[SimpleChartFrame::Snapshot][ID=' + this.ChartBorder.UIElement.ID + '] invoke canvasToTempFilePath' + '(width=' + width + ',height=' + height + ')'); + + wx.canvasToTempFilePath({ + x: 0, + y: 0, + width: width, + height: height, + canvasId: this.ChartBorder.UIElement.ID, + success: function (res) { + self.ScreenImagePath = res.tempFilePath; + JSConsole.Chart.Log(res.tempFilePath) + } + }) + } + + this.SetSizeChage = function (sizeChange) { + this.SizeChange = sizeChange; + + //画布的位置 + this.Position = { + X: this.ChartBorder.UIElement.offsetLeft, + Y: this.ChartBorder.UIElement.offsetTop, + W: this.ChartBorder.UIElement.clientWidth, + H: this.ChartBorder.UIElement.clientHeight + }; + } +} + +//旋转90度坐标 +function Rotate90SimpleChartFrame() { + this.newMethod = SimpleChartFrame; //派生 + this.newMethod(); + delete this.newMethod; + + this.DrawFrame = function () { + if (this.XPointCount > 0) { + let dInterval = this.ChartBorder.GetHeightEx() / (10 * this.XPointCount); //分10份, 数据4 间距6 + this.DistanceWidth = 4 * dInterval; + this.DataWidth = 6 * dInterval; + + if (this.DistanceWidth > this.MaxDistanceWidth) { + this.DistanceWidth = this.MaxDistanceWidth; + dInterval = this.ChartBorder.GetHeightEx() / this.XPointCount; + this.DataWidth = dInterval - this.MaxDistanceWidth; + } + } + + this.SplitXYCoordinate(); + this.DrawHorizontal(); + this.DrawVertical(); + } + + this.GetXFromIndex = function (value) { + if (value <= this.HorizontalMin) return this.ChartBorder.GetLeft(); + if (value >= this.HorizontalMax) return this.ChartBorder.GetRight(); + + var width = this.ChartBorder.GetWidth() * (value - this.HorizontalMin) / (this.HorizontalMax - this.HorizontalMin); + return this.ChartBorder.GetLeft() + width; + } + + this.GetYFromData = function (index) { + if (index < 0) index = 0; + if (index > this.xPointCount - 1) index = this.xPointCount - 1; + + var offset = this.ChartBorder.GetBottom() - 2 - this.DistanceWidth / 2 - this.DataWidth / 2; + for (var i = 1; i <= index; ++i) { + offset -= this.DistanceWidth + this.DataWidth; + } + + return offset; + } + + //画Y轴 + this.DrawHorizontal = function () { + var top = this.ChartBorder.GetTopEx(); + var bottom = this.ChartBorder.GetBottom(); + var right = this.ChartBorder.GetRight(); + var left = this.ChartBorder.GetLeft(); + + var yPrev = null; //上一个坐标y的值 + for (var i in this.VerticalInfo) { + let item = this.VerticalInfo[i]; + var y = this.GetYFromData(item.Value); + if (y != null && yPrev != null && Math.abs(y - yPrev) < 15) continue; //两个坐标在近了 就不画了 + + if (item.Message[0] != null) { + if (this.VerticalInfo[i].Font != null) + this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + this.Canvas.textAlign = "right"; + this.Canvas.textBaseline = "middle"; + this.Canvas.fillText(item.Message[0], left - 2, y); + } + yPrev = y; + } + } + + //画X轴 + this.DrawVertical = function () { + var top = this.ChartBorder.GetTopEx(); + var bottom = this.ChartBorder.GetBottom(); + var right = this.ChartBorder.GetRight(); + var left = this.ChartBorder.GetLeft(); + + var yText = bottom; + if (this.XMessageAlign == 'bottom') + yText = this.ChartBorder.GetChartHeight(); + else this.XMessageAlign = 'top'; + + var xPrev = null; //上一个坐标x的值 + let xPrevTextRight = null; + for (var i in this.HorizontalInfo) { + let item = this.HorizontalInfo[i]; + var x = this.GetXFromIndex(item.Value); + if (x > right) break; + if (xPrev != null && Math.abs(x - xPrev) < this.MinXDistance) continue; + + if (this.IsShowXLine) { + this.Canvas.strokeStyle = item.LineColor; + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), top); + this.Canvas.lineTo(ToFixedPoint(x), bottom); + this.Canvas.stroke(); + } + + if (item.Message[0] != null) { + let xTextRight = null; + let xTextLeft = null; + if (item.Font != null) + this.Canvas.font = item.Font; + + this.Canvas.fillStyle = item.TextColor; + + var testWidth = this.Canvas.measureText(item.Message[0]).width; + if (x < testWidth / 2) { + this.Canvas.textAlign = "left"; + this.Canvas.textBaseline = this.XMessageAlign; + xTextRight = x + testWidth; + xTextLeft = x; + } + else { + this.Canvas.textAlign = "center"; + this.Canvas.textBaseline = this.XMessageAlign; + xTextRight = x + testWidth / 2; + xTextLeft = x - testWidth / 2; + } + + if (xPrevTextRight != null && xPrevTextRight > xTextLeft) continue; + + //JSConsole.Chart.Log('[AverageWidthFrame::DrawVertical]', this.Canvas.fillStyle,x, yText); + + this.Canvas.fillText(item.Message[0], x, yText); + xPrevTextRight = xTextRight; + } + + xPrev = x; + } + } + +} + +function TooltipData() //提示信息 +{ + this.ChartPaint; + this.Data; +} + +//缩放因子 +var ZOOM_SEED = //0=柱子宽度 1=间距 +[ + [48, 10], [44, 10], + [40, 9], [36, 9], + [32, 8], [28, 8], + [24, 7], [20, 7], + [18, 6], [16, 6], + [14, 5], [12, 5], + [8, 4], [4, 4], [3, 3], + [3, 1], [2,1], [1,1], [1,0] + /* + [49, 10], [46, 9], [43, 8], + [41, 7.5], [39, 7], [37, 6], + [31, 5.5], [27, 5], [23, 4.5], + [21, 4], [18, 3.5], [16, 3], + [13, 2.5], [11, 2], [8, 1.5], + [6, 1], [3, 0.6], [2.2, 0.5], + */ + //太多了卡, + //[1.1,0.3], + //[0.9,0.2], [0.7,0.15], + //[0.6,0.12], [0.5,0.1], [0.4,0.08], + //[0.3,0.06], [0.2,0.04], [0.1,0.02] +]; + + + + +//线段 多数据(一个X点有多条Y数据) 支持横屏 +function ChartLineMultiData() { + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + + this.Draw = function () { + if (this.NotSupportMessage) { + this.DrawNotSupportmessage(); + return; + } + + if (!this.Data || !this.Data.Data) return; + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + + var bFirstPoint = true; + var drawCount = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) { + var aryValue = this.Data.Data[i]; + if (aryValue == null) continue; + + var x = this.ChartFrame.GetXFromIndex(j); + if (x > chartright) break; + + for (var index in aryValue) { + var value = aryValue[index].Value; + var y = this.ChartFrame.GetYFromData(value); + + if (bFirstPoint) { + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y, x); + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + } + else { + if (isHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + } + } + + if (drawCount > 0) this.Canvas.stroke(); + } + + this.GetMaxMin = function () { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) { + var aryValue = this.Data.Data[i]; + if (aryValue == null) continue; + + for (var index in aryValue) { + var value = aryValue[index].Value; + if (range.Max == null) range.Max = value; + if (range.Min == null) range.Min = value; + + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + } + + return range; + } +} + +//直线 水平直线 只有1个数据 支持横屏 +function ChartStraightLine() { + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(255,193,37)"; //线段颜色 + + this.Draw = function () { + if (!this.Data || !this.Data.Data) return; + if (this.Data.Data.length != 1) return; + + var isHScreen = this.ChartFrame.IsHScreen; + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetTop(); + var xPointCount = this.ChartFrame.XPointCount; + + var yValue = this.Data.Data[0]; + var y = this.ChartFrame.GetYFromData(yValue); + var xLeft = this.ChartFrame.GetXFromIndex(0); + var xRight = this.ChartFrame.GetXFromIndex(xPointCount - 1); + + var yFix = parseInt(y.toString()) + 0.5; + this.Canvas.beginPath(); + if (isHScreen) { + this.Canvas.moveTo(yFix, xLeft); + this.Canvas.lineTo(yFix, xRight); + } + else { + this.Canvas.moveTo(xLeft, yFix); + this.Canvas.lineTo(xRight, yFix); + } + this.Canvas.strokeStyle = this.Color; + this.Canvas.stroke(); + } + + this.GetMaxMin = function () { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = null; + range.Max = null; + + if (!this.Data || !this.Data.Data) return range; + if (this.Data.Data.length != 1) return range; + + range.Min = this.Data.Data[0]; + range.Max = this.Data.Data[0]; + + return range; + } +} + +//分钟线叠加 支持横屏 +function ChartOverlayMinutePriceLine() { + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.Color = "rgb(65,105,225)"; + this.MainData; //主图数据 + this.MainYClose; //主图股票的前收盘价 + + this.Name = "ChartOverlayMinutePriceLine"; + this.Title; + this.Symbol; //叠加的股票代码 + this.YClose; //叠加的股票前收盘 + + this.Draw = function () { + if (this.NotSupportMessage) { + this.DrawNotSupportmessage(); + return; + } + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen === true) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var minuteCount = this.ChartFrame.MinuteCount; + + var bFirstPoint = true; + var drawCount = 0; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) { + var value = this.Data.Data[i].Close; + if (value == null) continue; + var showValue = value / this.YClose * this.MainYClose; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(showValue); + + if (bFirstPoint) { + this.Canvas.strokeStyle = this.Color; + this.Canvas.beginPath(); + if (isHScreen) this.Canvas.moveTo(y, x); + else this.Canvas.moveTo(x, y); + bFirstPoint = false; + } + else { + if (isHScreen) this.Canvas.lineTo(y, x); + else this.Canvas.lineTo(x, y); + } + + ++drawCount; + + if (drawCount >= minuteCount) //上一天的数据和这天地数据线段要断开 + { + bFirstPoint = true; + this.Canvas.stroke(); + drawCount = 0; + } + } + + if (drawCount > 0) this.Canvas.stroke(); + } + + this.GetMaxMin = function () { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + if (this.YClose == null) return range; + + range.Min = this.MainYClose; + range.Max = this.MainYClose; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) { + var value = this.Data.Data[i].Close; + if (value == null) continue; + var value = value / this.YClose * this.MainYClose; + if (range.Max == null) range.Max = value; + if (range.Min == null) range.Min = value; + + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + + if (range.Max == this.MainYClose && range.Min == this.MainYClose) { + range.Max = this.MainYClose + this.MainYClose * 0.1; + range.Min = this.MainYClose - this.MainYClose * 0.1; + return range; + } + + var distance = Math.max(Math.abs(this.MainYClose - range.Max), Math.abs(this.MainYClose - range.Min)); + range.Max = this.MainYClose + distance; + range.Min = this.MainYClose - distance; + + return range; + } +} + +//基础图形的XY坐标互换柱子 +function ChartXYSubBar() { + this.newMethod = ChartBar; //派生 + this.newMethod(); + delete this.newMethod; + + this.BarID = 0; + + this.Draw = function () { + if (this.NotSupportMessage) { + this.DrawNotSupportmessage(); + return; + } + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartTop = this.ChartBorder.GetTopEx(); + var xPointCount = this.ChartFrame.XPointCount; + var yOffset = this.ChartBorder.GetBottom() - distanceWidth / 2.0 - 2.0; + + var xMiddle = this.ChartFrame.GetXFromIndex(0); //0 刻度 + + if (dataWidth >= 4) { + var barCount = this.ChartFrame.BarCount; + var subBarWidth = dataWidth; + var subBarOffset = 0; + if (barCount > 0) //多柱子需要把框架柱子宽度的平均分割 + { + subBarWidth = dataWidth / barCount; + subBarOffset = subBarWidth * this.BarID; + //JSConsole.Chart.Log('[ChartXYSubBar::Draw] ', subBarWidth, this.BarID, subBarOffset); + } + + xMiddle = ToFixedRect(xMiddle); //调整为整数 + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, yOffset -= (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var yBottom = yOffset + subBarOffset; + var yTop = yOffset + subBarOffset - dataWidth; + if (yBottom < chartTop) break; + + var x = this.ChartFrame.GetXFromIndex(value); + + if (value > 0) { + this.Canvas.fillStyle = this.UpBarColor; + let barWidth = ToFixedRect(Math.abs(x - xMiddle)); + let barHeight = subBarWidth; + if (Math.abs(chartTop - yBottom) < dataWidth) subBarWidth = Math.abs(chartTop - yBottom); //最后一根柱子可能会超出框架 + this.Canvas.fillRect(xMiddle, ToFixedRect(yTop), barWidth, ToFixedRect(barHeight + 0.5)); + } + else { + this.Canvas.fillStyle = this.DownBarColor; + //高度调整为整数 + let barWidth = ToFixedRect(Math.abs(x - xMiddle)); + let barHeight = subBarWidth; + if (Math.abs(chartTop - yBottom) < subBarWidth) barHeight = Math.abs(chartTop - yBottom); //最后一根柱子可能会超出框架 + this.Canvas.fillRect(xMiddle, ToFixedRect(yTop), -barWidth, ToFixedRect(barHeight + 0.5)); + } + + + } + } + else //太细了 直接画柱子 + { + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, yOffset -= (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var yBottom = yOffset; + var yTop = yOffset - dataWidth; + if (yTop < chartTop) break; + + var x = this.ChartFrame.GetXFromIndex(value); + var y = this.ChartFrame.GetYFromData(j); + + if (value > 0) this.Canvas.strokeStyle = this.UpBarColor; + else this.Canvas.strokeStyle = this.DownBarColor; + + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(xMiddle), y); + this.Canvas.stroke(); + } + } + } +} + +function ChartSubBar() { + this.newMethod = ChartBar; //派生 + this.newMethod(); + delete this.newMethod; + + this.BarID = 0; + + this.Draw = function () { + if (this.NotSupportMessage) { + this.DrawNotSupportmessage(); + return; + } + + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + var xPointCount = this.ChartFrame.XPointCount; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + + + var yBottom = this.ChartFrame.GetYFromData(0); + if (dataWidth >= 4) { + var barCount = this.ChartFrame.BarCount; + var subBarWidth = dataWidth; + var subBarOffset = 0; + if (barCount > 0) //多柱子需要把框架柱子宽度的平均分割 + { + subBarWidth = dataWidth / barCount; + subBarOffset = subBarWidth * this.BarID; + //JSConsole.Chart.Log('[ChartSubBar::Draw] ', subBarWidth, this.BarID, subBarOffset); + } + + yBottom = ToFixedRect(yBottom); //调整为整数 + for (let i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var left = xOffset + subBarOffset + var right = xOffset + subBarOffset + subBarWidth; + if (left > chartright) break; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (value > 0) { + this.Canvas.fillStyle = this.UpBarColor; + //高度调整为整数 + let height = ToFixedRect(Math.abs(yBottom - y)); + let barWidth = subBarWidth; + if (chartright - left < subBarWidth) barWidth = chartright - left; + if (yBottom - y > 0) y = yBottom - height; + else y = yBottom + height; + this.Canvas.fillRect(ToFixedRect(left), y, ToFixedRect(barWidth), height); + } + else { + this.Canvas.fillStyle = this.DownBarColor; + //高度调整为整数 + let height = ToFixedRect(Math.abs(yBottom - y)); + let barWidth = subBarWidth; + if (chartright - left < subBarWidth) barWidth = chartright - left; + if (yBottom - y > 0) y = yBottom - height; + else y = yBottom + height; + this.Canvas.fillRect(ToFixedRect(left), y, ToFixedRect(subBarWidth), -height); + } + } + } + else //太细了 直接画柱子 + { + for (let i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (value > 0) this.Canvas.strokeStyle = this.UpBarColor; + else this.Canvas.strokeStyle = this.DownBarColor; + + this.Canvas.beginPath(); + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yBottom); + this.Canvas.stroke(); + } + } + } +} + +//柱子 支持横屏 +function ChartBar() { + this.newMethod = IChartPainting; //派生 + this.newMethod(); + delete this.newMethod; + + this.UpBarColor = g_JSChartResource.UpBarColor; + this.DownBarColor = g_JSChartResource.DownBarColor; + + this.Draw = function () { + if (this.NotSupportMessage) { + this.DrawNotSupportmessage(); + return; + } + + var isHScreen = (this.ChartFrame.IsHScreen === true); + var dataWidth = this.ChartFrame.DataWidth; + var distanceWidth = this.ChartFrame.DistanceWidth; + var chartright = this.ChartBorder.GetRight(); + if (isHScreen) chartright = this.ChartBorder.GetBottom(); + var xPointCount = this.ChartFrame.XPointCount; + var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + 2.0; + if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + 2.0; + + var bFirstPoint = true; + var drawCount = 0; + var yBottom = this.ChartFrame.GetYFromData(0); + if (dataWidth >= 4) { + yBottom = ToFixedRect(yBottom); //调整为整数 + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (value > 0) { + this.Canvas.fillStyle = this.UpBarColor; + if (isHScreen) { + let height = ToFixedRect(Math.abs(yBottom - y)); //高度调整为整数 + y = Math.min(yBottom, y); + this.Canvas.fillRect(y, ToFixedRect(left), height, ToFixedRect(dataWidth)); + } + else { + let height = ToFixedRect(Math.abs(yBottom - y)); //高度调整为整数 + if (yBottom - y > 0) y = yBottom - height; + else y = yBottom + height; + this.Canvas.fillRect(ToFixedRect(left), y, ToFixedRect(dataWidth), height); + } + } + else { + this.Canvas.fillStyle = this.DownBarColor; + //高度调整为整数 + let height = ToFixedRect(Math.abs(yBottom - y)); + if (yBottom - y > 0) y = yBottom - height; + else y = yBottom + height; + this.Canvas.fillRect(ToFixedRect(left), y, ToFixedRect(dataWidth), -height); + } + } + } + else //太细了 直接画柱子 + { + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) { + var value = this.Data.Data[i]; + if (value == null || value == 0) continue; + + var left = xOffset; + var right = xOffset + dataWidth; + if (right > chartright) break; + + var x = this.ChartFrame.GetXFromIndex(j); + var y = this.ChartFrame.GetYFromData(value); + + if (value > 0) this.Canvas.strokeStyle = this.UpBarColor; + else this.Canvas.strokeStyle = this.DownBarColor; + + this.Canvas.beginPath(); + if (isHScreen) { + this.Canvas.moveTo(y, ToFixedPoint(x)); + this.Canvas.lineTo(yBottom, ToFixedPoint(x)); + } + else { + this.Canvas.moveTo(ToFixedPoint(x), y); + this.Canvas.lineTo(ToFixedPoint(x), yBottom); + } + this.Canvas.stroke(); + } + } + } + + this.GetMaxMin = function () { + var xPointCount = this.ChartFrame.XPointCount; + var range = {}; + range.Min = 0; + range.Max = null; + for (var i = this.Data.DataOffset, j = 0; i < this.Data.Data.length && j < xPointCount; ++i, ++j) { + var value = this.Data.Data[i]; + if (range.Max == null) range.Max = value; + if (range.Max < value) range.Max = value; + if (range.Min > value) range.Min = value; + } + + return range; + } +} + +/* + 画图工具 +*/ +function IChartDrawPicture() { + this.Frame; + this.Canvas; + this.Point = new Array() //画图的点 + this.Value = new Array(); //XValue,YValue + this.PointCount = 2; //画点的个数 + this.Status = 0; //0 开始画 1 完成第1个点 2 完成第2个点 10 完成 20 移动 + this.MovePointIndex = null; //移动哪个点 0-10 对应Point索引 100 整体移动 + + this.LineColor = g_JSChartResource.DrawPicture.LineColor[0]; //线段颜色 + this.PointColor = g_JSChartResource.DrawPicture.PointColor[0]; + + this.Draw = function () { + + } + + //Point => Value + this.PointToValue = function () { + if (!this.Frame) return false; + var data = this.Frame.Data; + if (!data) return false; + + for (var i in this.Point) { + var item = this.Point[i]; + var xValue = parseInt(this.Frame.GetXData(item.X)) + data.DataOffset; + var yValue = this.Frame.GetYData(item.Y); + + this.Value[i] = {}; + this.Value[i].XValue = xValue; + this.Value[i].YValue = yValue; + } + + return true; + } + + this.IsPointIn = function (x, y) { + return false; + } + + //Value => Point + this.ValueToPoint = function () { + if (!this.Frame) return false; + var data = this.Frame.Data; + if (!data) return false; + + this.Point = []; + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + this.Point[i] = pt; + } + } + + //xStep,yStep 移动的偏移量 + this.Move = function (xStep, yStep) { + if (this.Status != 20) return fasle; + if (!this.Frame) return false; + var data = this.Frame.Data; + if (!data) return false; + + if (this.MovePointIndex == 100) //整体移动 + { + for (var i in this.Point) { + this.Point[i].X += xStep; + this.Point[i].Y += yStep; + } + } + else if (this.MovePointIndex == 0 || this.MovePointIndex == 1) { + this.Point[this.MovePointIndex].X += xStep; + this.Point[this.MovePointIndex].Y += yStep; + } + } + + this.ClipFrame = function () { + var left = this.Frame.ChartBorder.GetLeft(); + var top = this.Frame.ChartBorder.GetTopEx(); + var width = this.Frame.ChartBorder.GetWidth(); + var height = this.Frame.ChartBorder.GetHeightEx(); + + this.Canvas.save(); + this.Canvas.beginPath(); + this.Canvas.rect(left, top, width, height); + this.Canvas.clip(); + } + + //计算需要画的点的坐标 + this.CalculateDrawPoint = function () { + if (this.Status < 2) return null; + if (!this.Point.length || !this.Frame) return null; + + var drawPoint = new Array(); + if (this.Status == 10) { + var data = this.Frame.Data; + if (!data) return null; + + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + drawPoint.push(pt); + } + } + else { + drawPoint = this.Point; + } + + return drawPoint; + } + + this.DrawPoint = function (aryPoint) { + if (!aryPoint || aryPoint.length <= 0) return; + + //画点 + this.ClipFrame(); + for (var i in aryPoint) { + var item = aryPoint[i]; + + this.Canvas.beginPath(); + this.Canvas.arc(item.X, item.Y, 5, 0, 360, false); + this.Canvas.fillStyle = this.PointColor; //填充颜色 + this.Canvas.fill(); //画实心圆 + this.Canvas.closePath(); + } + + this.Canvas.restore(); + } +} + +/* + 画图工具-线段 +*/ + +function ChartDrawPictureLine() { + this.newMethod = IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.Draw = function () { + var drawPoint = this.CalculateDrawPoint(); + if (!drawPoint) return; + + this.ClipFrame(); + + for (var i in drawPoint) { + var item = drawPoint[i]; + if (i == 0) { + this.Canvas.beginPath(); + this.Canvas.moveTo(item.X, item.Y); + } + else { + this.Canvas.lineTo(item.X, item.Y); + } + + } + + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn = function (x, y) { + if (!this.Frame || this.Status != 10) return -1; + + var data = this.Frame.Data; + if (!data) return -1; + + //是否在点上 + var aryPoint = new Array(); + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + + this.Canvas.beginPath(); + this.Canvas.arc(pt.X, pt.Y, 5, 0, 360); + //JSConsole.Chart.Log('['+i+']'+'x='+x+' y='+y+' dataX='+pt.X+" dataY="+pt.Y); + if (this.Canvas.isPointInPath(x, y)) + return i; + + aryPoint.push(pt); + } + + //是否在线段上 + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y + 5); + this.Canvas.lineTo(aryPoint[0].X, aryPoint[0].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y + 5); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x, y)) + return 100; + + return -1; + } +} + +/* + 画图工具-射线 +*/ +function ChartDrawPictureHaflLine() { + this.newMethod = IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.Draw = function () { + var drawPoint = this.CalculateDrawPoint(); + if (!drawPoint || drawPoint.length != 2) return; + + this.ClipFrame(); + + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + this.Canvas.lineTo(drawPoint[1].X, drawPoint[1].Y); + + var endPoint = this.CalculateEndPoint(drawPoint); + this.Canvas.lineTo(endPoint.X, endPoint.Y); + + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + this.CalculateEndPoint = function (aryPoint) { + var left = this.Frame.ChartBorder.GetLeft(); + var right = this.Frame.ChartBorder.GetRight(); + + var a = aryPoint[1].X - aryPoint[0].X; + var b = aryPoint[1].Y - aryPoint[0].Y; + + if (a > 0) { + var a1 = right - aryPoint[0].X; + var b1 = a1 * b / a; + var y = b1 + aryPoint[0].Y; + + var pt = new Point(); + pt.X = right; + pt.Y = y; + return pt; + } + else { + var a1 = aryPoint[0].X - left; + var b1 = a1 * b / Math.abs(a); + var y = b1 + aryPoint[0].Y; + + var pt = new Point(); + pt.X = left; + pt.Y = y; + return pt; + } + } + + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn = function (x, y) { + if (!this.Frame || this.Status != 10) return -1; + + var data = this.Frame.Data; + if (!data) return -1; + + //是否在点上 + var aryPoint = new Array(); + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + + this.Canvas.beginPath(); + this.Canvas.arc(pt.X, pt.Y, 5, 0, 360); + //JSConsole.Chart.Log('['+i+']'+'x='+x+' y='+y+' dataX='+pt.X+" dataY="+pt.Y); + if (this.Canvas.isPointInPath(x, y)) + return i; + + aryPoint.push(pt); + } + + //是否在线段上 + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y + 5); + this.Canvas.lineTo(aryPoint[0].X, aryPoint[0].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y + 5); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x, y)) + return 100; + + return -1; + } +} + +/* + 画图工具-矩形 +*/ +function ChartDrawPictureRect() { + this.newMethod = IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.Draw = function () { + var drawPoint = this.CalculateDrawPoint(); + if (!drawPoint || drawPoint.length != 2) return; + + this.ClipFrame(); + + this.Canvas.beginPath(); + this.Canvas.rect(drawPoint[0].X, drawPoint[0].Y, drawPoint[1].X - drawPoint[0].X, drawPoint[1].Y - drawPoint[0].Y); + + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.stroke(); + this.Canvas.closePath(); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn = function (x, y) { + if (!this.Frame || this.Status != 10) return -1; + + var data = this.Frame.Data; + if (!data) return -1; + + //是否在点上 + var aryPoint = new Array(); + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + + this.Canvas.beginPath(); + this.Canvas.arc(pt.X, pt.Y, 5, 0, 360); + //JSConsole.Chart.Log('['+i+']'+'x='+x+' y='+y+' dataX='+pt.X+" dataY="+pt.Y); + if (this.Canvas.isPointInPath(x, y)) + return i; + + aryPoint.push(pt); + } + + //是否在矩形边框上 + var linePoint = [{ X: aryPoint[0].X, Y: aryPoint[0].Y }, { X: aryPoint[1].X, Y: aryPoint[0].Y }]; + if (this.IsPointInLine(linePoint, x, y)) + return 100; + + linePoint = [{ X: aryPoint[1].X, Y: aryPoint[0].Y }, { X: aryPoint[1].X, Y: aryPoint[1].Y }]; + if (this.IsPointInLine2(linePoint, x, y)) + return 100; + + linePoint = [{ X: aryPoint[1].X, Y: aryPoint[1].Y }, { X: aryPoint[0].X, Y: aryPoint[1].Y }]; + if (this.IsPointInLine(linePoint, x, y)) + return 100; + + linePoint = [{ X: aryPoint[0].X, Y: aryPoint[1].Y }, { X: aryPoint[0].X, Y: aryPoint[0].Y }]; + if (this.IsPointInLine2(linePoint, x, y)) + return 100; + + return -1; + } + + //点是否在线段上 水平线段 + this.IsPointInLine = function (aryPoint, x, y) { + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y + 5); + this.Canvas.lineTo(aryPoint[0].X, aryPoint[0].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y - 5); + this.Canvas.lineTo(aryPoint[1].X, aryPoint[1].Y + 5); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x, y)) + return true; + } + + //垂直线段 + this.IsPointInLine2 = function (aryPoint, x, y) { + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X - 5, aryPoint[0].Y); + this.Canvas.lineTo(aryPoint[0].X + 5, aryPoint[0].Y); + this.Canvas.lineTo(aryPoint[1].X + 5, aryPoint[1].Y); + this.Canvas.lineTo(aryPoint[1].X - 5, aryPoint[1].Y); + this.Canvas.closePath(); + if (this.Canvas.isPointInPath(x, y)) + return true; + } +} + +/* + 画图工具-弧形 +*/ +function ChartDrawPictureArc() { + this.newMethod = IChartDrawPicture; //派生 + this.newMethod(); + delete this.newMethod; + + this.Draw = function () { + var drawPoint = this.CalculateDrawPoint(); + if (!drawPoint || drawPoint.length != 2) return; + + this.ClipFrame(); + + //this.Canvas.beginPath(); + //this.Canvas.rect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y); + if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第一象限 + { + var a = drawPoint[1].X - drawPoint[0].X; + var b = drawPoint[0].Y - drawPoint[1].Y; + var step = (a > b) ? 1 / a : 1 / b; + var xcenter = drawPoint[0].X; + var ycenter = drawPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 1.5 * Math.PI; i < 2 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + for (var j = 0; j <= 0.5 * Math.PI; j += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(j), ycenter + b * Math.sin(j) * -1); + } + } + else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第二象限 + { + var a = drawPoint[0].X - drawPoint[1].X; + var b = drawPoint[0].Y - drawPoint[1].Y; + var step = (a > b) ? 1 / a : 1 / b; + var xcenter = drawPoint[1].X; + var ycenter = drawPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 0; i <= Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + } + else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第三象限 + { + var a = drawPoint[0].X - drawPoint[1].X; + var b = drawPoint[1].Y - drawPoint[0].Y; + var step = (a > b) ? 1 / a : 1 / b; + var xcenter = drawPoint[0].X; + var ycenter = drawPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = 0.5 * Math.PI; i <= 1.5 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + } + else if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第四象限 + { + var a = drawPoint[1].X - drawPoint[0].X; + var b = drawPoint[1].Y - drawPoint[0].Y; + var step = (a > b) ? 1 / a : 1 / b; + var xcenter = drawPoint[1].X; + var ycenter = drawPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y); + for (var i = Math.PI; i <= 2 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + } + + + this.Canvas.strokeStyle = this.LineColor; + this.Canvas.stroke(); + //this.Canvas.closePath(); + this.Canvas.restore(); + + //画点 + this.DrawPoint(drawPoint); + } + + //0-10 鼠标对应的点索引 100=鼠标在正个图形上 -1 鼠标不在图形上 + this.IsPointIn = function (x, y) { + if (!this.Frame || this.Status != 10) return -1; + + var data = this.Frame.Data; + if (!data) return -1; + + //是否在点上 + var aryPoint = new Array(); + for (var i in this.Value) { + var item = this.Value[i]; + var pt = new Point(); + pt.X = this.Frame.GetXFromIndex(item.XValue - data.DataOffset); + pt.Y = this.Frame.GetYFromData(item.YValue); + + this.Canvas.beginPath(); + this.Canvas.arc(pt.X, pt.Y, 5, 0, 360); + //JSConsole.Chart.Log('['+i+']'+'x='+x+' y='+y+' dataX='+pt.X+" dataY="+pt.Y); + if (this.Canvas.isPointInPath(x, y)) + return i; + + aryPoint.push(pt); + } + + //是否在弧线上 + var ArcPoint = [{ X: aryPoint[0].X, Y: aryPoint[0].Y }, { X: aryPoint[1].X, Y: aryPoint[1].Y }]; + if (this.IsPointInArc(ArcPoint, x, y)) + return 100; + + return -1; + } + this.IsPointInArc = function (aryPoint, x, y) { + if (aryPoint.length != 2) + return false; + if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第一象限 + { + var a = aryPoint[1].X - aryPoint[0].X; + var b = aryPoint[0].Y - aryPoint[1].Y; + var step = (a > b) ? 1 / a : 1 / b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner; + var xcenter = aryPoint[0].X; + var ycenter = aryPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 1.5 * Math.PI; i < 2 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + for (var j = 0; j <= 0.5 * Math.PI; j += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(j), ycenter + b * Math.sin(j) * -1); + } + for (var k = 0.5 * Math.PI; k >= 0; k -= stepiner) { + this.Canvas.lineTo(xcenter + ainer * Math.cos(k), ycenter + biner * Math.sin(j) * -1); + } + for (var l = 2 * Math.PI; l >= 1.5 * Math.PI; l -= stepiner) { + this.Canvas.lineTo(xcenter + ainer * Math.cos(l), ycenter + biner * Math.sin(l) * -1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第二象限 + { + var a = aryPoint[0].X - aryPoint[1].X; + var b = aryPoint[0].Y - aryPoint[1].Y; + var step = (a > b) ? 1 / a : 1 / b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner; + var xcenter = aryPoint[1].X; + var ycenter = aryPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 0; i <= Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + for (var j = Math.PI; j >= 0; j -= stepiner) { + this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner * Math.sin(j) * -1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第三象限 + { + var a = aryPoint[0].X - aryPoint[1].X; + var b = aryPoint[1].Y - aryPoint[0].Y; + var step = (a > b) ? 1 / a : 1 / b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner; + var xcenter = aryPoint[0].X; + var ycenter = aryPoint[1].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = 0.5 * Math.PI; i <= 1.5 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + for (var j = 1.5 * Math.PI; j >= 0.5 * Math.PI; j -= stepiner) { + this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner * Math.sin(j) * -1); + } + this.Canvas.closePath(); + } + else if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第四象限 + { + var a = aryPoint[1].X - aryPoint[0].X; + var b = aryPoint[1].Y - aryPoint[0].Y; + var step = (a > b) ? 1 / a : 1 / b; + var ainer = a * 0.8; + var biner = b * 0.8; + var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner; + var xcenter = aryPoint[1].X; + var ycenter = aryPoint[0].Y; + this.Canvas.beginPath(); + this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y); + for (var i = Math.PI; i <= 2 * Math.PI; i += step) { + this.Canvas.lineTo(xcenter + a * Math.cos(i), ycenter + b * Math.sin(i) * -1); + } + for (var j = 2 * Math.PI; j >= Math.PI; j -= stepiner) { + this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner * Math.sin(j) * -1); + } + this.Canvas.closePath(); + } + if (this.Canvas.isPointInPath(x, y)) + return true; + else + return false; + + } + +} + +/* + 指标列表 指标信息都在这里,不够后面再加字段 +*/ +function JSIndexMap() { + +} + +JSIndexMap.Get = function (id) { + var indexMap = new Map( + [ + //公司自己的指标 + ["市场多空", { IsMainIndex: false, Create: function () { return new MarketLongShortIndex() } }], + ["市场择时", { IsMainIndex: false, Create: function () { return new MarketTimingIndex() } }], + ["市场关注度", { IsMainIndex: false, Create: function () { return new MarketAttentionIndex() } }], + ["指数热度", { IsMainIndex: false, Create: function () { return new MarketHeatIndex() } }], + ["自定义指数热度", { IsMainIndex: false, Create: function () { return new CustonIndexHeatIndex() }, Name: '自定义指数热度' }], + ["财务粉饰", { IsMainIndex: false, Create: function () { return new BenfordIndex() } }], + + //能图指标 + //["能图-趋势", { IsMainIndex: false, Create: function () { return new LighterIndex1() }, Name: '大盘/个股趋势' }], + //["能图-位置研判", { IsMainIndex: false, Create: function () { return new LighterIndex2() }, Name: '位置研判' }], + //["能图-点位研判", { IsMainIndex: false, Create: function () { return new LighterIndex3() }, Name: '点位研判' }], + //["能图-资金分析", { IsMainIndex: false, Create: function () { return new LighterIndex4() }, Name: '资金分析' }], + //["能图-市场关注度", { IsMainIndex: false, Create: function () { return new LighterIndex5() }, Name: '市场关注度' }] + ] + ); + + return indexMap.get(id); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +// K线图 控件 +// this.ChartPaint[0] K线画法 这个不要修改 +// +// +function KLineChartContainer(uielement) +{ + var _self = this; + this.newMethod = JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName = 'KLineChartContainer'; + this.WindowIndex = new Array(); + this.ColorIndex; //五彩K线 + this.TradeIndex; //交易指标/专家系统 + this.Symbol; + this.Name; + this.Period = 0; //周期 0=日线 1=周线 2=月线 3=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 9=季线 10=分笔线 11=120分钟 12=240分钟 + this.IsApiPeriod = false; //使用API计算周期 + this.Right = 0; //复权 0 不复权 1 前复权 2 后复权 + this.SourceData; //原始的历史数据 + this.MaxReqeustDataCount = 3000; //数据个数 + this.MaxRequestMinuteDayCount = 5; //分钟数据请求的天数 + this.PageSize = 200; //每页数据个数 + this.KLineDrawType = 0; //0=K线 1=收盘价线 2=美国线 + this.LoadDataSplashTitle = '下载历史数据'; + this.IsAutoUpdate = false; //是否自动更新行情数据 + this.AutoUpdateFrequency = 30000; //30秒更新一次数据 + this.AutoUpdateTimer; //自动定时器 + this.RightSpaceCount=1; + this.SourceDataLimit = new Map(); //每个周期缓存数据最大个数 key=周期 value=最大个数 + this.KLineSize=null; //{ DataWidth:, } + + this.StepPixel = 4; //移动一个数据需要的像素 + this.ZoomStepPixel = 5; //放大缩小手势需要的最小像素 + this.EnableZoomUpDown=null; //是否手势/键盘/鼠标允许缩放{ Touch:true/false, Mouse:true/false, Keyboard:true/false, Wheel:true/false } + + this.DragDownload = { + Day: { Enable: false, IsEnd: false, Status: 0 }, //日线数据拖拽下载(暂不支持) Status: 0空闲 1 下载中 + Minute: { Enable: false, IsEnd: false, status: 0 } //分钟数据拖拽下载 + }; + + this.KLineApiUrl = g_JSChartResource.Domain + "/API/KLine2"; //历史K线api地址 + this.MinuteKLineApiUrl = g_JSChartResource.Domain + '/API/KLine3'; //历史分钟数据 + this.RealtimeApiUrl = g_JSChartResource.Domain + "/API/Stock"; //实时行情api地址 + this.KLineMatchUrl = g_JSChartResource.Domain + "/API/KLineMatch"; //形态匹配 + this.DragMinuteKLineApiUrl = g_JSChartResource.Domain + '/API/KLine4'; //拖动数据下载 + this.DragKLineApiUrl = g_JSChartResource.Domain + '/API/KLine5'; //拖动日K数据下载 + + this.BeforeBindMainData = null; //function(funcName) 在BindMainData() 调用前回调用 + this.AfterBindMainData = null; //function(funcName) 在BindMainData() 调用前后调用 + + this.ResetDragDownload = function () + { + this.DragDownload.Day.Status = 0; + this.DragDownload.Day.IsEnd=false; + + this.DragDownload.Minute.Status = 0; + this.DragDownload.Minute.isEnd=false; + } + + this.ChartOperator = function (obj) //图形控制函数 {ID:JSCHART_OPERATOR_ID, ...参数 } + { + var id = obj.ID; + if (id === JSCHART_OPERATOR_ID.OP_SCROLL_LEFT || id === JSCHART_OPERATOR_ID.OP_SCROLL_RIGHT) //左右移动 { Step:移动数据个数 } + { + var isLeft = (id === JSCHART_OPERATOR_ID.OP_SCROLL_LEFT ? true : false); + var step = 1; + if (obj.Step > 0) step = obj.Step; + if (this.DataMove(step * this.StepPixel, isLeft)) //每次移动一个数据 + { + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.ResetFrameXYSplit(); + this.Draw(); + } + else + { + if (id===JSCHART_OPERATOR_ID.OP_SCROLL_RIGHT && this.DragDownloadData) + this.DragDownloadData(); + } + } + else if (id === JSCHART_OPERATOR_ID.OP_ZOOM_IN || id === JSCHART_OPERATOR_ID.OP_ZOOM_OUT) //缩放 + { + var cursorIndex = {}; + cursorIndex.Index = parseInt(Math.abs(this.CursorIndex - 0.5).toFixed(0)); + if (id === JSCHART_OPERATOR_ID.OP_ZOOM_IN) + { + if (!this.Frame.ZoomUp(cursorIndex)) return; + } + else + { + if (!this.Frame.ZoomDown(cursorIndex)) return; + } + this.CursorIndex = cursorIndex.Index; + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + this.Draw(); + } + else if (id === JSCHART_OPERATOR_ID.OP_GOTO_HOME) //返回最新 + { + var hisData = null; + if (!this.Frame.Data) hisData = this.Frame.Data; + else hisData = this.Frame.SubFrame[0].Frame.Data; + if (!hisData) return; //数据还没有到达 + + var showCount = this.PageSize; + //var pageSize = this.GetMaxMinPageSize(); + //if (pageSize.Max < showCount) showCount = pageSize.Max; + //else if (pageSize.Min > showCount) showCount = pageSize.Min; + + for (var i in this.Frame.SubFrame) //设置一屏显示的数据个数 + { + var item = this.Frame.SubFrame[i].Frame; + item.XPointCount = showCount; + } + + var index = hisData.Data.length - showCount; + hisData.DataOffset = index; + this.CursorIndex = 0; + + this.LastPoint.X = null; + this.LastPoint.Y = null; + + JSConsole.Chart.Log(`[KLineChartContainer::ChartOperator] OP_GOTO_HOME, dataOffset=${hisData.DataOffset} CursorIndex=${this.CursorIndex} PageSize=${showCount}`); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + this.UpdatePointByCursorIndex(); //更新十字光标位子 + } + } + + //创建windowCount 窗口个数 + this.Create = function (windowCount) + { + this.UIElement.JSChartContainer = this; + + //创建十字光标 + this.ChartCorssCursor = new ChartCorssCursor(); + this.ChartCorssCursor.Canvas = this.Canvas; + this.ChartCorssCursor.StringFormatX = g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY = g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + this.ChartCorssCursor.StringFormatY.LanguageID = this.LanguageID; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.SplashTitle = this.LoadDataSplashTitle; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame = new HQTradeFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 30; + this.Frame.ChartBorder.Left = 5; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + this.ChartCorssCursor.Frame = this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for (var i in this.Frame.SubFrame) + { + var titlePaint = new DynamicChartTitlePainting(); + titlePaint.Frame = this.Frame.SubFrame[i].Frame; + titlePaint.Canvas = this.Canvas; + titlePaint.LanguageID = this.LanguageID; + this.Frame.SubFrame[i].Frame.TitlePaint = titlePaint; + this.TitlePaint.push(titlePaint); + } + } + + //创建子窗口 + this.CreateChildWindow = function (windowCount) + { + for (var i = 0; i < windowCount; ++i) + { + var border = new ChartBorder(); + border.UIElement = this.UIElement; + + var frame = new KLineFrame(); + frame.Canvas = this.Canvas; + frame.ChartBorder = border; + frame.Identify = i; //窗口序号 + frame.RightSpaceCount = this.RightSpaceCount; //右边 + + frame.HorizontalMax = 20; + frame.HorizontalMin = 10; + + if (i == 0) + { + frame.YSplitOperator = new FrameSplitKLinePriceY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('price'); + frame.YSplitOperator.FrameSplitData2 = this.FrameSplitData.get('double'); + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + border.BottomSpace = 12; //主图上下留空间 + border.TopSpace = 12; + } + else + { + frame.YSplitOperator = new FrameSplitY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + frame.YSplitOperator.LanguageID = this.LanguageID; + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + + frame.YSplitOperator.Frame = frame; + frame.YSplitOperator.ChartBorder = border; + frame.XSplitOperator = new FrameSplitKLineX(); + frame.XSplitOperator.Frame = frame; + frame.XSplitOperator.ChartBorder = border; + + if (i != windowCount - 1) frame.XSplitOperator.ShowText = false; + + for (var j = frame.HorizontalMin; j <= frame.HorizontalMax; j += 1) + { + frame.HorizontalInfo[j] = new CoordinateInfo(); + frame.HorizontalInfo[j].Value = j; + if (i == 0 && j == frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1] = j.toString(); + frame.HorizontalInfo[j].Font = "14px 微软雅黑"; + } + + var subFrame = new SubFrameItem(); + subFrame.Frame = frame; + if (i == 0) subFrame.Height = 20; + else subFrame.Height = 10; + + this.Frame.SubFrame[i] = subFrame; + } + } + + this.CreateSubFrameItem = function (id) + { + var border = new ChartBorder(); + border.UIElement = this.UIElement; + + var frame = new KLineFrame(); + frame.Canvas = this.Canvas; + frame.ChartBorder = border; + frame.Identify = id; //窗口序号 + + if (this.ModifyIndexDialog) frame.ModifyIndexEvent = this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent = this.ChangeIndexDialog.DoModal; + + frame.HorizontalMax = 20; + frame.HorizontalMin = 10; + frame.YSplitOperator = new FrameSplitY(); + frame.YSplitOperator.LanguageID = this.LanguageID; + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame = frame; + frame.YSplitOperator.ChartBorder = border; + frame.XSplitOperator = new FrameSplitKLineX(); + frame.XSplitOperator.Frame = frame; + frame.XSplitOperator.ChartBorder = border; + frame.XSplitOperator.ShowText = false; + + //K线数据绑定 + var xPointCouont = this.Frame.SubFrame[0].Frame.XPointCount; + frame.XPointCount = xPointCouont; + frame.Data = this.ChartPaint[0].Data; + + for (var j = frame.HorizontalMin; j <= frame.HorizontalMax; j += 1) + { + frame.HorizontalInfo[j] = new CoordinateInfo(); + frame.HorizontalInfo[j].Value = j; + frame.HorizontalInfo[j].Message[1] = j.toString(); + frame.HorizontalInfo[j].Font = "14px 微软雅黑"; + } + + var subFrame = new SubFrameItem(); + subFrame.Frame = frame; + subFrame.Height = 10; + + return subFrame; + } + + //创建主图K线画法 + this.CreateMainKLine = function () + { + var kline = new ChartKLine(); + kline.Canvas = this.Canvas; + kline.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + kline.ChartFrame = this.Frame.SubFrame[0].Frame; + kline.Name = "Main-KLine"; + kline.DrawType = this.KLineDrawType; + + this.ChartPaint[0] = kline; + + this.TitlePaint[0] = new DynamicKLineTitlePainting(); + this.TitlePaint[0].Frame = this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas = this.Canvas; + this.TitlePaint[0].LanguageID = this.LanguageID; + + //主图叠加画法 + var paint = new ChartOverlayKLine(); + paint.Canvas = this.Canvas; + paint.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame = this.Frame.SubFrame[0].Frame; + paint.Name = "Overlay-KLine"; + paint.DrawType = this.KLineDrawType; + this.OverlayChartPaint[0] = paint; + + } + + //绑定主图K线数据 + this.BindMainData = function (hisData, showCount) + { + this.ChartPaint[0].Data = hisData; + this.ChartPaint[0].Symbol = this.Symbol; + + if (this.KLineSize) + { + if (this.KLineSize.DataWidth==null) + { + showCount=this.Frame.SubFrame[0].Frame.XPointCount-this.RightSpaceCount; + } + else + { + var obj=this.Frame.SetDataWidth(this.KLineSize.DataWidth); + showCount=obj.XPointCount-this.RightSpaceCount; + this.KLineSize.DataWidth=null; + } + } + + for (var i in this.Frame.SubFrame) + { + var item = this.Frame.SubFrame[i].Frame; + item.XPointCount = showCount + this.RightSpaceCount; + item.Data = this.ChartPaint[0].Data; + + item.XSplitOperator.Symbol = this.Symbol; + item.XSplitOperator.Period = this.Period; + } + + this.TitlePaint[0].Data = this.ChartPaint[0].Data; //动态标题 + this.TitlePaint[0].Symbol = this.Symbol; + this.TitlePaint[0].Name = this.Name; + this.TitlePaint[0].Period = this.Period; + + this.ChartCorssCursor.StringFormatX.Data = this.ChartPaint[0].Data; //十字光标 + this.Frame.Data = this.ChartPaint[0].Data; + + this.OverlayChartPaint[0].MainData = this.ChartPaint[0].Data; //K线叠加 + + var dataOffset = hisData.Data.length - showCount; + if (dataOffset < 0) dataOffset = 0; + this.ChartPaint[0].Data.DataOffset = dataOffset; + + this.ChartCorssCursor.StringFormatY.Symbol = this.Symbol; + + this.CursorIndex = showCount; + if (this.CursorIndex + dataOffset >= hisData.Data.length) this.CursorIndex = hisData.Data.length - 1 - dataOffset; + if (this.CursorIndex < 0) this.CursorIndex = 0; //不一定对啊 + } + + this.UpdateMainData = function (hisData, lastDataCount) //更新主图数据(不会放大缩小数据) + { + var frameHisdata = null; + if (!this.Frame.Data) frameHisdata = this.Frame.Data; + else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata = this.Frame.SubFrame[0].Frame.Data; + if (!frameHisdata) return; + + var xPointCount=this.Frame.SubFrame[0].Frame.XPointCount; //当前一屏能显示的数据个数 + var newDataCount = 0; + if (lastDataCount > 0 && hisData.Data.length > lastDataCount) + { + newDataCount = hisData.Data.length - lastDataCount; + JSConsole.Chart.Log(`[KLineChartContainer::UpdateMainData] [count=${lastDataCount}->${hisData.Data.length}], [newDataCount=${newDataCount}]`); + } + + this.ChartPaint[0].Data = hisData; + this.ChartPaint[0].Symbol = this.Symbol; + if (hisData.Data.length>xPointCount) //不满一屏的, 不需要调整索引 + this.ChartPaint[0].Data.DataOffset = frameHisdata.DataOffset + newDataCount; //加上数据增加的个数 + for (var i in this.Frame.SubFrame) + { + var item = this.Frame.SubFrame[i].Frame; + item.Data = this.ChartPaint[0].Data; + if (i==0) + { + item.YSplitOperator.Symbol = this.Symbol; + item.YSplitOperator.Data = this.ChartPaint[0].Data; //K线数据 + item.YSplitOperator.Period = this.Period; //周期 + } + } + + this.TitlePaint[0].Data = this.ChartPaint[0].Data; //动态标题 + this.TitlePaint[0].Symbol = this.Symbol; + this.TitlePaint[0].Name = this.Name; + + this.ChartCorssCursor.StringFormatX.Data = this.ChartPaint[0].Data; //十字光标 + this.Frame.Data = this.ChartPaint[0].Data; + + for (var i in this.OverlayChartPaint) //主图股票数据绑定到叠加股票上 + { + var item = this.OverlayChartPaint[i]; + item.MainData = this.ChartPaint[0].Data; + } + + this.ChartCorssCursor.StringFormatY.Symbol = this.Symbol; + } + + //创建指定窗口指标 + this.CreateWindowIndex = function (windowIndex) + { + this.WindowIndex[windowIndex].Create(this, windowIndex); + } + + this.BindIndexData = function (windowIndex, hisData) + { + if (!this.WindowIndex[windowIndex]) return; + if (typeof (this.WindowIndex[windowIndex].RequestData) == "function") //数据需要另外下载的. + { + this.WindowIndex[windowIndex].RequestData(this, windowIndex, hisData); + return; + } + if (typeof (this.WindowIndex[windowIndex].ExecuteScript) == 'function') //脚本指标 + { + this.WindowIndex[windowIndex].ExecuteScript(this, windowIndex, hisData); + return; + } + + this.WindowIndex[windowIndex].BindData(this, windowIndex, hisData); + } + + //执行指示(专家指示 五彩K线) + this.BindInstructionIndexData = function (hisData) + { + if (this.ColorIndex && typeof (this.ColorIndex.ExecuteScript) == 'function') //五彩K线 + { + this.ColorIndex.ExecuteScript(this, 0, hisData); + } + + if (this.TradeIndex && typeof (this.TradeIndex.ExecuteScript) == 'function') //交易指标 + { + this.TradeIndex.ExecuteScript(this, 0, hisData); + } + } + + //获取子窗口的所有画法 + this.GetChartPaint = function (windowIndex) + { + var paint = new Array(); + for (var i in this.ChartPaint) + { + if (i == 0) continue; //第1个K线数据除外 + var item = this.ChartPaint[i]; + if (item.ChartFrame == this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + return paint; + } + + this.AutoUpdateEvent = function (bStart) //自定更新事件, 是给websocket使用 + { + var eventID = bStart ? JSCHART_EVENT_ID.RECV_START_AUTOUPDATE : JSCHART_EVENT_ID.RECV_STOP_AUTOUPDATE; + if (!this.mapEvent.has(eventID)) return; + + var self = this; + var event = this.mapEvent.get(eventID); + var data = { Stock: { Symbol: this.Symbol, Name: this.Name, Right: this.Right, Period: this.Period } }; + if (bStart) + { + data.Callback = function (data) //数据到达更新回调 + { + if (ChartData.IsDayPeriod(self.Period, true)) self.RecvRealtimeData(data); + else if (ChartData.IsMinutePeriod(self.Period, true)) self.RecvMinuteRealtimeData(data); + else if (ChartData.IsSecondPeriod(self.Period)) self.RecvMinuteRealtimeData(data); + } + } + event.Callback(event, data, this); + } + + this.RequestHistoryData = function () + { + var self = this; + this.CancelAutoUpdate(); + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.ResetDragDownload(); + this.Draw(); + + if (this.NetworkFilter) + { + var obj = + { + Name: 'KLineChartContainer::RequestHistoryData', //类名:: + Explain: '日K数据', + Request: { + Url: self.KLineApiUrl, Type: 'POST', + Data: { symbol: self.Symbol, count: self.MaxReqeustDataCount, field: ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"] } + }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + self.AutoUpdateEvent(true); + self.AutoUpdate(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.KLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxReqeustDataCount + }, + method: 'POST', + dataType: 'json', + success: function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + self.AutoUpdateEvent(true); + self.AutoUpdate(); + } + }); + } + + this.RecvHistoryData = function (recvData) + { + var data = recvData.data; + var aryDayData = KLineChartContainer.JsonDataToHistoryData(data); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = aryDayData; + sourceData.DataType = 0; //0=日线数据 1=分钟数据 + sourceData.Symbol = data.symbol; + this.SourceData = sourceData; + + if (this.BeforeBindMainData) this.BeforeBindMainData("RecvHistoryData"); + + //显示的数据 + var bindData = new ChartData(); + bindData.Data = aryDayData; + bindData.Right = this.Right; + bindData.Period = this.Period; + bindData.DataType = 0; + + if (bindData.Right > 0 && !this.IsApiPeriod) //复权 + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period, false) && !this.IsApiPeriod) //周期数据 + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + //绑定数据 + this.Symbol = data.symbol; + this.Name = data.name; + this.BindMainData(bindData, this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("RecvHistoryData"); + this.Frame.SetSizeChage(true); //数据到达通知坐标框架 + this.BindInstructionIndexData(bindData); //执行指示脚本 + + var firstSubFrame; //主窗口 + for (var i = 0; i < this.Frame.SubFrame.length; ++i) //执行指标 + { + if (i == 0) firstSubFrame = this.Frame.SubFrame[i].Frame; + this.BindIndexData(i, bindData); + } + + if (firstSubFrame && firstSubFrame.YSplitOperator) + { + firstSubFrame.YSplitOperator.Symbol = this.Symbol; //绑定代码 + firstSubFrame.YSplitOperator.Data = this.ChartPaint[0].Data; //K线数据 + } + + //请求叠加数据 (主数据下载完再下载)) + this.ReqeustKLineInfoData(); + this.RequestOverlayHistoryData(); + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_HISTROY_DATA)) + { + var event = this.mapEvent.get(JSCHART_EVENT_ID.RECV_HISTROY_DATA); + var data = { HistoryData: bindData, Stock: { Symbol: this.Symbol, Name: this.Name } } + event.Callback(event, data, this); + } + else //老的回调暂时保留 + { + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvHistroyData', this); //单词拼写错误, 请使用下面的回调 + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvHistoryData', this); + } + } + + this.ReqeustHistoryMinuteData = function () + { + var self = this; + this.CancelAutoUpdate(); + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.ResetDragDownload(); + this.Draw(); + + if (this.NetworkFilter) + { + var obj = + { + Name: 'KLineChartContainer::ReqeustHistoryMinuteData', //类名 + Explain: '1分钟K线数据', + Request: + { + Url: self.MinuteKLineApiUrl, Type: 'POST', Data: { + symbol: self.Symbol, count: self.MaxRequestMinuteDayCount, + field: ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"] + } + }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteHistoryData(data); + self.AutoUpdateEvent(true); + self.AutoUpdate(); + }); + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.MinuteKLineApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol"], + "symbol": self.Symbol, + "start": -1, + "count": self.MaxRequestMinuteDayCount + }, + method: 'POST', + dataType: "json", + success: function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteHistoryData(data); + self.AutoUpdateEvent(true); + self.AutoUpdate(); + } + }); + } + + this.RecvMinuteHistoryData = function (recvData) + { + var data = recvData.data; + var aryDayData = KLineChartContainer.JsonDataToMinuteHistoryData(data); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = aryDayData; + sourceData.DataType = 1; //0=日线数据 1=分钟数据 + sourceData.Symbol = data.symbol; + this.SourceData = sourceData; + if (this.BeforeBindMainData) this.BeforeBindMainData("RecvMinuteHistoryData"); + + //显示的数据 + var bindData = new ChartData(); + bindData.Data = aryDayData; + bindData.Right = this.Right; + bindData.Period = this.Period; + bindData.DataType = 1; + bindData.Symbol = data.symbol; + + if (ChartData.IsMinutePeriod(bindData.Period, false) && !this.IsApiPeriod) //周期数据 + { + var periodData = sourceData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + //绑定数据 + this.Symbol = data.symbol; + this.Name = data.name; + this.BindMainData(bindData, this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("RecvMinuteHistoryData"); + this.Frame.SetSizeChage(true); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + var firstSubFrame; //主窗口 + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + { + if (i == 0) firstSubFrame = this.Frame.SubFrame[i].Frame; + this.BindIndexData(i, bindData); + } + + if (firstSubFrame && firstSubFrame.YSplitOperator) + { + firstSubFrame.YSplitOperator.Symbol = this.Symbol; //绑定代码 + firstSubFrame.YSplitOperator.Data = this.ChartPaint[0].Data; //K线数据 + } + + this.OverlayChartPaint[0].Data = null; //分钟数据不支持叠加 清空 + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_HISTROY_DATA)) + { + var event = this.mapEvent.get(JSCHART_EVENT_ID.RECV_HISTROY_DATA); + var data = { HistoryData: bindData, Stock: { Symbol: this.Symbol, Name: this.Name } } + event.Callback(event, data, this); + } + else + { + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvMinuteHistoryData', this); + } + } + + //请求实时行情数据 + this.RequestRealtimeData = function () + { + var self = this; + + if (this.NetworkFilter) + { + var obj = + { + Name: 'KLineChartContainer::RequestRealtimeData', //类名::函数名 + Explain: '当天最新日线数据', + Request: + { + Url: self.RealtimeApiUrl, Data: { + symbol: [self.Symbol], + field: ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", "amount", "date", "time"] + }, Type: 'POST' + }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.RecvRealtimeData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.RealtimeApiUrl, + data: + { + "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time"], + "symbol": [self.Symbol], + "start": -1 + }, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.RecvRealtimeData(data); + self.AutoUpdate(); + } + }); + } + + this.RecvRealtimeData = function (recvdata) + { + if (this.IsOnTouch == true) return; //正在操作中不更新数据 + var data=recvdata.data; + if (!data || !data.stock || !data.stock[0] || this.Symbol != data.stock[0].symbol) + { + JSConsole.Chart.Log('[KLineChartContainer::RecvRealtimeData] recvdata error', recvdata); + return; + } + + var realtimeData = KLineChartContainer.JsonDataToRealtimeData(data); + var item = this.SourceData.Data[this.SourceData.Data.length - 1]; //最新的一条数据 + var lastDataCount = this.GetHistoryDataCount(); //保存下上一次的数据个数 + + if (item.Date == realtimeData.Date) //实时行情数据更新 + { + //JSConsole.Chart.Log('[KLineChartContainer::RecvRealtimeData] update kline by minute data', realtimeData); + item.Close = realtimeData.Close; + item.High = realtimeData.High; + item.Low = realtimeData.Low; + item.Vol = realtimeData.Vol; + item.Amount = realtimeData.Amount; + } + else if (item.Date < realtimeData.Date) //新增加数据 + { + JSConsole.Chart.Log('[KLineChartContainer::RecvRealtimeData] insert kline by minute data', realtimeData); + var newItem = new HistoryData(); + newItem.YClose = realtimeData.YClose; + newItem.Open = realtimeData.Open; + newItem.Close = realtimeData.Close; + newItem.High = realtimeData.High; + newItem.Low = realtimeData.Low; + newItem.Vol = realtimeData.Vol; + newItem.Amount = realtimeData.Amount; + newItem.Date = realtimeData.Date; + //没有前收盘就用上一个数据的收盘价 + if (!IFrameSplitOperator.IsNumber(newItem.YClose) && this.SourceData.Data.length>0) + newItem.YClose=this.SourceData.Data[this.SourceData.Data.length-1].YClose; + + this.SourceData.Data.push(newItem); + } + else + { + return; + } + + var bindData = new ChartData(); + bindData.Data = this.SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + bindData.DataType = this.SourceData.DataType; + bindData.Symbol = this.Symbol; + + if (bindData.Right > 0 && ChartData.IsDayPeriod(bindData.Period,true) && !this.IsApiPeriod) //复权(日线数据才复权) + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if (!this.IsApiPeriod) + { + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + } + + + //绑定数据 + this.UpdateMainData(bindData, lastDataCount); + this.Frame.SetSizeChage(true); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) { + this.BindIndexData(i, bindData); + } + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + this.SendKLineUpdateEvent(bindData); + } + + this.GetHistoryDataCount = function () + { + var frameHisdata = null; + if (!this.Frame.Data) frameHisdata = this.Frame.Data; + else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata = this.Frame.SubFrame[0].Frame.Data; + if (!frameHisdata) return -1; + var lastDataCount = frameHisdata.Data.length; //上一个的数据长度 + return lastDataCount; + } + + this.RequestMinuteRealtimeData = function () + { + var self = this; + if (this.NetworkFilter) + { + var obj = + { + Name: 'KLineChartContainer::RequestMinuteRealtimeData', //类名:: + Explain: '当天1分钟K线数据', + Request: { + Url: self.RealtimeApiUrl, Data: { + symbol: [self.Symbol], + field: ["name", "symbol", "price", "yclose", "minutecount", "minute", "date", "time"] + }, Type: 'POST' + }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.RecvMinuteRealtimeData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.RealtimeApiUrl, + data: + { + "field": ["name", "symbol", "price", "yclose", "minutecount", "minute", "date", "time"], + "symbol": [self.Symbol], + "start": -1 + }, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.RecvMinuteRealtimeData(data); + self.AutoUpdate(); + } + }); + } + + this.SetSourceDatatLimit = function (aryLimit) + { + this.SourceDataLimit = new Map(); + for (var i in aryLimit) + { + var item = aryLimit[i]; + this.SourceDataLimit.set(item.Period, item.MaxCount); //每个周期缓存数据最大个数 key=周期 value=最大个数 + JSConsole.Chart.Log(`[KLineChartContainer::SetSourceDatatLimit] Period=${item.Period}, MaxCount=${item.MaxCount}`); + } + } + + this.ReduceSourceData = function () + { + if (!this.SourceDataLimit) return; + if (!this.SourceDataLimit.has(this.Period)) return; + + var limitCount = this.SourceDataLimit.get(this.Period); + if (limitCount < 50) return; + + var frameHisdata = null; + if (!this.Frame.Data) frameHisdata = this.Frame.Data; + else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata = this.Frame.SubFrame[0].Frame.Data; + if (!frameHisdata) return; + + var dataOffset = frameHisdata.DataOffset; + var removeCount = 0; + while (this.SourceData.Data.length > limitCount) + { + this.SourceData.Data.shift(); + --dataOffset; + ++removeCount; + } + + if (removeCount > 0) + { + if (dataOffset < 0) dataOffset = 0; + frameHisdata.DataOffset = dataOffset; + JSConsole.Chart.Log(`[KLineChartContainer::ReduceSourceData] remove data ${removeCount}, dataOffset=${dataOffset}`); + } + } + + this.RecvMinuteRealtimeData = function (recvData) + { + var data=recvData.data; + if (this.IsOnTouch == true) return; //正在操作中不更新数据 + if (data.ver == 2.0) + { + this.RecvMinuteRealtimeDataV2(data); //v2.0数据版本 + return; + } + + if (!data.stock || !data.stock[0] || this.Symbol != data.stock[0].symbol) return; + var realtimeData = KLineChartContainer.JsonDataToMinuteRealtimeData(data); + if (!realtimeData) return; + if (this.IsApiPeriod) this.ReduceSourceData(); //减少数据 + var lastDataCount = this.GetHistoryDataCount(); //保存下上一次的数据个数 + var lastSourceDataCount = this.SourceData.Data.length; + if (!this.SourceData.MergeMinuteData(realtimeData)) return; + JSConsole.Chart.Log(`[KLineChartContainer::RecvMinuteRealtimeData] update kline by 1 minute data [${lastSourceDataCount}->${this.SourceData.Data.length}]`); + + var bindData = new ChartData(); + bindData.Data = this.SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + bindData.DataType = this.SourceData.DataType; + bindData.Symbol = this.Symbol; + + if (bindData.Right > 0 && ChartData.IsDayPeriod(bindData.Period,true) && !this.IsApiPeriod) //复权(日线数据才复权) + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if (!this.IsApiPeriod) + { + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + } + + //绑定数据 + this.UpdateMainData(bindData, lastDataCount); + this.Frame.SetSizeChage(true); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) { + this.BindIndexData(i, bindData); + } + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + this.SendKLineUpdateEvent(bindData); + } + + this.RecvMinuteRealtimeDataV2 = function (data) //新版本的 + { + if (this.IsOnTouch == true) return; //正在操作中不更新数据 + var aryMinuteData = KLineChartContainer.JsonDataToMinuteHistoryData(data); + if (!aryMinuteData || aryMinuteData.length <= 0) return; + if (this.IsApiPeriod) this.ReduceSourceData(); //减少数据 + var lastDataCount = this.GetHistoryDataCount(); //保存下上一次的数据个数 + + if (!this.SourceData.MergeMinuteData(aryMinuteData)) return; + + JSConsole.Chart.Log(`[KLineChartContainer::RecvMinuteRealtimeDataV2] update kline by 1 minute data [${lastDataCount}->${this.SourceData.Data.length}]`); + + var bindData = new ChartData(); + bindData.Data = this.SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + bindData.DataType = this.SourceData.DataType; + bindData.Symbol = this.Symbol; + + if (bindData.Right > 0 && ChartData.IsDayPeriod(bindData.Period, true) && !this.IsApiPeriod) //复权(日线数据才复权) + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if ((ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) && !this.IsApiPeriod) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + //绑定数据 + this.UpdateMainData(bindData, lastDataCount); + this.Frame.SetSizeChage(true); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) { + this.BindIndexData(i, bindData); + } + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + this.SendKLineUpdateEvent(bindData); + } + + this.SendKLineUpdateEvent = function (bindData) + { + var event = this.GetEvent(JSCHART_EVENT_ID.RECV_KLINE_UPDATE_DATA); + if (event && event.Callback) + { + var data = { HistoryData: bindData, Stock: { Symbol: this.Symbol, Name: this.Name } } + event.Callback(event, data, this); + return true; + } + + return false; + } + + //周期切换 + this.ChangePeriod = function (period, option) + { + var isChangeKLineDrawType = false; + var right=null; //复权 + if (option && option.KLine) + { + if (IFrameSplitOperator.IsNumber(option.KLine.DrawType)) isChangeKLineDrawType = true; + if (IFrameSplitOperator.IsNumber(option.KLine.Right)) right=option.KLine.Right; + }; + + if (this.Period == period) + { + if (isChangeKLineDrawType) this.ChangeKLineDrawType(option.KLine.DrawType); + return; + } + + if (isChangeKLineDrawType) this.ChangeKLineDrawType(option.KLine.DrawType, false); //切换K线类型, 不重绘 + + var isDataTypeChange = true; + if (this.SourceData) + { + var isDataTypeChange=false; + if (period > CUSTOM_DAY_PERIOD_START && period <= CUSTOM_DAY_PERIOD_END) + { + if (this.SourceData.DataType != 0) isDataTypeChange = true; + } + else if ((period > CUSTOM_MINUTE_PERIOD_START && period <= CUSTOM_MINUTE_PERIOD_END) || + (period > CUSTOM_SECOND_PERIOD_START && period <= CUSTOM_SECOND_PERIOD_END)) + { + if (this.SourceData.DataType != 1) isDataTypeChange = true; + } + else + { + switch (period) + { + case 0: //日线 + case 1: //周 + case 2: //月 + case 3: //年 + case 21: //双周 + if (this.SourceData.DataType != 0) isDataTypeChange = true; + break; + case 4: //1分钟 + case 5: //5分钟 + case 6: //15分钟 + case 7: //30分钟 + case 8: //60分钟 + case 11: //2小时 + case 12: //4小时 + if (this.SourceData.DataType != 1) isDataTypeChange = true; + break; + } + } + } + + this.Period = period; + if (right!=null) this.Right=right; + if (isDataTypeChange == false && !this.IsApiPeriod) + { + this.Update(); + return; + } + + if (ChartData.IsDayPeriod(this.Period, true)) + { + this.CancelAutoUpdate(); //先停止更新 + this.AutoUpdateEvent(false); + this.RequestHistoryData(); //请求日线数据 + //this.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(this.Period, true) || ChartData.IsSecondPeriod(this.Period)) + { + this.CancelAutoUpdate(); //先停止更新 + this.AutoUpdateEvent(false); + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + } + + //复权切换 + this.ChangeRight = function (right) + { + if (IsIndexSymbol(this.Symbol)) return; //指数没有复权 + + if (right < 0 || right > 2) return; + + if (this.Right == right) return; + + this.Right = right; + + if (!this.IsApiPeriod) + { + this.Update(); + return; + } + else + { + if (ChartData.IsDayPeriod(this.Period, true)) + { + this.CancelAutoUpdate(); //先停止更新 + this.AutoUpdateEvent(false); + this.RequestHistoryData(); //请求日线数据 + //this.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(this.Period, true) || ChartData.IsSecondPeriod(this.Period)) + { + this.CancelAutoUpdate(); //先停止更新 + this.AutoUpdateEvent(false); + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + } + } + + //删除某一个窗口的指标 + this.DeleteIndexPaint = function (windowIndex) { + let paint = new Array(); //踢出当前窗口的指标画法 + for (let i in this.ChartPaint) { + let item = this.ChartPaint[i]; + + if (i == 0 || item.ChartFrame != this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = null; //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.IsLocked = false; //解除上锁 + this.Frame.SubFrame[windowIndex].Frame.YSplitScale = null; //清空固定刻度 + + this.ChartPaint = paint; + + //清空东条标题 + var titleIndex = windowIndex + 1; + this.TitlePaint[titleIndex].Data = []; + this.TitlePaint[titleIndex].Title = null; + } + + + this.ShowKLine = function (isShow) //显示隐藏主图K线 + { + if (this.ChartPaint.length <= 0 || !this.ChartPaint[0]) return; + this.ChartPaint[0].IsShow = isShow; + } + + this.SetInstructionData = function (type, instructionData) //设置指示数据 + { + if (this.ChartPaint.length <= 0 || !this.ChartPaint[0]) return; + if (type == 2) //五彩K线 + { + this.ChartPaint[0].ColorData = instructionData.Data; + } + else if (type == 1) //专家指示 + { + this.ChartPaint[0].TradeData = { Sell: instructionData.Sell, Buy: instructionData.Buy }; + } + } + + this.ChangeInstructionIndex = function (indexName) + { + let scriptData = new JSCommonIndexScript.JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if (indexInfo.InstructionType != 1 && indexInfo.InstructionType != 2) return; + + this.ChangeInstructionScriptIndex(indexInfo); + + } + + this.ChangeInstructionScriptIndex = function (indexData) + { + if (indexData.InstructionType == 1) //交易系统 + { + this.TradeIndex = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args, indexData); //脚本执行 + } + else if (indexData.InstructionType == 2) //五彩K线 + { + this.ColorIndex = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args, indexData); //脚本执行 + } + else + { + return; + } + + var bindData = this.ChartPaint[0].Data; + this.BindInstructionIndexData(bindData); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.CancelInstructionIndex = function () //取消指示数据 + { + if (this.ChartPaint.length <= 0 || !this.ChartPaint[0]) return; + + this.ColorIndex=null; + this.TradeIndex=null; + this.ChartPaint[0].ColorData = null; //五彩K线数据取消掉 + this.ChartPaint[0].TradeData = null; //交易系统数据取消 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //切换成 脚本指标 + this.ChangeScriptIndex = function (windowIndex, indexData) + { + this.DeleteIndexPaint(windowIndex); + this.WindowIndex[windowIndex] = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args, indexData); //脚本执行 + + var bindData = this.ChartPaint[0].Data; + this.BindIndexData(windowIndex, bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //切换api指标 + this.ChangeAPIIndex = function (windowIndex, indexData) + { + this.DeleteIndexPaint(windowIndex); + //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 } + var apiItem = indexData.API; + this.WindowIndex[windowIndex] = new APIScriptIndex(apiItem.Name, apiItem.Script, apiItem.Args, indexData); + + var bindData = this.ChartPaint[0].Data; + this.BindIndexData(windowIndex, bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + + //切换指标 指定切换窗口指标 + this.ChangeIndex = function (windowIndex, indexName, option) + { + if (option && option.API) //切换api指标 + return this.ChangeAPIIndex(windowIndex, option); + + var indexItem = JSIndexMap.Get(indexName); + if (!indexItem) + { + //查找系统指标 + let scriptData = new JSCommonIndexScript.JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if (indexInfo.IsMainIndex) + { + windowIndex = 0; //主图指标只能在主图显示 + } + else + { + if (windowIndex == 0) windowIndex = 1; //幅图指标,不能再主图显示 + } + let indexData = indexInfo; + if (option) + { + if (option.FloatPrecision >= 0) indexData.FloatPrecision = option.FloatPrecision; + if (option.StringFormat > 0) indexData.StringFormat = option.StringFormat; + if (option.Args) indexData.Args = option.Args; + } + + return this.ChangeScriptIndex(windowIndex, indexData); + } + + //主图指标 + if (indexItem.IsMainIndex) + { + if (windowIndex > 0) windowIndex = 0; //主图指标只能在主图显示 + } + else + { + if (windowIndex == 0) windowIndex = 1; //幅图指标,不能再主图显示 + } + + var paint = new Array(); //踢出当前窗口的指标画法 + for (var i in this.ChartPaint) + { + var item = this.ChartPaint[i]; + if (i == 0 || item.ChartFrame != this.Frame.SubFrame[windowIndex].Frame) paint.push(item); + } + + //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = null; + this.Frame.SubFrame[windowIndex].Frame.YSplitScale=null; + + this.ChartPaint = paint; + + //清空东条标题 + var titleIndex = windowIndex + 1; + this.TitlePaint[titleIndex].Data = []; + this.TitlePaint[titleIndex].Title = null; + + this.WindowIndex[windowIndex] = indexItem.Create(); + this.CreateWindowIndex(windowIndex); + + var bindData = this.ChartPaint[0].Data; + this.BindIndexData(windowIndex, bindData); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.ChangeKLineDrawType = function (drawType, isDraw) + { + if (this.KLineDrawType == drawType) return; + + this.KLineDrawType = drawType; + for (var i in this.ChartPaint) + { + var item = this.ChartPaint[i]; + if (i == 0) item.DrawType = this.KLineDrawType; + else if (item.ClassName == 'ChartVolStick') item.KLineDrawType = this.KLineDrawType + } + + if (this.OverlayChartPaint[0]) this.OverlayChartPaint[0].DrawType = this.KLineDrawType; //叠加K线修改 + + if (isDraw == false) return; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + //获取当天的显示的指标 + this.GetIndexInfo = function () + { + var aryIndex = []; + for (var i in this.WindowIndex) + { + var item = this.WindowIndex[i]; + var info = { Name: item.Name }; + if (item.ID) info.ID = item.ID; + aryIndex.push(info); + } + + return aryIndex; + } + + this.ChangeIndexTemplate = function (option) //切换指标模板 可以设置指标窗口个数 每个窗口的指标 + { + if (!option.Windows) return; + var count = option.Windows.length; + if (count <= 0) return; + var currentLength = this.Frame.SubFrame.length; + + var period=null, right=null; + if (option.KLine) + { + if (IFrameSplitOperator.IsNumber(option.KLine.Period) && option.KLine.Period!=this.Period) period=option.KLine.Period; //周期 + if (IFrameSplitOperator.IsNumber(option.KLine.Right) && option.KLine.Right!=this.Right) right=option.KLine.Right; //复权 + } + + var bRefreshData= (period!=null || right!=null); + + //清空所有的指标图型 + for (var i = 0; i < currentLength; ++i) + { + this.DeleteIndexPaint(i); + var frame = this.Frame.SubFrame[i]; + frame.YSpecificMaxMin = null; + frame.IsLocked = false; + frame.YSplitScale = null; + } + + if (currentLength > count) + { + this.Frame.SubFrame.splice(count, currentLength - count); + this.WindowIndex.splice(count, currentLength - count); + } + else + { + for (var i = currentLength; i < count; ++i) //创建新的指标窗口 + { + var subFrame = this.CreateSubFrameItem(i); + this.Frame.SubFrame[i] = subFrame; + var titlePaint = new DynamicChartTitlePainting(); + titlePaint.Frame = this.Frame.SubFrame[i].Frame; + titlePaint.Canvas = this.Canvas; + titlePaint.LanguageID = this.LanguageID; + this.TitlePaint[i + 1] = titlePaint; + } + } + + var systemScript = new JSCommonIndexScript.JSIndexScript(); + var bindData = this.ChartPaint[0].Data; + for (var i = 0; i < count; ++i) + { + var windowIndex = i; + var item=option.Windows[i]; + var frameItem=null; + if(option.Frame && option.Frame.length>i) frameItem=option.Frame[i]; + var titleIndex = windowIndex + 1; + this.TitlePaint[titleIndex].Data = []; + this.TitlePaint[titleIndex].Title = null; + + if (item.Script) //自定义指标 + { + this.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item); //脚本执行 + if (!bRefreshData) this.BindIndexData(windowIndex,bindData); //执行脚本 + } + else + { + var indexID = item.Index; + var indexItem = JSIndexMap.Get(indexID); + if (indexItem) + { + this.WindowIndex[i] = indexItem.Create(); + this.CreateWindowIndex(windowIndex); + if (!bRefreshData) this.BindIndexData(windowIndex, bindData); + } + else + { + var indexInfo = systemScript.Get(indexID); + if (indexInfo) + { + var args = indexInfo.Args; + if (option.Windows[i].Args) args = option.Windows[i].Args; + let indexData = + { + Name: indexInfo.Name, Script: indexInfo.Script, Args: args, ID: indexID, + //扩展属性 可以是空 + KLineType: indexInfo.KLineType, YSpecificMaxMin: indexInfo.YSpecificMaxMin, YSplitScale: indexInfo.YSplitScale, + FloatPrecision: indexInfo.FloatPrecision, Condition: indexInfo.Condition, + OutName:indexInfo.OutName + }; + + this.WindowIndex[i] = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args, indexData); //脚本执行 + if (!bRefreshData) this.BindIndexData(windowIndex, bindData); //执行脚本 + } + } + } + + if (item.IndexParamSpace >= 0) this.Frame.SubFrame[i].Frame.IndexParamSpace = item.IndexParamSpace; + if (item.IsDrawTitleBG==true) this.Frame.SubFrame[i].Frame.IsDrawTitleBG=item.IsDrawTitleBG; + + if (frameItem) + { + if (frameItem.SplitCount) this.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount = frameItem.SplitCount; + if (frameItem.IsShowBorder == false) this.Frame.SubFrame[i].Frame.IsShowBorder = frameItem.IsShowBorder; + if (frameItem.IsShowXLine === false || frameItem.IsShowXLine ===true) this.Frame.SubFrame[i].Frame.IsShowXLine = frameItem.IsShowXLine; + if (frameItem.IsShowYLine===false ||frameItem.IsShowYLine===true) this.Frame.SubFrame[i].Frame.IsShowYLine=frameItem.IsShowYLine; + + if (frameItem.IsShowLeftText === false || item.IsShowLeftText === true) this.Frame.SubFrame[i].Frame.IsShowYText[0] = frameItem.IsShowLeftText; //显示左边刻度 + if (frameItem.IsShowRightText === false || item.IsShowRightText === true) this.Frame.SubFrame[i].Frame.IsShowYText[1] = frameItem.IsShowRightText; //显示右边刻度 + } + } + + //最后一个显示X轴坐标 + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + { + var item = this.Frame.SubFrame[i].Frame; + if (i == this.Frame.SubFrame.length - 1) item.XSplitOperator.ShowText = true; + else item.XSplitOperator.ShowText = false; + } + + if (!bRefreshData) + { + this.UpdataDataoffset(); //更新数据偏移 + this.Frame.SetSizeChage(true); + this.ResetFrameXYSplit(); + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + else + { + this.Frame.SetSizeChage(true); + if (period!=null) this.ChangePeriod(period, option); + else if (right!=null) this.ChangeRight(right); + } + } + + this.RemoveIndexWindow=function(id) + { + JSConsole.Chart.Log('[KLineChartContainer::RemoveIndexWindow] remove id', id); + if (id==0) return; + if (!this.Frame.SubFrame) return; + if (id>=this.Frame.SubFrame.length) return; + + var delFrame=this.Frame.SubFrame[id].Frame; + this.DeleteIndexPaint(id); + this.Frame.SubFrame.splice(id,1); + this.WindowIndex.splice(id,1); + this.TitlePaint.splice(id+1,1); //删除对应的动态标题 + + for(var i=0;i0) + { + var aryDrawPicture=[]; + for(var i=0;i 0 && ChartData.IsDayPeriod(bindData.Period, true)) //复权(日线数据才复权) + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 (0= 日线,4=1分钟线 不需要处理)) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + //绑定数据 + this.BindMainData(bindData, this.PageSize); + if (this.AfterBindMainData) this.AfterBindMainData("Update"); + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) { + this.BindIndexData(i, bindData); + } + + //叠加数据周期调整 + if (this.OverlayChartPaint[0].SourceData) + { + if (ChartData.IsMinutePeriod(this.Period, true)) //分钟不支持 清空掉 + { + this.OverlayChartPaint[0].Data = null; + } + else + { + var bindData = new ChartData(); + bindData.Data = this.OverlayChartPaint[0].SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + + if (bindData.Right > 0 && !IsIndexSymbol(this.OverlayChartPaint[0].Symbol)) //复权数据 + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + var aryOverlayData = this.SourceData.GetOverlayData(bindData.Data); //和主图数据拟合以后的数据 + bindData.Data = aryOverlayData; + + if (ChartData.IsDayPeriod(bindData.Period, false)) //周期数据 + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + this.OverlayChartPaint[0].Data = bindData; + } + } + + this.ReqeustKLineInfoData(); + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + //切换股票代码 + this.ChangeSymbol = function (symbol) + { + this.CancelAutoUpdate(); //先停止更新 + this.AutoUpdateEvent(false); + this.Symbol = symbol; + if (IsIndexSymbol(symbol)) this.Right = 0; //指数没有复权 + + if (this.Frame && this.Frame.SubFrame) //清空指标 + { + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + this.DeleteIndexPaint(i); + } + + if (ChartData.IsDayPeriod(this.Period, true)) + { + this.RequestHistoryData(); //请求日线数据 + //this.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(this.Period, true) || ChartData.IsSecondPeriod(this.Period)) + { + this.ReqeustHistoryMinuteData(); //请求分钟数据 + } + } + + this.ReqeustKLineInfoData = function () + { + if (this.ChartPaint.length > 0) + { + var klinePaint = this.ChartPaint[0]; + klinePaint.InfoData = new Map(); + } + + //信息地雷信息 + for (var i in this.ChartInfo) + { + this.ChartInfo[i].RequestData(this); + } + } + + //设置K线信息地雷 + this.SetKLineInfo = function (aryInfo, bUpdate) + { + this.ChartInfo = []; //清空信息地雷 + for (var i in aryInfo) + { + var infoItem = JSKLineInfoMap.Get(aryInfo[i]); + if (!infoItem) continue; + var item = infoItem.Create(); + item.MaxReqeustDataCount = this.MaxReqeustDataCount; + this.ChartInfo.push(item); + } + + if (bUpdate == true) this.ReqeustKLineInfoData(); + } + + this.SetPolicyInfo = function (aryPolicy, bUpdate) + { + if (!aryPolicy || !aryPolicy.length) return; + var infoItem = JSKLineInfoMap.Get('策略选股'); + if (!infoItem) return; + var policyInfo = infoItem.Create(); + policyInfo.SetPolicyList(aryPolicy); + policyInfo.MaxReqeustDataCount = this.MaxReqeustDataCount; + this.ChartInfo.push(policyInfo); + + if (bUpdate == true) this.ReqeustKLineInfoData(); + } + + //叠加股票 + this.OverlaySymbol = function (symbol,option) + { + var paint = this.OverlayChartPaint[0]; + if (!paint.MainData) return false; + + paint.Symbol = symbol; + if (option) + { + if (paint.Color) paint.Color = option.Color; + } + if (ChartData.IsDayPeriod(this.Period, true)) this.RequestOverlayHistoryData(); //请求日线数据 + + return true; + } + + this.GetRequestDataCount = function () //K线请求数据个数 (由于可以拖拽下载历史数据,所有原来固定个数的就不能用了) + { + var result = { MaxRequestDataCount: this.MaxReqeustDataCount, MaxRequestMinuteDayCount: this.MaxRequestMinuteDayCount }; + + if (!this.SourceData || !this.SourceData.Data || this.SourceData.Data.length <= 0) return result; + + if (ChartData.IsDayPeriod(this.Period, true)) + { + var lCount = this.SourceData.Data.length; + if (lCount > result.MaxRequestDataCount) result.MaxRequestDataCount = lCount; + } + else if (ChartData.IsMinutePeriod(this.Period, true)) + { + + } + + return result; + } + + this.RequestOverlayHistoryData = function () + { + if (!this.OverlayChartPaint.length) return; + + var symbol = this.OverlayChartPaint[0].Symbol; + if (!symbol) return; + + var self = this; + var dataCount = this.GetRequestDataCount(); + var firstDate = this.SourceData.Data[0].Date; + if (this.NetworkFilter) + { + var obj = + { + Name: 'KLineChartContainer::RequestOverlayHistoryData', //类名:: + Explain: '叠加股票日K线数据', + Request: { + Url: self.KLineApiUrl, Data: { + symbol: symbol, count: dataCount.MaxRequestDataCount, "first": { date: firstDate }, + field: ["name", "symbol", "yclose", "open", "price", "high", 'vol', 'amount'] + }, Type: 'POST' + }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.RecvOverlayHistoryData(data); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + //请求数据 + wx.request({ + url: this.KLineApiUrl, + data: + { + "field":["name","symbol","yclose","open","price","high"], + "symbol": symbol, + "start": -1, + "count": this.MaxReqeustDataCount + }, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.RecvOverlayHistoryData(data); + } + }); + } + + this.RecvOverlayHistoryData = function (recvData) + { + var data = recvData.data; + var aryDayData = KLineChartContainer.JsonDataToHistoryData(data); + + //原始叠加数据 + var sourceData = new ChartData(); + sourceData.Data = aryDayData; + + var bindData = new ChartData(); + bindData.Data = aryDayData; + bindData.Period = this.Period; + bindData.Right = this.Right; + + if (bindData.Right > 0 && !IsIndexSymbol(data.symbol)) //复权数据 ,指数没有复权) + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + var aryOverlayData = this.SourceData.GetOverlayData(bindData.Data); //和主图数据拟合以后的数据 + bindData.Data = aryOverlayData; + + if (ChartData.IsDayPeriod(bindData.Period, false)) //周期数据 + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + this.OverlayChartPaint[0].Data = bindData; + this.OverlayChartPaint[0].SourceData = sourceData; + this.OverlayChartPaint[0].Title = data.name; + this.OverlayChartPaint[0].Symbol = data.symbol; + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType = 1; //调整为百份比坐标 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + //取消叠加股票 + this.ClearOverlaySymbol = function () + { + this.OverlayChartPaint[0].Symbol = null; + this.OverlayChartPaint[0].Data = null; + this.OverlayChartPaint[0].SourceData = null; + this.OverlayChartPaint[0].TooltipRect = []; + this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType = 0; //调整一般坐标 + + this.UpdateFrameMaxMin(); + this.Draw(); + } + + //创建画图工具 + this.CreateChartDrawPicture = function (name) { + return false; + } + + this.SetChartDrawPictureFirstPoint = function (x, y) { + var drawPicture = this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (!this.Frame.SubFrame || this.Frame.SubFrame.length <= 0) return false; + + for (var i in this.Frame.SubFrame) { + var frame = this.Frame.SubFrame[i].Frame; + var left = frame.ChartBorder.GetLeft(); + var top = frame.ChartBorder.GetTopEx(); + var height = frame.ChartBorder.GetHeight(); + var width = frame.ChartBorder.GetWidth(); + + this.Canvas.rect(left, top, width, height); + if (this.Canvas.isPointInPath(x, y)) { + drawPicture.Frame = frame; + break; + } + } + + if (!drawPicture.Frame) return false; + + drawPicture.Point[0] = new Point(); + drawPicture.Point[0].X = x - this.UIElement.getBoundingClientRect().left; + drawPicture.Point[0].Y = y - this.UIElement.getBoundingClientRect().top; + drawPicture.Status = 1; //第1个点完成 + } + + this.SetChartDrawPictureSecondPoint = function (x, y) { + var drawPicture = this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + drawPicture.Point[1] = new Point(); + drawPicture.Point[1].X = x - this.UIElement.getBoundingClientRect().left; + drawPicture.Point[1].Y = y - this.UIElement.getBoundingClientRect().top; + + drawPicture.Status = 2; //设置第2个点 + } + + //xStep,yStep 移动的偏移量 + this.MoveChartDrawPicture = function (xStep, yStep) { + var drawPicture = this.CurrentChartDrawPicture; + if (!drawPicture) return false; + + //JSConsole.Chart.Log("xStep="+xStep+" yStep="+yStep); + drawPicture.Move(xStep, yStep); + + return true; + } + + this.FinishChartDrawPicturePoint = function () { + var drawPicture = this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (drawPicture.PointCount != drawPicture.Point.length) return false; + + drawPicture.Status = 10; //完成 + drawPicture.PointToValue(); + + this.ChartDrawPicture.push(drawPicture); + this.CurrentChartDrawPicture = null; + + return true; + } + + this.FinishMoveChartDrawPicture = function () { + var drawPicture = this.CurrentChartDrawPicture; + if (!drawPicture) return false; + if (drawPicture.PointCount != drawPicture.Point.length) return false; + + drawPicture.Status = 10; //完成 + drawPicture.PointToValue(); + + this.CurrentChartDrawPicture = null; + return true; + } + + //清空所有的画线工具 + this.ClearChartDrawPicture = function (drawPicture) { + if (!drawPicture) { + this.ChartDrawPicture = []; + this.Draw(); + } + else { + for (var i in this.ChartDrawPicture) { + if (this.ChartDrawPicture[i] == drawPicture) { + this.ChartDrawPicture.splice(i, 1); + this.Draw(); + } + } + } + } + + //更新信息地雷 + this.UpdataChartInfo = function () + { + //TODO: 根据K线数据日期来做map, 不在K线上的合并到下一个k线日期里面 + var mapInfoData = new Map(); + if (this.Period==0) //日线数据 根据日期 + { + for (var i in this.ChartInfo) + { + var infoData = this.ChartInfo[i].Data; + for (var j in infoData) + { + var item = infoData[j]; + if (mapInfoData.has(item.Date.toString())) + mapInfoData.get(item.Date.toString()).Data.push(item); + else + mapInfoData.set(item.Date.toString(), { Data: new Array(item) }); + } + } + } + else if (ChartData.IsDayPeriod(this.Period,false)) + { + mapInfoData=new Map(); + var hisData=this.ChartPaint[0].Data; + if (hisData && hisData.Data && hisData.Data.length>0) + { + var fristKItem=hisData.Data[0]; + var aryInfo=[]; + for(var i in this.ChartInfo) + { + var infoItem=this.ChartInfo[i]; + for(var j in infoItem.Data) + { + var item=infoItem.Data[j]; + if (item.Date>=fristKItem.Date) //在K线范围内的才显示 + aryInfo.push(item); + } + } + aryInfo.sort(function(a,b) { return a.Date-b.Date }); //排序 + + for(var i=0;i 0) //复权 + { + var rightData = bindData.GetRightDate(bindData.Right); + bindData.Data = rightData; + } + + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + } + + this.WindowIndex[index].BindData(this, index, bindData); + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + //修改参数指标 + this.ChangeWindowIndexParam = function (index) { + this.WindowIndex[index].Index[0].Param += 1; + this.WindowIndex[index].Index[1].Param += 1; + + this.UpdateWindowIndex(index); + } + + this.OnDoubleClick = function (x, y, e) + { + var tooltip = new TooltipData(); + for (var i in this.ChartPaint) + { + var item = this.ChartPaint[i]; + if (item.GetTooltipData(x, y, tooltip)) + break; + } + + if (!tooltip.Data) return; + e.data = { Chart: this, Tooltip: tooltip }; + } + + this.CancelAutoUpdate=function() //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + //数据自动更新 + this.AutoUpdate = function (waitTime) //waitTime 更新时间 + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + if (this.IsDestroy) return; + + var self = this; + var marketStatus = MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus == 0 || marketStatus == 3) return; //闭市,盘后 + + var frequency = this.AutoUpdateFrequency; + if (marketStatus == 1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function () { self.AutoUpdate(); }, frequency); + } + else if (marketStatus == 2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function () + { + if (ChartData.IsDayPeriod(self.Period, true)) + { + self.RequestRealtimeData(); //更新最新行情 + //self.ReqeustKLineInfoData(); + } + else if (ChartData.IsMinutePeriod(self.Period, true) || ChartData.IsSecondPeriod(self.Period)) + { + self.RequestMinuteRealtimeData(); //请求分钟数据 + } + }, frequency); + } + } + + this.GetMaxPageSize = function () + { + let width = this.Frame.ChartBorder.GetWidth(); + let barWidth = (ZOOM_SEED[ZOOM_SEED.length - 1][0] + ZOOM_SEED[ZOOM_SEED.length - 1][1]); + let pageSize = parseInt(width / barWidth) - 8; + JSConsole.Chart.Log(`[KLineChartContainer::GetMaxPageSize] width=${width} barWidth=${barWidth} pageSize=${pageSize}`); + return pageSize + } + + //数据拖拽下载 + this.DragDownloadData = function () + { + var data = null; + if (!this.Frame.Data) data = this.Frame.Data; + else data = this.Frame.SubFrame[0].Frame.Data; + if (!data) return false; + if (data.DataOffset > 0) return; + + if (ChartData.IsMinutePeriod(this.Period, true)) //下载分钟数据 + { + JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Minute:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`); + if (!this.DragDownload.Minute.Enable) return; + if (this.DragDownload.Minute.IsEnd) return; //全部下载完了 + if (this.DragDownload.Minute.Status != 0) return; + this.RequestDragMinuteData(); + } + else if (ChartData.IsDayPeriod(this.Period, true)) // 下载日线 + { + JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Day:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`); + if (!this.DragDownload.Day.Enable) return; + if (this.DragDownload.Day.IsEnd) return; //全部下载完了 + if (this.DragDownload.Day.Status != 0) return; + this.RequestDragDayData(); + } + } + + this.RequestDragMinuteData = function () + { + var self = this; + this.AutoUpdateEvent(false, 'KLineChartContainer::RequestDragMinuteData'); //停止自动更新 + this.CancelAutoUpdate(); + var download = this.DragDownload.Minute; + download.Status = 1; + var firstItem = this.SourceData.Data[0]; //最新的一条数据 + var postData = + { + "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"], + "symbol": self.Symbol, + "enddate": firstItem.Date, + "endtime": firstItem.Time, + "count": self.MaxRequestMinuteDayCount, + "first": { date: firstItem.Date, time: firstItem.Time }, + }; + + if (this.NetworkFilter) { + var obj = + { + Name: 'KLineChartContainer::RequestDragMinuteData', //类名::函数 + Explain: '拖拽1分钟K线数据下载', + Request: { Url: this.DragMinuteKLineApiUrl, Type: 'POST', Data: postData, Period:this.Period }, + DragDownload: download, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.RecvDragMinuteData(data); + download.Status = 0; + self.AutoUpdateEvent(true, 'KLineChartContainer::RequestDragMinuteData'); //自动更新 + self.AutoUpdate(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.DragMinuteKLineApiUrl, + data: postData, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.RecvDragMinuteData(data); + download.Status = 0; + self.AutoUpdateEvent(true, 'KLineChartContainer::RequestDragMinuteData'); //自动更新 + self.AutoUpdate(); + } + }); + } + + this.RecvDragMinuteData = function (recvdata) + { + var data=recvdata.data; + var aryDayData = KLineChartContainer.JsonDataToMinuteHistoryData(data); + var lastDataCount = this.GetHistoryDataCount(); //保存下上一次的数据个数 + + for (var i in aryDayData) //数据往前插 + { + var item = aryDayData[i]; + this.SourceData.Data.splice(i, 0, item); + } + + var bindData = new ChartData(); + bindData.Data = this.SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + bindData.DataType = this.SourceData.DataType; + bindData.Symbol = this.Symbol; + + if (!this.IsApiPeriod) + { + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + } + + //绑定数据 + this.UpdateMainData(bindData, lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + { + this.BindIndexData(i, bindData); + } + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.RequestDragDayData = function () + { + var self = this; + this.AutoUpdateEvent(false, 'KLineChartContainer::RequestDragDayData'); //停止自动更新 + this.CancelAutoUpdate(); + var download = this.DragDownload.Day; + download.Status = 1; + var firstItem = this.SourceData.Data[0]; //最新的一条数据 + var postData = + { + "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"], + "symbol": self.Symbol, + "enddate": firstItem.Date, + "count": self.MaxReqeustDataCount, + "first": { date: firstItem.Date } + }; + + if (this.NetworkFilter) { + var obj = + { + Name: 'KLineChartContainer::RequestDragDayData', //类名::函数 + Explain: '拖拽日K数据下载', + Request: { Url: this.DragKLineApiUrl, Type: 'POST', Data: postData , Period:this.Period, Right:this.Right }, + DragDownload: download, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.RecvDragDayData(data); + download.Status = 0; + self.AutoUpdateEvent(true, 'KLineChartContainer::RequestDragDayData'); //自动更新 + self.AutoUpdate(); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.DragKLineApiUrl, + data: postData, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.RecvDragDayData(data); + download.Status = 0; + self.AutoUpdateEvent(true, 'KLineChartContainer::RequestDragDayData'); //自动更新 + self.AutoUpdate(); + } + }); + } + + this.RecvDragDayData = function (recvdata) + { + var data = recvdata.data; + var aryDayData = KLineChartContainer.JsonDataToHistoryData(data); + if (!aryDayData || aryDayData.length<=0) + { + this.DragDownload.Day.IsEnd=true; //下完了 + return; + } + + var lastDataCount = this.GetHistoryDataCount(); //保存下上一次的数据个数 + + for (var i in aryDayData) //数据往前插 + { + var item = aryDayData[i]; + this.SourceData.Data.splice(i, 0, item); + } + + var bindData = new ChartData(); + bindData.Data = this.SourceData.Data; + bindData.Period = this.Period; + bindData.Right = this.Right; + bindData.DataType = this.SourceData.DataType; + bindData.Symbol = this.Symbol; + + if (!this.IsApiPeriod) + { + if (ChartData.IsDayPeriod(bindData.Period, false) || ChartData.IsMinutePeriod(bindData.Period, false)) //周期数据 (0= 日线,4=1分钟线 不需要处理) + { + var periodData = bindData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + } + + //绑定数据 + this.UpdateMainData(bindData, lastDataCount); + this.BindInstructionIndexData(bindData); //执行指示脚本 + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + { + this.BindIndexData(i, bindData); + } + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.SetCustomVerical = function (windowId, data) + { + if (!this.Frame) return; + if (windowId >= this.Frame.SubFrame.length) return; + + var item = this.Frame.SubFrame[windowId]; + if (item.Frame) item.Frame.CustomVerticalInfo = data; + } + + this.OnSize=function() + { + if (!this.Frame) return; + if (!this.Frame.OnSize) return; + + var obj=this.Frame.OnSize(); + this.Frame.SetSizeChage(true); + if (obj.Changed) + { + this.UpdataDataoffset(); + this.UpdatePointByCursorIndex(); + this.UpdateFrameMaxMin(); + } + + this.Draw(); + } +} + +//API 返回数据 转化为array[] +KLineChartContainer.JsonDataToHistoryData = function (data) +{ + var list = data.data; + var aryDayData = new Array(); + if (!list) return aryDayData; + + var upperSymbol = null; + if (data.symbol) upperSymbol = data.symbol.toUpperCase(); + var isFutures = false; //是否是期货 + isFutures = MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol); + + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, position = 8; + for (var i = 0; i < list.length; ++i) + { + var item = new HistoryData(); + var jsData = list[i]; + item.Date = jsData[date]; + item.Open = jsData[open]; + item.YClose = jsData[yclose]; + item.Close = jsData[close]; + item.High = jsData[high]; + item.Low = jsData[low]; + item.Vol = jsData[vol]; //原始单位股 + item.Amount = jsData[amount]; + if (IFrameSplitOperator.IsNumber(jsData[position])) item.Position = jsData[position];//期货持仓 + + if (!IFrameSplitOperator.IsNumber(item.Open)) continue; + + aryDayData.push(item); + } + + return aryDayData; +} + +KLineChartContainer.JsonDataToRealtimeData = function (data) +{ + var symbol = data.stock[0].symbol; + var upperSymbol = symbol.toUpperCase(); + var isSHSZ = MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + + var item = new HistoryData(); + item.Date = data.stock[0].date; + item.Open = data.stock[0].open; + item.YClose = data.stock[0].yclose; + item.High = data.stock[0].high; + item.Low = data.stock[0].low; + item.Vol=data.stock[0].vol; //单位股 + item.Amount = data.stock[0].amount; + item.Close = data.stock[0].price; + if (IFrameSplitOperator.IsNumber(data.stock[0].position)) item.Position = data.stock[0].position; //持仓量 + return item; +} + +KLineChartContainer.JsonDataToMinuteRealtimeData = function (data) +{ + var symbol = data.stock[0].symbol; + var upperSymbol = symbol.toUpperCase(); + var isSHSZ = MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures = MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol); + var aryMinuteData = new Array(); + var preClose = data.stock[0].yclose; //前一个数据价格 + var date = data.stock[0].date; + if (isFutures && data.stock[0].yclearing) preClose = data.stock[0].yclearing; //期货使用昨结算价 + + for (var i in data.stock[0].minute) + { + var jsData = data.stock[0].minute[i]; + var item = new HistoryData(); + + item.Close = jsData.price; + item.Open = jsData.open; + item.High = jsData.high; + item.Low = jsData.low; + item.Vol = jsData.vol; //单位股 + item.Amount = jsData.amount; + if (jsData.date > 0) item.Date = jsData.date; + else item.Date = date; + item.Time = jsData.time; + item.YClose = preClose; + if (IFrameSplitOperator.IsNumber(jsData.position)) item.Position = jsData.position; //持仓量 + + if (!IFrameSplitOperator.IsNumber(item.Close)) //当前没有价格 使用上一个价格填充 + { + item.Close = preClose; + item.Open = item.High = item.Low = item.Close; + } + + //价格是0的 都用空 + if (!IFrameSplitOperator.IsNumber(item.Open)) item.Open = null; + if (!IFrameSplitOperator.IsNumber(item.Close)) item.Close = null; + if (!IFrameSplitOperator.IsNumber(item.High)) item.High = null; + if (!IFrameSplitOperator.IsNumber(item.Low)) item.Low = null; + + //上次价格 + if (IFrameSplitOperator.IsNumber(jsData.price)) preClose = jsData.price; + + aryMinuteData[i] = item; + } + + return aryMinuteData; +} + +//API 返回数据 转化为array[] +KLineChartContainer.JsonDataToMinuteHistoryData = function (data) +{ + var upperSymbol = null; + if (data.symbol) upperSymbol = data.symbol.toUpperCase(); + var isSHSZ = false; + if (upperSymbol) isSHSZ = MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures = false; //是否是期货 + if (upperSymbol) isFutures = MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol); + + var list = data.data; + var aryDayData = new Array(); + var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8, position = 9; + for (var i = 0; i < list.length; ++i) + { + var item = new HistoryData(); + var jsData=list[i]; + item.Date = jsData[date]; + item.Open = jsData[open]; + item.YClose = jsData[yclose]; + item.Close = jsData[close]; + item.High = jsData[high]; + item.Low = jsData[low]; + item.Vol = jsData[vol]; //原始单位股 + item.Amount = jsData[amount]; + item.Time = jsData[time]; + if (IFrameSplitOperator.IsNumber(jsData[position])) item.Position = jsData[position]; //期货持仓 + + aryDayData.push(item); + } + + return aryDayData; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +// 走势图 +// +function MinuteChartContainer(uielement) +{ + this.newMethod = JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName = 'MinuteChartContainer'; + this.WindowIndex = new Array(); + this.Symbol; + this.Name; + this.SourceData; //原始的历史数据 + this.OverlaySourceData; //叠加的原始数据 + this.IsAutoUpdate = false; //是否自动更新行情数据 + this.AutoUpdateFrequency = 30000; //30秒更新一次数据 + this.AutoUpdateTimer; //更新定时器 + this.TradeDate = 0; //行情交易日期 + this.LoadDataSplashTitle = '下载分钟数据'; + this.UpdateUICallback; //数据到达回调 + + this.DayCount = 1; //显示几天的数据 + this.DayData; //多日分钟数据 + + this.MinuteApiUrl = g_JSChartResource.Domain + "/API/Stock"; + this.HistoryMinuteApiUrl = g_JSChartResource.Domain + "/API/StockMinuteData"; //历史分钟数据 + + //手机拖拽 + this.ontouchstart = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + this.IsOnTouch = true; + var jsChart = this; + if (jsChart.DragMode == 0) return; + + if (this.IsPhoneDragging(e)) + { + if (jsChart.TryClickLock) + { + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + var x = touches[0].clientX; + var y = touches[0].clientY; + if (jsChart.TryClickLock(x, y)) return; + } + + //长按2秒,十字光标 + this.ClearTouchTimer(); + + var drag = + { + "Click": {}, + "LastMove": {}, //最后移动的位置 + }; + + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + + drag.Click.X = touches[0].clientX; + drag.Click.Y = touches[0].clientY; + drag.LastMove.X = touches[0].clientX; + drag.LastMove.Y = touches[0].clientY; + + var T_ShowCorssCursor=()=> + { + if (this.ChartCorssCursor.IsShow === true) //移动十字光标 + { + var x = drag.Click.X; + var y = drag.Click.Y; + if (jsChart.IsForceLandscape) y = jsChart.UIElement.Height - drag.Click.Y; //强制横屏Y计算 + jsChart.OnMouseMove(x, y, e); + } + } + + if (this.EnableZoomIndexWindow) + { + this.PhoneDBClick.AddTouchStart(touches[0].clientX, touches[0].clientY, Date.now()); + JSConsole.Chart.Log("[MinuteChartContainer::OnTouchStart] PhoneDBClick ", this.PhoneDBClick); + } + + if (this.EnableScrollUpDown==true) + { + this.TouchTimer=setTimeout(()=> + { + this.MouseDrag=null; + T_ShowCorssCursor(); + }, 800); + } + + this.MouseDrag=drag; + if (this.EnableScrollUpDown==false) + T_ShowCorssCursor(); + } + } + + this.ontouchmove = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var jsChart = this; + var drag = jsChart.MouseDrag; + var touches = this.GetToucheData(e, jsChart.IsForceLandscape); + if (this.ChartCorssCursor.IsShow === true && this.IsPhoneDragging(e)) + { + if (drag == null) + { + var x = touches[0].clientX; + var y = touches[0].clientY; + this.LastMovePoint={ X:x, Y:y }; + + if (this.DrawMoveTimer) return; + this.DrawMoveTimer=setTimeout(()=> + { + if (!this.LastMovePoint) return; + this.OnMouseMove(this.LastMovePoint.X, this.LastMovePoint.Y, e); + this.DrawMoveTimer=null; + }, this.DrawMoveWaitTime); + } + } + + if (drag!=null) + { + //TODO:上下滚动 + this.ClearTouchTimer(); + + this.MouseDrag=null; + var x = touches[0].clientX; + var y = touches[0].clientY; + this.OnMouseMove(x,y,e); + } + } + + //创建 + //windowCount 窗口个数 + this.Create = function (windowCount) + { + this.UIElement.JSChartContainer = this; + + //创建十字光标 + this.ChartCorssCursor = new ChartCorssCursor(); + this.ChartCorssCursor.Canvas = this.Canvas; + this.ChartCorssCursor.StringFormatX = new HQMinuteTimeStringFormat(); + this.ChartCorssCursor.StringFormatY = new HQPriceStringFormat(); + this.ChartCorssCursor.StringFormatY.LanguageID = this.LanguageID; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.SplashTitle = this.LoadDataSplashTitle; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame = new HQTradeFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 25; + this.Frame.ChartBorder.Left = 50; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + this.Frame.ZoomStartWindowIndex=2; + this.ChartCorssCursor.Frame = this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for (var i in this.Frame.SubFrame) + { + var titlePaint = new DynamicChartTitlePainting(); + titlePaint.Frame = this.Frame.SubFrame[i].Frame; + titlePaint.Canvas = this.Canvas; + titlePaint.LanguageID = this.LanguageID; + + this.TitlePaint.push(titlePaint); + } + + this.ChartCorssCursor.StringFormatX.Frame = this.Frame.SubFrame[0].Frame; + } + + //创建子窗口 + this.CreateChildWindow = function (windowCount) + { + for (var i = 0; i < windowCount; ++i) + { + var border = new ChartBorder(); + border.UIElement = this.UIElement; + + var frame = new MinuteFrame(); + frame.Canvas = this.Canvas; + frame.ChartBorder = border; + frame.Identify=i; + if (i < 2) frame.ChartBorder.TitleHeight = 0; + frame.XPointCount = 243; + + var DEFAULT_HORIZONTAL = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + frame.HorizontalMax = DEFAULT_HORIZONTAL[0]; + frame.HorizontalMin = DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length - 1]; + + if (i == 0) + { + frame.YSplitOperator = new FrameSplitMinutePriceY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('price'); + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + else + { + frame.YSplitOperator = new FrameSplitY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + frame.YSplitOperator.LanguageID = this.LanguageID; + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + } + + frame.YSplitOperator.Frame = frame; + frame.YSplitOperator.ChartBorder = border; + frame.XSplitOperator = new FrameSplitMinuteX(); + frame.XSplitOperator.Frame = frame; + frame.XSplitOperator.ChartBorder = border; + if (i != windowCount - 1) frame.XSplitOperator.ShowText = false; + frame.XSplitOperator.Operator(); + + for (var j in DEFAULT_HORIZONTAL) + { + frame.HorizontalInfo[j] = new CoordinateInfo(); + frame.HorizontalInfo[j].Value = DEFAULT_HORIZONTAL[j]; + if (i == 0 && j == frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1] = DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font = "14px 微软雅黑"; + } + + var subFrame = new SubFrameItem(); + subFrame.Frame = frame; + if (i == 0) subFrame.Height = 20; + else subFrame.Height = 10; + + this.Frame.SubFrame[i] = subFrame; + } + } + + this.CreateSubFrameItem=function(id) + { + var border=new ChartBorder(); + border.UIElement=this.UIElement; + + var frame=new MinuteFrame(); + frame.Canvas=this.Canvas; + frame.ChartBorder=border; + frame.Identify=id; //窗口序号 + frame.XPointCount=243; + + if (id>=2) + { + if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal; + } + + var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1]; + frame.HorizontalMax=DEFAULT_HORIZONTAL[0]; + frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1]; + + frame.YSplitOperator=new FrameSplitY(); + frame.YSplitOperator.LanguageID=this.LanguageID; + frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double'); + frame.YSplitOperator.Frame=frame; + frame.YSplitOperator.ChartBorder=border; + frame.XSplitOperator=new FrameSplitMinuteX(); + frame.XSplitOperator.Frame=frame; + frame.XSplitOperator.ChartBorder=border; + frame.XSplitOperator.ShowText=false; + frame.XSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + frame.YSplitOperator.GetEventCallback=(id)=> { return this.GetEventCallback(id); } + + //K线数据绑定 + var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount; + frame.XPointCount=xPointCouont; + frame.Data=this.ChartPaint[0].Data; + + for(var j in DEFAULT_HORIZONTAL) + { + frame.HorizontalInfo[j]= new CoordinateInfo(); + frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j]; + frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font="14px 微软雅黑"; + } + + var subFrame=new SubFrameItem(); + subFrame.Frame=frame; + subFrame.Height=10; + + return subFrame; + } + + this.UpdateXShowText=function() + { + var bLastFrame=true; + for(var i=this.Frame.SubFrame.length-1;i>=0;--i) + { + var item=this.Frame.SubFrame[i].Frame; + var subFrame=this.Frame.SubFrame[i]; + + if (bLastFrame) + { + item.XSplitOperator.ShowText=true; + if (subFrame.Height>0) bLastFrame=false; + } + else + { + item.XSplitOperator.ShowText=false; + } + } + } + + //删除某一个窗口的指标 + this.DeleteIndexPaint = function (windowIndex) + { + let paint = new Array(); + for (let i in this.ChartPaint) //踢出当前窗口的指标画法 + { + let item = this.ChartPaint[i]; + if (i == 0 || item.ChartFrame != this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + //清空指定最大最小值 + this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = null; + this.Frame.SubFrame[windowIndex].Frame.IsLocked = false; //解除上锁 + + this.ChartPaint = paint; + + //清空东条标题 + var titleIndex = windowIndex + 1; + this.TitlePaint[titleIndex].Data = []; + this.TitlePaint[titleIndex].Title = null; + } + + this.CreateStockInfo = function () { + this.ExtendChartPaint[0] = new StockInfoExtendChartPaint(); + this.ExtendChartPaint[0].Canvas = this.Canvas; + this.ExtendChartPaint[0].ChartBorder = this.Frame.ChartBorder; + this.ExtendChartPaint[0].ChartFrame = this.Frame; + + this.Frame.ChartBorder.Right = 300; + } + + //创建主图K线画法 + this.CreateMainKLine = function () + { + //分钟线 + var minuteLine = new ChartMinutePriceLine(); + minuteLine.Canvas = this.Canvas; + minuteLine.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + minuteLine.ChartFrame = this.Frame.SubFrame[0].Frame; + minuteLine.Name = "Minute-Line"; + minuteLine.Color = g_JSChartResource.Minute.PriceColor; + minuteLine.AreaColor = g_JSChartResource.Minute.AreaPriceColor; + this.ChartPaint[0] = minuteLine; + + //分钟线均线 + var averageLine = new ChartMinutePriceLine(); + averageLine.Canvas = this.Canvas; + averageLine.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + averageLine.ChartFrame = this.Frame.SubFrame[0].Frame; + averageLine.Name = "Minute-Average-Line"; + averageLine.Color = g_JSChartResource.Minute.AvPriceColor; + averageLine.IsDrawArea = false; + this.ChartPaint[1] = averageLine; + + //成交量柱子 + var chartVol = new ChartMinuteVolumBar(); + chartVol.Color = g_JSChartResource.Minute.VolBarColor; + chartVol.Canvas = this.Canvas; + chartVol.ChartBorder = this.Frame.SubFrame[1].Frame.ChartBorder; + chartVol.ChartFrame = this.Frame.SubFrame[1].Frame; + chartVol.Name = "Minute-Vol-Bar"; + this.ChartPaint[2] = chartVol; + + //持仓线 + var chartPosition=new ChartSubLine(); + chartPosition.Color = g_JSChartResource.Minute.PriceColor; + chartPosition.Canvas = this.Canvas; + chartPosition.ChartBorder = this.Frame.SubFrame[1].Frame.ChartBorder; + chartPosition.ChartFrame = this.Frame.SubFrame[1].Frame; + chartPosition.Name = "Minute-Position-Line"; + this.ChartPaint[3] = chartPosition; + + this.TitlePaint[0] = new DynamicMinuteTitlePainting(); + this.TitlePaint[0].Frame = this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas = this.Canvas; + this.TitlePaint[0].LanguageID = this.LanguageID; + + //主图叠加画法 + var paint = new ChartOverlayMinutePriceLine(); + paint.Canvas = this.Canvas; + paint.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame = this.Frame.SubFrame[0].Frame; + paint.Name = "Overlay-Minute"; + this.OverlayChartPaint[0] = paint; + } + + //切换成 脚本指标 + this.ChangeScriptIndex = function (windowIndex, indexData) + { + this.DeleteIndexPaint(windowIndex); + this.WindowIndex[windowIndex] = new ScriptIndex(indexData.Name, indexData.Script, indexData.Args, indexData); //脚本执行 + + var bindData = this.SourceData; + this.BindIndexData(windowIndex, bindData); //执行脚本 + + this.UpdataDataoffset(); //更新数据偏移 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Draw(); + } + + this.ChangeIndex = function (windowIndex, indexName, option) + { + if (this.Frame.SubFrame.length < 3) return; + + //查找系统指标 + let scriptData = new JSCommonIndexScript.JSIndexScript(); + let indexInfo = scriptData.Get(indexName); + if (!indexInfo) return; + if (windowIndex < 2) windowIndex = 2; + if (windowIndex >= this.Frame.SubFrame.length) windowIndex = 2; + + let indexData = + { + Name: indexInfo.Name, Script: indexInfo.Script, Args: indexInfo.Args, ID: indexName, + //扩展属性 可以是空 + KLineType: indexInfo.KLineType, YSpecificMaxMin: indexInfo.YSpecificMaxMin, YSplitScale: indexInfo.YSplitScale, + FloatPrecision: indexInfo.FloatPrecision, Condition: indexInfo.Condition, StringFormat: indexInfo.StringFormat + }; + + if (option) + { + if (option.FloatPrecision >= 0) indexData.FloatPrecision = option.FloatPrecision; + if (option.StringFormat > 0) indexData.StringFormat = option.StringFormat; + if (option.Args) indexData.Args = option.Args; + } + + return this.ChangeScriptIndex(windowIndex, indexData); + } + + this.RemoveIndexWindow=function(id) + { + JSConsole.Chart.Log('[MinuteChartContainer::RemoveIndexWindow] remove id', id); + + if (id<2) return; + if (!this.Frame.SubFrame) return; + if (id>=this.Frame.SubFrame.length) return; + + var delFrame=this.Frame.SubFrame[id].Frame; + this.DeleteIndexPaint(id); + this.Frame.SubFrame.splice(id,1); + this.WindowIndex.splice(id,1); + this.TitlePaint.splice(id+1,1); //删除对应的动态标题 + + for(var i=0;i 10) return; + this.DayCount = count; + this.CancelAutoUpdate(); //先停止定时器 + this.RequestData(); + } + + //叠加股票 只支持日线数据 + this.OverlaySymbol = function (symbol,option) + { + var paint = this.OverlayChartPaint[0]; + if (!paint.MainData) return false; + + paint.Symbol = symbol; + if (option) + { + if (option.Color) paint.Color=option.Color; + } + + if (this.DayCount <= 1) this.RequestOverlayMinuteData(); //请求数据 + else this.RequestOverlayHistoryMinuteData(); + + return true; + } + + this.TryClickLock = function (x, y) { + for (let i in this.Frame.SubFrame) { + var item = this.Frame.SubFrame[i]; + if (!item.Frame.IsLocked) continue; + if (!item.Frame.LockPaint) continue; + + var tooltip = new TooltipData(); + if (!item.Frame.LockPaint.GetTooltipData(x, y, tooltip)) continue; + + tooltip.HQChart = this; + if (tooltip.Data.Callback) tooltip.Data.Callback(tooltip); + return true; + } + + return false; + } + + this.RequestData = function () + { + if (this.DayCount <= 1) this.RequestMinuteData(); + else this.RequestHistoryMinuteData(); //请求数据 + } + + this.RecvMinuteDataEvent = function () + { + if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_MINUTE_DATA)) return; + + var event = this.mapEvent.get(JSCHART_EVENT_ID.RECV_MINUTE_DATA); + var data = { MinuteData: this.SourceData, Stock: { Symbol: this.Symbol, Name: this.Name } } + event.Callback(event, data, this); + } + + this.RequestHistoryMinuteData = function () //请求历史分钟数据 + { + var self = this; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + + if (this.NetworkFilter) + { + var obj = + { + Name: 'MinuteChartContainer::RequestHistoryMinuteData', //类名:: + Explain: '多日分时数据', + Request: { Url: self.HistoryMinuteApiUrl, Data: { daycount: self.DayCount, symbol: self.Symbol }, Type: 'POST' }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol": self.Symbol, + "daycount": self.DayCount + }, + method: "post", + dataType: "json", + success: function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + } + }); + } + + this.RecvHistoryMinuteData = function (recvdata) + { + var data = recvdata.data; + if (data.code!=0) + { + JSConsole.Chart.Log('[MinuteChartContainer::RecvHistoryMinuteData] failed.',data); + return; + } + this.DayData = MinuteChartContainer.JsonDataToMinuteDataArray(data);; + this.Symbol = data.symbol; + this.Name = data.name; + + this.CaclutateLimitPrice(this.DayData[0].YClose, data.data[0].limitprice); //计算涨停价格 + + this.UpdateHistoryMinuteUI(); + this.RecvMinuteDataEvent(); + this.RequestOverlayHistoryMinuteData(); + + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvHistoryMinuteData', this); + + this.AutoUpdate(); + } + + this.CaclutateLimitPrice=function(yClose, limitData) + { + this.LimitPrice=null; + //var limitData=data.stock[0].limitprice; + if (limitData && limitData.max>0 && limitData.min>0) //API里带涨停价格 直接使用 + { + this.LimitPrice={ Max:limitData.max, Min:limitData.min }; + return; + } + + var range=MARKET_SUFFIX_NAME.GetLimitPriceRange(this.Symbol, this.Name); //通过规则获取涨停价格 + if (!range) + { + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} no limit price.`) + return; + } + + //var yClose=data.stock[0].yclose; + if (yClose<=0) return; + this.LimitPrice={ Max:yClose*(1+range.Max), Min:yClose*(1+range.Min) }; + + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} yClose:${yClose} max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`); + + this.LimitPrice.Max=parseFloat(this.LimitPrice.Max.toFixed(2)); + this.LimitPrice.Min=parseFloat(this.LimitPrice.Min.toFixed(2)); + JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} tofixed(2) max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`); + } + + this.UpdateHistoryMinuteUI = function () + { + var allMinuteData = this.HistoryMinuteDataToArray(this.DayData); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = allMinuteData; + + this.SourceData = sourceData; + this.TradeDate = this.DayData[0].Date; + + this.BindMainData(sourceData, this.DayData[0].YClose); + //if (MARKET_SUFFIX_NAME.IsChinaFutures(this.Symbol)) this.ChartPaint[1].Data = null; //期货均线暂时不用 + + if (this.Frame.SubFrame.length > 2) + { + var bindData = new ChartData(); + bindData.Data = allMinuteData; + for (var i = 2; i < this.Frame.SubFrame.length; ++i) + { + this.BindIndexData(i, bindData); + } + } + + for (let i in this.Frame.SubFrame) + { + var item = this.Frame.SubFrame[i]; + item.Frame.XSplitOperator.Symbol = this.Symbol; + item.Frame.XSplitOperator.DayCount = this.DayData.length; + item.Frame.XSplitOperator.DayData = this.DayData; + item.Frame.XSplitOperator.Operator(); //调整X轴个数 + item.Frame.YSplitOperator.Symbol = this.Symbol; + } + + this.ChartCorssCursor.StringFormatY.Symbol = this.Symbol; + this.ChartCorssCursor.StringFormatX.Symbol = this.Symbol; + this.TitlePaint[0].IsShowDate = true; + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + + this.HistoryMinuteDataToArray = function (data) //把多日分钟数据转化为单数组 + { + var result = []; + for (var i = data.length - 1; i >= 0; --i) { + var item = data[i]; + for (var j in item.Data) { + result.push(item.Data[j]); + } + } + return result; + } + + this.UpdateLatestMinuteData = function (data, date) //更新最新交易日的分钟数据 + { + for (var i in this.DayData) { + var item = this.DayData[i]; + if (item.Date === date) { + item.Data = data; + break; + } + } + } + + //请求分钟数据 + this.RequestMinuteData = function () + { + var self = this; + + var fields = + [ + "name", "symbol", + "yclose", "open", "price", "high", "low", + "vol", "amount", + "date", "time", + "minute", "minutecount" + ]; + + var upperSymbol = this.Symbol.toUpperCase(); + if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) //期货的需要加上结算价 + { + fields.push("clearing"); + fields.push("yclearing"); + } + + if (this.NetworkFilter) + { + var obj = + { + Name: 'MinuteChartContainer::RequestMinuteData', //类名::函数名 + Explain: '最新分时数据', + Request: { Url: self.MinuteApiUrl, Data: { field: fields, symbol: [self.Symbol] }, Type: 'POST' }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteData(data); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: this.MinuteApiUrl, + data: + { + "field": fields, + "symbol": [this.Symbol], + "start": -1 + }, + method: "post", + dataType: "json", + success: function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvMinuteData(data); + } + }); + } + + this.RecvMinuteData = function (data) + { + var aryMinuteData = MinuteChartContainer.JsonDataToMinuteData(data.data); + + if (this.DayCount > 1) //多日走势图 + { + this.UpdateLatestMinuteData(aryMinuteData, data.data.stock[0].date); + this.UpdateHistoryMinuteUI(); + this.RecvMinuteDataEvent(); + this.RequestOverlayMinuteData(); //更新最新叠加数据 + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvMinuteData', this); + this.AutoUpdate(); + return; + } + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = aryMinuteData; + + this.TradeDate = data.data.stock[0].date; + + this.SourceData = sourceData; + this.Symbol = data.data.stock[0].symbol; + this.Name = data.data.stock[0].name; + var yClose = data.data.stock[0].yclose; + var upperSymbol = this.Symbol.toUpperCase(); + var isFutures = MARKET_SUFFIX_NAME.IsFutures(upperSymbol); + if (data.data.stock[0].yclearing && isFutures) yClose = data.data.stock[0].yclearing; //期货使用前结算价 + this.CaclutateLimitPrice(yClose, data.data.stock[0].limitprice); //计算涨停价格 + var extendData = { High: data.data.stock[0].high, Low: data.data.stock[0].low }; + this.BindMainData(sourceData, yClose, extendData); + + for (let i in this.Frame.SubFrame) //把股票代码设置到X轴刻度类里 + { + var item = this.Frame.SubFrame[i]; + item.Frame.XSplitOperator.Symbol = this.Symbol; + item.Frame.XSplitOperator.DayCount = 1; + item.Frame.XSplitOperator.Operator(); //调整X轴个数 + item.Frame.YSplitOperator.Symbol = this.Symbol; + } + + //计算指标 + if (this.Frame.SubFrame.length > 2) + { + var bindData = new ChartData(); + bindData.Data = aryMinuteData; + for (var i = 2; i < this.Frame.SubFrame.length; ++i) + { + this.BindIndexData(i, bindData); + } + } + + this.ChartCorssCursor.StringFormatY.Symbol = this.Symbol; + this.ChartCorssCursor.StringFormatX.Symbol = this.Symbol; + + var chartInfo = this.GetChartMinuteInfo(); + if (chartInfo) chartInfo.SourceData = this.SourceData; //数据绑定到信息地雷上 + this.RecvMinuteDataEvent(); + this.RequestMinuteInfoData(); + this.RequestOverlayMinuteData();//请求叠加数据 (主数据下载完再下载) + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvMinuteData', this); + + this.AutoUpdate(); + } + + //请求叠加数据 (主数据下载完再下载)) + this.RequestOverlayMinuteData = function () + { + if (!this.OverlayChartPaint.length) return; + + var symbol = this.OverlayChartPaint[0].Symbol; + if (!symbol) return; + + var self = this; + var date = this.TradeDate; //最后一个交易日期 + + if (this.NetworkFilter) + { + var obj = + { + Name: 'MinuteChartContainer::RequestOverlayMinuteData', //类名::函数名 + Explain: '叠加股票最新分时数据', + Request: { Url: self.HistoryMinuteApiUrl, Data: { days: [date], symbol: symbol }, Type: 'POST' }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) + { + self.RecvOverlayMinuteData(data); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + + //请求数据 + wx.request({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol": symbol, + "days": [date], + }, + method: "post", + dataType: "json", + success: function (data) { + self.RecvOverlayMinuteData(data); + } + }); + } + + this.RecvOverlayMinuteData = function (recvData) { + var data = recvData.data; + var aryMinuteData = MinuteChartContainer.JsonDataToMinuteDataArray(data); + + var sourceData = null; + var yClose; + if (this.DayCount > 1) //多日数据 + { + if (aryMinuteData.length <= 0) return; + + var minuteData = aryMinuteData[0]; + for (var i in this.OverlaySourceData) { + var item = this.OverlaySourceData[i]; + if (item.Date == minuteData.Date) { + this.OverlaySourceData[i] = minuteData; + var allMinuteData = this.HistoryMinuteDataToArray(this.OverlaySourceData); + var sourceData = new ChartData(); + sourceData.Data = allMinuteData; + yClose = minuteData.YClose; + break; + } + } + if (sourceData == null) return; + } + else { + if (aryMinuteData.length > 0) sourceData = aryMinuteData[0]; + else sourceData = new ChartData(); + yClose = sourceData.YClose; + } + + this.OverlayChartPaint[0].Data = sourceData; + this.OverlayChartPaint[0].Title = data.name; + this.OverlayChartPaint[0].Symbol = data.symbol; + this.OverlayChartPaint[0].YClose = yClose; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvOverlayMinuteData', this); + } + + this.RequestOverlayHistoryMinuteData = function () + { + if (!this.OverlayChartPaint.length) return; + var symbol = this.OverlayChartPaint[0].Symbol; + if (!symbol) return; + + var self = this; + var days = []; + for (var i in this.DayData) + { + var item = this.DayData[i]; + days.push(item.Date); + } + + if (days.length <= 0) return; + + if (this.NetworkFilter) + { + var obj = + { + Name: 'MinuteChartContainer::RequestOverlayHistoryMinuteData', //类名::函数名 + Explain: '叠加股票多日分时数据', + Request: { Url: self.HistoryMinuteApiUrl, Data: { days: days, symbol: symbol }, Type: 'POST' }, + Self: this, + PreventDefault: false + }; + this.NetworkFilter(obj, function (data) + { + self.RecvOverlayHistoryMinuteData(data); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: self.HistoryMinuteApiUrl, + data: + { + "symbol": symbol, + "days": days + }, + method: "post", + dataType: "json", + async: true, + success: function (data) { + self.RecvOverlayHistoryMinuteData(data); + } + }); + } + + this.RecvOverlayHistoryMinuteData = function (recvData) //叠加历史的分钟数据 + { + var data = recvData.data; + var dayData = MinuteChartContainer.JsonDataToMinuteDataArray(data); + var overlayDayData = []; + for (var i in this.DayData) { + var item = this.DayData[i]; + var bFind = false; + for (var j in dayData) { + if (item.Date == dayData[j].Date) { + overlayDayData.push(dayData[i]); + bFind = true; + break; + } + } + if (!bFind) //当天不存在叠加数据, 存空 + { + var empytData = new ChartData(); + empytData.Date = item.Date; + } + } + + this.OverlaySourceData = overlayDayData; + var allMinuteData = this.HistoryMinuteDataToArray(overlayDayData); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = allMinuteData; + + var yClose = overlayDayData[0].YClose; + this.OverlayChartPaint[0].Data = sourceData; + this.OverlayChartPaint[0].Title = data.name; + this.OverlayChartPaint[0].Symbol = data.symbol; + this.OverlayChartPaint[0].YClose = yClose; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvOverlayHistoryMinuteData', this); + } + + this.CancelAutoUpdate = function () //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + //数据自动更新 + this.AutoUpdate = function () + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + if (this.IsDestroy) return; + + var self = this; + var marketStatus = MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus == 0 || marketStatus == 3) return; //闭市,盘后 + + var frequency = this.AutoUpdateFrequency; + if (marketStatus == 1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function () { self.AutoUpdate(); }, frequency); + } + else if (marketStatus == 2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function () + { + //self.ResetOverlaySymbolStatus(); + self.RequestMinuteData(); + }, frequency); + } + } + + this.StopAutoUpdate = function () + { + this.CancelAutoUpdate(); + this.IsAutoUpdate = false; + } + + this.BindIndexData = function (windowIndex, hisData) + { + if (!this.WindowIndex[windowIndex]) return; + + if (typeof (this.WindowIndex[windowIndex].RequestData) == "function") //数据需要另外下载的. + { + this.WindowIndex[windowIndex].RequestData(this, windowIndex, hisData); + return; + } + + if (typeof (this.WindowIndex[windowIndex].ExecuteScript) == 'function') + { + this.WindowIndex[windowIndex].ExecuteScript(this, windowIndex, hisData); + return; + } + + this.WindowIndex[windowIndex].BindData(this, windowIndex, hisData); + } + + + this.BindMainData = function (minuteData, yClose, extendData) //绑定分钟数据 + { + //分钟数据 + var bindData = new ChartData(); + bindData.Data = minuteData.GetClose(); + this.ChartPaint[0].Data = bindData; + this.ChartPaint[0].YClose = yClose; + this.ChartPaint[0].NotSupportMessage = null; + + this.Frame.SubFrame[0].Frame.YSplitOperator.YClose = yClose; + this.Frame.SubFrame[0].Frame.YSplitOperator.Data = bindData; + + //均线 + bindData = new ChartData(); + bindData.Data = minuteData.GetMinuteAvPrice(); + this.ChartPaint[1].Data = bindData; + + this.Frame.SubFrame[0].Frame.YSplitOperator.AverageData = bindData; + this.Frame.SubFrame[0].Frame.YSplitOperator.OverlayChartPaint = this.OverlayChartPaint; + this.Frame.SubFrame[0].Frame.YSplitOperator.LimitPrice=this.LimitPrice; + if (extendData) + { + this.Frame.SubFrame[0].Frame.YSplitOperator.High = extendData.High; + this.Frame.SubFrame[0].Frame.YSplitOperator.Low = extendData.Low; + } + + //成交量 + this.ChartPaint[2].Data = minuteData; + this.ChartPaint[2].YClose = yClose; + + var upperSymbol=this.Symbol.toUpperCase(); + var bFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol); + var bSHO = MARKET_SUFFIX_NAME.IsSHO(upperSymbol); + + if (bFutures || bSHO) + { + this.ChartPaint[3].Data.Data = minuteData.GetPosition(); + } + else + { + this.ChartPaint[3].Data.Data=null; + } + + this.TitlePaint[0].Data = this.SourceData; //动态标题 + this.TitlePaint[0].Symbol = this.Symbol; + this.TitlePaint[0].Name = this.Name; + this.TitlePaint[0].YClose = yClose; + + if (this.ChartCorssCursor) + { + if (this.ChartCorssCursor.StringFormatY) + { + this.ChartCorssCursor.StringFormatY.YClose=yClose; + this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data; //十字光标 + } + } + + if (this.ExtendChartPaint[0]) + { + this.ExtendChartPaint[0].Symbol = this.Symbol; + this.ExtendChartPaint[0].Name = this.Name; + } + + this.OverlayChartPaint[0].MainData = this.ChartPaint[0].Data; //叠加 + this.OverlayChartPaint[0].MainYClose = yClose; + } + + //获取子窗口的所有画法 + this.GetChartPaint = function (windowIndex) + { + var paint = new Array(); + for (var i in this.ChartPaint) + { + if (i < 3) continue; //分钟 均线 成交量 3个线不能改 + var item = this.ChartPaint[i]; + if (item.ChartFrame == this.Frame.SubFrame[windowIndex].Frame) + paint.push(item); + } + + return paint; + } + + //创建指定窗口指标 + this.CreateWindowIndex = function (windowIndex) + { + this.WindowIndex[windowIndex].Create(this, windowIndex); + } + + this.CreateExtendChart = function (name, option) //创建扩展图形 + { + var chart; + switch (name) + { + case 'MinuteTooltip': + chart = new MinuteTooltipPaint(); + chart.Canvas = this.Canvas; + chart.ChartBorder = this.Frame.ChartBorder; + chart.ChartFrame = this.Frame; + chart.HQChart = this; + option.LanguageID = this.LanguageID; + chart.SetOption(option); + this.ExtendChartPaint.push(chart); + return chart; + default: + return null; + } + } + + this.SetMinuteInfo = function (aryInfo, bUpdate) + { + this.ChartInfo = []; //先清空 + for (var i in aryInfo) + { + var infoItem = JSMinuteInfoMap.Get(aryInfo[i]); + if (!infoItem) continue; + var item = infoItem.Create(); + this.ChartInfo.push(item); + } + + if (bUpdate == true) this.RequestMinuteInfoData(); + } + + this.GetChartMinuteInfo = function () + { + return this.ChartInfoPaint; + } + + this.CreateMinuteInfo = function (option) //在Create()以后 在调用 + { + var chart = new ChartMinuteInfo(); + chart.Canvas = this.Canvas; + chart.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + chart.ChartFrame = this.Frame.SubFrame[0].Frame; + chart.ChartMinutePrice = this.ChartPaint[0]; + if (option && chart.SetOption) chart.SetOption(option); + this.ChartInfoPaint = chart; + return chart; + } + + //信息地雷数据请求 + this.RequestMinuteInfoData = function () + { + if (this.ChartInfo.length <= 0) return; + + var chart = this.GetChartMinuteInfo(); + if (!chart) chart = this.CreateMinuteInfo(null); //不存在就创建 + + chart.SourceData = this.SourceData; + + //信息地雷信息 + for (var i in this.ChartInfo) { + this.ChartInfo[i].RequestData(this); + } + } + + //更新信息地雷 + this.UpdataChartInfo = function () + { + var chart = this.GetChartMinuteInfo(); + if (!chart) return; + + var infoMap = new Map(); + for (var i in this.ChartInfo) + { + var infoData = this.ChartInfo[i].Data; + for (var j in infoData) + { + var item = infoData[j]; + var dateTime = `${item.Date} ${item.Time}`; + if (infoMap.has(dateTime)) infoMap.get(dateTime).Data.push(item); + else infoMap.set(dateTime, { Data: new Array(item) }); + } + } + + chart.Data = infoMap; + } +} + +//API 返回数据 转化为array[] +MinuteChartContainer.JsonDataToMinuteData = function (data) +{ + var upperSymbol = data.stock[0].symbol.toUpperCase(); + var isSHSZ = MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures = MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) || MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol); + var isSHO = MARKET_SUFFIX_NAME.IsSHO(upperSymbol); //上海股票期权 + + var preClose = data.stock[0].yclose; //前一个数据价格 + var preAvPrice = data.stock[0].yclose; //前一个均价 + var yClose = data.stock[0].yclose; + if (isFutures && data.stock[0].yclearing) yClose = preClose = preAvPrice = data.stock[0].yclearing; //期货使用昨结算价 + + var date = data.stock[0].date; + var aryMinuteData = new Array(); + for (var i in data.stock[0].minute) + { + var jsData = data.stock[0].minute[i]; + var item = new MinuteData(); + + if (jsData.price) preClose = jsData.price; + if (jsData.avprice) preAvPrice = jsData.avprice; + + item.Close = jsData.price; + item.Open = jsData.open; + item.High = jsData.high; + item.Low = jsData.low; + if (isSHSZ) item.Vol = jsData.vol / 100; //原始单位股 + else item.Vol = jsData.vol; + item.Amount = jsData.amount; + item.Increase = jsData.increase; + item.Risefall = jsData.risefall; + item.AvPrice = jsData.avprice; + + if (!item.Close) //当前没有价格 使用上一个价格填充 + { + item.Close = preClose; + item.Open = item.High = item.Low = item.Close; + } + if (!item.AvPrice) item.AvPrice = preAvPrice; + + if (jsData.date > 0) date = jsData.date; //分钟数据中有日期 优先使用 + item.DateTime = date.toString() + " " + jsData.time.toString(); + item.Date = data.stock[0].date; + item.Time = jsData.time; + if (80) + { + item.Date=jsData[8]; //日期 + item.DateTime=item.Date.toString()+" "+jsData[0].toString(); + } + if (isFutures || isSHO) item.Position = jsData.position; //期货 期权有持仓 + + if (i == 0) //第1个数据 写死9:25 + { + item.IsFristData = true; + //if (isSHSZ) item.DateTime = data.stock[0].date.toString() + " 0925"; + if (item.Close <= 0) //第1分钟 没数据就用开盘价 + { + item.Close = data.stock[0].open; + item.High = item.Low = data.stock[0].open; + item.AvPrice = data.stock[0].open; + JSConsole.Chart.Log('[MinuteChartContainer::JsonDataToMinuteData] first minute data is empty.', data.stock[0].symbol, jsData); + } + } + + //价格是0的 都用空 + if (item.Open <= 0) item.Open = null; + if (item.Close <= 0) item.Close = null; + if (item.AvPrice <= 0) item.AvPrice = null; + if (item.High <= 0) item.High = null; + if (item.Low <= 0) item.Low = null; + + if (yClose && item.Close) item.Increase = (item.Close - yClose) / yClose * 100; //涨幅 (最新价格-昨收)/昨收*10 + + aryMinuteData[i] = item; + } + + return aryMinuteData; +} + +//多日日线数据API 转化成array[]; +MinuteChartContainer.JsonDataToMinuteDataArray = function (data) +{ + var upperSymbol = data.symbol.toUpperCase(); + var isSHSZ = MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol); + var isFutures = MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) || MARKET_SUFFIX_NAME.IsNYMEX(upperSymbol); + var isSHO = MARKET_SUFFIX_NAME.IsSHO(upperSymbol); //上海股票期权 + var result = []; + for (var i in data.data) + { + var minuteData = []; + var dayData = data.data[i]; + var date = dayData.date; + var yClose = dayData.yclose; //前收盘 计算涨幅 + var preClose = yClose; //前一个数据价格 + var preAvPrice=null; //前一个均价 + for (var j in dayData.minute) + { + var jsData = dayData.minute[j]; + if (jsData[2]) preClose = jsData[2]; //保存上一个收盘数据 + var item = new MinuteData(); + item.Close = jsData[2]; + item.Open = jsData[1]; + item.High = jsData[3]; + item.Low = jsData[4]; + if(isSHSZ) item.Vol = jsData[5] / 100; //原始单位股 + else item.Vol = jsData[5]; + item.Amount = jsData[6]; + + if (7 < jsData.length && jsData[7] > 0) + { + item.AvPrice = jsData[7]; //均价 + preAvPrice = jsData[7]; + } + + if (!item.Close) //当前没有价格 使用上一个价格填充 + { + item.Close = preClose; + item.Open = item.High = item.Low = item.Close; + } + + if (!item.AvPrice && preAvPrice) item.AvPrice = preAvPrice; + + if (item.Close && yClose) item.Increase = (item.Close - yClose) / yClose * 100; + else item.Increase = null; + + item.DateTime = date.toString() + " " + jsData[0].toString(); + item.Date = date; + item.Time = jsData[0]; + if (80) + { + item.Date=jsData[8]; //日期 + item.DateTime=item.Date.toString()+" "+jsData[0].toString(); + } + if ((isFutures || isSHO) && 9 < jsData.length) item.Position = jsData[9]; //持仓 + + if (j == 0 ) + { + //if (isSHSZ) item.DateTime = date.toString() + " 0925";//第1个数据 写死9:25 + item.IsFristData = true; + } + + //价格是0的 都用空 + if (item.Open <= 0) item.Open = null; + if (item.Close <= 0) item.Close = null; + if (item.AvPrice <= 0) item.AvPrice = null; + if (item.High <= 0) item.High = null; + if (item.Low <= 0) item.Low = null; + if (item.AvPrice <= 0) item.AvPrice = null; + + minuteData[j] = item; + } + + var newData = new ChartData(); + newData.Data = minuteData; + newData.YClose = yClose; + newData.Close = dayData.close; + newData.Date = date; + + result.push(newData); + } + + return result; +} + +/* + 历史分钟走势图 +*/ +function HistoryMinuteChartContainer(uielement) { + this.newMethod = MinuteChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.HistoryMinuteApiUrl = "https://opensourcecache.zealink.com/cache/minuteday/day/"; + + + //创建主图K线画法 + this.CreateMainKLine = function () { + //分钟线 + var minuteLine = new ChartMinutePriceLine(); + minuteLine.Canvas = this.Canvas; + minuteLine.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + minuteLine.ChartFrame = this.Frame.SubFrame[0].Frame; + minuteLine.Name = "Minute-Line"; + minuteLine.Color = g_JSChartResource.Minute.PriceColor; + + this.ChartPaint[0] = minuteLine; + + //分钟线均线 + var averageLine = new ChartLine(); + averageLine.Canvas = this.Canvas; + averageLine.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + averageLine.ChartFrame = this.Frame.SubFrame[0].Frame; + averageLine.Name = "Minute-Average-Line"; + averageLine.Color = g_JSChartResource.Minute.AvPriceColor; + this.ChartPaint[1] = averageLine; + + var averageLine = new ChartMinuteVolumBar(); + averageLine.Color = g_JSChartResource.Minute.VolBarColor; + averageLine.Canvas = this.Canvas; + averageLine.ChartBorder = this.Frame.SubFrame[1].Frame.ChartBorder; + averageLine.ChartFrame = this.Frame.SubFrame[1].Frame; + averageLine.Name = "Minute-Vol-Bar"; + this.ChartPaint[2] = averageLine; + + + this.TitlePaint[0] = new DynamicMinuteTitlePainting(); + this.TitlePaint[0].Frame = this.Frame.SubFrame[0].Frame; + this.TitlePaint[0].Canvas = this.Canvas; + this.TitlePaint[0].IsShowDate = true; + + /* + //主图叠加画法 + var paint=new ChartOverlayKLine(); + paint.Canvas=this.Canvas; + paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder; + paint.ChartFrame=this.Frame.SubFrame[0].Frame; + paint.Name="Overlay-KLine"; + this.OverlayChartPaint[0]=paint; + */ + + } + + //设置交易日期 + this.ChangeTradeDate = function (trdateDate) { + if (!trdateDate) return; + + this.TradeDate = trdateDate; + this.RequestData(); //更新数据 + } + + this.RequestData = function () { + var date = new Date(); + var nowDate = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate(); + if (nowDate == this.TradeDate) this.RequestMinuteData(); + else this.RequestHistoryMinuteData(); + } + + //请求分钟数据 + this.RequestHistoryMinuteData = function () { + var self = this; + var url = this.HistoryMinuteApiUrl + this.TradeDate.toString() + "/" + this.Symbol + ".json"; + + wx.request({ + url: url, + method: "get", + dataType: "json", + success: function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteData(data); + }, + error: function (reqeust) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryMinuteError(reqeust); + } + }); + } + + this.RecvHistoryMinuteError = function (reqeust) { + if (reqeust.status != 404) return; + + var sourceData = new ChartData(); + this.SourceData = sourceData; + + for (var i in this.ChartPaint) { + this.ChartPaint[i].Data = sourceData; + if (i == 0) this.ChartPaint[i].NotSupportMessage = '没有权限访问!'; + } + + this.TitlePaint[0].Data = this.SourceData; //动态标题 + this.TitlePaint[0].Symbol = this.Symbol; + this.TitlePaint[0].Name = null; + + this.Draw(); + } + + this.RecvHistoryMinuteData = function (recvData) { + if (recvData.statusCode != 200) { + var sourceData = new ChartData(); + this.SourceData = sourceData; + + for (var i in this.ChartPaint) { + this.ChartPaint[i].Data = sourceData; + if (i == 0) this.ChartPaint[i].NotSupportMessage = '没有权限访问!'; + } + + this.TitlePaint[0].Data = this.SourceData; //动态标题 + this.TitlePaint[0].Symbol = this.Symbol; + this.TitlePaint[0].Name = null; + + this.Draw(); + return; + } + + var data = recvData.data; + var aryMinuteData = HistoryMinuteChartContainer.JsonDataToMinuteData(data); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = aryMinuteData; + + this.TradeDate = data.date; + + this.SourceData = sourceData; + this.Symbol = data.symbol; + this.Name = data.name; + + this.BindMainData(sourceData, data.day.yclose); + + if (this.Frame.SubFrame.length > 2) { + var bindData = new ChartData(); + bindData.Data = aryMinuteData; + for (var i = 2; i < this.Frame.SubFrame.length; ++i) { + this.BindIndexData(i, bindData); + } + } + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + //this.AutoUpdata(); + } + +} + +//API 返回数据 转化为array[] +HistoryMinuteChartContainer.JsonDataToMinuteData = function (data) +{ + var aryMinuteData = new Array(); + for (var i in data.minute.time) + { + var item = new MinuteData(); + if (data.minute.price[i] <= 0 && i > 0) //当前这一分钟价格为空,使用上一分钟的数据 + { + item.Close = aryMinuteData[i - 1].Close; + item.Open = aryMinuteData[i - 1].Close; + item.High = item.Close; + item.Low = item.Close; + item.Vol = data.minute.vol[i] / 100; //原始单位股 + item.Amount = data.minute.amount[i]; + item.DateTime = data.date.toString() + " " + data.minute.time[i].toString(); + item.Date = data.date; + item.Time = data.minute.time[i]; + //item.Increate=jsData.increate; + //item.Risefall=jsData.risefall; + item.AvPrice = aryMinuteData[i - 1].AvPrice; + } + else + { + item.Close = data.minute.price[i]; + item.Open = data.minute.open[i]; + item.High = data.minute.high[i]; + item.Low = data.minute.low[i]; + item.Vol = data.minute.vol[i] / 100; //原始单位股 + item.Amount = data.minute.amount[i]; + item.DateTime = data.date.toString() + " " + data.minute.time[i].toString(); + item.Date = data.date; + item.Time = data.minute.time[i]; + //item.Increate=jsData.increate; + //item.Risefall=jsData.risefall; + item.AvPrice = data.minute.avprice[i]; + } + + //价格是0的 都用空 + if (item.Open <= 0) item.Open = null; + if (item.Close <= 0) item.Close = null; + if (item.AvPrice <= 0) item.AvPrice = null; + if (item.High <= 0) item.High = null; + if (item.Low <= 0) item.Low = null; + + aryMinuteData[i] = item; + } + + return aryMinuteData; +} + +///////////////////////////////////////////////////////////////////////////// +// 自定义指数 +// +function CustomKLineChartContainer(uielement) { + this.newMethod = KLineChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName = 'CustomKLineChartContainer'; + this.ChangeRight = null; //没有复权设置 + this.LoadDataSplashTitle = '计算指数数据'; + + this.CustomKLineApiUrl = g_JSChartResource.Domain + "/API/IndexCalculate"; //自定义指数计算地址 + this.CustomStock; //成分 + this.QueryDate = { Start: 20180101, End: 20180627 }; //计算时间区间 + + this.RequestHistoryData = function () { + var self = this; + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + wx.request({ + url: this.CustomKLineApiUrl, + data: + { + "stock": self.CustomStock, + "Name": self.Symbol, + "date": { "startdate": self.QueryDate.Start, "enddate": self.QueryDate.End } + }, + method: 'POST', + dataType: "json", + async: true, + success: function (data) { + self.ChartSplashPaint.EnableSplash(false); + self.RecvHistoryData(data); + } + }); + } + + this.RecvHistoryData = function (recvData) + { + var data = recvData.data; + var aryDayData = KLineChartContainer.JsonDataToHistoryData(data); + + //原始数据 + var sourceData = new ChartData(); + sourceData.Data = aryDayData; + sourceData.DataType = 0; //0=日线数据 1=分钟数据 + sourceData.Symbol = data.symbol; + + //显示的数据 + var bindData = new ChartData(); + bindData.Data = aryDayData; + bindData.Right = 0; //指数没有复权 + bindData.Period = this.Period; + bindData.DataType = 0; + bindData.Symbol = data.symbol; + + if (ChartData.IsDayPeriod(this.Period, false)) //周期数据 + { + var periodData = sourceData.GetPeriodData(bindData.Period); + bindData.Data = periodData; + } + + //绑定数据 + this.SourceData = sourceData; + this.Name = data.name; + this.BindMainData(bindData, this.PageSize); + + for (var i = 0; i < this.Frame.SubFrame.length; ++i) + { + this.BindIndexData(i, bindData); + } + //this.BindIndexData(0,hisData); + //this.BindIndexData(1,hisData); + //this.BindIndexData(2,hisData); + + //刷新画图 + this.UpdataDataoffset(); //更新数据偏移 + this.UpdatePointByCursorIndex(); //更新十字光标位子 + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + + if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvHistoryData', this); + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// K线横屏显示 +// +function KLineChartHScreenContainer(uielement) +{ + this.newMethod = KLineChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName = 'KLineChartHScreenContainer'; + + this.OnMouseMove = function (x, y, e) + { + this.LastPoint.X = x; + this.LastPoint.Y = y; + this.CursorIndex = this.Frame.GetXData(y); + + this.DrawDynamicInfo(); + } + + //手机拖拽 + this.ontouchstart = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var jsChart = this; + if (jsChart.DragMode == 0) return; + + jsChart.IsOnTouch = true; + jsChart.PhonePinch = null; + + if (this.IsPhoneDragging(e)) + { + if (jsChart.TryClickLock || this.TryClickIndexTitle) + { + var touches = this.GetToucheData(e); + var x = touches[0].clientX; + var y = touches[0].clientY; + if (jsChart.TryClickLock && jsChart.TryClickLock(x, y)) return; + if (jsChart.TryClickIndexTitle && jsChart.TryClickIndexTitle(x, y)) return; + } + + //长按2秒,十字光标 + if (this.TouchTimer != null) clearTimeout(this.TouchTimer); + if (this.ChartCorssCursor.IsShow == true) + { + this.TouchTimer = setTimeout(function () { + if (drag.Click.X == drag.LastMove.X && drag.Click.Y == drag.LastMove.Y) //手指没有移动,出现十字光标 + { + var mouseDrag = jsChart.MouseDrag; + jsChart.MouseDrag = null; + //移动十字光标 + var x = drag.Click.X; + var y = drag.Click.Y; + jsChart.OnMouseMove(x, y, e); + } + }, 800); + } + + + var drag = { "Click": {}, "LastMove": {}, };//最后移动的位置 + var touches = this.GetToucheData(e); + + drag.Click.X = touches[0].clientX; + drag.Click.Y = touches[0].clientY; + drag.LastMove.X = touches[0].clientX; + drag.LastMove.Y = touches[0].clientY; + + jsChart.MouseDrag = drag; + } + else if (this.IsPhonePinching(e)) + { + var phonePinch = { "Start": {}, "Last": {}}; + var touches = this.GetToucheData(e); + + phonePinch.Start = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + phonePinch.Last = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + + jsChart.PhonePinch = phonePinch; + } + } + + + this.ontouchmove = function (e) + { + if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return; + + var jsChart = this; + var touches = this.GetToucheData(e); + + if (this.IsPhoneDragging(e)) + { + var drag = jsChart.MouseDrag; + if (drag == null) + { + var x = touches[0].clientX; + var y = touches[0].clientY; + jsChart.OnMouseMove(x, y, e); + } + else + { + var moveSetp = Math.abs(drag.LastMove.Y - touches[0].clientY); + moveSetp = parseInt(moveSetp); + if (jsChart.DragMode == 1) //数据左右拖拽 + { + if (moveSetp < 5) return; + + var isLeft = true; + if (drag.LastMove.Y < touches[0].clientY) isLeft = false;//右移数据 + + if (jsChart.DataMove(moveSetp, isLeft)) + { + jsChart.UpdataDataoffset(); + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + + drag.LastMove.X = touches[0].clientX; + drag.LastMove.Y = touches[0].clientY; + } + } + } + else if (this.IsPhonePinching(e)) + { + var phonePinch = jsChart.PhonePinch; + if (!phonePinch) return; + + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Touch===false) return; + + var yHeight = Math.abs(touches[0].pageX - touches[1].pageX); + var yLastHeight = Math.abs(phonePinch.Last.X - phonePinch.Last.X2); + var yStep = yHeight - yLastHeight; + if (Math.abs(yStep) < 5) return; + + if (yStep > 0) //放大 + { + var cursorIndex = {}; + cursorIndex.Index = parseInt(Math.abs(jsChart.CursorIndex - 0.5).toFixed(0)); + if (!jsChart.Frame.ZoomUp(cursorIndex)) return; + jsChart.CursorIndex = cursorIndex.Index; + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdataDataoffset(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + else //缩小 + { + var cursorIndex = {}; + cursorIndex.Index = parseInt(Math.abs(jsChart.CursorIndex - 0.5).toFixed(0)); + if (!jsChart.Frame.ZoomDown(cursorIndex)) return; + jsChart.CursorIndex = cursorIndex.Index; + jsChart.UpdataDataoffset(); + jsChart.UpdatePointByCursorIndex(); + jsChart.UpdateFrameMaxMin(); + jsChart.ResetFrameXYSplit(); + jsChart.Draw(); + } + + phonePinch.Last = { "X": touches[0].pageX, "Y": touches[0].pageY, "X2": touches[1].pageX, "Y2": touches[1].pageY }; + } + } + + + uielement.onmousedown = function (e) //鼠标拖拽 + { + if (!this.JSChartContainer) return; + if (this.JSChartContainer.DragMode == 0) return; + + if (this.JSChartContainer.TryClickLock) { + var x = e.clientX - this.getBoundingClientRect().left; + var y = e.clientY - this.getBoundingClientRect().top; + if (this.JSChartContainer.TryClickLock(x, y)) return; + } + + + var drag = + { + "Click": {}, + "LastMove": {}, //最后移动的位置 + }; + + drag.Click.X = e.clientX; + drag.Click.Y = e.clientY; + drag.LastMove.X = e.clientX; + drag.LastMove.Y = e.clientY; + + this.JSChartContainer.MouseDrag = drag; + document.JSChartContainer = this.JSChartContainer; + this.JSChartContainer.SelectChartDrawPicture = null; + + uielement.ondblclick = function (e) { + var x = e.clientX - this.getBoundingClientRect().left; + var y = e.clientY - this.getBoundingClientRect().top; + + if (this.JSChartContainer) + this.JSChartContainer.OnDoubleClick(x, y, e); + } + + document.onmousemove = function (e) { + if (!this.JSChartContainer) return; + //加载数据中,禁用鼠标事件 + if (this.JSChartContainer.ChartSplashPaint && this.JSChartContainer.ChartSplashPaint.IsEnableSplash == true) return; + + var drag = this.JSChartContainer.MouseDrag; + if (!drag) return; + + var moveSetp = Math.abs(drag.LastMove.Y - e.clientY); + + if (this.JSChartContainer.DragMode == 1) //数据左右拖拽 + { + if (moveSetp < 5) return; + + var isLeft = true; + if (drag.LastMove.Y < e.clientY) isLeft = false;//右移数据 + + if (this.JSChartContainer.DataMove(moveSetp, isLeft)) { + this.JSChartContainer.UpdataDataoffset(); + this.JSChartContainer.UpdatePointByCursorIndex(); + this.JSChartContainer.UpdateFrameMaxMin(); + this.JSChartContainer.ResetFrameXYSplit(); + this.JSChartContainer.Draw(); + } + + drag.LastMove.X = e.clientX; + drag.LastMove.Y = e.clientY; + } + }; + + document.onmouseup = function (e) { + //清空事件 + document.onmousemove = null; + document.onmouseup = null; + + //清空数据 + this.JSChartContainer.MouseDrag = null; + this.JSChartContainer.CurrentChartDrawPicture = null; + this.JSChartContainer = null; + } + } + + //创建 + //windowCount 窗口个数 + this.Create = function (windowCount) { + this.UIElement.JSChartContainer = this; + + //创建十字光标 + this.ChartCorssCursor = new ChartCorssCursor(); + this.ChartCorssCursor.Canvas = this.Canvas; + this.ChartCorssCursor.StringFormatX = g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + this.ChartCorssCursor.StringFormatY = g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.HQChart=this; + + //创建框架容器 + this.Frame = new HQTradeHScreenFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 30; + this.Frame.ChartBorder.Left = 5; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + this.ChartCorssCursor.Frame = this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for (var i in this.Frame.SubFrame) { + var titlePaint = new DynamicChartTitlePainting(); + titlePaint.Frame = this.Frame.SubFrame[i].Frame; + titlePaint.Canvas = this.Canvas; + + this.TitlePaint.push(titlePaint); + } + } + + //创建子窗口 + this.CreateChildWindow = function (windowCount) + { + for (var i = 0; i < windowCount; ++i) + { + var border = new ChartBorder(); + border.UIElement = this.UIElement; + + var frame = new KLineHScreenFrame(); + frame.Canvas = this.Canvas; + frame.ChartBorder = border; + frame.Identify = i; //窗口序号 + frame.RightSpaceCount = this.RightSpaceCount; //右边 + + if (this.ModifyIndexDialog) frame.ModifyIndexEvent = this.ModifyIndexDialog.DoModal; //绑定菜单事件 + if (this.ChangeIndexDialog) frame.ChangeIndexEvent = this.ChangeIndexDialog.DoModal; + + frame.HorizontalMax = 20; + frame.HorizontalMin = 10; + + if (i == 0) + { + frame.YSplitOperator = new FrameSplitKLinePriceY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('price'); + //主图上下间距 + border.TopSpace = 12; + border.BottomSpace = 12; + } + else + { + frame.YSplitOperator = new FrameSplitY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + //frame.IsLocked = true; + } + + frame.YSplitOperator.Frame = frame; + frame.YSplitOperator.ChartBorder = border; + frame.XSplitOperator = new FrameSplitKLineX(); + frame.XSplitOperator.Frame = frame; + frame.XSplitOperator.ChartBorder = border; + + if (i != windowCount - 1) frame.XSplitOperator.ShowText = false; + + for (var j = frame.HorizontalMin; j <= frame.HorizontalMax; j += 1) + { + frame.HorizontalInfo[j] = new CoordinateInfo(); + frame.HorizontalInfo[j].Value = j; + if (i == 0 && j == frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1] = j.toString(); + frame.HorizontalInfo[j].Font = "14px 微软雅黑"; + } + + var subFrame = new SubFrameItem(); + subFrame.Frame = frame; + if (i == 0) + subFrame.Height = 20; + else + subFrame.Height = 10; + + this.Frame.SubFrame[i] = subFrame; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 走势图横屏显示 +// +function MinuteChartHScreenContainer(uielement) { + this.newMethod = MinuteChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName = 'MinuteChartHScreenContainer'; + + this.OnMouseMove = function (x, y, e) { + this.LastPoint.X = x; + this.LastPoint.Y = y; + this.CursorIndex = this.Frame.GetXData(y); + + this.DrawDynamicInfo(); + } + + //创建 + //windowCount 窗口个数 + this.Create = function (windowCount) { + this.UIElement.JSChartContainer = this; + + //创建十字光标 + this.ChartCorssCursor = new ChartCorssCursor(); + this.ChartCorssCursor.Canvas = this.Canvas; + this.ChartCorssCursor.StringFormatX = new HQMinuteTimeStringFormat(); + this.ChartCorssCursor.StringFormatY = new HQPriceStringFormat(); + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + this.ChartSplashPaint.SplashTitle = this.LoadDataSplashTitle; + + //创建框架容器 + this.Frame = new HQTradeHScreenFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 25; + this.Frame.ChartBorder.Left = 50; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + this.ChartCorssCursor.Frame = this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateChildWindow(windowCount); + this.CreateMainKLine(); + + //子窗口动态标题 + for (var i in this.Frame.SubFrame) { + var titlePaint = new DynamicChartTitlePainting(); + titlePaint.Frame = this.Frame.SubFrame[i].Frame; + titlePaint.Canvas = this.Canvas; + + this.TitlePaint.push(titlePaint); + } + + this.ChartCorssCursor.StringFormatX.Frame = this.Frame.SubFrame[0].Frame; + } + + //创建子窗口 + this.CreateChildWindow = function (windowCount) { + for (var i = 0; i < windowCount; ++i) { + var border = new ChartBorder(); + border.UIElement = this.UIElement; + + var frame = new MinuteHScreenFrame(); + frame.Canvas = this.Canvas; + frame.ChartBorder = border; + if (i < 2) frame.ChartBorder.TitleHeight = 0; + frame.XPointCount = 243; + + var DEFAULT_HORIZONTAL = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + frame.HorizontalMax = DEFAULT_HORIZONTAL[0]; + frame.HorizontalMin = DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length - 1]; + + if (i == 0) { + frame.YSplitOperator = new FrameSplitMinutePriceY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('price'); + } + else { + frame.YSplitOperator = new FrameSplitY(); + frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + } + + frame.YSplitOperator.Frame = frame; + frame.YSplitOperator.ChartBorder = border; + frame.XSplitOperator = new FrameSplitMinuteX(); + frame.XSplitOperator.Frame = frame; + frame.XSplitOperator.ChartBorder = border; + if (i != windowCount - 1) frame.XSplitOperator.ShowText = false; + frame.XSplitOperator.Operator(); + + for (var j in DEFAULT_HORIZONTAL) { + frame.HorizontalInfo[j] = new CoordinateInfo(); + frame.HorizontalInfo[j].Value = DEFAULT_HORIZONTAL[j]; + if (i == 0 && j == frame.HorizontalMin) continue; + + frame.HorizontalInfo[j].Message[1] = DEFAULT_HORIZONTAL[j].toString(); + frame.HorizontalInfo[j].Font = "14px 微软雅黑"; + } + + var subFrame = new SubFrameItem(); + subFrame.Frame = frame; + if (i == 0) + subFrame.Height = 20; + else + subFrame.Height = 10; + + this.Frame.SubFrame[i] = subFrame; + } + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// K线训练,包含横屏 +// +function KLineTrainChartContainer(uielement, bHScreen) +{ + if (bHScreen === true) + { + this.newMethod = KLineChartHScreenContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + } + else + { + this.newMethod = KLineChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + } + + this.ClassName2 = 'KLineTrainChartContainer'; + this.BuySellPaint; //买卖点画法 + this.TrainDataCount = 300; //训练数据个数 + this.AutoRunTimer = null; //K线自动前进定时器 + this.BuySellData = []; //模拟买卖数据 {Buy:{Price:价格,Date:日期,Time:时间} , Sell:{Price:价格,Date:日期,Time:时间} + this.TrainDataIndex; //当前训练的数据索引 + this.TrainCallback; //训练回调 (K线每前进一次就调用一次) + this.DragMode = 1; + + this.TrainStartEnd = {}; + this.KLineSourceData; //原始K线数据 对应 SourceData + this.TrainInfo = { Start: {}, End: {} }; // Index:数据索引, Date:日期 Time:时间 + + this.CreateBuySellPaint = function () //在主窗口建立以后 创建买卖点 + { + var chart = new ChartBuySell(); + chart.Canvas = this.Canvas; + chart.ChartBorder = this.Frame.SubFrame[0].Frame.ChartBorder; + chart.ChartFrame = this.Frame.SubFrame[0].Frame; + chart.Name = "KLine-Train-BuySell"; + this.ChartPaintEx[0] = chart; + } + + this.GetKDataIndexByDateTime = function (kData, dateTime) + { + if (!dateTime || !kData) return -1; + + for (var i in kData) + { + var item = kData[i]; + if (ChartData.IsMinutePeriod(this.Period, true)) + { + if (IFrameSplitOperator.IsNumber(this.TrainStartDate.Time)) + { + if (item.Date >= this.TrainStartDate.Date && item.Time >= this.TrainStartDate.Time) + return parseInt(i); + } + else + { + if (item.Date >= this.TrainStartDate.Date) + return parseInt(i); + } + } + else if (ChartData.IsDayPeriod(this.Period, true) || ChartData.IsTickPeriod(this.Period)) + { + if (item.Date >= this.TrainStartDate.Date) + return parseInt(i); + } + } + + return -1; + } + + this.AfterBindMainData = function (funcName) + { + if (!this.ChartPaintEx[0]) this.CreateBuySellPaint(); + + var hisData = this.ChartPaint[0].Data; + this.ChartPaintEx[0].Data = hisData; + + var showItem = hisData.Data[hisData.Data.length - 1]; + + //最后一个显示数据 + this.TrainInfo.LastShowData = showItem; + //最后一个原始数据 + this.TrainInfo.LastData = this.SourceData.Data[this.SourceData.Data.length - 1]; + + if (funcName != 'Update') + this.UpdateTrainUICallback("开始"); + } + + this.BeforeBindMainData = function (funcName) + { + if (funcName == "Update") return; + + //全量数据 需要过滤 + this.KLineSourceData = new ChartData(); + this.KLineSourceData.Data = this.SourceData.Data.slice(0); + var count = this.SourceData.Data.length; + var lEnd = count - this.TrainDataCount - 20; + var findIndex = this.GetKDataIndexByDateTime(this.SourceData.Data, this.TrainStartDate); + if (findIndex >= 0) + { + lEnd = findIndex + 1; + if (count - lEnd < this.TrainDataCount) this.TrainDataCount = count - lEnd; + } + + //训练起始日期 + var index = lEnd - 1; + var kItem = this.SourceData.Data[index]; + this.TrainInfo.Start.Index = index; + this.TrainInfo.Start.Date = kItem.Date; + this.TrainInfo.Start.Time = kItem.Time; + + //训练结束日期 + this.TrainInfo.End.Index = index; + this.TrainInfo.End.Date = kItem.Date; + this.TrainInfo.End.Time = kItem.Time; + + //最后一个数据 + this.TrainInfo.LastData = kItem; + + //修改数据个数 + this.SourceData.Data.length = lEnd; + } + + this.Run = function (option) + { + if (this.AutoRunTimer) return; + if (this.TrainDataCount <= 0) return; + + var self = this; + this.AutoRunTimer = setInterval(function () { + if (!self.MoveNextKLineData(option)) clearInterval(self.AutoRunTimer); + }, 1000); + } + + this.MoveNextKLineData = function (option) //{PageSize:, Step:} + { + if (this.TrainDataCount <= 0) return false; + + var step = 1, moveStep=0; + if (option && option.Step > 1) step = option.Step; + for (var i = 0; i < step; ++i) + { + var index = this.TrainInfo.End.Index + 1; + if (index >= this.KLineSourceData.Data.length) break; + + var kItem = this.KLineSourceData.Data[index]; + this.SourceData.Data.push(kItem); + + this.TrainInfo.End.Index = index; + this.TrainInfo.End.Date = kItem.Date; + this.TrainInfo.End.Time = kItem.Time; + --this.TrainDataCount; + ++moveStep; + + + + if (this.TrainDataCount <= 0) break; + } + + if (moveStep == 0) return false; + + //使用当前页数据个数移动K线 + var pageSize = this.Frame.GetCurrentPageSize(); + if (IFrameSplitOperator.IsNumber(pageSize)) + this.PageSize = pageSize - this.RightSpaceCount; + + this.Update(); + + if (this.TrainDataCount <= 0) + { + this.FinishTrainData(); + this.UpdateTrainUICallback("结束"); + return false; + } + + this.UpdateTrainUICallback("训练中"); + return true; + } + + this.UpdateTrainUICallback = function (description) + { + //新的监听事件 + if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP)) return; + var item = this.mapEvent.get(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP); + if (!item.Callback) return; + + var data = + { + TrainDataCount: this.TrainDataCount, + BuySellData: this.BuySellData, + KLine: + { + Start: { Index: this.TrainInfo.Start.Index, Date: this.TrainInfo.Start.Date }, + End: { Index: this.TrainInfo.End.Index, Date: this.TrainInfo.End.Date } + }, + LastData: this.TrainInfo.LastData, + LastShowData: this.TrainInfo.LastShowData + }; + if (IFrameSplitOperator.IsNumber(this.TrainInfo.Start.Time)) data.KLine.Start.Time = this.TrainInfo.Start.Time; + if (IFrameSplitOperator.IsNumber(this.TrainInfo.End.Time)) data.KLine.End.Time = this.TrainInfo.End.Time; + if (description) data.Description = description + + if (this.TrainDataCount <= 0) + { + data.Symbol = this.Symbol; + data.Name = this.Name; + } + + item.Callback(item, data, this); + } + + this.FinishTrainData = function () + { + + } + + this.Stop = function () + { + if (this.AutoRunTimer != null) clearInterval(this.AutoRunTimer); + this.AutoRunTimer = null; + } + + this.BuyOrSell = function (obj, bDraw) //{ Price:价格, Vol:数量, Op: 买/卖 0=buy 1=sell, ID:单号 } bDraw是否立即绘制图标 + { + var kItem = this.TrainInfo.LastShowData; + if (!kItem) return false; + + var buySellPaint = this.ChartPaintEx[0]; + if (!buySellPaint) return false; + + var hisData = this.ChartPaint[0].Data; + if (!hisData || hisData.Data.length <= 0) return false; + + var index = hisData.Data.length - 1; //数据索引 + var buyItem = { Date: this.TrainInfo.End.Date, Time: this.TrainInfo.End.Time, Price: obj.Price, Vol: obj.Vol, Op: 0, ID: obj.ID }; + if (obj.Op == 1) buyItem.Op = 1; + var key = index; + buyItem.Key = key; + + this.BuySellData.push(buyItem); + buySellPaint.AddTradeItem(buyItem); + + if (bDraw == true) this.Draw(); + } + + this.RestartTrain = function (option) // { Symbol:, Period:周期, Right:复权, Train:{ DataCount:, DateTime: } } + { + JSConsole.Chart.Log('[KLineTrainChartContainer::RestartTrain] option ', option); + + this.TrainInfo = { Start: {}, End: {} }; + this.BuySellData = []; + this.KLineSourceData = null; + + var buySellPaint = this.ChartPaintEx[0]; + if (buySellPaint) + { + buySellPaint.Data = null; + buySellPaint.ClearTradeData(); + } + + if (option.Symbol) this.Symbol = option.Symbol; + if (IFrameSplitOperator.IsNumber(option.Period)) this.Period = option.Period; + if (IFrameSplitOperator.IsNumber(option.Right)) this.Right = option.Right; + if (option.Train) + { + if (option.Train.DataCount > 1) this.TrainDataCount = option.Train.DataCount; + if (option.Train.DateTime) this.TrainStartDate = option.Train.DateTime; + } + + var symbol = this.Symbol; + this.ChangeSymbol(symbol); + } +} + +///////////////////////////////////////////////////////////////////////////////// +// 深度图 +// +function DepthChartContainer(uielement) +{ + this.newMethod=JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.ClassName="DepthChartContainer"; + this.Symbol; + + //数据 + this.MapAsk=new Map(); + this.MapBid=new Map(); + + this.IsAutoUpdate=false; //是否自动更新行情数据 + this.AutoUpdateFrequency=30000; //30秒更新一次数据 + this.AutoUpdateTimer; + + this.DefaultZoom=0.8; //默认显示80%的盘口 (0 - 1) + this.MaxVolRate=1.1; + + this.Create=function(option) + { + this.UIElement.JSChartContainer=this; + + //创建十字光标 + this.ChartCorssCursor=new DepthChartCorssCursor(); + this.ChartCorssCursor.Canvas=this.Canvas; + this.ChartCorssCursor.HQChart=this; + //this.ChartCorssCursor.StringFormatX=g_DivTooltipDataForamt.Create("CorssCursor_XStringFormat"); + //this.ChartCorssCursor.StringFormatX.LanguageID=this.LanguageID; + //this.ChartCorssCursor.StringFormatY=g_DivTooltipDataForamt.Create("CorssCursor_YStringFormat"); + //this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID; + + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架 + this.Frame=new DepthChartFrame(); + this.Frame.ChartBorder=new ChartBorder(); + this.Frame.ChartBorder.UIElement=this.UIElement; + this.Frame.ChartBorder.Top=30; + this.Frame.ChartBorder.Left=5; + this.Frame.ChartBorder.Bottom=20; + this.Frame.ChartBorder.TitleHeight=0; + this.Frame.Canvas=this.Canvas; + + var ySplitOper=new FrameSplitY(); + ySplitOper.FrameSplitData=this.FrameSplitData.get('double'); + ySplitOper.LanguageID=this.LanguageID; + ySplitOper.Frame=this.Frame; + ySplitOper.SplitCount=5; + ySplitOper.IgnoreYValue=[0]; + ySplitOper.LineType=3; + ySplitOper.ChartBorder=this.Frame.ChartBorder; + this.Frame.YSplitOperator=ySplitOper; + + var xSplitOper=new FrameSplitXDepth(); + xSplitOper.Frame=this.Frame;; + xSplitOper.ChartBorder=this.Frame.ChartBorder;; + xSplitOper.LanguageID=this.LanguageID; + xSplitOper.LineType=3; + this.Frame.XSplitOperator=xSplitOper + + if (this.ChartCorssCursor) this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + var chartItem=new ChartOrderbookDepth(); + chartItem.Canvas=this.Canvas; + chartItem.ChartBorder=this.Frame.ChartBorder; + chartItem.ChartFrame=this.Frame; + chartItem.Name="深度图" + this.ChartPaint.push(chartItem); + } + + this.ontouchstart=function(e) + { + this.IsOnTouch=true; + this.TouchDrawCount=0; + this.PhonePinch=null; + + var isSingleTouch=this.IsSingleTouch(e); + if (this.EnableScrollUpDown==false || !isSingleTouch) //多点触屏 + { + + } + + if (this.IsPhoneDragging(e)) + { + var drag= + { + "Click":{}, + "LastMove":{} //最后移动的位置 + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + drag.Click.X=touches[0].clientX; + drag.Click.Y=touches[0].clientY; + drag.LastMove.X=touches[0].clientX; + drag.LastMove.Y=touches[0].clientY; + + this.MouseDrag=drag; + + var x = drag.Click.X; + var y = drag.Click.Y; + this.OnMouseMove(x, y, e); + } + else if (this.IsPhonePinching(e)) + { + var phonePinch= + { + "Start":{}, + "Last":{} + }; + + var touches=this.GetToucheData(e,this.IsForceLandscape); + + phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + + this.PhonePinch=phonePinch; + } + } + + this.ontouchmove=function(e) + { + var touches=this.GetToucheData(e,false); + if (this.IsPhoneDragging(e)) + { + var drag=this.MouseDrag; + if (drag==null) + { + var x = touches[0].clientX; + var y = touches[0].clientY; + this.OnMouseMove(x,y,e); + } + else + { + this.MouseDrag=null; + var x = touches[0].clientX; + var y = touches[0].clientY; + this.OnMouseMove(x,y,e); + } + } + else if (this.IsPhonePinching(e)) + { + var phonePinch=this.PhonePinch; + if (!phonePinch) return; + + if (this.EnableZoomUpDown && this.EnableZoomUpDown.Touch===false) return; + + var yHeight = Math.abs(touches[0].pageY - touches[1].pageY); + var yLastHeight = Math.abs(phonePinch.Last.Y - phonePinch.Last.Y2); + var yStep = yHeight - yLastHeight; + var xHeight = Math.abs(touches[0].pageX - touches[1].pageX); + var xLastHeight = Math.abs(phonePinch.Last.X - phonePinch.Last.X2); + var xStep = xHeight - xLastHeight; + var minStep=this.ZoomStepPixel; + if (Math.abs(yStep) 0) //放大 + { + if (!this.Frame.ZoomUp()) return; + this.UpdateFrameMaxMin(); + this.Draw(); + } + else //缩小 + { + if (!this.Frame.ZoomDown()) return; + this.UpdateFrameMaxMin(); + this.Draw(); + } + + phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY}; + } + } + + this.ontouchend=function(e) + { + JSConsole.Chart.Log('[DepthChartContainer::OnTouchEnd]',e); + this.IsOnTouch = false; + this.Draw(); + this.TouchDrawCount=0; + } + + this.OnMouseMove = function (x, y, e, bFullDraw) + { + var lastY = this.LastPoint.Y; + this.LastPoint.X = x; + this.LastPoint.Y = y; + this.FullDraw(); + } + + this.ChangeSymbol=function(symbol) + { + this.CancelAutoUpdate(); //先停止定时器 + this.Symbol=symbol; + this.MapBid=new Map(); + this.MapAsk=new Map(); + this.Frame.VerticalRange.Differ=null; + + this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); + this.ChartSplashPaint.EnableSplash(true); + this.Draw(); + + this.RequestDepthData(); + } + + this.RequestDepthData=function() //全量历史数据 + { + var self=this; + if (this.NetworkFilter) + { + var obj= + { + Name:'DepthChartContainer::RequestDepthData', //类名:: + Explain:'深度图数据', + Request:{ Data: { symbol:self.Symbol } }, + Self:this, + PreventDefault:false + }; + this.NetworkFilter(obj, function(data) + { + self.ChartSplashPaint.EnableSplash(false); + self.RecvDepthData(data); + self.AutoUpdate(); + }); + + if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求 + } + } + + this.RecvDepthData=function(data) + { + this.UpdateAskAndBid(data); + + var aryAsk=Array.from(this.MapAsk.values()); //卖 右边 + aryAsk.sort((a,b)=> { return a.Price-b.Price; }); + var sumVol=0; + for(var i in aryAsk) + { + var item=aryAsk[i]; + sumVol+=item.Vol; + + aryAsk[i]={Price:item.Price, Vol:sumVol }; + } + + var aryBid=Array.from(this.MapBid.values()); //买 左边 + aryBid.sort((a,b)=> { return b.Price-a.Price; }); + var sumVol=0; + for(var i in aryBid) + { + var item=aryBid[i]; + sumVol+=item.Vol; + + aryBid[i]={Price:item.Price, Vol:sumVol }; + } + + var drawData={ Asks:aryAsk, Bids:aryBid }; + var chart=this.ChartPaint[0]; + chart.Data=drawData; + + this.Frame.XSplitOperator.Symbol=this.Symbol; + this.ChartCorssCursor.Data=drawData; + this.ChartCorssCursor.Symbol=this.Symbol; + + this.UpdateFramePriceList(); + this.UpdateFrameMaxMin(); + + this.Draw(); + } + + this.UpdateAskAndBid=function(data) //更新数据 + { + if(data.datatype=="snapshot") //全量数据 + { + this.MapBid=new Map(); + this.MapAsk=new Map(); + } + + for(var i in data.asks) + { + var item=data.asks[i]; + var price=parseFloat(item[0]); + var vol=parseFloat(item[1]); + + if (this.MapAsk.has(price)) + { + var value=this.MapAsk.get(price); + if (vol<=0) this.MapAsk.delete(price); + else value.Vol=vol; + } + else + { + if (vol>0) this.MapAsk.set(price, { Price:price, Vol:vol}); + } + } + + for(var i in data.bids) + { + var item=data.bids[i]; + var price=parseFloat(item[0]); + var vol=parseFloat(item[1]); + + if (this.MapBid.has(price)) + { + var value=this.MapBid.get(price); + if (vol<=0) this.MapBid.delete(price); + else value.Vol=vol; + } + else + { + if (vol>0) this.MapBid.set(price, { Price:price, Vol:vol}); + } + } + } + + this.UpdateFramePriceList=function() + { + var aryAskPrice=Array.from(this.MapAsk.keys()); + var aryBidPrice=Array.from(this.MapBid.keys()); + + aryAskPrice.sort((a,b)=> { return a-b; }); + aryBidPrice.sort((a,b)=> { return a-b; }); + + if (aryAskPrice.length>1 && aryBidPrice.length>1) + { + var askMin=aryAskPrice[0], askMax=aryAskPrice[aryAskPrice.length-1]; + var bidMin=aryBidPrice[0], bidMax=aryBidPrice[aryBidPrice.length-1]; + var askDifference=askMax-askMin; //卖差值 + var bidDifference=bidMax-bidMin; //买差值 + var difference=Math.max(askDifference, bidDifference); //取最大的差值,2边调整 + + var ask={Min:askMin, Max:askMin+difference}; + var bid={Max:bidMax, Min:bidMax-difference}; + var range={ Max:ask.Max, Min:bid.Min }; + } + + this.Frame.SetPriceList(aryAskPrice,aryBidPrice); + var xRange=this.Frame.VerticalRange; + xRange.Max=range.Max; + xRange.Center=range.Min+(range.Max-range.Min)/2; + xRange.Min=range.Min; + xRange.MaxDiffer=difference; //差值 + xRange.Ask=ask; + xRange.Bid=bid; + if (!IFrameSplitOperator.IsNumber(xRange.Differ)) + xRange.Differ=difference*this.DefaultZoom; + + xRange.Min=xRange.Center-xRange.Differ; + xRange.Max=xRange.Center+xRange.Differ; + } + + this.UpdateFrameMaxMin=function() + { + var range=this.ChartPaint[0].GetMaxMin(); + + this.Frame.HorizontalMax=range.Max*this.MaxVolRate; + this.Frame.HorizontalMin=0; + this.Frame.XYSplit=true; + } + + this.CancelAutoUpdate=function() //关闭停止更新 + { + if (typeof (this.AutoUpdateTimer) == 'number') + { + clearTimeout(this.AutoUpdateTimer); + this.AutoUpdateTimer = undefined; + } + } + + this.StopAutoUpdate=function() + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + this.IsAutoUpdate=false; + } + + this.AutoUpdate=function() //数据自动更新 + { + this.CancelAutoUpdate(); + if (!this.IsAutoUpdate) return; + if (!this.Symbol) return; + if (this.IsDestroy) return; + + var self = this; + var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol); + if (marketStatus==0 || marketStatus==3) return; //闭市,盘后 + + var frequency=this.AutoUpdateFrequency; + if (marketStatus==1) //盘前 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.AutoUpdate(); + },frequency); + } + else if (marketStatus==2) //盘中 + { + this.AutoUpdateTimer=setTimeout(function() + { + self.RequestDepthData(); + },frequency); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 简单的图形框架 +// +function SimlpleChartContainer(uielement) { + this.newMethod = JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.WindowIndex = new Array(); + this.MainDataControl; //主数据类(对外的接口类) + this.SubDataControl = new Array(); + this.FrameType = 0; //框架类型 + + this.YSplitCount = 4; + + //创建 + this.Create = function () { + this.UIElement.JSChartContainer = this; + + //创建十字光标 + //this.ChartCorssCursor=new ChartCorssCursor(); + //this.ChartCorssCursor.Canvas=this.Canvas; + //this.ChartCorssCursor.StringFormatX=new HQDateStringFormat(); + //this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat(); + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架容器 + if (this.FrameType == 1) this.Frame = new Rotate90SimpleChartFrame(); + else this.Frame = new SimpleChartFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 30; + this.Frame.ChartBorder.Left = 5; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + if (this.ChartCorssCursor) this.ChartCorssCursor.Frame = this.Frame; //十字光标绑定框架 + this.ChartSplashPaint.Frame = this.Frame; + + this.CreateMainChart(); + + } + + this.SetMainDataConotrl = function (dataControl) { + if (!dataControl) return; + + this.MainDataControl = dataControl; + this.ChartPaint = []; //图形 + this.Frame.BarCount = 0; + + let yStringFormat = this.Frame.YSplitOperator.StringFormat;//保存配置 + + this.CreateMainChart(); + this.Frame.YSplitOperator.StringFormat = yStringFormat;//还原配置 + this.Draw(); + this.RequestData(); + } + + //创建主数据画法 + this.CreateMainChart = function () + { + if (!this.MainDataControl) return; + + let barIndex = 0; + for (let i in this.MainDataControl.DataType) + { + let item = this.MainDataControl.DataType[i]; + if (item.Type == "BAR") + { + let chartItem; + if (this.FrameType == 1) chartItem = new ChartXYSubBar(); + else chartItem = new ChartSubBar(); + + chartItem.BarID = barIndex; + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = item.Name; + if (item.Color) chartItem.UpBarColor = item.Color; + if (item.Color2) chartItem.DownBarColor = item.Color2; + + this.ChartPaint.push(chartItem); + ++this.Frame.BarCount; + ++barIndex; + } + else if (item.Type == "LINE") + { + let chartItem = new ChartLine(); + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = item.Name; + if (item.Color) chartItem.Color = item.Color; + + this.ChartPaint.push(chartItem); + } + } + + var floatPrecision = 2; //设置纵坐标的小数位数 默认为2 + var ignoreYValue=null + if (this.Frame.YSplitOperator) + { + floatPrecision = this.Frame.YSplitOperator.FloatPrecision; //备份上次实例化的值 + if (this.Frame.YSplitOperator.IgnoreYValue) ignoreYValue = this.Frame.YSplitOperator.IgnoreYValue; + } + + this.Frame.YSplitOperator = new FrameSplitY(); + this.Frame.YSplitOperator.FloatPrecision = floatPrecision; //实例化纵坐标分割后 赋给备份的值 + this.Frame.YSplitOperator.IgnoreYValue = ignoreYValue; + + this.Frame.YSplitOperator.SplitCount = this.YSplitCount; + this.Frame.YSplitOperator.FrameSplitData = this.FrameSplitData.get('double'); + this.Frame.YSplitOperator.Frame = this.Frame; + this.Frame.YSplitOperator.ChartBorder = this.Frame.ChartBorder; + + this.Frame.XSplitOperator = new FrameSplitXData(); + this.Frame.XSplitOperator.Frame = this.Frame; + this.Frame.XSplitOperator.ChartBorder = this.Frame.ChartBorder; + + + // this.TitlePaint[0]=new DynamicKLineTitlePainting(); + // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + // this.TitlePaint[0].Canvas=this.Canvas; + } + + this.RequestData = function () { + if (!this.MainDataControl) return; + + this.MainDataControl.JSChartContainer = this; + this.MainDataControl.RequestData(); + } + + this.UpdateMainData = function (dataControl) { + let lCount = 0; + for (let i in dataControl.Data) { + let itemData = new ChartData(); + itemData.Data = dataControl.Data[i]; + this.ChartPaint[i].Data = itemData; + if (lCount < itemData.Data.length) lCount = itemData.Data.length; + } + + this.Frame.XPointCount = lCount; + this.Frame.Data = this.ChartPaint[0].Data; + this.Frame.XData = dataControl.XData; + + this.UpdateFrameMaxMin(); //调整坐标最大 最小值 + this.Frame.SetSizeChage(true); + this.Draw(); + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// 饼图图形框架 +// +function PieChartContainer(uielement) { + this.Radius; //半径 + this.newMethod = JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.MainDataControl; //主数据类(对外的接口类) + + //鼠标移动 + this.OnMouseMove = function (x, y, e) { } + + //创建 + this.Create = function () { + this.UIElement.JSChartContainer = this; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架容器 + this.Frame = new NoneFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 30; + this.Frame.ChartBorder.Left = 5; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + + this.ChartSplashPaint.Frame = this.Frame; + this.CreateMainChart(); + } + + this.SetMainDataConotrl = function (dataControl) { + if (!dataControl) return; + + this.MainDataControl = dataControl; + this.ChartPaint = []; //图形 + + this.CreateMainChart(); + this.Draw(); + this.RequestData(); + } + + //创建主数据画法 + this.CreateMainChart = function () + { + if (!this.MainDataControl) return; + + for (let i in this.MainDataControl.DataType) + { + let item = this.MainDataControl.DataType[i]; + if (item.Type == "PIE") + { + var chartItem = new ChartPie(); + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = item.Name; + + this.ChartPaint.push(chartItem); + } + else if (item.Type == 'CIRCLE') + { + var chartItem = new ChartCircle(); + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = item.Name; + + this.ChartPaint.push(chartItem); + } + else if (item.Type == 'RADAR') //雷达图 + { + var chartItem = new ChartRadar(); + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = item.Name; + if (item.StartAngle) chartItem.StartAngle = item.StartAngle; + if (item.TitleFont) chartItem.TitleFont = item.TitleFont; + this.ChartPaint.push(chartItem); + } + } + + // this.TitlePaint[0]=new DynamicKLineTitlePainting(); + // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + // this.TitlePaint[0].Canvas=this.Canvas; + } + + this.RequestData = function () { + if (!this.MainDataControl) return; + + this.MainDataControl.JSChartContainer = this; + this.MainDataControl.RequestData(); + } + + this.UpdateMainData = function (dataControl) { + for (let i in dataControl.Data) { + let itemData = new ChartData(); + itemData.Data = dataControl.Data[i]; + this.ChartPaint[i].Data = itemData; + } + this.Frame.SetSizeChage(true); + this.Draw(); + } + +} + +//地图 +function MapChartContainer(uielement) { + this.newMethod = JSChartContainer; //派生 + this.newMethod(uielement); + delete this.newMethod; + + this.MainDataControl; //主数据类(对外的接口类) + + //鼠标移动 + this.OnMouseMove = function (x, y, e) { + + } + + //创建 + this.Create = function () { + this.UIElement.JSChartContainer = this; + + //创建等待提示 + this.ChartSplashPaint = new ChartSplashPaint(); + this.ChartSplashPaint.Canvas = this.Canvas; + + //创建框架容器 + this.Frame = new NoneFrame(); + this.Frame.ChartBorder = new ChartBorder(); + this.Frame.ChartBorder.UIElement = this.UIElement; + this.Frame.ChartBorder.Top = 30; + this.Frame.ChartBorder.Left = 5; + this.Frame.ChartBorder.Bottom = 20; + this.Frame.Canvas = this.Canvas; + + this.ChartSplashPaint.Frame = this.Frame; + this.CreateMainChart(); + } + + this.SetMainDataConotrl = function (dataControl) { + if (!dataControl) return; + + this.MainDataControl = dataControl; + this.ChartPaint = []; //图形 + + this.CreateMainChart(); + this.Draw(); + this.RequestData(); + } + + //创建主数据画法 + this.CreateMainChart = function () { + if (!this.MainDataControl) return; + + let chartItem = new ChartChinaMap(); + chartItem.Canvas = this.Canvas; + chartItem.ChartBorder = this.Frame.ChartBorder; + chartItem.ChartFrame = this.Frame; + chartItem.Name = this.MainDataControl.DataType[0].Name; + + if (this.Radius) chartItem.Radius = this.Radius; + + this.ChartPaint.push(chartItem); + + // this.TitlePaint[0]=new DynamicKLineTitlePainting(); + // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame; + // this.TitlePaint[0].Canvas=this.Canvas; + } + + this.RequestData = function () { + if (!this.MainDataControl) return; + + this.MainDataControl.JSChartContainer = this; + this.MainDataControl.RequestData(); + } + + this.UpdateMainData = function (dataControl) { + this.ChartPaint[0].Data = dataControl.Data[0]; + + this.Frame.SetSizeChage(true); + this.Draw(); + } +} + +var HQ_DATA_TYPE = +{ + KLINE_ID: 0, //K线 + MINUTE_ID: 2, //当日走势图 + HISTORY_MINUTE_ID: 3,//历史分钟走势图 + MULTIDAY_MINUTE_ID: 4,//多日走势图 +}; + + + + +function APIScriptIndex(name, script, args, option) //后台执行指标 +{ + this.newMethod = ScriptIndex; //派生 + this.newMethod(name, script, args, option); + delete this.newMethod; + + this.ApiUrl; //指标执行api地址 + this.HQDataType; + + if (option.API) + { + if (option.API.Url) this.ApiUrl = option.API.Url; + if (option.API.Name) this.Name = this.ID = option.API.Name; + if (option.API.ID) this.ID = option.API.ID; + } + + this.ExecuteScript = function (hqChart, windowIndex, hisData) + { + JSConsole.Chart.Log('[APIScriptIndex::ExecuteScript] name, Arguments ', this.Name, this.Arguments); + + //数据类型 + let hqDataType = HQ_DATA_TYPE.KLINE_ID; //默认K线 + if (hqChart.ClassName === 'MinuteChartContainer' || hqChart.ClassName === 'MinuteChartHScreenContainer') + { + if (hqChart.DayCount > 1) hqDataType = HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟 + else hqDataType = HQ_DATA_TYPE.MINUTE_ID; //分钟数据 + } + else if (hqChart.ClassName === 'HistoryMinuteChartContainer') + { + hqDataType = HQ_DATA_TYPE.HISTORY_MINUTE_ID; //历史分钟 + } + + var args = []; + if (this.Arguments) + { + for (var i in this.Arguments) + { + var item = this.Arguments[i]; + args.push({ name: item.Name, value: item.Value }); + } + } + + var requestCount = hqChart.GetRequestDataCount(); + var self = this; + var postData = + { + indexname: this.ID, symbol: hqChart.Symbol, script: this.Script, args: args, + period: hqChart.Period, right: hqChart.Right, maxdatacount: requestCount.MaxRequestDataCount, maxminutedaycount: requestCount.MaxRequestMinuteDayCount, hqdatatype: hqDataType + }; + if (hqDataType == HQ_DATA_TYPE.MULTIDAY_MINUTE_ID || hqDataType == HQ_DATA_TYPE.MINUTE_ID) postData.daycount = hqChart.DayCount; + this.HQDataType = hqDataType; + + if (hqChart.NetworkFilter) + { + var obj = + { + Name: 'APIScriptIndex::ExecuteScript', //类名:: + Explain: '指标计算', + Request: { Url: self.ApiUrl, Type: 'POST', Data: postData }, + Self: this, + HQChart: hqChart, + PreventDefault: false + }; + + hqChart.NetworkFilter(obj, function (data) + { + self.RecvAPIData(data, hqChart, windowIndex, hisData); + }); + + if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求 + } + + wx.request({ + url: self.ApiUrl, + data: postData, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) + { + self.RecvAPIData(recvData, hqChart, windowIndex, hisData); + }, + error: function (request) + { + self.RecvError(request); + } + }); + } + + this.RecvAPIData = function (recvData, hqChart, windowIndex, hisData) + { + var data=recvData.data; + JSConsole.Chart.Log('[APIScriptIndex::RecvAPIData] recv data ', this.Name, data); + if (data.code != 0) return; + + if (data.outdata && data.outdata.name) this.Name = data.outdata.name; + + if (data.outdata.args) //外部修改显示参数 + { + this.Arguments = []; + for (var i in data.outdata.args) + { + var item = data.outdata.args[i]; + this.Arguments.push({ Name: item.name, Value: item.value }); + } + } + + if (this.HQDataType == HQ_DATA_TYPE.KLINE_ID) + { + this.OutVar = this.FittingData(data.outdata, hqChart); + JSConsole.Chart.Log('[APIScriptIndex::RecvAPIData] conver to OutVar ', this.OutVar); + } + else + { + this.OutVar = this.FittingMinuteData(data.outdata, hqChart); //走势图数据 + } + this.BindData(hqChart, windowIndex, hisData); + + if (this.IsLocked == false) //不上锁 + { + hqChart.Frame.SubFrame[windowIndex].Frame.SetLock(null); + } + else //上锁 + { + let lockData = + { + IsLocked: true, Callback: this.LockCallback, IndexName: this.Name, ID: this.LockID, + BG: this.LockBG, Text: this.LockText, TextColor: this.LockTextColor, Font: this.LockFont, Count: this.LockCount, MinWidth: this.LockMinWidth + }; + hqChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData); + } + + hqChart.UpdataDataoffset(); //更新数据偏移 + hqChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + hqChart.Draw(); + + if (hqChart.GetIndexEvent) + { + var event = hqChart.GetIndexEvent(); //指标计算完成回调 + if (event) + { + var data = + { + OutVar: this.OutVar, WindowIndex: windowIndex, Name: this.Name, Arguments: this.Arguments, HistoryData: hisData, + Stock: { Symbol: hqChart.Symbol, Name: hqChart.Name } + }; + event.Callback(event, data, this); + } + } + } + + this.FittingData = function (jsonData, hqChart) + { + var outVar = jsonData.outvar; + var date = jsonData.date; + var time = jsonData.time; + var kdata = hqChart.ChartPaint[0].Data; + + //把数据拟合到kdata上 + var result = []; + + for (var i in outVar) + { + var item = outVar[i]; + var indexData = []; + var outVarItem = { Name: item.name, Type: item.type }; + if (item.color) outVarItem.Color = item.color; + if (item.data) + { + outVarItem.Data = this.FittingArray(item.data, date, time, hqChart); + + if (item.color) outVarItem.Color = item.color; + if (item.linewidth >= 1) outVarItem.LineWidth = item.linewidth; + if (item.isshow == false) outVarItem.IsShow = false; + if (item.isexdata == true) outVarItem.IsExData = true; + + result.push(outVarItem); + } + else if (item.Draw) + { + var draw = item.Draw; + var drawItem = {}; + if (draw.DrawType == 'DRAWICON') //图标 + { + drawItem.Icon = draw.Icon; + drawItem.Name = draw.Name; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingArray(draw.DrawData, date, time, hqChart); + outVarItem.Draw = drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType == 'DRAWTEXT') //文本 + { + drawItem.Text = draw.Text; + drawItem.Name = draw.Name; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingArray(draw.DrawData, date, time, hqChart); + outVarItem.Draw = drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType == 'STICKLINE') //柱子 + { + drawItem.Name = draw.Name; + drawItem.Type = draw.Type; + drawItem.Width = draw.Width; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingArray(draw.DrawData, date, time, hqChart, 1); + outVarItem.Draw = drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType == 'MULTI_LINE') + { + drawItem.Text = draw.Text; + drawItem.Name = draw.Name; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingMultiLine(draw.DrawData, date, time, hqChart); + outVarItem.Draw = drawItem; + if (draw.LineDash) drawItem.LineDash=draw.LineDash; + //if (draw.Arrow) drawItem.Arrow=draw.Arrow; + if (IFrameSplitOperator.IsNumber(draw.LineWidth)) drawItem.LineWidth=draw.LineWidth; + result.push(outVarItem); + } + else if (draw.DrawType == 'MULTI_BAR') + { + drawItem.Text = draw.Text; + drawItem.Name = draw.Name; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingMultiLine(draw.DrawData, date, time, hqChart); + outVarItem.Draw = drawItem; + + result.push(outVarItem); + } + else if (draw.DrawType == 'MULTI_TEXT') + { + drawItem.Text = draw.Text; + drawItem.Name = draw.Name; + drawItem.DrawType = draw.DrawType; + drawItem.DrawData = this.FittingMultiText(draw.DrawData, date, time, hqChart); + this.GetKLineData(drawItem.DrawData, hqChart); + outVarItem.Draw = drawItem; + result.push(outVarItem); + } + else if (draw.DrawType=="MULTI_HTMLDOM") //外部自己创建dom + { + drawItem.Text=draw.Text; + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.Callback=draw.Callback; + drawItem.DrawData=this.FittingMultiText(draw.DrawData,date,time,hqChart); + this.GetKLineData(drawItem.DrawData, hqChart); + outVarItem.Draw=drawItem; + result.push(outVarItem); + } + else if (draw.DrawType=="KLINE_BG") + { + drawItem.Name=draw.Name; + drawItem.DrawType=draw.DrawType; + drawItem.DrawData={ Color:draw.Color, Angle:draw.Angle }; + drawItem.DrawData.Data=this.FittingKLineBG(draw.DrawData, hqChart); + + outVarItem.Draw=drawItem; + outVarItem.Name=draw.DrawType; + result.push(outVarItem); + } + } + } + + return result; + } + + // h, high, low l. + this.GetKLineData=function(data,hqChart) + { + if (!data) return; + if (!Array.isArray(data)) return; + var kData=hqChart.ChartPaint[0].Data; //K线 + + for(var i in data) + { + var item=data[i]; + if (!IFrameSplitOperator.IsString(item.Value)) continue; + if(!IFrameSplitOperator.IsNumber(item.Index)) continue; + if (item.Index<0 || item.Index>=kData.Data.length) continue; + var valueName=item.Value.toUpperCase(); + var kItem=kData.Data[item.Index]; + switch(valueName) + { + case "HIGH": + case "H": + item.Value=kItem.High; + break; + case "L": + case "LOW": + item.Value=kItem.Low; + break; + } + } + } + + this.FittingKLineBG=function(data, hqChart) + { + var kData=hqChart.ChartPaint[0].Data; //K线 + var result=[]; + if (ChartData.IsDayPeriod(hqChart.Period,true)) //日线 + { + var bFill=false; + for(var i=0,j=0;i=data.length) + { + ++i; + continue; + } + var dataItem=data[j]; + + if (dataItem.DatekItem.Date) + { + ++i; + } + else + { + bFill=true; + result[i]=1; + ++j; + ++i; + } + } + + if (bFill) return result; + } + else if (ChartData.IsMinutePeriod(hqChart.Period,true)) //分钟线 + { + var bFill=false; + for(var i=0,j=0;i=data.length) + { + ++i; + continue; + } + var dataItem=data[j]; + + if (dataItem.DatekItem.Date || (dataItem.Date==kItem.Date && dataItem.Time>kItem.Time)) + { + ++i; + } + else + { + bFill=true; + result[i]=1; + ++j; + ++i; + } + } + + if (bFill) return result; + } + + return null; + } + + this.FittingArray = function (sourceData, date, time, hqChart, arrayType) //arrayType 0=单值数组 1=结构体 + { + var kdata = hqChart.ChartPaint[0].Data; //K线 + + var arySingleData = []; + for (var i in sourceData) + { + var value = sourceData[i]; + var indexItem = new SingleData(); //单列指标数据 + indexItem.Date = date[i]; + if (time && i < time.length) indexItem.Time = time[i]; + indexItem.Value = value; + arySingleData.push(indexItem); + } + + var aryFittingData; + if (ChartData.IsDayPeriod(hqChart.Period,true)) + aryFittingData = kdata.GetFittingData(arySingleData); //数据和主图K线拟合 + else + aryFittingData = kdata.GetMinuteFittingData(arySingleData); //数据和主图K线拟合 + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + var result; + if (arrayType == 1) result = bindData.GetObject(); + else result = bindData.GetValue(); + return result; + } + + this.FittingMultiLine = function (sourceData, date, time, hqChart) + { + var kdata = hqChart.ChartPaint[0].Data; //K线 + + if (ChartData.IsDayPeriod(hqChart.Period, true)) //日线 + { + var aryPoint = []; + for (var i in sourceData) + { + var item = sourceData[i]; + for (var j in item.Point) + { + var point = item.Point[j]; + aryPoint.push(point); + } + } + + aryPoint.sort(function (a, b) { return a.Date - b.Date; }); + kdata.GetDateIndex(aryPoint); + return sourceData; + } + else if (ChartData.IsMinutePeriod(hqChart.Period, true)) //分钟线 + { + var aryPoint = []; + for (var i in sourceData) + { + var item = sourceData[i]; + for (var j in item.Point) + { + var point = item.Point[j]; + aryPoint.push(point); + } + } + + aryPoint.sort(function (a, b) { + if (a.Date == b.Date) return a.Time - b.Time; + return a.Date - b.Date; + }); + + kdata.GetDateTimeIndex(aryPoint); + return sourceData; + } + + return null; + } + + this.FittingMultiText = function (sourceData, date, time, hqChart) + { + var kdata = hqChart.ChartPaint[0].Data; //K线 + + if (ChartData.IsDayPeriod(hqChart.Period, true)) //日线 + { + sourceData.sort(function (a, b) { return a.Date - b.Date; }); + kdata.GetDateIndex(sourceData); + return sourceData; + } + else if (ChartData.IsMinutePeriod(hqChart.Period, true)) //分钟线 + { + sourceData.sort(function (a, b) { + if (a.Date == b.Date) return a.Time - b.Time; + return a.Date - b.Date; + } + ); + + kdata.GetDateTimeIndex(sourceData); + return sourceData; + } + + return null; + } +} + +//市场多空 +function MarketLongShortIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('市场多空'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo("多空指标", null), + new IndexInfo("多头区域", null), + new IndexInfo("空头区域", null) + ); + + this.Index[0].LineColor = g_JSChartResource.Index.LineColor[0]; + this.Index[1].LineColor = g_JSChartResource.UpBarColor; + this.Index[2].LineColor = g_JSChartResource.DownBarColor; + + this.LongShortData; //多空数据 + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = null; + if (i == 0) + paint = new ChartLine(); + else + paint = new ChartStraightLine(); + + paint.Color = this.Index[i].LineColor; + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.LongShortData = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: g_JSChartResource.Index.MarketLongShortApiUrl, + data: + { + + }, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) { + self.RecvData(recvData, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + if (recvData.data.data.length <= 0) return; + + var aryData = new Array(); + for (var i in recvData.data.data) { + var item = recvData.data.data[i]; + var indexData = new SingleData(); + indexData.Date = item[0]; + indexData.Value = item[1]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.LongShortData = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data = this.LongShortData; + paint[0].NotSupportMessage = null; + paint[1].Data.Data[0] = 8; + paint[2].Data.Data[0] = 1; + + //指定[0,9] + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 9, Min: 0, Count: 3 }; + + var titleIndex = windowIndex + 1; + + for (var i in paint) { + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, this.Index[i].Name, this.Index[i].LineColor); + if (i > 0) hqChart.TitlePaint[titleIndex].Data[i].DataType = "StraightLine"; + } + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + + if (hqChart.UpdateUICallback) hqChart.UpdateUICallback('MarketLongShortIndex', paint, { WindowIndex: windowIndex, HistoryData: hisData }); //通知上层回调 + return true; + } + +} + +//市场择时 +function MarketTimingIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('市场择时'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo("因子择时", null) + ); + + this.TimingData; //择时数据 + this.TitleColor = g_JSChartResource.FrameSplitTextColor + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = new ChartMACD(); + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.LongShortData = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: g_JSChartResource.Index.MarketLongShortApiUrl, + data: + { + + }, + method: 'POST', + dataType: "json", + async: true, + success: function (recvData) { + self.RecvData(recvData, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + if (recvData.data.data.length <= 0) return; + + var aryData = new Array(); + for (var i in recvData.data.data) { + var item = recvData.data.data[i]; + var indexData = new SingleData(); + indexData.Date = item[0]; + indexData.Value = item[2]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.TimingData = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data = this.TimingData; + paint[0].NotSupportMessage = null; + + var titleIndex = windowIndex + 1; + + for (var i in paint) { + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, this.Index[i].Name, this.TitleColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat = STRING_FORMAT_TYPE.THOUSANDS; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision = 0; + } + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + + if (hqChart.UpdateUICallback) hqChart.UpdateUICallback('MarketTimingIndex', paint, { WindowIndex: windowIndex, HistoryData: hisData }); //通知上层回调 + return true; + } +} + +//市场关注度 +function MarketAttentionIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('市场关注度'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo("市场关注度指数", null) + ); + + this.Data; //关注度数据 + this.TitleColor = g_JSChartResource.FrameSplitTextColor; + this.ApiUrl = g_JSChartResource.Index.MarketAttentionApiUrl; + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = new ChartBar(); //柱子 + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + paint.UpBarColor = paint.DownBarColor = 'rgb(243,152,0)'; + + hqChart.ChartPaint.push(paint); + } + } + + //调整框架 + this.SetFrame = function (hqChart, windowIndex, hisData) { + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 6, Min: 0, Count: 3 }; + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.Data = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: this.ApiUrl, + data: + { + "symbol": param.HQChart.Symbol, + "startdate": 20100101, + }, + method: 'POST', + dataType: "json", + success: function (recvData) { + self.RecvData(recvData.data, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + if (recvData.date.length < 0) return; + + var aryData = new Array(); + for (var i in recvData.date) { + var indexData = new SingleData(); + indexData.Date = recvData.date[i]; + indexData.Value = recvData.value[i]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.Data = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + this.SetFrame(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + + // if (typeof (this.UpdateUICallback) == 'function') this.UpdateUICallback('RecvHistoryData', this); + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + //paint[0].Data.Data=SWLData; + paint[0].Data.Data = this.Data; + paint[0].NotSupportMessage = null; + + var titleIndex = windowIndex + 1; + + for (var i in paint) { + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, this.Index[i].Name, this.TitleColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat = STRING_FORMAT_TYPE.THOUSANDS; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision = 0; + } + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + return true; + } +} + + +/* + 行业,指数热度 +*/ +function MarketHeatIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('指数/行业热度'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo("热度指数", 5), + new IndexInfo('MA', 10), + new IndexInfo('MA', null) + ); + + this.Data; //关注度数据 + + this.ApiUrl = g_JSChartResource.Index.MarketHeatApiUrl; + + this.Index[0].LineColor = g_JSChartResource.FrameSplitTextColor; + this.Index[1].LineColor = g_JSChartResource.Index.LineColor[0]; + this.Index[2].LineColor = g_JSChartResource.Index.LineColor[1]; + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = null; + if (i == 0) { + paint = new ChartMACD(); //柱子 + } + else { + paint = new ChartLine(); + paint.Color = this.Index[i].LineColor; + } + + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.Data = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: this.ApiUrl, + data: + { + "symbol": param.HQChart.Symbol, + "startdate": 20100101, + }, + method: 'POST', + dataType: "json", + success: function (recvData) { + self.RecvData(recvData.data, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + if (recvData.date.length < 0) return; + + var aryData = new Array(); + for (var i in recvData.date) { + var indexData = new SingleData(); + indexData.Date = recvData.date[i]; + indexData.Value = recvData.value[i]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.Data = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + paint[0].Data.Data = this.Data; + paint[0].NotSupportMessage = null; + + var MA = HQIndexFormula.MA(this.Data, this.Index[0].Param); + paint[1].Data.Data = MA; + + var MA2 = HQIndexFormula.MA(this.Data, this.Index[1].Param); + paint[2].Data.Data = MA2; + + var titleIndex = windowIndex + 1; + + for (var i in paint) { + var name = ""; //显示的名字特殊处理 + if (i == 0) + name = hqChart.Name + this.Index[i].Name; + else + name = "MA" + this.Index[i - 1].Param; + + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, name, this.Index[i].LineColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat = STRING_FORMAT_TYPE.DEFAULT; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision = 2; + } + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + + return true; + } + +} + +//自定义指数热度 +function CustonIndexHeatIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('Market-Heat'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo('区域', 3), + new IndexInfo("热度指数", 10), + new IndexInfo('MA', 5), + new IndexInfo('MA', 10) + ); + + this.Data; //热度数据 + + this.ApiUrl = g_JSChartResource.Index.CustomIndexHeatApiUrl; + + this.Index[1].LineColor = g_JSChartResource.Index.LineColor[1]; + this.Index[2].LineColor = g_JSChartResource.Index.LineColor[2]; + this.Index[3].LineColor = g_JSChartResource.Index.LineColor[3]; + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = null; + if (i == 0) { + paint = new ChartStraightArea(); + } + else { + paint = new ChartLine(); + paint.Color = this.Index[i].LineColor; + } + + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.Data = []; + + if (param.HQChart.Period > 0) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "不支持周期切换"); + param.HQChart.Draw(); + return false; + } + + //请求数据 + wx.request({ + url: this.ApiUrl, + data: + { + "stock": param.HQChart.CustomStock, + "date": { "startdate": param.HQChart.QueryDate.Start, "enddate": param.HQChart.QueryDate.End }, + "day": [this.Index[0].Param, this.Index[1].Param], + }, + method: 'POST', + dataType: "json", + success: function (recvData) { + self.RecvData(recvData, param); + } + }); + + return true; + } + + this.RecvData = function (recvData, param) { + let data = recvData.data; + if (data.data == null || data.data.length < 0) return; + + //JSConsole.Chart.Log(recvData.data); + let aryData = new Array(); + for (let i in data.data) { + let item = data.data[i]; + let indexData = new SingleData(); + indexData.Date = item[0]; + indexData.Value = item[1]; + aryData.push(indexData); + } + + var aryFittingData = param.HistoryData.GetFittingData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.Data = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + let paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + paint[0].NotSupportMessage = null; + + paint[0].Data.Data = + [ + { Value: 0, Value2: 0.2, Color: 'rgb(46,139,87)', Title: '较差区', TitleColor: 'rgb(245,255 ,250)' }, + { Value: 0.19, Value2: 0.4, Color: 'rgb(255,140,0)', Title: '变热区', TitleColor: 'rgb(245,255 ,250)' }, + { Value: 0.39, Value2: 0.8, Color: 'rgb(255,106,106)', Title: '较好区', TitleColor: 'rgb(245,255 ,250)' }, + { Value: 0.79, Value2: 1, Color: 'rgb(255,69,0)', Title: '过热区', TitleColor: 'rgb(245,255 ,250)' } + ]; + + paint[1].Data.Data = this.Data; + + let MA = HQIndexFormula.MA(this.Data, this.Index[2].Param); + paint[2].Data.Data = MA; + + let MA2 = HQIndexFormula.MA(this.Data, this.Index[3].Param); + paint[3].Data.Data = MA2; + + //指定框架最大最小[0,1] + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 1, Min: 0, Count: 3 }; + + let titleIndex = windowIndex + 1; + + for (let i = 1; i < paint.length; ++i) { + let name = this.Index[i].Name; //显示的名字特殊处理 + if (name == 'MA') name = "MA" + this.Index[i].Param; + + hqChart.TitlePaint[titleIndex].Data[i] = new DynamicTitleData(paint[i].Data, name, this.Index[i].LineColor); + hqChart.TitlePaint[titleIndex].Data[i].StringFormat = STRING_FORMAT_TYPE.DEFAULT; + hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision = 2; + } + + + hqChart.TitlePaint[titleIndex].Title = '热度' + '(' + this.Index[0].Param + ',' + this.Index[1].Param + ',' + this.Index[2].Param + ',' + this.Index[3].Param + ')'; + + return true; + } + +} + +/* + 本福特系数(财务粉饰) +*/ +function BenfordIndex() { + this.newMethod = BaseIndex; //派生 + this.newMethod('财务风险'); + delete this.newMethod; + + this.Index = new Array( + new IndexInfo('区域', null), + new IndexInfo("系数", null), + ); + + this.Data; //财务数据 + + this.ApiUrl = g_JSChartResource.Index.StockHistoryDayApiUrl; + + this.Index[0].LineColor = g_JSChartResource.Index.LineColor[0]; + this.Index[1].LineColor = 'rgb(105,105,105)'; + + this.Create = function (hqChart, windowIndex) { + for (var i in this.Index) { + var paint = null; + if (i == 0) + paint = new ChartStraightArea(); + else if (i == 1) + paint = new ChartLineMultiData(); + + if (paint) { + paint.Color = this.Index[i].LineColor; + paint.Canvas = hqChart.Canvas; + paint.Name = this.Name + "-" + i.toString(); + paint.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder; + paint.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame; + + hqChart.ChartPaint.push(paint); + } + } + } + + //请求数据 + this.RequestData = function (hqChart, windowIndex, hisData) { + var self = this; + var param = + { + HQChart: hqChart, + WindowIndex: windowIndex, + HistoryData: hisData + }; + + this.Data = []; + + if (param.HQChart.Period != 2) //周期数据 + { + this.NotSupport(param.HQChart, param.WindowIndex, "只支持月线"); + param.HQChart.Draw(); + return false; + } + + var aryField = ["finance.benford", "announcement2.quarter", "announcement1.quarter", "announcement3.quarter", "announcement4.quarter"]; + var aryCondition = + [ + { item: ["date", "int32", "gte", "20130101"] }, + { + item: ["announcement1.year", "int32", "gte", 0, + "announcement2.year", "int32", "gte", 0, + "announcement3.year", "int32", "gte", 0, + "announcement4.year", "int32", "gte", 0, + "or"] + } + ]; + //请求数据 + wx.request({ + url: this.ApiUrl, + data: + { + "symbol": [param.HQChart.Symbol], + "field": aryField, + "condition": aryCondition + }, + method: 'POST', + dataType: "json", + success: function (recvData) { + self.RecvData(recvData, param); + } + }); + + return true; + } + + this.JsonDataToMapSingleData = function (recvData) { + var stockData = recvData.stock[0].stockday; + var mapData = new Map(); + for (var i in stockData) { + var item = stockData[i]; + var indexData = new SingleData(); + indexData.Date = item.date; + indexData.Value = new Array(); + if (item.finance1 != null && item.announcement1 != null) { + let year = item.announcement1.year; + let quarter = item.announcement1.quarter; + let value = item.finance1.benford; + indexData.Value.push({ Year: year, Quarter: quarter, Value: value }); + } + if (item.finance2 != null && item.announcement2 != null) { + let year = item.announcement2.year; + let quarter = item.announcement2.quarter; + let value = item.finance2.benford; + indexData.Value.push({ Year: year, Quarter: quarter, Value: value }); + } + if (item.finance3 != null && item.announcement3 != null) { + let year = item.announcement3.year; + let quarter = item.announcement3.quarter; + let value = item.finance3.benford; + indexData.Value.push({ Year: year, Quarter: quarter, Value: value }); + } + if (item.finance4 != null && item.announcement4 != null) { + let year = item.announcement4.year; + let quarter = item.announcement4.quarter; + let value = item.finance4.benford; + indexData.Value.push({ Year: year, Quarter: quarter, Value: value }); + } + + mapData.set(indexData.Date, indexData); + } + + var aryData = new Array(); + for (var item of mapData) { + aryData.push(item[1]); + } + + return aryData; + } + + this.RecvData = function (recvData, param) { + JSConsole.Chart.Log(recvData); + if (recvData.data.stock == null || recvData.data.stock.length <= 0) return; + + var aryData = this.JsonDataToMapSingleData(recvData.data); + var aryFittingData = param.HistoryData.GetFittingMonthData(aryData); + + var bindData = new ChartData(); + bindData.Data = aryFittingData; + bindData.Period = param.HQChart.Period; //周期 + bindData.Right = param.HQChart.Right; //复权 + + this.Data = bindData.GetValue(); + this.BindData(param.HQChart, param.WindowIndex, param.HistoryData); + + param.HQChart.UpdataDataoffset(); //更新数据偏移 + param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值 + param.HQChart.Draw(); + } + + + this.BindData = function (hqChart, windowIndex, hisData) { + var paint = hqChart.GetChartPaint(windowIndex); + + if (paint.length != this.Index.length) return false; + + paint[0].NotSupportMessage = null; + + paint[0].Data.Data = + [ + // { Value: 0, Value2: 0.2, Color: 'rgb(50,205,50)', Title: '安全区', TitleColor: 'rgb(245,255 ,250)' }, + // { Value: 0.2, Value2: 0.4, Color: 'rgb(255,140,0)', Title: '预警区', TitleColor: 'rgb(245,255 ,250)' }, + // { Value: 0.4, Value2: 1, Color: 'rgb(255,106,106)', Title: '警示区', TitleColor: 'rgb(245,255 ,250)' } + { Value: 0, Value2: 0.2, Color: 'rgb(219,255,193)', Title: '安全区', TitleColor: 'rgb(66,192,99)' }, + { Value: 0.2, Value2: 0.4, Color: 'rgb(255,228,170)', Title: '预警区', TitleColor: 'rgb(255,124,3)' }, + { Value: 0.4, Value2: 1, Color: 'rgb(254,219,212)', Title: '警示区', TitleColor: 'rgb(255,0,0)' } + ]; + + paint[1].Data.Data = this.Data; + + //指定框架最大最小[0,1] + hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 1, Min: 0, Count: 3 }; + hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale = [0.2,0.4]; + + var titleIndex = windowIndex + 1; + + hqChart.TitlePaint[titleIndex].Data[1] = new DynamicTitleData(paint[1].Data, this.Index[1].Name, this.Index[1].LineColor); + hqChart.TitlePaint[titleIndex].Data[1].DataType = "MultiReport"; + + hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle(); + + return true; + } +} + +//是否是指数代码 +function IsIndexSymbol(symbol) { + var upperSymbol = symbol.toUpperCase(); + if (upperSymbol.indexOf('.SH') > 0) { + upperSymbol = upperSymbol.replace('.SH', ''); + if (upperSymbol.charAt(0) == '0' && parseInt(upperSymbol) <= 3000) return true; + + } + else if (upperSymbol.indexOf('.SZ') > 0) { + upperSymbol = upperSymbol.replace('.SZ', ''); + if (upperSymbol.charAt(0) == '3' && upperSymbol.charAt(1) == '9') return true; + } + else if (upperSymbol.indexOf('.CI') > 0) //自定义指数 + { + return true; + } + + return false; +} + +//导出统一使用JSCommon命名空间名 +module.exports = +{ + JSCommon: + { + JSCanvasElement: JSCanvasElement, + JSChart: JSChart, + Guid: Guid, + IFrameSplitOperator: IFrameSplitOperator, + ChartData: ChartData, + DataPlus: DataPlus, + KLineTooltipPaint: KLineTooltipPaint, + MARKET_SUFFIX_NAME: MARKET_SUFFIX_NAME, + JSCommonCoordinateData:JSCommonCoordinateData, + FrameSplitKLineX:FrameSplitKLineX, + FrameSplitKLinePriceY:FrameSplitKLinePriceY, + JSCHART_EVENT_ID:JSCHART_EVENT_ID + }, +}; + + + diff --git a/uni_modules/jones-hqchart2/js_sdk/用户协议.txt b/uni_modules/jones-hqchart2/js_sdk/用户协议.txt new file mode 100644 index 0000000..7665533 --- /dev/null +++ b/uni_modules/jones-hqchart2/js_sdk/用户协议.txt @@ -0,0 +1,40 @@ + HQChart用户协议 + +用户应当充分阅读本协议,用户使用HQChart插件亦视为接受本协议。 +一、定义 + HQChart:由本人开发的专业的K线图展示及数据计算引擎插件。 + 用户: 以任何形式使用HQChart插件的自然人、法人或其他组织。 +二、用户保证 + 1. 用户同意并保证,合法并如约使用HQChart插件。用户对其行为产生的任何法律责任自行独立承担,与HQChart插件作者无关。 +三、用户的权利与义务 + 1. 用户有权通过修改HQChart源码进行2次开发,无需经得HQChart插件作者授权. + 2. 用户在使用HQChart插件期间,承诺并保证: + (1) 遵守所有中华人民共和国法律法规和国际上有关互联网和短信的协议、规定、程序和惯例. + (2) 不利用HQChart插件进行金融诈骗. + (3) 不行使其他可能给HQChart插件带来任何不利影响的行为或者法律及政策禁止的行为。 + 3. 用户不得利用HQChart插件发送以下内容的短信: + (1)接受方事先没有提出要求或者同意接受的广告等宣传性或营销性内容; + (2)含有病毒、恶意代码、色情、反动等不良信息或有害信息 + (3)地产、医疗、教育、留学、移民等相关内容 + (4)冒充任何人或机构,或以虚伪不实的方式谎称或使人误认为与任何人或任何机构有关。 + (5)侵犯他人著作权或其他知识产权,或违反保密、雇佣或不披露协议披露他人商业秘密或保密信息。 + 4. 用户应当按照本协议,妥善使用HQChart插件并为其行为负责。因用户行为导致HQChart插件作者遭受诉讼、索赔及/或处罚的,用户应赔偿HQChart插件作者因此造成的全部损失。 +四、HQChart作者的权利与义务 + 1. HQChart作者对用户行为无审核监督义务。因用户行为给HQChart作者造成的一切损失,HQChart作者有权向用户追偿。 + 2. HQChart插件对其提供给用户的质量做出如下承诺 + 1). 源码全部开源 + 2). 在作者技术能力范围以内,提供免费或有偿的技术支持服务 + 3.HQChart作者有权对HQChart插件进行定期或不定期的升级或完善,用户可以选择升级新版本或不升级使用原有版本。如用户因此受到影响,HQChart作者无需为此承担任何责任。 + 4. HQChart插件作者有权根据自己的判断对用户是否违反本协议约定。用户若有违反的,HQChart插件作者有权随时中止或终止本协议。HQChart作者无需就此征得用户同意或提前通知用户 + 5. 鉴于互联网服务的特殊性,对因不可抗力、第三方服务、黑客攻击、政策影响及其他任何非. HQChart插件作者原因引起的技术支持中断等,HQChart插件作者均无需承担任何责任。 + +五、 免责声明 + 1. 用户明确同意其使用HQChart插件所存在的风险及其后果将完全由其自己承担,HQChart插件作者对用户不承担任何责任。如因用户违反有关法律、法规或本协议项下的任何条款而给HQChart插件作者或任何其他第三人造成损失,用户同意承担由此造成的损害赔偿责任。 + +六、其他 + 1. 本协议的版权归HQChart插件作者,HQChart插件作者保留对本协议的一切解释和修改权利。 + 2. 本协议的订立、执行和争议的解决均应适用中华人民共和国法律。如双方就本协议内容或其执行发生任何争议,双方应尽量友好协商解决;协商不成时,任何一方均可向HQChart插件作者所在地有管辖权的人民法院提起诉讼。 + 3. HQChart插件作者行使本协议的任何权利或规定,不构成对前述权利之放弃。 + 4. 如本协议中的任何条款完全或部分无效,本协议的其余条款仍有效并且有约束力 + + diff --git a/uni_modules/jones-hqchart2/package.json b/uni_modules/jones-hqchart2/package.json new file mode 100644 index 0000000..a976002 --- /dev/null +++ b/uni_modules/jones-hqchart2/package.json @@ -0,0 +1,80 @@ +{ + "id": "jones-hqchart2", + "displayName": "jones-hqchart2", + "version": "1.1.10333", + "description": "HQChart - H5, 微信小程序 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据替换接口", + "keywords": [ + "jones-hqchart2" +], + "repository": "https://github.com/jones2000/HQChart", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/hqchart" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/jones-hqchart2/readme.md b/uni_modules/jones-hqchart2/readme.md new file mode 100644 index 0000000..358a954 --- /dev/null +++ b/uni_modules/jones-hqchart2/readme.md @@ -0,0 +1,285 @@ +# jones-hqchart2 +# 介绍 + +> HQChart + H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据替换接口 + +# 开源 +- 源码地址:[https://github.com/jones2000/HQChart](https://github.com/jones2000/HQChart) +- 镜像地址:[https://gitee.com/jones2000/HQChart](https://gitee.com/jones2000/HQChart) + +# 用户协议 +- [用户协议](https://gitee.com/jones2000/HQChart/blob/master/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE.txt) + +# demo说明 +* hqchart2_test3 内置组件模式 +* hqchart2_test 单画布模式 +* hqchart2_test2 多画布模式 +* hqchart2_template 如何在外部封装hqchart模板 +* hqchart2_renderjs 使用renderjs创建hqchart, 直接使用npm hqchart + +# app demo apk下载 +- [股票数据对接 app demo下载](https://opensource2.zealink.com/uniapp_demoapk/hqchart_shsz_uniapp_20210301.apk) +- [火币安卓app demo下载](https://opensource2.zealink.com/uniapp_demoapk/hqchart_huobi_uniapp.20210301.apk) +- [A股真实数据(新浪+腾讯行情源)app demo下载](https://opensource2.zealink.com/uniapp_demoapk/hqchart_astock_demo.20210505.01.apk) + + + +> 功能: +# 1. K线图 +- 支持前复权,后复权 +- 支持日线,月线,周线,年线.分钟线 +- 主图支持股票叠加 +- K线形状支持 空心K线,实心K线,美国线,收盘价线 +- 支持常用指标指标(目前以录入系统指标80多个),支持自定义通达信语法脚本指标( 均线,BOLL,MACD,KDJ,VOL,RSI,BRAR,WR,BIAS,OBV,DMI,CR,PSY,CCI, DMA,TRIX,VR,EMV,ROC,MIM,FSL,CYR,MASS,WAD,CHO ..... ) +- 支持画图工具(小程序不支持) +- 线段,射线,矩形,圆弧线,水平线,趋势线,平行线,平行通道,价格通道线,文本,江恩角度线,阻速线,黄金分割,百分比线,波段线,三角形,对称角度,斐波那契周期线,平行四边形,圆, iconfont图片 +- 支持区间统计, 区间形态匹配 (微信小程序版本不支持) +- 数据鼠标左右拖拽移动, 键盘移动十字光标移动,键盘缩放 +- 支持通达信语法指标 +- 支持五彩K线(目前录入系统五彩K线30多个), 支持自定义通达信语法脚本的五彩K线 +- 支持专家系统指标 +- 支持个股筹码图 +# 2. 走势图 +- 支持指标 +- 支持股票叠加 +- 支持沪深和港股,国内期货(开发中) +- 分钟数据显示 +- 支持多日分钟数据显示 +- +# 3. 网页demo +* [K线图](https://opensource2.zealink.com/hqweb/demo/phone7.html) +* [走势图](https://opensource2.zealink.com/hqweb/demo/phone8.html) +* [走势图手机页面](https://opensource2.zealink.com/hqweb/demo/phone2.html) +* [K线图手机页面](https://opensource2.zealink.com/hqweb/demo/phone.html) +* [横版走势图手机页面](https://opensource2.zealink.com/hqweb/demo/phone10.html) +* [横版K线图手机页面](https://opensource2.zealink.com/hqweb/demo/phone9.html) +* [多日走势图](https://opensource2.zealink.com/hqweb/demo/phone15.html) +* [个股筹码图](https://opensource2.zealink.com/hqweb/demo/phone18.html) +* [指标回测(手机版)](https://opensource2.zealink.com/hqweb/operatebsh5/index.html?symbol=000001.sz) +* [手机K线训练](https://opensource2.zealink.com/hqweb/demo/demo_ktrain.html) +* [手机K线训练横屏](https://opensource2.zealink.com/hqweb/demo/demo_ktrain2.html) +* [弹幕功能](https://opensource2.zealink.com/hqweb/demo/phone21.html) +* [多指标叠加](https://opensource2.zealink.com/hqweb/demo/phone22.html) +* [截面数据(财务数据)计算器](https://opensource2.zealink.com/hqweb/demo/sectiondatatest.html) +* [走势图-大盘异动](https://opensource2.zealink.com/hqweb/demo/phone23.html) +* [分笔K线图](https://opensource2.zealink.com/hqweb/demo/phone24.html) + + + +# 4.使用教程 +## H5教程 +1. [HQChart使用教程1-如何快速创建一个K线图页面](https://blog.csdn.net/jones2000/article/details/90272733) +2. [HQChart使用教程2-如何把自定义指标显示在K线图页面](https://blog.csdn.net/jones2000/article/details/90273684) +3. [HQChart使用教程3-如何把指标上锁显示在K线图页面](https://blog.csdn.net/jones2000/article/details/90285723) +4. [HQChart使用教程4-如何自定义K线图颜色风格](https://blog.csdn.net/jones2000/article/details/90286933) +5. [HQChart使用教程5-K线图控件操作函数说明](https://blog.csdn.net/jones2000/article/details/90301000) +6. [HQChart使用教程6-如何获取K线图上的指标数据进行回测](https://blog.csdn.net/jones2000/article/details/90314625) +7. [HQChart使用教程7-如何快速创建一个分时图页面](https://blog.csdn.net/jones2000/article/details/90319619) +8. [HQChart使用教程9-如何快速创建K线训练页面](https://blog.csdn.net/jones2000/article/details/90478687) +9. [HQChart使用教程10-手机端页面设置的几个特殊属性](https://blog.csdn.net/jones2000/article/details/90727468) +10. [HQChart使用教程11-如何把K线数据API替换成自己的API数据](https://blog.csdn.net/jones2000/article/details/90747715) +11. [HQChart使用教程8-如何快速创建一个横屏分时图页面](https://blog.csdn.net/jones2000/article/details/90453776) +12. [HQChart使用教程14-分析家语法执行器](https://blog.csdn.net/jones2000/article/details/93731637) +13. [HQChart使用教程13-5分钟完成一个小程序K线图](https://blog.csdn.net/jones2000/article/details/91471252) +14. [HQChart使用教程12-如何在K线图上添加弹幕](https://blog.csdn.net/jones2000/article/details/91125408) +15. [HQChart使用教程15-分析家语法执行器python版本](https://blog.csdn.net/jones2000/article/details/94738592) +16. [HQChart使用教程16-py中使用麦语言指标可视化](https://blog.csdn.net/jones2000/article/details/94920596) +17. [HQChart使用教程17-多技术指标独立坐标叠加](https://blog.csdn.net/jones2000/article/details/95618901) +18. [HQChart使用教程18-K线截图](https://blog.csdn.net/jones2000/article/details/95738306) +19. [HQChart使用教程19-基于HQChart的后台单股票指标计算服务](https://blog.csdn.net/jones2000/article/details/96479448) +20. [HQChart使用教程20-单股票截面数据(财务数据)计算器](https://blog.csdn.net/jones2000/article/details/97135592) +21. [HQChart使用教程21-十字光标设置说明](https://blog.csdn.net/jones2000/article/details/97682466) +22. [HQChart使用教程22-如何创建移动筹码图](https://blog.csdn.net/jones2000/article/details/97928892) +23. [HQChart使用教程23-Y轴刻度显示设置](https://blog.csdn.net/jones2000/article/details/98320020) +24. [HQChart使用教程24-多语言设置](https://blog.csdn.net/jones2000/article/details/98734091) +25. [HQChart使用教程25-叠加多个品种设置](https://blog.csdn.net/jones2000/article/details/98878463) +26. [HQChart使用教程26-K线图及走势图数据自动更新设置](https://blog.csdn.net/jones2000/article/details/99483328) +27. [HQChart使用教程27-动态设置K线图指标模板](https://blog.csdn.net/jones2000/article/details/100079989) +28. [HQChart使用教程28-如何创建系统指标](https://blog.csdn.net/jones2000/article/details/100103486) +29. [HQChart使用教程31-走势图异动数据设置](https://blog.csdn.net/jones2000/article/details/100191957) +30. [HQChart使用教程32-如何K线图显示自定义SVG矢量图标](https://blog.csdn.net/jones2000/article/details/100613634) +33. [HQChart使用教程39-指标中如何绘制文本分割线](https://blog.csdn.net/jones2000/article/details/101487482) +34. [HQChart使用教程40-如何自定义分钟周期或日线周期K线](https://blog.csdn.net/jones2000/article/details/101722958) +35. [HQChart使用教程41-分钟K线设置拖拽自动下载历史数据](https://blog.csdn.net/jones2000/article/details/102471720) +36. [HQChart使用教程42-K线图如何对接数字货币](https://blog.csdn.net/jones2000/article/details/102493905) +37. [HQChart使用教程43-日K线设置拖拽自动下载历史数据](https://blog.csdn.net/jones2000/article/details/102511317) +38. [HQChart使用教程45-如何动态修改指标参数](https://blog.csdn.net/jones2000/article/details/102594672) +39. [HQChart使用教程46-分钟周期数据计算外部接口](https://blog.csdn.net/jones2000/article/details/102628045) +40. [HQChart使用教程47-如何自定义右键菜单](https://blog.csdn.net/jones2000/article/details/102720671) +41. [HQChart使用教程48-如何自定义X轴刻度](https://blog.csdn.net/jones2000/article/details/102741428) +42. [HQChart使用教程49-指标配置项说明](https://blog.csdn.net/jones2000/article/details/102928907) +43. [HQChart使用教程50-Y轴自定义刻度设置说明](https://blog.csdn.net/jones2000/article/details/103174483) +44. [HQChart使用教程51-指标切换按钮事件说明-h5版本](https://blog.csdn.net/jones2000/article/details/103187576) +45. [HQChart使用教程52-自定义手机端K线图Tooltip](https://blog.csdn.net/jones2000/article/details/103820718) +46. [HQChart使用教程53-log日志输出控制](https://blog.csdn.net/jones2000/article/details/104122774) +47. [HQChart使用教程54-K线缩放控制按钮接口说明](https://blog.csdn.net/jones2000/article/details/104346016) +48. [HQChart使用教程55-自定义PC端K线图Tooltip](https://blog.csdn.net/jones2000/article/details/104443471) +49. [HQChart使用教程56-内置品种对应后缀列表说明](https://blog.csdn.net/jones2000/article/details/104457569) +50. [HQChart使用教程57-如何调整K线的柱子缩放大小](https://blog.csdn.net/jones2000/article/details/104817724) +51. [HQChart使用教程58-如何在K线右侧绘制面积图(如深度图)](https://blog.csdn.net/jones2000/article/details/105026997) +52. [HQChart使用教程59-跨周期跨股票函数STKINDI使用说明](https://blog.csdn.net/jones2000/article/details/105401909) +53. [HQChart使用教程60-新版k线训练使用教程](https://blog.csdn.net/jones2000/article/details/105760924) +54. [HQChart使用教程61-画图工具接口使用教程](https://blog.csdn.net/jones2000/article/details/105835428) +55. [HQChart使用教程62-品种小数位数设置](https://blog.csdn.net/jones2000/article/details/106592730) +56. [HQChart使用教程64-前端自定义周期算法接口](https://blog.csdn.net/jones2000/article/details/107633707) +57. [HQChart使用教程65-设置指标输出动态变量名](https://blog.csdn.net/jones2000/article/details/108675254) +58. [HQChart使用教程66-自定义数据下载文字提示效果](https://blog.csdn.net/jones2000/article/details/109007027) +59. [HQChart使用教程67-鼠标点击K线柱子监听事件](https://blog.csdn.net/jones2000/article/details/109119390) +60. [HQChart使用教程68-配置DRAWTEXT,DRAWICON,DRAWNUMBER字体大小](https://blog.csdn.net/jones2000/article/details/109244069) +61. [HQChart使用教程69-获取指定股票的K线数据](https://blog.csdn.net/jones2000/article/details/111736960) +62. [HQChart使用教程70-通达信指标翻译](https://blog.csdn.net/jones2000/article/details/112998609) +63. [HQChart使用教程71-如何自定义Y轴刻度线](https://blog.csdn.net/jones2000/article/details/113666565) +64. [HQChart使用教程72-画图工具波浪尺刻度配置](https://blog.csdn.net/jones2000/article/details/113923817) +65. [HQChart使用教程73-使用Vue3.0创建HQChart图形](https://blog.csdn.net/jones2000/article/details/114954091) +66. [HQChart使用教程74-使用快速创建数字币深度图](https://blog.csdn.net/jones2000/article/details/115322752) +67. [HQChart使用教程75-K线图鼠标单击事件接口](https://blog.csdn.net/jones2000/article/details/115707759) +68. [HQChart使用教程76-K线图手势事件接口](https://blog.csdn.net/jones2000/article/details/115862159) +69. [HQChart使用教程77-Y轴刻度文字创建事件回调](https://blog.csdn.net/jones2000/article/details/116376898)
      +70. [HQChart使用教程78-分时图集合竞价](https://blog.csdn.net/jones2000/article/details/116523681)
      +71. [HQChart使用教程79-异常处理接口](https://blog.csdn.net/jones2000/article/details/117524401)
      +72. [HQChart使用教程80-自定义指标标题信息](https://blog.csdn.net/jones2000/article/details/117803420)
      +73. [HQChart使用教程81-自定义指标窗口高度](https://jones2000.blog.csdn.net/article/details/118652171)
      +74. [HQChart使用教程82-动态修改叠加指标参数](https://jones2000.blog.csdn.net/article/details/118681399)
      +75. [HQChart使用教程83-K线图最高最低价显示配置](https://jones2000.blog.csdn.net/article/details/118856130)
      + +## 小程序教程 +1. [HQChart小程序教程1-如何快速的创建一个K线图](https://developers.weixin.qq.com/community/develop/article/doc/0006c451ac81589915b89d1c55bc13) +2. [HQChart小程序教程2-如何使用新版2D画布创建一个K线图](https://blog.csdn.net/jones2000/article/details/105632095) +3. [HQChart小程序教程3-新版2D单画布如何切换K线图和分时图](https://blog.csdn.net/jones2000/article/details/108378355) + +## uni-app教程 +1. [HQChart使用教程35-如何在uni-app创建K线图(h5)](https://blog.csdn.net/jones2000/article/details/101039026) +2. [HQChart使用教程36-如何在uni-app创建走势图(h5)](https://blog.csdn.net/jones2000/article/details/101039673) +3. [HQChart使用教程37-如何在uni-app创建k线图(app)](https://blog.csdn.net/jones2000/article/details/101075683) +4. [HQChart使用教程38-如何在uni-app创建走势图(app)](https://blog.csdn.net/jones2000/article/details/101481960) +5. [HQChart使用教程44-uniapp使用条件编译同时支持h5,app,小程序](https://blog.csdn.net/jones2000/article/details/102529190) +6. [HQChart使用教程60-解决uniapp-app页面隐藏后在显示白屏的问题](https://blog.csdn.net/jones2000/article/details/105484202) +7. [HQChart使用教程63-uniapp使用renderjs+hqchart(h5)](https://blog.csdn.net/jones2000/article/details/106933985) +8. [HQChart实战教程40-如何制作hqchart组件(uniapp版本)](https://blog.csdn.net/jones2000/article/details/116034602) + +## 第3方数据前端接入教程(走势图) +1. [HQChart使用教程29-走势图如何对接第3方数据1](https://blog.csdn.net/jones2000/article/details/100132357) +2. [HQChart使用教程29-走势图如何对接第3方数据2-最新分时数据](https://blog.csdn.net/jones2000/article/details/100149703) +3. [HQChart使用教程29-走势图如何对接第3方数据3-多日分时数据](https://blog.csdn.net/jones2000/article/details/100150842) +4. [HQChart使用教程29-走势图如何对接第3方数据4-叠加股票分时数据](https://blog.csdn.net/jones2000/article/details/100167703) +5. [HQChart使用教程29-走势图如何对接第3方数据4-异动提示信息](https://blog.csdn.net/jones2000/article/details/100516071) +6. [HQChart使用教程29-走势图如何对接第3方数据5-指标数据](https://blog.csdn.net/jones2000/article/details/102426337) +7. [HQChart使用教程29-走势图如何对接第3方数据6-websocket分钟数据](https://blog.csdn.net/jones2000/article/details/102568258) +8. [HQChart使用教程29-走势图如何对接第3方数据7-叠加股票最新分时数据](https://blog.csdn.net/jones2000/article/details/110525351) + +## 第3方数据前端接入教程(K线图) +1. [HQChart使用教程30-K线图如何对接第3方数据1](https://blog.csdn.net/jones2000/article/details/100181279) +2. [HQChart使用教程30-K线图如何对接第3方数据2-日K数据](https://blog.csdn.net/jones2000/article/details/100552022) +3. [HQChart使用教程30-K线图如何对接第3方数据3-1分钟K数据](https://blog.csdn.net/jones2000/article/details/100557649) +4. [HQChart使用教程30-K线图如何对接第3方数据4-流通股本数据](https://blog.csdn.net/jones2000/article/details/100574186) +5. [HQChart使用教程30-K线图如何对接第3方数据5-指标数据](https://blog.csdn.net/jones2000/article/details/100579223) +6. [HQChart使用教程30-K线图如何对接第3方数据6-分笔K线数据](https://blog.csdn.net/jones2000/article/details/100671849) +7. [HQChart使用教程30-K线图如何对接第3方数据7-日K数据分页下载](https://blog.csdn.net/jones2000/article/details/101275824) +8. [HQChart使用教程30-K线图如何对接第3方数据8-1分钟K线数据分页下载](https://blog.csdn.net/jones2000/article/details/101277092) +9. [HQChart使用教程30-K线图如何对接第3方数据9-BS指标数据](https://blog.csdn.net/jones2000/article/details/101350429) +10. [HQChart使用教程30-K线图如何对接第3方数据10-如何绘制自定义线段或多边行指标数据](https://blog.csdn.net/jones2000/article/details/101694618) +11. [HQChart使用教程30-K线图如何对接第3方数据11-如何绘制多组自定义图标](https://blog.csdn.net/jones2000/article/details/101757384) +12. [HQChart使用教程30-K线图如何对接第3方数据12-如何在指标中绘制文字](https://blog.csdn.net/jones2000/article/details/101864046) +13. [HQChart使用教程30-K线图如何对接第3方数据13-使用websocket更新最新K线数据](https://blog.csdn.net/jones2000/article/details/102138784) +14. [HQChart使用教程30-K线图如何对接第3方数据14-轮询增量更新日K数据](https://blog.csdn.net/jones2000/article/details/102518334) +15. [HQChart使用教程30-K线图如何对接第3方数据15-轮询增量更新1分钟K线数据](https://blog.csdn.net/jones2000/article/details/102518422) +16. [HQChart使用教程30-K线图如何对接第3方数据16-日K叠加股票](https://blog.csdn.net/jones2000/article/details/102661873) +17. [HQChart使用教程30-K线图如何对接第3方数据17-分钟K叠加股票](https://blog.csdn.net/jones2000/article/details/102887690) +18. [HQChart使用教程30-K线图如何对接第3方数据18-如何绘制自定义柱子](https://blog.csdn.net/jones2000/article/details/104417736) +19. [HQChart使用教程30-K线图如何对接第3方数据19-如何绘制彩色K线柱](https://blog.csdn.net/jones2000/article/details/104859784) +20. [HQChart使用教程30-K线图如何对接第3方数据20-信息地雷公告数据](https://blog.csdn.net/jones2000/article/details/105876161) +21. [HQChart使用教程30-K线图如何对接第3方数据21-跨周期函数数据](https://blog.csdn.net/jones2000/article/details/109063625) +22. [HQChart使用教程30-K线图如何对接第3方数据22-FINVALUE函数数据](https://blog.csdn.net/jones2000/article/details/111387095) +23. [HQChart使用教程30-K线图如何对接第3方数据23-FINANCE函数数据](https://blog.csdn.net/jones2000/article/details/111999910) +24. [HQChart使用教程30-K线图如何对接第3方数据24-如何填充K线背景色](https://blog.csdn.net/jones2000/article/details/112342980) +25. [HQChart使用教程30-K线图如何对接第3方数据25-指标脚本自定义变量](https://blog.csdn.net/jones2000/article/details/112755911) +26. [HQChart使用教程30-K线图如何对接第3方数据26-指标脚本自定义函数](https://blog.csdn.net/jones2000/article/details/112809781) +27. [HQChart使用教程30-K线图如何对接第3方数据27-如何在指标中渲染DOM元素](https://blog.csdn.net/jones2000/article/details/114006164) +28. [HQChart使用教程30-K线图如何对接第3方数据28-大盘数据](https://blog.csdn.net/jones2000/article/details/117712105) +29. [HQChart使用教程30-K线图如何对接第3方数据29-板块字符串函数数据](https://jones2000.blog.csdn.net/article/details/118887416) + + +## 实战教程 +1. [HQChart实战教程1-外汇分时图](https://blog.csdn.net/jones2000/article/details/103254501) +2. [HQChart实战教程2-使用跨周期写指标](https://blog.csdn.net/jones2000/article/details/103275668) +3. [HQChart实战教程3-http+ws对接分钟K线数据](https://blog.csdn.net/jones2000/article/details/103882063) +4. [HQChart实战教程4-http+ws对接日K线数据](https://blog.csdn.net/jones2000/article/details/103966271) +5. [HQChart实战教程5-http+ws对接单日分时图数据](https://blog.csdn.net/jones2000/article/details/103966925) +6. [HQChart实战教程6-自定义分时图](https://blog.csdn.net/jones2000/article/details/104165374) +7. [HQChart实战教程7-自定义显示手势点击K线显示信息](https://blog.csdn.net/jones2000/article/details/104168610) +8. [HQChart实战教程8-如何手动重新初始化hqchart](https://blog.csdn.net/jones2000/article/details/105302626) +9. [HQChart实战教程9-自定义A股分时图](https://blog.csdn.net/jones2000/article/details/105587559) +10. [HQChart实战教程14-K线图对接第3方http/https数据教程整理](https://blog.csdn.net/jones2000/article/details/106064879) +11. [HQChart实战教程17-K线沙盘推演](https://blog.csdn.net/jones2000/article/details/106776837) +12. [HQChart实战教程18-多股同列](https://blog.csdn.net/jones2000/article/details/107193410) + +## 火币对接完整教程(付费文章) +1. [HQChart实战教程10-全ws数据对接HQChart(数字货币对接实战)](https://blog.csdn.net/jones2000/article/details/105698038) +2. [HQChart实战教程11-火币网ws数据对接](https://blog.csdn.net/jones2000/article/details/105721190) +3. [HQChart实战教程12-火币网ws数据对接分时图](https://blog.csdn.net/jones2000/article/details/105756659) +4. [HQChart实战教程13-火币网ws数据对接K线(uniapp)](https://blog.csdn.net/jones2000/article/details/105804461) +5. [HQChart实战教程15-火币网ws数据对接拖拽下载历史K线图](https://blog.csdn.net/jones2000/article/details/106205584) +6. [HQChart实战教程16-K线图风格配色篇-仿火币网H5配色](https://blog.csdn.net/jones2000/article/details/106226272) + +## 币安对接(源码收费) +1. [HQChart实战教程36-数字货币安币对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/114468807) +2. [HQChart实战教程41-新浪+腾讯A股数据源对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/117139756) + +## httpA股数据对接教程(付费文章) +### 日K线 +1. [HQChart实战教程29-A股日K线数据对接-Vue版本](https://blog.csdn.net/jones2000/article/details/113099783) +2. [HQChart实战教程30-A股日K线数据对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/113101342) +3. [HQChart实战教程34-A股日K线数据对接-小程序版本](https://blog.csdn.net/jones2000/article/details/113577904) +### 分钟K线 +1. [HQChart实战教程31-A股分钟K线数据对接-Vue版本](https://blog.csdn.net/jones2000/article/details/113101407) +2. [HQChart实战教程32-A股分钟K线数据对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/113101448) +### 分时图 +1. [HQChart实战教程33-A股分时图数据对接-Vue版本](https://blog.csdn.net/jones2000/article/details/113226866) +2. [HQChart实战教程35-A股分时图数据对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/113777111) +### 后台指标 +1. [HQChart实战教程36-A股后台指标对接-uniapp版本](https://blog.csdn.net/jones2000/article/details/114991081) +### 新浪接口对接 +1. [HQChart实战教程37-新浪分钟K线数据对接-js版本](https://blog.csdn.net/jones2000/article/details/115388377) +2. [HQChart实战教程38-新浪期货数据对接-js版本](https://blog.csdn.net/jones2000/article/details/115408971) + +## 高级应用实战教程(付费文章) +1. [HQChart实战教程19 - PC端分时图定制tooltip](https://blog.csdn.net/jones2000/article/details/108633991) +2. [HQChart实战教程20 - PC端K线图定制tooltip](https://blog.csdn.net/jones2000/article/details/108639960) +3. [HQChart实战教程21 - unapp app端分时图定制tooltip](https://blog.csdn.net/jones2000/article/details/108657043) +4. [HQChart实战教程21 - uniapp app端K线图定制tooltip](https://blog.csdn.net/jones2000/article/details/108674679) +5. [HQChart实战教程22 - PC端定制区间选择菜单](https://blog.csdn.net/jones2000/article/details/108907629) +6. [HQChart实战教程23 - 点击K线显示历史分钟走势图](https://blog.csdn.net/jones2000/article/details/109127873) +7. [HQChart实战教程24 - 自定义K线画图工具设置框(线段类)](https://blog.csdn.net/jones2000/article/details/109217719) +8. [HQChart实战教程25 - 自定义K线画图工具设置框(文字类)](https://blog.csdn.net/jones2000/article/details/109267078) +9. [HQChart实战教程30 - 配置K线画图](https://blog.csdn.net/jones2000/article/details/113819121) +10. [HQChart实战教程26 - K线画图工具增加自定义图标](https://blog.csdn.net/jones2000/article/details/109529224) +11. [HQChart实战教程27 - 走势图最后一个数据增加动画点](https://blog.csdn.net/jones2000/article/details/111599341) +12. [HQChart实战教程28 - 动态切换颜色风格](https://blog.csdn.net/jones2000/article/details/112563596) +13. [HQChart实战教程29 - 指标参数保存到本地缓存](https://blog.csdn.net/jones2000/article/details/113349967) +14. [HQChart实战教程39 - K线图键盘事件重载](https://blog.csdn.net/jones2000/article/details/115921430) + + + + +## 设计文档: +1. [如何(c++,js)写一个传统的K线图和走势图1](https://blog.csdn.net/jones2000/article/details/84779481)
      +2. [如何(c++,js)写一个传统的K线图和走势图2-走势图](https://blog.csdn.net/jones2000/article/details/84840770)
      +3. [如何(c++,js)写一个传统的K线图和走势图3-多指标窗口模式如何实现的](https://blog.csdn.net/jones2000/article/details/84979910)
      +4. [如何(c++,js)写一个传统的K线图和走势图3-十字光标的绘制](https://blog.csdn.net/jones2000/article/details/85123680)
      +5. [如何(c++,js)写一个传统的K线图和走势图4-K线图](https://blog.csdn.net/jones2000/article/details/85235463)
      +6. [如何(c++,js)写一个传统的K线图和走势图5-移动筹码图](https://blog.csdn.net/jones2000/article/details/85356163)
      + +## HQChartPy2介绍(py版本指标引擎) +1. [HQChart(C++)指标计算引擎-介绍](https://blog.csdn.net/jones2000/article/details/107464517)
      +2. [HQChart(C++)指标计算引擎-安装](https://blog.csdn.net/jones2000/article/details/107712259)
      +3. [HQChart(C++)指标计算引擎-py接口类FastHQChart介绍](https://blog.csdn.net/jones2000/article/details/107725170)
      +4. [HQChart(C++)指标计算引擎-py接口类IHQData K线数据对接](https://blog.csdn.net/jones2000/article/details/107728903)
      + +## HQChartPy2数据对接教程 (以tushare数据为例子) +1. [hqchartPy2数据对接教程1-K线数据](https://blog.csdn.net/jones2000/article/details/112060412)
      +2. [hqchartPy2数据对接教程2-股本数据,筹码分布函数](https://blog.csdn.net/jones2000/article/details/112060761)
      +3. [hqchartPy2数据对接教程3-FINANCE数据](https://blog.csdn.net/jones2000/article/details/112095070)
      +4. [hqchartPy2数据对接教程4-DYNAINFO函数](https://blog.csdn.net/jones2000/article/details/112334485)
      +5. [hqchartPy2数据对接教程5-引用指定股票数据函数](https://blog.csdn.net/jones2000/article/details/112335307)
      +6. [hqchartPy2指标选股-KDJ选股](https://blog.csdn.net/jones2000/article/details/113667444)
      \ No newline at end of file