/* 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 { JSNetwork } from "./umychart.network.wechart.js" //配色资源 import { g_JSChartResource, JSCHART_LANGUAGE_ID, g_JSChartLocalization, } from './umychart.resource.wechat.js' import { IFrameSplitOperator, } from './umychart.framesplit.wechat.js' import { JSIndexScript, } from './umychart.index.data.wechat.js' import { HQ_DATA_TYPE, ChartData, HistoryData, SingleData, MinuteData, } from "./umychart.data.wechat.js"; import { g_MinuteCoordinateData, MARKET_SUFFIX_NAME , } from "./umychart.coordinatedata.wechat.js"; var g_JSComplierResource= { Domain : "http://127.0.0.1:8080", //API域名 CacheDomain : "http://127.0.0.1:8087", //缓存域名 CustomFunction: //定制函数 { Data:new Map() //自定义函数 key=函数名, Value:{ID:函数名, Callback: } }, CustomDataFunction: //自定义数据函数 { //自定义函数 key=变量名, Value:{ Name:变量名, Description:描述信息, ArgCount:参数个数 } Data:new Map( [ [ "L2_VOLNUM", { Name:"L2_VOLNUM", Description:"单数分档,按: N(0--1):(超大+大)/(中+小),M(0--1):买/卖二类,沪深京品种的资金流向,仅日线以上周期,用于特定版本", ArgCount:2 } ], [ "L2_VOL", { Name:"L2_VOL", Description:"成交量分档,按: N(0--3):超大/大/中/小四档处理,M(0--3):买入/卖出/主买/主卖四类,沪深京品种的资金流向,仅日线以上周期,用于特定版本", ArgCount:2 } ], [ "L2_AMO", { Name:"L2_AMO", Description:"成交额分档,按: N(0--3):超大/大/中/小四档处理,M(0--3):买入/卖出/主买/主卖四类,沪深京品种的资金流向,仅日线以上周期,用于特定版本", ArgCount:2 } ] ]) }, CustomVariant: //自定义变量 { Data:new Map() //自定义函数 key=变量名, Value:{ Name:变量名, Description:描述信息 } }, IsCustomFunction:function(name) { if (g_JSComplierResource.CustomFunction.Data.has(name)) return true; return false; }, IsCustomDataFunction:function(name) { if (g_JSComplierResource.CustomDataFunction.Data.has(name)) return true; return false; }, IsCustomVariant:function(name) { if (g_JSComplierResource.CustomVariant.Data.has(name)) return true; return false; }, 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; }, } 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===0x25B2 || cp===0x25BC) || ((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===0x25B2 || cp===0x25BC) || ((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.Dynainfo=[]; //{ID:, Args:, FunctionName: } DYNAINFO() 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}); //最新的个股行情数据 for(var i=0;i0) { 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.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.AssignmentExpression=function (operator, left, right) { return { Type:Syntax.AssignmentExpression, Operator:operator, Left:left, Right:right }; } //成员变量, 不需要检测 this.MemberIdentifier=function(name, token) { return { Type:Syntax.Identifier, Name:name}; } 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.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; } } 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.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 (Array.isArray(data) && Array.isArray(data2)) { 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 (!IFrameSplitOperator.IsVaild(data)) continue; if (!IFrameSplitOperator.IsVaild(data2[i])) continue; result[i]=(data>data2[i] ? 1 : 0); } } else //数组-单数据 { for(let i in data) { result[i]=null; if (!IFrameSplitOperator.IsVaild(data[i])) continue; if (!IFrameSplitOperator.IsVaild(data2)) continue; result[i]=(data[i]>data2 ? 1 : 0); } } return result; } //大于等于 this.GTE=function(data,data2) { let isNumber=IFrameSplitOperator.IsNumber(data); let isNumber2=IFrameSplitOperator.IsNumber(data2); //单数值比较 if (isNumber && isNumber2) return (data>=data2 ? 1 : 0); //都是数组比较 let result=[]; if (Array.isArray(data) && Array.isArray(data2)) { 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 (!IFrameSplitOperator.IsVaild(data)) continue; if (!IFrameSplitOperator.IsVaild(data2[i])) continue; result[i]=(data>=data2[i] ? 1 : 0); } } else //数组-单数据 { for(let i in data) { result[i]=null; if (!IFrameSplitOperator.IsVaild(data[i])) continue; if (!IFrameSplitOperator.IsVaild(data2)) continue; result[i]=(data[i]>=data2 ? 1 : 0); } } return result; } //小于 this.LT=function(data,data2) { let isNumber=IFrameSplitOperator.IsNumber(data); let isNumber2=IFrameSplitOperator.IsNumber(data2); //单数值比较 if (isNumber && isNumber2) return (data=data2 ? 1 : 0); //都是数组比较 let result=[]; if (Array.isArray(data) && Array.isArray(data2)) { 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 (IFrameSplitOperator.IsNumber(data)) //单数值 { if (n<0) return result; var kData=this.SymbolData.Data.Data; if (!kData || kData.length<=0) return result; var count=kData.length; for(var i=n;i=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+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 && 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 = []; if (Array.isArray(n)) { for( var i=0;ii+1) period=i+1; var lastSMA=null; var sma=null; for(var j=0;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) { 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) { if (this.IsNumber(data)) { return 0-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 (!IFrameSplitOperator.IsNonEmptyArray(data)) return result; n=parseInt(n); if (n<=0) n=data.length; else if (n>data.length) n=data.length; var nStart=this.GetFirstVaildIndex(data); if (nStart>=data.length) return result; var nMax = nStart; 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; } /* HV(X,N):求X在N个周期内(不包含当前k线)的最高值。 注: 1、若N为0则从第一个有效值开始算起(不包含当前K线); 2、当N为有效值,但当前的k线数不足N根,按照实际的根数计算,第一根k线返回空值; 3、N为空值时,返回空值。 4、N可以是变量。 例1: HH:HV(H,10);//求前10根k线的最高点。 例2: N:=BARSLAST(DATE<>REF(DATE,1))+1; ZH:VALUEWHEN(DATE<>REF(DATE,1),HV(H,N));//在分钟周期上,求昨天最高价。 例3: HV(H,5) 和 REF(HHV(H,5),1) 的结果是一样的,用HV编写更加方便。 */ this.HV=function(data,n) { var result=this.HHV(data,n); return this.REF(result,1); } /* 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 (!IFrameSplitOperator.IsNonEmptyArray(data)) return result; n=parseInt(n); if (n<=0) n=data.length; else if (n>data.length) n=data.length; var nStart=this.GetFirstVaildIndex(data); if (nStart>=data.length) return result; var nMin=nStart; if (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; } /* LV(X,N) 求X在N个周期内的最小值(不包含当前k线) 注: 1、若N为0则从第一个有效值开始算起; 2、当N为有效值,但当前的k线数不足N根,按照实际的根数计算; 3、N为空值时,返回空值。 4、N可以是变量。 例1: LL:LV(L,10);//求前面10根k线的最低点。(不包含当前k线) 例2: N:=BARSLAST(DATE<>REF(DATE,1))+1;//分钟周期,日内k线根数 ZL:VALUEWHEN(DATE<>REF(DATE,1),LV(L,N));//在分钟周期上,求昨天最低价。 例3: LV(L,5) 和 REF(LLV(L,5),1) 的结果是一样的,用LV编写更加方便。 */ this.LV=function(data,n) { var result=this.LLV(data,n); return this.REF(result,1); } this.STD=function(data,n) { var result=[]; var nStart=this.GetFirstVaildIndex(data); if (!IFrameSplitOperator.IsNumber(n)) return result; if(nStart+n>data.length || n<1) return result; var i=nStart, j=0, bFirst=true, dTotal=0, dAvg=0; for(i+=n-1;idata.length || n<1) return result; var i=nStart, j=0, bFirst=true, dTotal=0, dAvg=0; for(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; } /* CROSSDOWN(A,B):表示当A从上方向下穿B,成立返回1(Yes),否则返回0(No) 注:1、CROSSDOWN(A,B)等同于CROSS(B,A),CROSSDOWN(A,B)编写更利于理解 例1: MA5:=MA(C,5); MA10:=MA(C,10); CROSSDOWN(MA5,MA10)//MA5下穿MA10 */ this.CROSSDOWN=function(data,data2) { return this.CROSS(data2,data); } //累乘 this.MULAR=function(data,n) { var result=[]; if(data.length0)) continue; for(var j=i, k=0 ;j>=0 && k=0;--i) { if (this.IsNumber(n[i])) { num = parseInt(n[i]); break; } } } else { num = parseInt(n); } if (!this.IsNumber(num)) return result; var datanum = data.length; var i = 0, j = 0, k = 0; var E = 0, DEV = 0; for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i) { result[i] = null; } if (num < 1 || i+num>datanum) 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;idata.length || n<1) return result; var i=nStart, j=0, bFirst=true, dTotal=0, dAvg=0; for(i+=n-1;i= 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); } } else if (Array.isArray(data) && Array.isArray(n)) { var start=this.GetFirstVaildIndex(data); for(var i=start; 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; 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-latestID=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; } /* 过滤连续出现的信号. 用法:TFILTER(买入条件,卖出条件,N);过滤掉买入(卖出)信号发出后,下一个反向信号发出前的所有买入(卖出)信号. N=1表示仅对买入信号过滤; N=2表示仅对卖出信号过滤; N=0表示对买入和卖出信号都过滤,返回1,2表示买入或卖出条件成立; 同一K线上只能有一个信号; 例如: ENTERLONG:TFILTER(买入,卖出,1); EXITLONG:TFILTER(买入,卖出,2); TFILTER(D,K,1) 等价于 D AND COUNT(D, BARSLAST(K)) == 1 TFILTER(D,K,2) 等价于 K AND COUNT(K, BARSLAST(D)) == 1 TFILTER(D,K,0) 需要做个判断,如果满足 D AND COUNT(D, BARSLAST(K)) == 1 则返回1,如果满足 K AND COUNT(K, BARSLAST(D)) == 1 则返回2 */ this.TFILTER=function(data,data2,n) { if (!this.IsNumber(n)) return []; if (n==1) { return this.And(data,this.EQ(this.COUNT(data,this.BARSLAST(data2)),1)); } else if (n==2) { return this.And(data2,this.EQ(this.COUNT(data2,this.BARSLAST(data)),1)); } else if (n==0) { var result=this.And(data2,this.EQ(this.COUNT(data2,this.BARSLAST(data)),1)); var value=this.And(data2,this.EQ(this.COUNT(data2,this.BARSLAST(data)),1)); for(var i=0; i0) result[i]=2; } return result; } return []; } /* 过滤连续出现的信号. 用法:FILTER(X,N):X满足条件后,将其后N周期内的数据置为0,N为常量. 例如: FILTER(CLOSE>OPEN,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; if (IFrameSplitOperator.IsNumber(n)) { n=parseInt(n); var i=0; for(;i=0 && k1) 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 5000 || dMinPrice < 0 || dMaxPrice>5000 || dMinPrice < 0) this.ThrowUnexpectedNode(node,'COST() 历史K线最大最小值错误, 超出(0,5000)范围'); 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 5000 || dMinPrice < 0 || dMaxPrice>5000 || dMinPrice < 0) this.ThrowUnexpectedNode(node,'WINNER() 历史K线最大最小值错误, 超出(0,5000)范围'); 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; } this.ISVALID=function(data) { if (Array.isArray(data)) { var result=[]; for(var i=0;iOPEN,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) { if (this.IsNumber(data) || this.IsNumber(data2)) return 0; 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) return 1; else return 0; } else if (Array.isArray(data) && IFrameSplitOperator.IsString(str2)) { for(var i=0;i=0?1:0; } else if (IFrameSplitOperator.IsNumber(item)) { str=item.toString(); result[i]=str.indexOf(str2)>=0?1:0; } else { result[i]=0; } } } return result; } this.STRLEN=function(data) { if (IFrameSplitOperator.IsString(data)) return data.length; if (Array.isArray(data)) { var result=[]; 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;i0,X=0,X<0分别返回1,0,-1 */ this.SIGN=function(data) { if (Array.isArray(data)) { var result=[]; for(var i=0;i0) result[i]=1; else if (item==0) result[i]=0; else result[i]=-1; } return result; } else { if (data>0) return 1; else if (data==0) return 0; else return -1; } } /* 统计连续满足条件的周期数. 用法: BARSLASTCOUNT(X),统计连续满足X条件的周期数. 例如: BARSLASTCOUNT(CLOSE>OPEN)表示统计连续收阳的周期数 */ 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; } /* 求指定时刻距0时有多长时间. 用法: TIMETOSEC(time) TIMETOSEC(time).返回time时刻距0时有多长时间,单位为秒.有效时间为(0-235959) 例如: TIMETOSEC(93000)返回34200. */ this.TIMETOSEC=function(time) { var hour=parseInt(time/10000); var minute=parseInt((time%10000)/100); var sec=time%100; var value=hour*60*60+minute*60+sec; return value; } /* 求0时后若干秒是什么时间. 用法: SECTOTIME(N) SECTOTIME(N).返回0时后N秒是什么时间.有效秒数为(0-86399) 例如: SECTOTIME(34200)返回93000. */ this.SECTOTIME=function(data) { var daySec = 24 * 60 * 60; var hourSec= 60 * 60; var minuteSec=60; var dd = Math.floor(data / daySec); var hh = Math.floor((data % daySec) / hourSec); var mm = Math.floor((data % hourSec) / minuteSec); var ss=data%minuteSec; var value=hh*10000+mm*100+ss; return value; } this.MOD=function(data, data2) { var result=[]; let isNumber=typeof(data)=='number'; let isNumber2=typeof(data2)=='number'; //单数值 if (isNumber && isNumber2) { return JSAlgorithm.MOD(data,data2); } else if (!isNumber && !isNumber2) //都是数组相加 { let count=Math.max(data.length, data2.length); for(let i=0;iOPEN,10),表示前10日内存在着阳线 this.ANY=function(data, n) { if (n<=0) n=1; var result=[]; if (Array.isArray(data)) { if (n>=data.length) return 0; for(var i=n, j=0;i0) { value=1; break; } } result[i]=value; } return result; } else if (IFrameSplitOperator.IsNumber(data)) { if (data<=0) return 0; if (n>=this.SymbolData.Data.Data.length) return 0; for(var i=n; iOPEN,10),表示前10日内一直阳线 this.ALL=function(data, n) { if (n<=0) n=1; var result=[]; if (Array.isArray(data)) { if (n>=data.length) return 0; for(var i=n, j=0;i0)) { value=0; break; } } result[i]=value; } return result; } else if (IFrameSplitOperator.IsNumber(data)) { if (data<=0) return 0; if (n>=this.SymbolData.Data.Data.length) return 0; for(var i=n; iOPEN,HIGH,TESTSKIP(1));L;表示当日收阳则返回最高值,并执行下一句"L;",否则退出公式计算 */ this.IFC=function(data) { if (Array.isArray(data)) { var item=data[data.length-1]; if (IFrameSplitOperator.IsNumber(item)) return item>0; return false; } else if (IFrameSplitOperator.IsNumber(data)) { return data>0; } else { return false; } } /* 有效数据右对齐. 用法: ALIGNRIGHT(X)有效数据向右移动,左边空出来的周期填充无效值 例如:TC:=IF(CURRBARSCOUNT=2 || CURRBARSCOUNT=5,DRAWNULL,C);XC:ALIGNRIGHT(TC);删除了两天的收盘价,并将剩余数据右移 */ this.ALIGNRIGHT=function(data) { if (Array.isArray(data)) { var result=[]; var index=data.length-1; for(var i=data.length-1;i>=0;--i) { var item=data[i]; if (IFrameSplitOperator.IsNumber(item) || IFrameSplitOperator.IsString(item)) { result[index]=item; --index; } } for(var i=index;i>=0;--i) { result[i]=null; } return result; } else { return data; } } //格式化字符串 "{0}-{1}", C, O; this.STRFORMAT=function(strFormat,args,node) { var aryParam=strFormat.match(/{\d+}/g); if (!IFrameSplitOperator.IsNonEmptyArray(aryParam)) return null; var mapParam=new Map(); //key=index, value={Text} var maxIndex=-1; for(var i=0;i=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; } } //是否是字符串 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); var isFixedPosition=false; if (price==="TOP"|| price==="BOTTOM") { result.FixedPosition=price; isFixedPosition=true; } 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;当VAL1O,COLORBLUE,0);//收盘价大于开盘价,用蓝色绘制实心K线 */ this.DRAWCOLORKLINE=function(condition, color, empty) { let drawData=[]; let result={ DrawData:drawData, DrawType:'DRAWCOLORKLINE', IsEmptyBar:!(empty==0), Color:color }; if (Array.isArray(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) { 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,decimal) { let drawData={ Value:[], Text:[] }; let result={ DrawData:drawData, DrawType:'DRAWNUMBER' }; var dec=2; //小数位数 if (IFrameSplitOperator.IsNumber(decimal)) dec=decimal; var priceData={ DataType:0, SingleValue:null, ArrayValue:null }; //SingleValue=单值 ArrayValue=数组 if (Array.isArray(data)) { priceData.ArrayValue=data; priceData.DataType=2; } else { if (data==="TOP"|| data==="BOTTOM") result.FixedPosition=data; priceData.SingleValue=data; priceData.DataType=1; } var numberData={ DataType:0, SingleValue:null,ArrayValue:null }; if (Array.isArray(data2)) { numberData.ArrayValue=data2; numberData.DataType=2; } else { //单值 numberData.SingleValue=data2; numberData.DataType=1; if (IFrameSplitOperator.IsNumber(data2)) { if (IFrameSplitOperator.IsInteger(data2)) numberData.SingleValue=data2.toString(); else text=data2.toFixed(dec); } } if (Array.isArray(condition)) { for(var i=0; iOPEN,LOW,1)表示当收阳时在最低价位置画1号图标. */ this.DRAWICON = function (condition, data, type, markID) { if (IFrameSplitOperator.IsString(type)) //把ICO1=>1 { var value=type.replace('ICO',""); type=parseInt(value); } let icon = g_JSComplierResource.GetDrawTextIcon(type); if (!icon) icon = { Symbol: '●', Color: 'rgb(0,139,69)'}; let drawData = []; let result = { DrawData: drawData, DrawType: 'DRAWICON', Icon: icon , IconID:type}; if (markID) result.MarkID=markID; 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; } /* ICON:在k线图上,显示小图标。 用法:ICON(TYPE,ICON); 当TYPE为1,则在K线最高价位置显示图标ICON,当TYPE为0,则在最低价位置显示 图标ICON。 注: 1、该函数与判断条件连用,如:COND,ICON(TYPE,ICON); 2、该函数支持在函数后设置垂直对齐方式:VALIGN0(上对齐)、VALIGN1(中对齐)、VALIGN2(下对齐) 即可以写为如下格式: CLOSEOPEN,ICON(1,'ICO1');//表示K线收盘大于开盘时,在最高价上显示图标1。 写完“ICON(1,” 以后,点击插入图标按钮,再单击选中的图标插入到函数中,图标用 'ICO1'~'ICO105'表示 */ this.ICON=function(position, type) { if (IFrameSplitOperator.IsString(type)) //把ICO1=>1 { var value=type.replace('ICO',""); type=parseInt(value); } var icon=g_JSComplierResource.GetDrawTextIcon(type); if (!icon) icon={ Symbol: '●', Color: 'rgb(0,139,69)'}; let drawData=[]; let result={DrawData:drawData, DrawType:'ICON',Icon:icon}; 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; } //填充部分背景. //用法: //DRAWGBK_DIV(COND,COLOR1,COLOR2,填色方式,填充范围),填充满足COND条件的背景区域 //填色方式:0是上下渐进 1是左右渐进 2是用COLOR1画框线 3是用COLOR1画框线, 用COLOR2填充 //填充范围:0为整个区域 1为最高最低区 2为开盘收盘区 //例如: //DRAWGBK_DIV(C>O,RGB(0,255,0),RGB(255,0,0),0,0); this.DRAWGBK_DIV=function(condition, color, color2, colorType, fillType) { var drawData={ AryColor:[color, color2], ColorType:colorType, FillType:fillType, Data:[] }; var result={ DrawData:drawData, DrawType:'DRAWGBK_DIV' }; if (!this.SymbolData || !this.SymbolData.Data || !IFrameSplitOperator.IsNonEmptyArray(this.SymbolData.Data.Data)) return result; var aryKData=this.SymbolData.Data.Data; if (Array.isArray(condition)) { for(var i=0; i0) drawData.LineDotted=dotted; } drawData.Data=[]; for(var i=0;ilineCache.End.Value) { for(let i=1;i0) this.MaxRequestDataCount=option.MaxRequestDataCount; if (option.MaxRequestMinuteDayCount>0) this.MaxRequestMinuteDayCount=option.MaxRequestMinuteDayCount; if (option.KLineApiUrl) this.KLineApiUrl=option.KLineApiUrl; if (option.NetworkFilter) this.NetworkFilter = option.NetworkFilter; if (option.DayCount>0) this.DayCount=option.DayCount; if (option.IsApiPeriod) this.IsApiPeriod=option.IsApiPeriod; if (option.Right) this.Right=option.Right; if (option.Period) this.Period=option.Period; if (option.KLineRange) this.KLineDateTimeRange=option.KLineRange; if (option.DrawInfo) this.DrawInfo=option.DrawInfo; } this.GetLatestDataKey=function(key) { var key=`DYNAINFO-${key}`; return key; } //最新行情 this.GetLatestData=function(jobItem) { var aryArgs=this.JobArgumentsToArray(jobItem, 1); var lID=aryArgs[0]; var key=this.GetLatestDataKey(lID); if (this.LatestData.has(key)) return this.Execute.RunNextJob(); var self=this; JSNetwork.HttpRequest({ 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.ver==2.0) { this.RecvLatestDataVer2(data); return; } if (!data.stock || data.stock.length!=1) return; let stock=data.stock[0]; if (!stock) return; if (IFrameSplitOperator.IsNumber(stock.yclose)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.YCLOSE,stock.yclose); if (IFrameSplitOperator.IsNumber(stock.open)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.OPEN,stock.open); if (IFrameSplitOperator.IsNumber(stock.high)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.HIGH,stock.high); if (IFrameSplitOperator.IsNumber(stock.low)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.LOW,stock.low); if (IFrameSplitOperator.IsNumber(stock.price)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.CLOSE,stock.price); if (IFrameSplitOperator.IsNumber(stock.vol)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.VOL,stock.vol); if (IFrameSplitOperator.IsNumber(stock.amount)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.AMOUNT,stock.amount); if (IFrameSplitOperator.IsNumber(stock.increase)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.INCREASE,stock.increase); if (IFrameSplitOperator.IsNumber(stock.exchangerate)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.EXCHANGERATE,stock.exchangerate); if (IFrameSplitOperator.IsNumber(stock.amplitude)) this.LatestData.set(DYNAINFO_ARGUMENT_ID.AMPLITUDE,stock.amplitude); /* 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] symbol, LatestData', stock.symbol, this.LatestData); } //data:[{ id:, value: }] this.RecvLatestDataVer2=function(recvData) { let data=recvData.data; if (!IFrameSplitOperator.IsNonEmptyArray(data.data)) return; var symbol=data.symbol; for(var i=0;i=0 && dayCount<5;--j, ++dayCount) { var item=data.data[j]; if (IFrameSplitOperator.IsNumber(item[6])) sumVol+=item[6]; } if (dayCount>0) { avgVol5=sumVol/dayCount/minuteCount; var item=data.data[i]; mapAvgVol5.set(item[0], { //for debug //Vol5:sumVol, MinuteCount:minuteCount,,Count:dayCount, AvgVol5:avgVol5 } ); } } } if (mapAvgVol5.size>0) this.ExtendData.set(key,mapAvgVol5); JSConsole.Complier.Log('[JSSymbolData::RecvVolRateData]', mapAvgVol5); } 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 mapAvgVol5=this.ExtendData.get(key); var totalVol=0, preDate=0, avgVol5=null; for(var i=0, j=0;i0) { 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)) //请求日线数据 { 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年的数据 确保股票剔除停牌日期以后可以对上 }, method: "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 }, method: "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(recvData,job) { var data=recvData.data; JSConsole.Complier.Log('[JSSymbolData::RecvOtherSymbolKData] 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(recvData,job) { var data=recvData.data; 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(recvData, job) { var data=recvData.data; 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.GetIndexData=function() { if (this.IndexData) return this.Execute.RunNextJob(); var self=this; if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) //请求日线数据 { 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年的数据 确保股票剔除停牌日期以后可以对上 }, 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)) //请求分钟数据 { JSNetwork.HttpRequest({ 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); JSNetwork.HttpRequest({ 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) //当天分钟数据 { 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 }, method: 'POST', dataType: "json", async: true, success: function (recvData) { self.RecvMinuteData(recvData); self.Execute.RunNextJob(); } }); return; } if (JSCommonData.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, "period":this.Period, "right":this.Right } }, 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 }, 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)) //请求分钟数据 { 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, "period":this.Period, "right":this.Right } }, 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 }, 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.IsApiPeriod) //后台周期 前端不处理 { } else { 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.Data.Right=this.Right; this.Data.Period=this.Period; 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 (this.IsApiPeriod) //后台周期 前端不处理 { } else { if (JSCommonData.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 (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(); 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) && this.DataType==HQ_DATA_TYPE.KLINE_ID) //!! A股K线量单位时股,分时图单位还是手 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.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.GetTotalBarsCount=function() { let lCount=this.Data.Data.length; return lCount; } this.GetTotalTradeMinuteCount=function() { var data=g_MinuteCoordinateData.GetCoordinateData(this.Symbol); if (data && data.Count>0) return data.Count-1; return 242; } //BARPOS 返回从第一根K线开始到当前的周期数。 //注: //1、BARPOS返回本地已有的K线根数,从本机上存在的数据开始算起。 //2、本机已有的第一根K线上返回值为1。 this.GetBarPos=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 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'; //请求数据 JSNetwork.HttpRequest({ 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.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:"数据地址", 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.GetProFinance=function(jobItem) { var jobID=jobItem.ID; var finder=null; for(var i=0;i0) 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 (this.DataType==HQ_DATA_TYPE.KLINE_ID) { 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; } else { aryFittingData=kdata.GetMinuteFittingTradeData(recvData, 0); //数据和主图分钟拟合 } 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; iitem.Low) low=item.Low; } return low; } return []; } this.JobArgumentsToArray=function(job, lCount) { var args=job.Args; if (args.length!=lCount) { var token=job.Token; this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName}() Error: argument count error.`); } var aryValue=[]; for(var i=0;i0) result.Symbol=symbol.replace('.SH', ".sh"); else if (symbol.indexOf('.SZ')>0) result.Symbol=symbol.replace('.SZ', ".sz"); else if (symbol.indexOf("SH")==0) result.Symbol=symbol.slice(2)+".sh"; else if (symbol.indexOf("SZ")==0) result.Symbol=symbol.slice(2)+".sz"; else result.Symbol=symbol; 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; i0) { 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, IsApiPeriod:this.IsApiPeriod, KLineRange:dateTimeRange //K线数据范围 }; //执行脚本 var run=JSComplier.Execute(systemItem.Script,option,(error, indexInfo)=>{this.ExecuteScriptIndexError(error,indexInfo)}); } this.CallDynamicScriptIndex=function(job, varTable) { var callInfo=job.DynamicName; 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,`CallDynamicScriptIndex() 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,`CallDynamicScriptIndex() Error: '${callInfo}' ${indexInfo.Name} 指标不存在`); } indexInfo.SytemIndex=systemItem; if (!this.ReadDynamicIndexArgumentValue(job.Args, indexInfo, varTable)) { var token=job.Token; this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`CallDynamicScriptIndex() ${indexInfo.Name} 指标参数错误 : ${indexInfo.Error} `); } 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.RecvDynamicScriptIndexData(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, IsApiPeriod:this.IsApiPeriod, KLineRange:dateTimeRange //K线数据范围 }; //执行脚本 var run=JSComplier.Execute(systemItem.Script,option,(error, indexInfo)=>{this.ExecuteScriptIndexError(error,indexInfo)}); } this.ReadDynamicIndexArgumentValue=function(args, result, varTable) { result.Args=[]; for(var i =0;i=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}`); } if (job.FunctionName=="CALCSTOCKINDEX") { if (!this.ReadIndexFunctionOut(job.Args[2],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, IsApiPeriod:this.IsApiPeriod, 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=0;i0) strValue+=","; strValue+=`${item.Value}`; } } var strArgs=`(${strValue})`; //保存所有的指标数据, 下面用到了就可以不用算了 for(var i=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="ALL"; if (indexInfo.Out) out=indexInfo.Out; else if (IFrameSplitOperator.IsPlusNumber(indexInfo.OutIndex)) out=`Out[${indexInfo.OutIndex-1}]`; var key=`(${indexInfo.Symbol},${indexInfo.PeriodID}), ${indexInfo.Name}(${indexParam})=>${out}`; return key; } 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, funcName) { var indexInfo={ PeriodID:this.Period }; if (!this.ReadSymbolArgumentValue(args[0],indexInfo)) //读取代码 this.Execute.ThrowUnexpectedNode(node,`${funcName}() 股票代码错误: ${indexInfo.Error}`); if (!this.ReadIndexFunctionValue(args[1],indexInfo)) //读取指标 this.Execute.ThrowUnexpectedNode(node,`${funcName}() 指标错误: ${indexInfo.Error}`); if (funcName=="CALCSTOCKINDEX") { if (!this.ReadIndexFunctionOut(args[2],indexInfo)) //读取返回值索引 this.Execute.ThrowUnexpectedNode(node, `${funcName}() Error: ${indexInfo.Error}`); } var systemIndex=new JSIndexScript(); var systemItem=systemIndex.Get(indexInfo.Name); if (!systemItem) this.Execute.ThrowUnexpectedNode(node,`${funcName}() 指标错误: ${indexInfo.Name} 指标不存在`); indexInfo.SytemIndex=systemItem; //系统指标 if (!this.ReadIndexArgumentValue(args,indexInfo)) this.Execute.ThrowUnexpectedNode(node,`${funcName}() 指标参数错误: ${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 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]; if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除 //上涨 下跌家数 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]; 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; } /* 取得该周期的时分秒,适用于日线以下周期. 用法: TIME2 函数返回有效值范围为(000000-235959) */ this.TIME2=function() { var result=[]; if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result; for(let i=0;i0) 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; } this.HOUR=function() { var result=[]; if (!this.Data || !this.Data.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return result; for(var 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.UnaryExpression) { var varName="__temp_u_"+i+"__"; var varInfo={ }; if (this.ReadUnaryExpression(item.Expression, varInfo)) { var type=0; this.OutVarTable.push({Name:varName, Data:varInfo.OutVar,Type:type, NoneName:true}); } } else if (item.Expression.Type==Syntax.MemberExpression) //MA.MA2 { var outVar=this.ReadMemberVariable(item.Expression); if (outVar) { var type=0; var varName="__temp_di_"+i+"__"; 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) { var varName; var draw; var color, upColor, downColor, stickType; var lineWidth; var colorStick=false; var pointDot=false; var upDownDot=false; var circleDot=false; var lineStick=false; var stick=false; var volStick=false; var lineArea=false; var stepLine=false; var isShow = true; var isExData = false; var isDotLine = false; var isOverlayLine = false; //叠加线 var isSingleLine=false; //独立线段 var isNoneName=false; var isShowTitle=true; //显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如: //DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE; var isDrawAbove=false; //VALIGN0,VALIGN1,VALIGN2 设置文字垂直对齐方式(上中下) //ALIGN0,ALIGN1,ALIGN2 设置文字水平对齐方式(左中右) var drawAlign=-1, drawVAlign=-1; var fontSize=-1; var bgConfig=null; var vLineConfig=null; var xOffset=null, yOffset=null; var lineDash=null; for(var j=0 ; j=0) outVar.DrawAlign=drawAlign; if (drawVAlign>=0) outVar.DrawVAlign=drawVAlign; if (fontSize>0) outVar.DrawFontSize=fontSize; if (bgConfig) outVar.Background=bgConfig; if (vLineConfig) outVar.VerticalLine=vLineConfig; if (IFrameSplitOperator.IsNumber(xOffset)) outVar.XOffset=xOffset; if (IFrameSplitOperator.IsNumber(yOffset)) outVar.YOffset=yOffset; 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 (IFrameSplitOperator.IsNonEmptyArray(lineDash)) value.LineDash=lineDash; if (isOverlayLine == true) value.IsOverlayLine = true; if (isSingleLine == true) value.IsSingleLine = true; if (isShowTitle==false) value.IsShowTitle=false; if (stepLine==true) value.Type=7; this.OutVarTable.push(value); } } } if (this.Interrupt && this.Interrupt.Exit) { JSConsole.Complier.Log('[JSExecute::RunAST] Interrupt', this.Interrupt); //中断退出 break; } } JSConsole.Complier.Log('[JSExecute::RunAST]', this.VarTable); return this.OutVarTable; } this.ReadUnaryExpression=function(item, varInfo) { var argument=item.Argument; var outVar=null; if (argument.Type==Syntax.Literal) { outVar=argument.Value; if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); } else if (argument.Type==Syntax.Identifier) { var varName=argument.Name; outVar=this.ReadVariable(varName,item.Expression); if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); } else if (argument.Type==Syntax.BinaryExpression) { outVar=argument.Out; if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); } else if (argument.Type==Syntax.CallExpression) { var callItem=argument; if (this.Draw.IsDrawFunction(callItem.Callee.Name) ) { return false; } else if (callItem.Callee.Name==="IFC" && callItem.Draw) { return false; } else { outVar=callItem.Out; if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar); } } else { return false; } if (item.Operator=='-') { if (outVar) outVar=this.Algorithm.Subtract(0,outVar); } varInfo.OutVar=outVar; return true; } this.GetOutIconData=function(cond, iconDraw) { if (Array.isArray(cond)) { for(var i=0; i0) strValue+=","; strValue+=`${value}`; } var strArgs=`(${strValue})`; var key=`${outName}#${strArgs}`; if (period) key+=`#${period}`; if (!this.VarTable.has(name)) return null; var indexData=this.VarTable.get(name); var value=indexData[key]; return value; } //函数调用 this.VisitCallExpression=function(node) { let funcName=node.Callee.Name; let args=[]; for(let i=0; i=1) break; //IFC先处理第1个条件参数 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 (node.Callee.Type==Syntax.Literal) //指标调用'MA.MA1'(6,12,18) { node.Out=[]; node.Draw=null; var data=this.GetDynamicScriptIndex(node, args); if (data) node.Out=data; return node.Out; } if (funcName==="IFC") { //IFC(X,A,B)若X不为0则执行A,否则执行B.IFC与IF函数的区别:根据X的值来选择性执行A、B表达式. var bResult=this.Algorithm.IFC(args[0]); var item=bResult? node.Arguments[1] : node.Arguments[2]; var 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); node.Out=value; if (item.Draw) node.Draw=item.Draw; return node.Out; } //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; } if (g_JSComplierResource.IsCustomDataFunction(funcName)) { var functionInfo=g_JSComplierResource.CustomDataFunction.Data.get(funcName); node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:functionInfo.ArgCount, Node:node } ); node.Draw=null; 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], args[3]); node.Out = []; break; case "ICON": node.Draw=this.Draw.ICON(args[0],args[1]); node.Out=[]; break; case "BACKGROUND": node.Draw=this.Draw.BACKGROUND(args[0],args[1],args[2],args[3],args[4],args[5]); node.Out=[]; break; case "CKLINE": node.Draw=this.Draw.CKLINE(args[0],args[1],args[2],args[3],args[4]); 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': case "DRAWKLINE1": 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 "DRAWCOLORKLINE": node.Draw=this.Draw.DRAWCOLORKLINE(args[0],args[1],args[2]); 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], args[3]); 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 "UPCOLOR": node.Out=this.Draw.UPCOLOR(args[0]); break; case "DOWNCOLOR": node.Out=this.Draw.DOWNCOLOR(args[0]); break; case "STICKTYPE": node.Out=this.Draw.STICKTYPE(args[0]); break; case "XMOVE": node.Out=this.Draw.XMOVE(args[0]); break; case "YMOVE": node.Out=this.Draw.YMOVE(args[0]); break; case "LINEDASH": node.Out=this.Draw.LINEDASH(args); 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 '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 "DRAWGBK_DIV": node.Draw=this.Draw.DRAWGBK_DIV(args[0],args[1],args[2],args[3],args[4]); node.Out=[]; 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": case "SCJYVALUE": case "BKJYVALUE": node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } ); break; case "GPJYONE": case "SCJYONE": case "BKJYONE": node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:4, 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 "STKINDI": case "CALCSTOCKINDEX": node.Out=this.SymbolData.GetScriptIndexOutData(args,node,funcName); 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 "SYSPARAM": node.Out=this.SymbolData.SysParam(args[0], this); break; case "TESTSKIP": var bExit=this.Algorithm.TESTSKIP(args[0],node); node.Out=null; if (bExit) { this.Interrupt.Exit=true; if (node && node.Marker) { var marker=node.Marker; this.Interrupt.Line=marker.Line; this.Interrupt.Index=marker.Index; this.Interrupt.Column=marker.Column; } } 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,drawValue=null; if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression) value=this.VisitBinaryExpression(right); else if (right.Type==Syntax.CallExpression) { value=this.VisitCallExpression(right); if (right.Draw) drawValue=right.Draw; } 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) value=this.VisitUnaryExpression(right); if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitAssignmentExpression]' , varName, ' = ',value); if (drawValue) this.VarDrawTable.set(varName, drawValue); 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; } //获取节点值,BinaryExpression,LogicalExpression会重新计算 this.GetNodeValueEx=function(item) { var value=null; 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); return value; } this.GetNodeValue=function(node) { switch(node.Type) { case Syntax.Literal: //数字 return node.Value; case Syntax.UnaryExpression: var value=this.VisitUnaryExpression(node); return value; case Syntax.Identifier: var value=this.ReadVariable(node.Name,node); return value; case Syntax.BinaryExpression: case Syntax.LogicalExpression: return node.Out; case Syntax.CallExpression: return this.VisitCallExpression(node); case Syntax.MemberExpression: return this.ReadMemberVariable(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; } //颜色转rgb JSComplier.ColorVarToRGB=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+“BBGGRR”:BB、GG、RR表示蓝色、绿色和红色的分量,每种颜色的取值范围是00-FF,采用了16进制。 //例如:MA5:MA(CLOSE,5),COLOR00FFFF表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。 if (colorName.indexOf('COLOR')==0) { var strColor=colorName.substr(5); if (strColor.length!=6) return null; var value=strColor.substr(0,2); var b=parseInt(value,16); value=strColor.substr(2,2); var g=parseInt(value,16); value=strColor.substr(4,2); var r=parseInt(value,16); return `rgb(${r},${g},${b})`; } //格式为RGBX+“RRGGBB”:RR、GG、BB表示红色、绿色和的蓝色分量,每种颜色的取值范围是00-FF,采用了16进制。 //例如:MA5:MA(CLOSE,5),RGBXFFFF00表示纯红色与纯绿色的混合色:RGBX008080表示淡蓝色和淡绿色的混合色。 if (colorName.indexOf("RGBX")==0) { var strColor=colorName.substr(4); if (strColor.length!=6) return null; var value=strColor.substr(0,2); var r=parseInt(value,16); value=strColor.substr(2,2); var g=parseInt(value,16); value=strColor.substr(4,2); var b=parseInt(value,16); return `rgb(${r},${g},${b})`; } return null; } /* 执行 option.Symbol=股票代码 option.Name=股票名称 option.Data=这个股票的ChartData option.Right=复权 option.MaxRequestDataCount=请求数据的最大个数 */ 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; } //请求数据 JSNetwork.HttpRequest({ 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; } //请求数据 JSNetwork.HttpRequest({ 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 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] }, 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); */ var JSCommonComplier= { JSComplier: JSComplier, g_JSComplierResource: g_JSComplierResource, }; export { JSCommonComplier, ErrorHandler, JSComplier, JSParser, Syntax, JS_EXECUTE_JOB_ID, g_JSComplierResource, }; /* module.exports = { JSCommonComplier: { JSComplier: JSComplier, g_JSComplierResource: g_JSComplierResource, }, //单个类导出 JSCommonComplier_ErrorHandler: ErrorHandler, JSCommonComplier_JSComplier:JSComplier, JSCommonComplier_JSParser:JSParser, JSCommonComplier_Syntax:Syntax, JS_EXECUTE_JOB_ID:JS_EXECUTE_JOB_ID, g_JSComplierResource:g_JSComplierResource, }; */