UNPKG

hqchart

Version:

HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接

8,809 lines 351 kB
"use strict";

//API默认地址
var g_JSComplierResource = {
    Domain: "https://opensource.zealink.com", //API域名
    CacheDomain: "https://opensourcecache.zealink.com", //缓存域名

    DrawIcon: { Family: 'iconfont',
        Data: new Map([[1, { Text: "\uE625", Color: 'rgb(255,106,106)' }], //向上箭头
        [2, { Text: "\uE68B", Color: 'rgb(46,139,87)' }], //向下箭头
        [11, { Text: "\uE624", Color: 'rgb(245,159,40)' }], //点赞
        [12, { Text: "\uE600", Color: 'rgb(245,159,40)' }], [13, { Text: "\uE70F", Color: 'rgb(209,37,35)' }], //B
        [14, { Text: "\uE64C", Color: 'rgb(127,209,59)' }], //S
        [9, { Text: "\uE626", Color: 'rgb(245,159,40)' }], //$
        [36, { Text: "\uE68C", Color: 'rgb(255,106,106)' }], //关闭 红色
        [37, { Text: "\uE68C", Color: 'rgb(46,139,87)' }], //关闭 绿色
        [38, { Text: "\uE68D", Color: 'rgb(238,44,44)' }], //▲
        [39, { Text: "\uE68E", Color: 'rgb(0,139,69)' }], //▼
        [46, { Text: "\uE64D", Color: 'rgb(51,51,51)' }]])
    }
};

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 FromCodePoint(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 IsWhiteSpace(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 IsLineTerminator(cp) {
        return cp === 0x0A || cp === 0x0D || cp === 0x2028 || cp === 0x2029;
    },

    // https://tc39.github.io/ecma262/#sec-names-and-keywords
    IsIdentifierStart: function IsIdentifierStart(cp) {
        return cp === 0x24 || cp === 0x5F || cp >= 0x41 && cp <= 0x5A || cp >= 0x61 && cp <= 0x7A || cp === 0x5C || cp >= 0x80 && Regex.NonAsciiIdentifierStart.test(Character.FromCodePoint(cp));
    },

    IsIdentifierPart: function IsIdentifierPart(cp) {
        return cp === 0x24 || cp === 0x5F || cp >= 0x41 && cp <= 0x5A || cp >= 0x61 && cp <= 0x7A || cp >= 0x30 && cp <= 0x39 || cp === 0x5C || cp >= 0x80 && Regex.NonAsciiIdentifierPart.test(Character.FromCodePoint(cp));
    },

    // https://tc39.github.io/ecma262/#sec-literals-numeric-literals
    IsDecimalDigit: function IsDecimalDigit(cp) {
        return cp >= 0x30 && cp <= 0x39; // 0..9
    },

    IsHexDigit: function IsHexDigit(cp) {
        return cp >= 0x30 && cp <= 0x39 || cp >= 0x41 && cp <= 0x46 || cp >= 0x61 && cp <= 0x66; // a..f
    },

    isOctalDigit: function isOctalDigit(cp) {
        return cp >= 0x30 && cp <= 0x37; // 0..7
    }
};

var TOKEN_NAME = {};
TOKEN_NAME[1 /* BooleanLiteral */] = 'Boolean';
TOKEN_NAME[2 /* EOF */] = '<end>';
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) {
        var 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) {
        var msg = 'Line ' + line + ': ' + description;
        var error = this.ConstructError(msg, col);
        error.Index = index;
        error.LineNumber = line;
        error.Description = description;
        return error;
    };

    this.ThrowError = function (index, line, col, description) {
        var 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) {
        var cp = this.Source.charCodeAt(i);
        if (cp >= 0xD800 && cp <= 0xDBFF) {
            var 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 };
        var 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 () {
        var type = void 0;
        var start = this.Index;
        //0x5C 反斜杠
        var 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) {
            var 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 () {
        var start = this.Index++; //start 保存进来的位置
        while (!this.IsEOF()) {
            var 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 () {
        var start = this.Index;
        var str = this.Source[this.Index];
        switch (str) {
            case '(':
                ++this.Index;
                break;
            case ')':
            case ';':
            case ',':
                ++this.Index;
                break;
            default:
                str = this.Source.substr(this.Index, 3);
                if (str == 'AND') {
                    this.Index += 3;
                } else {
                    str = this.Source.substr(this.Index, 2);
                    if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '<=' || str === '>=' || str === '=>' || str == ':=' || str == 'OR' || str == '<>') {
                        this.Index += 2;
                    } else {
                        str = this.Source[this.Index];
                        if ('<>=!+-*%&|^/:'.indexOf(str) >= 0) ++this.Index;
                    }
                }
        }

        if (this.Index == start) this.ThrowUnecpectedToken();

        return { Type: 7 /*Punctuator*/, Value: str, LineNumber: this.LineNumber, LineStart: this.LineStart, Start: start, End: this.Index };
    };

    //字符串 https://tc39.github.io/ecma262/#sec-literals-string-literals
    this.ScanStringLiteral = function () {
        var start = this.Index;
        var quote = this.Source[this.Index];

        ++this.Index;
        var octal = false;
        var str = '';
        while (!this.IsEOF()) {
            var 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 () {
        var start = this.Index;
        var ch = this.Source[this.Index];
        var 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 () {
        var comments = void 0;
        var start = this.Index == 0;
        while (!this.IsEOF()) {
            var 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;
                        var comment = this.SkipSingleLineComment(2);
                        start = true;
                    } else {
                        break;
                    }
                } else if (ch == 0x7B) //{ }  注释
                {
                    this.Index += 1;
                    var _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) {
        var comments = [];
        while (!this.IsEOF()) {
            var 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) {
            var comments = this.Scanner.ScanComments();
            if (!this.Scanner.IsEOF()) {
                var token = this.Scanner.Lex();

                var 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.IsNeedFinanceData = new Set(); //需要下载的财务数据
    this.IsNeedMarginData = new Set();
    this.IsNeedNewsAnalysisData = new Set(); //新闻统计数据
    this.IsNeedBlockIncreaseData = new Set(); //是否需要市场涨跌股票数据统计
    this.IsNeedSymbolExData = new Set(); //下载股票行情的其他数据
    this.IsNeedHK2SHSZData = new Set(); //下载北上资金数据

    this.GetDataJobList = function () //下载数据任务列表
    {
        var jobs = [];
        if (this.IsNeedSymbolData) jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA });
        if (this.IsNeedIndexData) jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA });
        if (this.IsNeedLatestData) jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA });

        //涨跌停家数统计
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
            for (var _iterator = this.IsNeedBlockIncreaseData[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                var blockSymbol = _step.value;

                jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA, Symbol: blockSymbol });
            }

            //加载财务数据
        } catch (err) {
            _didIteratorError = true;
            _iteratorError = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion && _iterator.return) {
                    _iterator.return();
                }
            } finally {
                if (_didIteratorError) {
                    throw _iteratorError;
                }
            }
        }

        var _iteratorNormalCompletion2 = true;
        var _didIteratorError2 = false;
        var _iteratorError2 = undefined;

        try {
            for (var _iterator2 = this.IsNeedFinanceData[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                var jobID = _step2.value;

                if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA) //股息率 需要总市值
                    jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA });

                jobs.push({ ID: jobID });
            }

            //加载融资融券
        } catch (err) {
            _didIteratorError2 = true;
            _iteratorError2 = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion2 && _iterator2.return) {
                    _iterator2.return();
                }
            } finally {
                if (_didIteratorError2) {
                    throw _iteratorError2;
                }
            }
        }

        var _iteratorNormalCompletion3 = true;
        var _didIteratorError3 = false;
        var _iteratorError3 = undefined;

        try {
            for (var _iterator3 = this.IsNeedMarginData[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
                var jobID = _step3.value;

                jobs.push({ ID: jobID });
            }

            //加载北上资金
        } catch (err) {
            _didIteratorError3 = true;
            _iteratorError3 = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion3 && _iterator3.return) {
                    _iterator3.return();
                }
            } finally {
                if (_didIteratorError3) {
                    throw _iteratorError3;
                }
            }
        }

        var _iteratorNormalCompletion4 = true;
        var _didIteratorError4 = false;
        var _iteratorError4 = undefined;

        try {
            for (var _iterator4 = this.IsNeedHK2SHSZData[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
                var jobID = _step4.value;

                jobs.push({ ID: jobID });
            }

            //加载新闻统计
        } catch (err) {
            _didIteratorError4 = true;
            _iteratorError4 = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion4 && _iterator4.return) {
                    _iterator4.return();
                }
            } finally {
                if (_didIteratorError4) {
                    throw _iteratorError4;
                }
            }
        }

        var _iteratorNormalCompletion5 = true;
        var _didIteratorError5 = false;
        var _iteratorError5 = undefined;

        try {
            for (var _iterator5 = this.IsNeedNewsAnalysisData[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
                var jobID = _step5.value;

                jobs.push({ ID: jobID });
            }

            //行情其他数据
        } catch (err) {
            _didIteratorError5 = true;
            _iteratorError5 = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion5 && _iterator5.return) {
                    _iterator5.return();
                }
            } finally {
                if (_didIteratorError5) {
                    throw _iteratorError5;
                }
            }
        }

        var _iteratorNormalCompletion6 = true;
        var _didIteratorError6 = false;
        var _iteratorError6 = undefined;

        try {
            for (var _iterator6 = this.IsNeedSymbolExData[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
                var jobID = _step6.value;

                jobs.push({ ID: jobID });
            }
        } catch (err) {
            _didIteratorError6 = true;
            _iteratorError6 = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion6 && _iterator6.return) {
                    _iterator6.return();
                }
            } finally {
                if (_didIteratorError6) {
                    throw _iteratorError6;
                }
            }
        }

        return jobs;
    };

    this.VerifySymbolVariable = function (varName) {
        var setIndexName = new Set(['INDEXA', 'INDEXC', 'INDEXH', 'INDEXL', "INDEXO", "INDEXV", 'INDEXDEC', 'INDEXADV']);
        if (setIndexName.has(varName)) {
            this.IsNeedIndexData = true;
            return;
        }

        var setSymbolDataName = new Set(['CLOSE', 'C', 'VOL', 'V', 'OPEN', 'O', 'HIGH', 'H', 'LOW', 'L', 'AMOUNT']);
        if (setSymbolDataName.has(varName)) {
            this.IsNeedSymbolData = true;
            return;
        }

        if (varName === 'VOLR') {
            if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA)) this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA);
        }

        //流通股本(手)
        if (varName === 'CAPITAL') {
            if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA)) this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA);
        }

        if (varName === 'EXCHANGE') {
            if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA)) this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
        }
    };

    this.VerifySymbolFunction = function (callee, args) {
        if (callee.Name == 'DYNAINFO') {
            this.IsNeedLatestData = true;
            return;
        }

        //财务函数
        if (callee.Name == 'FINANCE') {
            var jobID = JS_EXECUTE_JOB_ID.GetFinnanceJobID(args[0].Value);
            if (jobID && !this.IsNeedFinanceData.has(jobID)) this.IsNeedFinanceData.add(jobID);
            return;
        }

        if (callee.Name === 'MARGIN') {
            var _jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(args[0].Value);
            if (_jobID && !this.IsNeedMarginData.has(_jobID)) this.IsNeedMarginData.add(_jobID);
            return;
        }

        if (callee.Name === 'NEWS') {
            var _jobID2 = JS_EXECUTE_JOB_ID.GetNewsAnalysisID(args[0].Value);
            if (_jobID2 && !this.IsNeedNewsAnalysisData.has(_jobID2)) this.IsNeedNewsAnalysisData.add(_jobID2);
            return;
        }

        if (callee.Name === 'HK2SHSZ') //北上资金
            {
                var _jobID3 = JS_EXECUTE_JOB_ID.GetHK2SHSZJobID(args[0].Value);
                if (_jobID3 && !this.IsNeedHK2SHSZData.has(_jobID3)) this.IsNeedHK2SHSZData.add(_jobID3);
                return;
            }

        if (callee.Name == 'COST' || callee.Name == 'WINNER') //筹码都需要换手率
            {
                if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA)) this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
                return;
            }

        if (callee.Name === 'BETA') //beta需要下载上证指数
            {
                this.IsNeedIndexData = true;
                return;
            }

        if (callee.Name == 'UPCOUNT' || callee.Name == 'DOWNCOUNT') //上涨下跌个数
            {
                var blockSymbol = args[0].Value;
                if (!this.IsNeedBlockIncreaseData.has(blockSymbol)) this.IsNeedBlockIncreaseData.add(blockSymbol);
                return;
            }
    };

    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) {
        var logical = operator === '||' || operator === '&&' || operator == 'AND' || operator == 'OR';
        var type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression;

        return { Type: type, Operator: operator, Left: left, Right: right };
    };

    this.Literal = function (value, raw) {
        return { Type: Syntax.Literal, Value: value, Raw: raw };
    };

    this.Identifier = function (name) {
        this.VerifySymbolVariable(name);

        return { Type: Syntax.Identifier, Name: name };
    };

    this.AssignmentExpression = function (operator, left, right) {
        return { Type: Syntax.AssignmentExpression, Operator: operator, Left: left, Right: right };
    };

    this.UnaryExpression = function (operator, argument) {
        return { Type: Syntax.UnaryExpression, Operator: operator, Argument: argument, Prefix: true };
    };

    this.EmptyStatement = function () {
        return { Type: Syntax.EmptyStatement };
    };

    this.CallExpression = function (callee, args) {
        this.VerifySymbolFunction(callee, args);

        return { Type: Syntax.CallExpression, Callee: callee, Arguments: args };
    };
}

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,
        ']': 0,
        '||': 1,
        'OR': 1,
        '&&': 2,
        'AND': 2,
        '|': 3,
        '^': 4,
        '&': 5,
        '==': 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;
        }

        var column = token.Start - token.LineStart;
        var 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) {
        var 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;
        var op = this.LookAhead.Value;

        return op == '=' || op == ':' || op == ':=';
    };

    this.GetTokenRaw = function (token) {
        return this.Scanner.Source.slice(token.Start, token.End);
    };

    this.NextToken = function () {
        var 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;
        }

        var 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 () {
        var node = this.CreateNode();
        var 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 () {
        var token = this.LookAhead;
        var node = this.CreateNode();
        var expr = this.ParseExpression();
    };

    this.ParseDirectivePrologues = function () {
        var firstRestricted = null;
        var body = [];
        while (true) {
            var token = this.LookAhead;
            if (token.Type != 8 /*StringLiteral*/) break;

            var statement = this.ParseDirective();
            body.push(statement);
        }

        return body;
    };

    // https://tc39.github.io/ecma262/#sec-block
    this.ParseStatementListItem = function () {
        var statement = void 0;
        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 () {
        var statement = void 0;
        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 */:
                var 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 () {
        var node = this.CreateNode();
        this.Expect(';');
        return this.Finalize(node, this.Node.EmptyStatement());
    };

    //https://tc39.github.io/ecma262/#sec-labelled-statements
    this.ParseLabelledStatement = function () {
        var node = this.CreateNode();
        var expr = this.ParseExpression();
        this.ConsumeSemicolon();
        var statement = new this.Node.ExpressionStatement(expr);

        return this.Finalize(node, statement);
    };

    // https://tc39.github.io/ecma262/#sec-comma-operator
    this.ParseExpression = function () {
        var startToken = this.LookAhead;
        var expr = this.IsolateCoverGrammar(this.ParseAssignmentExpression);
        if (this.Match(',')) {
            var 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 () {
        var expr = void 0;

        var startToken = this.LookAhead;
        var token = startToken;
        expr = this.ParseConditionalExpression();

        if (this.MatchAssign()) {
            if (!this.Context.IsAssignmentTarget) {
                var 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();
            var operator = token.Value;
            var 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 () {
        var startToken = this.LookAhead;
        var expr = this.InheritCoverGrammar(this.ParseBinaryExpression);

        return expr;
    };

    this.ParseBinaryExpression = function () {
        var startToken = this.LookAhead;
        var expr = this.InheritCoverGrammar(this.ParseExponentiationExpression);
        var token = this.LookAhead;
        var prec = this.BinaryPrecedence(token);
        if (prec > 0) {
            this.NextToken();
            this.Context.IsAssignmentTarget = false;
            this.Context.IsBindingElement = false;
            var markers = [startToken, this.LookAhead];
            var left = expr;
            var right = this.IsolateCoverGrammar(this.ParseExponentiationExpression);
            var stack = [left, token.Value, right];
            var 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();
                    var operator = stack.pop();
                    precedences.pop();
                    left = stack.pop();
                    markers.pop();
                    var 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));
            }

            var _i = stack.length - 1;
            expr = stack[_i];
            var lastMarker = markers.pop();
            while (_i > 1) {
                var marker = markers.pop();
                var lastLineStart = lastMarker && lastMarker.LineStart;
                var _node = this.StartNode(marker, lastLineStart);
                var _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 () {
        var startToken = this.LookAhead;
        var expr = this.InheritCoverGrammar(this.ParseUnaryExpression);

        return expr;
    };

    this.ParseUnaryExpression = function () {
        var expr = void 0;
        if (this.Match('+') || this.Match('-')) {
            var node = this.StartNode(this.LookAhead);
            var 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 () {
        var expr = void 0;
        var startToken = this.LookAhead;
        expr = this.InheritCoverGrammar(this.ParseLeftHandSideExpressionAllowCall);

        return expr;
    };

    this.ParseLeftHandSideExpressionAllowCall = function () {
        var startToken = this.LookAhead;
        var expr = void 0;
        expr = this.InheritCoverGrammar(this.ParsePrimaryExpression);

        while (true) {
            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));
            } else {
                break;
            }
        }

        return expr;
    };

    // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions
    this.ParseArguments = function () {
        this.Expect('(');
        var args = [];
        if (!this.Match(')')) {
            while (true) {
                var 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 () {
        var node = this.CreateNode();
        var expr = void 0;
        var token, raw;
        switch (this.LookAhead.Type) {
            case 3:
                /* Identifier */
                expr = this.Finalize(node, this.Node.Identifier(this.NextToken().Value));
                break;
            case 6: /* NumericLiteral */
            case 8:
                /* StringLiteral */
                this.Context.IsAssignmentTarget = false;
                this.Context.IsBindingElement = false;
                token = this.NextToken();
                raw = this.GetTokenRaw(token);
                expr = this.Finalize(node, this.Node.Literal(token.Value, raw));
                break;
            case 7:
                /* Punctuator */
                switch (this.LookAhead.Value) {
                    case '(':
                        this.Context.IsBindingElement = false;
                        expr = this.InheritCoverGrammar(this.ParseGroupExpression);
                        break;
                    default:
                        expr = this.ThrowUnexpectedToken(this.NextToken());
                }
                break;
            default:
                expr = this.ThrowUnexpectedToken(this.NextToken());
        }

        return expr;
    };

    this.ParseGroupExpression = function () {
        var expr = void 0;
        this.Expect('(');
        if (this.Match(')')) {
            this.NextToken();
        } else {
            var startToken = this.LookAhead;
            var params = [];
            var arrow = false;
            this.Context.IsBindingElement = true;
            expr = this.InheritCoverGrammar(this.ParseAssignmentExpression);
            if (this.Match(',')) {
                var 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 () {
        var node = this.CreateNode();
        var 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.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) {
        var op = token.Value;
        var precedence = void 0;

        if (token.Type === 7 /* Punctuator */) precedence = this.PeratorPrecedence[op] || 0;else precedence = 0;

        return precedence;
    };

    this.IsolateCoverGrammar = function (parseFunction) {
        var previousIsBindingElement = this.Context.IsBindingElement;
        var previousIsAssignmentTarget = this.Context.IsAssignmentTarget;
        var previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError;

        this.Context.IsBindingElement = true;
        this.Context.IsAssignmentTarget = true;
        this.Context.FirstCoverInitializedNameError = null;
        var 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) {
        var previousIsBindingElement = this.Context.IsBindingElement;
        var previousIsAssignmentTarget = this.Context.IsAssignmentTarget;
        var previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError;
        this.Context.IsBindingElement = true;
        this.Context.IsAssignmentTarget = true;
        this.Context.FirstCoverInitializedNameError = null;

        var 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) {
        var msg = message || "执行异常";

        return this.ErrorHandler.ThrowError(index, line, column, msg);
    };

    this.UnexpectedTokenError = function (token, message) {
        var msg = message || Messages.UnexpectedToken;
        var value = 'ILLEGAL';
        if (token) {
            if (!message) {}
            value = token.Value;
        }

        msg = msg.replace("%0", value);
        if (token && typeof token.LineNumber == 'number') {
            var index = token.Start;
            var _line = token.LineNumber;
            var lastMarkerLineStart = this.LastMarker.Index - this.LastMarker.Column;
            var column = token.Start - lastMarkerLineStart + 1;
            return this.ErrorHandler.CreateError(index, _line, column, msg);
        } else {
            var _index = this.LastMarker.Index;
            var _line2 = this.LastMarker.Line;
            var _column = this.LastMarker.Column + 1;
            return this.ErrorHandler.CreateError(_index, _line2, _column, msg);
        }
    };
}

/*
    算法类
*/
function JSAlgorithm(errorHandler, symbolData) {
    this.ErrorHandler = errorHandler;
    this.SymbolData = symbolData; //股票数据

    //相加
    this.Add = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值相加
        if (isNumber && isNumber2) return data + data2;

        //都是数组相加
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i2 = 0; _i2 < count; ++_i2) {
                result[_i2] = null; //初始化

                if (_i2 < data.length && _i2 < data2.length) {
                    if (this.IsNumber(data[_i2]) && this.IsNumber(data2[_i2])) result[_i2] = data[_i2] + data2[_i2];
                }
            }

            return result;
        }

        //单数据和数组相加
        var value = void 0;
        var aryData = void 0;
        if (isNumber) {
            value = data;
            aryData = data2;
        } else {
            value = data2;
            aryData = data;
        }

        for (var _i3 in aryData) {
            result[_i3] = null;
            if (this.IsNumber(aryData[_i3]) && this.IsNumber(value)) result[_i3] = value + aryData[_i3];
        }

        return result;
    };

    //相减
    this.Subtract = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值相减
        if (isNumber && isNumber2) return data - data2;

        //都是数组相减
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i4 = 0; _i4 < count; ++_i4) {
                result[_i4] = null; //初始化

                if (_i4 < data.length && _i4 < data2.length) {
                    if (this.IsNumber(data[_i4]) && this.IsNumber(data2[_i4])) result[_i4] = data[_i4] - data2[_i4];
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i5 in data2) {
                    result[_i5] = null;
                    if (this.IsNumber(data) && this.IsNumber(data2[_i5])) result[_i5] = data - data2[_i5];
                }
            } else //数组-单数据
            {
                for (var _i6 in data) {
                    result[_i6] = null;
                    if (this.IsNumber(data[_i6]) && this.IsNumber(data2)) result[_i6] = data[_i6] - data2;
                }
            }

        return result;
    };

    //相乘
    this.Multiply = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值相乘
        if (isNumber && isNumber2) return data * data2;

        //都是数组相乘
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i7 = 0; _i7 < count; ++_i7) {
                result[_i7] = null; //初始化

                if (_i7 < data.length && _i7 < data2.length) {
                    if (this.IsNumber(data[_i7]) && this.IsNumber(data2[_i7])) result[_i7] = data[_i7] * data2[_i7];
                }
            }

            return result;
        }

        //单数据和数组相乘
        var value = void 0;
        var aryData = void 0;
        if (isNumber) {
            value = data;
            aryData = data2;
        } else {
            value = data2;
            aryData = data;
        }

        for (var _i8 in aryData) {
            result[_i8] = null;
            if (this.IsNumber(aryData[_i8]) && this.IsNumber(value)) result[_i8] = value * aryData[_i8];
        }

        return result;
    };

    //相除
    this.Divide = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值相除
        if (isNumber && isNumber2) {
            if (data2 == 0) return null; //除0判断
            return data / data2;
        }

        //都是数组相除
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i9 = 0; _i9 < count; ++_i9) {
                result[_i9] = null; //初始化

                if (_i9 < data.length && _i9 < data2.length) {
                    if (this.IsNumber(data[_i9]) && this.IsDivideNumber(data2[_i9])) result[_i9] = data[_i9] / data2[_i9];
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i10 in data2) {
                    result[_i10] = null;
                    if (this.IsNumber(data) && this.IsDivideNumber(data2[_i10])) result[_i10] = data / data2[_i10];
                }
            } else //数组-单数据
            {
                for (var _i11 in data) {
                    result[_i11] = null;
                    if (this.IsNumber(data[_i11]) && this.IsDivideNumber(data2)) result[_i11] = data[_i11] / data2;
                }
            }

        return result;
    };

    //大于
    this.GT = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data > data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i12 = 0; _i12 < count; ++_i12) {
                result[_i12] = null; //初始化

                if (_i12 < data.length && _i12 < data2.length) {
                    if (!isNaN(data[_i12]) && !isNaN(data2[_i12])) result[_i12] = data[_i12] > data2[_i12] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i13 in data2) {
                    result[_i13] = null;
                    if (!isNaN(data) && !isNaN(data2[_i13])) result[_i13] = data > data2[_i13] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i14 in data) {
                    result[_i14] = null;
                    if (!isNaN(data[_i14]) && !isNaN(data2)) result[_i14] = data[_i14] > data2 ? 1 : 0;
                }
            }

        return result;
    };

    //大于等于
    this.GTE = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data >= data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i15 = 0; _i15 < count; ++_i15) {
                result[_i15] = null; //初始化

                if (_i15 < data.length && _i15 < data2.length) {
                    if (!isNaN(data[_i15]) && !isNaN(data2[_i15])) result[_i15] = data[_i15] >= data2[_i15] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i16 in data2) {
                    result[_i16] = null;
                    if (!isNaN(data) && !isNaN(data2[_i16])) result[_i16] = data >= data2[_i16] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i17 in data) {
                    result[_i17] = null;
                    if (!isNaN(data[_i17]) && !isNaN(data2)) result[_i17] = data[_i17] >= data2 ? 1 : 0;
                }
            }

        return result;
    };

    //小于
    this.LT = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data < data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i18 = 0; _i18 < count; ++_i18) {
                result[_i18] = null; //初始化

                if (_i18 < data.length && _i18 < data2.length) {
                    if (!isNaN(data[_i18]) && !isNaN(data2[_i18])) result[_i18] = data[_i18] < data2[_i18] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i19 in data2) {
                    result[_i19] = null;
                    if (!isNaN(data) && !isNaN(data2[_i19])) result[_i19] = data < data2[_i19] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i20 in data) {
                    result[_i20] = null;
                    if (!isNaN(data[_i20]) && !isNaN(data2)) result[_i20] = data[_i20] < data2 ? 1 : 0;
                }
            }

        return result;
    };

    //小于等于
    this.LTE = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data >= data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i21 = 0; _i21 < count; ++_i21) {
                result[_i21] = null; //初始化

                if (_i21 < data.length && _i21 < data2.length) {
                    if (!isNaN(data[_i21]) && !isNaN(data2[_i21])) result[_i21] = data[_i21] <= data2[_i21] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i22 in data2) {
                    result[_i22] = null;
                    if (!isNaN(data) && !isNaN(data2[_i22])) result[_i22] = data <= data2[_i22] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i23 in data) {
                    result[_i23] = null;
                    if (!isNaN(data[_i23]) && !isNaN(data2)) result[_i23] = data[_i23] <= data2 ? 1 : 0;
                }
            }

        return result;
    };

    //等于
    this.EQ = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data == data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i24 = 0; _i24 < count; ++_i24) {
                result[_i24] = null; //初始化

                if (_i24 < data.length && _i24 < data2.length) {
                    if (!isNaN(data[_i24]) && !isNaN(data2[_i24])) result[_i24] = data[_i24] == data2[_i24] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i25 in data2) {
                    result[_i25] = null;
                    if (!isNaN(data) && !isNaN(data2[_i25])) result[_i25] = data == data2[_i25] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i26 in data) {
                    result[_i26] = null;
                    if (!isNaN(data[_i26]) && !isNaN(data2)) result[_i26] = data[_i26] == data2 ? 1 : 0;
                }
            }

        return result;
    };

    //不等于
    this.NEQ = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值比较
        if (isNumber && isNumber2) return data != data2 ? 1 : 0;

        //都是数组比较
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i27 = 0; _i27 < count; ++_i27) {
                result[_i27] = null; //初始化

                if (_i27 < data.length && _i27 < data2.length) {
                    if (!isNaN(data[_i27]) && !isNaN(data2[_i27])) result[_i27] = data[_i27] != data2[_i27] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i28 in data2) {
                    result[_i28] = null;
                    if (!isNaN(data) && !isNaN(data2[_i28])) result[_i28] = data != data2[_i28] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i29 in data) {
                    result[_i29] = null;
                    if (!isNaN(data[_i29]) && !isNaN(data2)) result[_i29] = data[_i29] != data2 ? 1 : 0;
                }
            }

        return result;
    };

    //AND  &&
    this.And = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值 &&
        if (isNumber && isNumber2) return data && data2 ? 1 : 0;

        //都是数组 &&
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i30 = 0; _i30 < count; ++_i30) {
                result[_i30] = null; //初始化

                if (_i30 < data.length && _i30 < data2.length) {
                    if (!isNaN(data[_i30]) && !isNaN(data2[_i30])) result[_i30] = data[_i30] && data2[_i30] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i31 in data2) {
                    result[_i31] = null;
                    if (!isNaN(data) && !isNaN(data2[_i31])) result[_i31] = data && data2[_i31] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i32 in data) {
                    result[_i32] = null;
                    if (!isNaN(data[_i32]) && !isNaN(data2)) result[_i32] = data[_i32] && data2 ? 1 : 0;
                }
            }

        return result;
    };

    //OR  ||
    this.Or = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值 &&
        if (isNumber && isNumber2) return data || data2 ? 1 : 0;

        //都是数组 &&
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i33 = 0; _i33 < count; ++_i33) {
                result[_i33] = null; //初始化

                if (_i33 < data.length && _i33 < data2.length) {
                    if (!isNaN(data[_i33]) && !isNaN(data2[_i33])) result[_i33] = data[_i33] || data2[_i33] ? 1 : 0;
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i34 in data2) {
                    result[_i34] = null;
                    if (!isNaN(data) && !isNaN(data2[_i34])) result[_i34] = data || data2[_i34] ? 1 : 0;
                }
            } else //数组-单数据
            {
                for (var _i35 in data) {
                    result[_i35] = null;
                    if (!isNaN(data[_i35]) && !isNaN(data2)) result[_i35] = data[_i35] || data2 ? 1 : 0;
                }
            }

        return result;
    };

    this.IF = function (data, trueData, falseData) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof trueData == 'number';
        var isNumber3 = typeof falseData == 'number';

        //单数值
        if (isNumber) {
            if (isNumber2 && isNumber3) return data ? trueData : falseData;

            return data ? trueData : falseData;
        }

        //都是数组
        var result = [];
        for (var _i36 in data) {
            if (data[_i36]) {
                if (isNumber2) result[_i36] = trueData;else result[_i36] = trueData[_i36];
            } else {
                if (isNumber3) result[_i36] = falseData;else result[_i36] = falseData[_i36];
            }
        }

        return result;
    };

    /*
    根据条件求不同的值,同IF判断相反.
    用法: IFN(X,A,B)若X不为0则返回B,否则返回A
    例如: IFN(CLOSE>OPEN,HIGH,LOW)表示该周期收阴则返回最高值,否则返回最低值
    */

    this.IFN = function (data, trueData, falseData) {
        return this.IF(data, falseData, trueData);
    };

    //指标函数 函数名全部大写
    this.REF = function (data, n) {
        var 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 (var _i37 = 0; _i37 < n; ++_i37) {
                result.unshift(null);
            }
        } else //n 为数组的情况
            {
                for (var _i38 = 0; _i38 < data.length; ++_i38) {
                    result[_i38] = null;
                    if (_i38 >= n.length) continue;
                    var value = n[_i38];
                    if (value > 0 && value <= _i38) result[_i38] = data[_i38 - value];else if (_i38) result[_i38] = result[_i38 - 1];else result[_i38] = data[_i38];
                }
            }

        return result;
    };

    this.MAX = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值
        if (isNumber && isNumber2) return Math.max(data, data2);

        //都是数组
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i39 = 0; _i39 < count; ++_i39) {
                result[_i39] = null; //初始化

                if (_i39 < data.length && _i39 < data2.length) {
                    if (!isNaN(data[_i39]) && !isNaN(data2[_i39])) result[_i39] = Math.max(data[_i39], data2[_i39]);
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i40 in data2) {
                    result[_i40] = null;
                    if (!isNaN(data) && !isNaN(data2[_i40])) result[_i40] = Math.max(data, data2[_i40]);
                }
            } else //数组-单数据
            {
                for (var _i41 in data) {
                    result[_i41] = null;
                    if (!isNaN(data[_i41]) && !isNaN(data2)) result[_i41] = Math.max(data[_i41], data2);
                }
            }

        return result;
    };

    this.MIN = function (data, data2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof data2 == 'number';

        //单数值
        if (isNumber && isNumber2) return Math.min(data, data2);

        //都是数组
        var result = [];
        if (!isNumber && !isNumber2) {
            var count = Math.max(data.length, data2.length);
            for (var _i42 = 0; _i42 < count; ++_i42) {
                result[_i42] = null; //初始化

                if (_i42 < data.length && _i42 < data2.length) {
                    if (!isNaN(data[_i42]) && !isNaN(data2[_i42])) result[_i42] = Math.min(data[_i42], data2[_i42]);
                }
            }

            return result;
        }

        if (isNumber) //单数据-数组
            {
                for (var _i43 in data2) {
                    result[_i43] = null;
                    if (!isNaN(data) && !isNaN(data2[_i43])) result[_i43] = Math.min(data, data2[_i43]);
                }
            } else //数组-单数据
            {
                for (var _i44 in data) {
                    result[_i44] = null;
                    if (!isNaN(data[_i44]) && !isNaN(data2)) result[_i44] = Math.min(data[_i44], data2);
                }
            }

        return result;
    };

    //取正数
    this.ABS = function (data) {
        var result = [];

        for (var _i45 in data) {
            result[_i45] = null;
            if (!isNaN(data[_i45])) result[_i45] = Math.abs(data[_i45]);
        }

        return result;
    };

    this.MA = function (data, dayCount) {
        if (dayCount <= 0) dayCount = 1;

        var result = [];
        if (!data || !data.length) return result;

        for (var i = 0; i < data.length; ++i) {
            result[i] = null;
            if (this.IsNumber(data[i])) break;
        }

        var data = data.slice(0); //复制一份数据出来
        for (var days = 0; i < data.length; ++i, ++days) {
            if (days < dayCount - 1) {
                result[i] = null;
                continue;
            }

            var preValue = data[i - (dayCount - 1)];
            var sum = 0;
            for (var _j = dayCount - 1; _j >= 0; --_j) {
                var value = data[i - _j];
                if (!this.IsNumber(value)) {
                    value = preValue; //空数据就取上一个数据
                    data[i - _j] = value;
                } else {
                    preValue = value;
                }
                sum += value;
            }

            result[i] = sum / dayCount;
        }

        return result;
    };

    //指数平均数指标 EMA(close,10)
    this.EMA = function (data, dayCount) {
        var result = [];

        var offset = 0;
        if (offset >= data.length) return result;

        //取首个有效数据
        for (; offset < data.length; ++offset) {
            if (data[offset] != null && !isNaN(data[offset])) break;
        }

        var p1Index = offset;
        var p2Index = offset + 1;

        result[p1Index] = data[p1Index];
        for (var i = offset + 1; i < data.length; ++i, ++p1Index, ++p2Index) {
            result[p2Index] = (2 * data[p2Index] + (dayCount - 1) * result[p1Index]) / (dayCount + 1);
        }

        return result;
    };

    /* 
        SMA 移动平均
        返回移动平均。
        用法: SMA(X,N,M) X的M日移动平均,M为权重,如Y=(X*M+Y'*(N-M))/N 
    */
    this.SMA = function (data, n, m) {
        var result = [];

        var i = 0;
        var lastData = null;
        for (; i < data.length; ++i) {
            if (data[i] == null || isNaN(data[i])) continue;
            lastData = data[i];
            result[i] = lastData; //第一天的数据
            break;
        }

        for (++i; i < data.length; ++i) {
            result[i] = (m * data[i] + (n - m) * lastData) / n;
            lastData = result[i];
        }

        return result;
    };

    /*
    求动态移动平均.
    用法: DMA(X,A),求X的动态移动平均.
    算法: 若Y=DMA(X,A)则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值,A必须小于1.
    例如:DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价
    */
    this.DMA = function (data, data2) {
        var result = [];
        if (data.length < 0 || data.length != data2.length) return result;

        var index = 0;
        for (; index < data.length; ++index) {
            if (data[index] != null && !isNaN(data[index]) && data2[index] != null && !isNaN(data2[index])) {
                result[index] = data[index];
                break;
            }
        }

        for (index = index + 1; index < data.length; ++index) {
            if (data[index] == null || data2[index] == null) result[index] = null;else {
                if (data2[index] < 1) result[index] = data2[index] * data[index] + (1 - data2[index]) * result[index - 1];else result[index] = data[index];
            }
        }

        return result;
    };
    /*
    返回加权移动平均
    用法:WMA(X,N):X的N日加权移动平均.
    算法:Yn=(1*X1+2*X2+...+n*Xn)/(1+2+...+n)
     */
    this.WMA = function (data, dayCount) {
        var result = [];
        if (!data || !data.length) return result;
        if (dayCount < 1) dayCount = 1;
        var i = 0;
        for (i = 0; i < data.length && !this.IsNumber(data[i]); ++i) {
            result[i] = null;
        }
        var data = data.slice(0);
        for (var days = 0; i < data.length; ++i, ++days) {
            if (days < dayCount - 1) {
                result[i] = null;
                continue;
            }
            var preValue = data[i - (dayCount - 1)];
            var sum = 0;
            var count = 0;
            for (var j = dayCount - 1; j >= 0; ++j) {
                var value = data[i - j];
                if (!this.IsNumber(value)) {
                    value = preValue;
                    data[i - j] = value;
                } else preValue = value;

                count += dayCount - j;
                sum += value * (dayCount - j);
            }
            result[i] = sum / count;
        }
        return result;
    };
    /*
    返回平滑移动平均
    用法:MEMA(X,N):X的N日平滑移动平均,如Y=(X+Y'*(N-1))/N
    MEMA(X,N)相当于SMA(X,N,1)
    */
    this.MEMA = function (data, dayCount) {
        var 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) {
        var result = [];
        if (dayCount >= data.length) return result;

        var i = dayCount;
        for (; i < data.length; ++i) //获取第1个有效数据
        {
            if (data[i] != null) {
                result[i] = data[i];
                break;
            }
        }

        for (i = i + 1; i < data.length; ++i) {
            if (result[i - 1] != null && data[i] != null) result[i] = (2 * data[i] + (dayCount - 1) * result[i - 1]) / (dayCount + 1);else if (result[i - 1] != null) result[i] = result[i - 1];
        }

        return result;
    };

    //加权平滑平均,MEMA[i]=SMA[i]*para+(1-para)*SMA[i-1] para=2/(1+__para)
    this.EXPMEMA = function (data, dayCount) {
        var result = [];
        if (dayCount >= data.length) return result;

        var index = 0;
        for (; index < data.length; ++index) {
            if (data[index] && !isNaN(data[index])) break;
        }

        var sum = 0;
        for (var i = 0; index < data.length && i < dayCount; ++i, ++index) {
            if (data[index] && !isNaN(data[index])) sum += data[index];else sum += data[index - 1];
        }

        result[index - 1] = sum / dayCount;
        for (; index < data.length; ++index) {
            if (result[index - 1] != null && data[index] != null) result[index] = (2 * data[index] + (dayCount - 1) * result[index - 1]) / (dayCount + 1);else if (result[index - 1] != null) result[index] = result[index - 1];
        }

        return result;
    };
    /*
    向前累加到指定值到现在的周期数.
    用法:SUMBARS(X,A):将X向前累加直到大于等于A,返回这个区间的周期数
    例如:SUMBARS(VOL,CAPITAL)求完全换手到现在的周期数
     */
    this.SUMBARS = function (data, data2) {
        var result = [];
        if (!data || !data.length || !data2 || !data2.length) return result;
        var start = 0,
            i = 0,
            j = 0;
        for (; start < data.length && !this.IsNumber(data[start]); ++start) {
            result[start] = null;
        }

        for (i = data.length - 1; i >= start; --i) {
            var total = 0;
            for (j = i, total = 0; j >= start && total < data2[i]; --j) {
                total += data[j];
            }if (j < start) result[i] = null;else result[i] = i - j;
        }
        for (i = start + 1; i < data.length; ++i) {
            if (result[i] == null) result[i] = result[i - 1];
        }
        return result;
    };
    /*
    求相反数.
    用法:REVERSE(X)返回-X.
    例如:REVERSE(CLOSE)返回-CLOSE
     */
    this.REVERSE = function (data) {
        var result = [];
        var i = 0;
        for (; 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) {
        var result = [];

        for (var _i46 = 0; _i46 < data.length; ++_i46) {
            var count = 0;
            for (var _j2 = 0; _j2 < n && _i46 - _j2 >= 0; ++_j2) {
                if (data[_i46 - _j2]) ++count;
            }

            result[_i46] = count;
        }

        return result;
    };

    /*
    HHV 最高值
    求最高值。
    用法: HHV(X,N) 求N周期内X最高值,N=0则从第一个有效值开始。
    例如: HHV(HIGH,30) 表示求30日最高价。
    */
    this.HHV = function (data, n) {
        var result = [];
        if (Array.isArray(n)) {
            var max = null;
            for (var i = 0, j = 0; i < data.length; ++i) {
                result[i] = null;
                if (i >= n.length) continue;

                max = null;
                var count = n[i];
                if (count > 0 && count <= i) {
                    for (j = i - count; j <= i; ++j) {
                        if (max == null || max < data[j]) max = data[j];
                    }
                } else {
                    count = i;
                    for (j = 0; j <= i; ++j) {
                        if (max == null || max < data[j]) max = data[j];
                    }
                }

                result[i] = max;
            }
        } else {
            if (n > data.length) return result;
            if (n <= 0) n = data.length - 1;

            var nMax = 0;
            for (nMax = 0; nMax < data.length; ++nMax) {
                if (this.IsNumber(data[nMax])) break;
            }

            if (nMax < data.length) result[nMax] = data[nMax];
            for (var i = nMax + 1, j = 2; i < data.length && j < n; ++i, ++j) {
                if (data[i] >= data[nMax]) nMax = i;
                result[i] = data[nMax];
            }

            for (; i < data.length; ++i) {
                if (i - nMax < n) {
                    nMax = data[i] < data[nMax] ? nMax : i;
                } else {
                    for (j = nMax = i - n + 2; j <= i; ++j) {
                        nMax = data[j] < data[nMax] ? nMax : j;
                    }
                }

                result[i] = data[nMax];
            }
        }

        return result;
    };

    /*
    LLV 最低值
    求最低值。
    用法: LLV(X,N) 求N周期内X最低值,N=0则从第一个有效值开始。
    例如: LLV(LOW,0) 表示求历史最低价。
    */
    this.LLV = function (data, n) {
        var result = [];
        if (Array.isArray(n)) {
            for (var i = 0; i < data.length; ++i) {
                result[i] = null;
                if (i >= n.length) continue;

                var min = null;
                var count = n[i];
                if (count > 0 && count <= i) {
                    for (var j = i - count; j <= i; ++j) {
                        if (min == null || min > data[j]) min = data[j];
                    }
                } else {
                    count = i;
                    for (var j = 0; j <= i; ++j) {
                        if (min == null || min > data[j]) min = data[j];
                    }
                }

                result[i] = min;
            }
        } else {
            if (n > data.length) return result;
            if (n <= 0) n = data.length - 1;

            var min = null;
            for (var i = n; i < data.length; ++i, ++j) {
                if (min == null || i < n + min) //最小值是否在当前周期里面
                    {
                        min = data[i] > data[min] ? min : i;
                    } else {
                    for (var j = (min = i - n + 1) + 1; j <= i; ++j) {
                        if (data[j] < data[min]) min = j;
                    }
                }
                result[i] = data[min];
            }
        }

        return result;
    };

    this.STD = function (data, n) {
        var result = [];

        var total = 0;
        var averageData = []; //平均值
        for (var i = n - 1; i < data.length; ++i) {
            total = 0;
            for (var j = 0; j < n; ++j) {
                total += data[i - j];
            }

            averageData[i] = total / n;
        }

        for (var i = n - 1; i < data.length; ++i) {
            total = 0;
            for (var j = 0; j < n; ++j) {
                total += Math.pow(data[i - j] - averageData[i], 2);
            }

            result[i] = Math.sqrt(total / n);
        }

        return result;
    };

    //平均绝对方差
    this.AVEDEV = function (data, n) {
        var result = [];

        var total = 0;
        var averageData = []; //平均值
        for (var i = n - 1; i < data.length; ++i) {
            total = 0;
            for (var j = 0; j < n; ++j) {
                total += data[i - j];
            }

            averageData[i] = total / n;
        }

        for (var i = n - 1; i < data.length; ++i) {
            total = 0;
            for (var j = 0; j < n; ++j) {
                total += Math.abs(data[i - j] - averageData[i]);
            }

            result[i] = total / n;
        }

        return result;
    };

    //上穿
    this.CROSS = function (data, data2) {
        var result = [];

        if (Array.isArray(data) && Array.isArray(data2)) {
            if (data.length != data2.length) return result = [];

            var index = 0;
            for (; index < data.length; ++index) {
                if (this.IsNumber(data[index]) && this.IsNumber(data2[index])) break;
            }

            for (++index; index < data.length; ++index) {
                result[index] = data[index] > data2[index] && data[index - 1] < data2[index - 1] ? 1 : 0;
            }
        } else if (Array.isArray(data) && typeof data2 == 'number') {
            var index = 0;
            for (; index < data.length; ++index) {
                if (this.IsNumber(data[index])) break;
            }

            for (++index; index < data.length; ++index) {
                result[index] = data[index] > data2 && data[index - 1] < data2 ? 1 : 0;
            }
        } else if (typeof data == 'number' && Array.isArray(data2)) {
            var index = 0;
            for (; index < data2.length; ++index) {
                if (this.IsNumber(data2[index])) break;
            }

            for (++index; index < data2.length; ++index) {
                result[index] = data2[index] < data && data2[index - 1] > data ? 1 : 0;
            }
        }

        return result;
    };

    //累乘
    this.MULAR = function (data, n) {
        var result = [];
        if (data.length < n) return result;

        var index = n;
        for (; index < data.length; ++index) {
            if (data[index] != null && !isNaN(data[index])) {
                result[index] = data[index];
                break;
            }
        }

        for (++index; index < data.length; ++index) {
            result[index] = result[index - 1] * data[index];
        }

        return result;
    };

    this.SUM = function (data, n) {
        var result = [];

        if (n == 0) {
            result[0] = data[0];

            for (var i = 1; i < data.length; ++i) {
                result[i] = result[i - 1] + data[i];
            }
        } else {
            for (var i = n - 1, j = 0; i < data.length; ++i, ++j) {
                for (var k = 0; k < n; ++k) {
                    if (k == 0) result[i] = data[k + j];else result[i] += data[k + j];
                }
            }
        }

        return result;
    };

    /*
        BARSCOUNT 有效数据周期数
        求总的周期数。
        用法: BARSCOUNT(X) 第一个有效数据到当前的天数。
        例如: BARSCOUNT(CLOSE) 对于日线数据取得上市以来总交易日数,对于分笔成交取得当日成交笔数,对于1分钟线取得当日交易分钟数。
    */
    this.BARSCOUNT = function (data) {
        var result = [];
        var days = null;
        for (var _i47 in data) {
            result[_i47] = 0;
            if (days == null) {
                if (!this.IsNumber(data[_i47])) continue;

                days = 0;
            }

            result[_i47] = days;
            ++days;
        }

        return result;
    };

    //DEVSQ 数据偏差平方和
    //DEVSQ(X,N)  返回数据偏差平方和。
    this.DEVSQ = function (data, n) {
        var result = [];
        if (typeof n != 'number') n = parseInt(n); //字符串的转成数值型
        var num = n;
        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) {
        var isNumber = typeof data == 'number';
        if (isNumber) return data ? 0 : 1;

        var result = [];
        for (var _i48 in data) {
            result[_i48] = null;
            if (this.IsNumber(data[_i48])) result[_i48] = data[_i48] ? 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;
        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; j < num && j <= i; ++j) {
                Ex += i - j;
                Ey += data[i - j];
            }
            Ex /= num;
            Ey /= num;
            for (j = 0; j < num && j <= i; ++j) {
                Sxy += (i - j - Ex) * (data[i - j] - Ey);
                Sxx += (i - j - Ex) * (i - j - Ex);
            }
            Slope = Sxy / Sxx;
            Const = (Ey - Ex * Slope) / num;
            result[i] = Slope * num + Const;
        }

        return result;
    };

    //SLOPE 线性回归斜率
    //SLOPE(X,N)  返回线性回归斜率。
    this.SLOPE = function (data, n) {
        var result = [];
        if (typeof n != 'number') n = parseInt(n); //字符串的转成数值型
        if (n < 1 || !data.length) return result;
        if (n >= data.length) return result;

        var start = 0;
        for (var _i49 = 0; _i49 < data.length; ++_i49, ++start) {
            result[_i49] = null;
            if (this.IsNumber(data[_i49])) break;
        }

        var x = void 0,
            y = void 0,
            xy = void 0,
            xx = void 0;
        for (var _i50 = start + n - 1; _i50 < data.length; ++_i50) {
            result[_i50] = null;
            x = y = xy = xx = 0;
            for (var j = 0; j < n && j <= _i50; ++j) {
                x += _i50 - j; //数据索引相加
                y += data[_i50 - j]; //数据相加
            }

            x = x / n;y = y / n;
            for (j = 0; j < n && j <= _i50; ++j) {
                xy += (_i50 - j - x) * (data[_i50 - j] - y);
                xx += (_i50 - j - x) * (_i50 - j - x);
            }

            if (xx) result[_i50] = xy / xx;else if (_i50) result[_i50] = result[_i50 - 1];
        }

        return result;
    };

    //STDP 总体标准差
    //STDP(X,N)  返回总体标准差。
    this.STDP = function (data, n) {
        var result = [];
        if (typeof n != 'number') n = parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num < 1 || num >= datanum) return result;
        var i = 0,
            j = 0;
        for (i = 0; i < datanum && !this.IsNumber(data[i]); ++i) {
            result[i] = null;
        }
        var SigmaPowerX = 0,
            SigmaX = 0,
            MidResult;
        for (; i < datanum && j < num; ++i, ++j) {
            SigmaPowerX += data[i] * data[i];
            SigmaX += data[i];
        }
        if (j == num) {
            MidResult = num * SigmaPowerX - SigmaX * SigmaX;
            result[i - 1] = Math.sqrt(MidResult) / num;
        }
        for (; i < datanum; ++i) {
            SigmaPowerX += data[i] * data[i] - data[i - num] * data[i - num];
            SigmaX += data[i] - data[i - num];
            MidResult = num * SigmaPowerX - SigmaX * SigmaX;
            result[i] = Math.sqrt(MidResult) / num;
        }

        return result;
    };

    //VAR 估算样本方差
    //VAR(X,N)  返回估算样本方差。
    this.VAR = function (data, n) {
        var result = [];
        if (typeof n != 'number') n = parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num <= 1 || num >= datanum) return result;
        var i, j;
        for (i = 0; i < datanum && !this.IsNumber(data[i]); ++i) {
            result[i] = null;
        }
        var SigmaPowerX, SigmaX;
        for (j = 0, i = i + num - 1; i < datanum; ++i) {
            SigmaPowerX = SigmaX = 0;
            for (j = 0; j < num && j <= i; ++j) {
                SigmaPowerX += data[i - j] * data[i - j];
                SigmaX += data[i - j];
            }
            result[i] = (num * SigmaPowerX - SigmaX * SigmaX) / num * (num - 1);
        }

        return result;
    };

    //VARP 总体样本方差
    //VARP(X,N)  返回总体样本方差 。
    this.VARP = function (data, n) {
        var result = [];
        if (typeof n != 'number') n = parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num < 1 || num >= datanum) return result;
        var i = 0,
            j = 0;
        for (i = 0; i < datanum && !this.IsNumber(data[i]); ++i) {
            result[i] = null;
        }
        var SigmaPowerX = 0,
            SigmaX = 0;
        for (; i < datanum && j < num; ++i, ++j) {
            SigmaPowerX += data[i] * data[i];
            SigmaX += data[i];
        }
        if (j == num) result[i - 1] = (num * SigmaPowerX - SigmaX * SigmaX) / (num * num);
        for (; i < datanum; ++i) {
            SigmaPowerX += data[i] * data[i] - data[i - num] * data[i - num];
            SigmaX += data[i] - data[i - num];
            result[i] = (num * SigmaPowerX - SigmaX * SigmaX) / (num * num);
        }

        return result;
    };

    //RANGE(A,B,C)表示A>B AND A<C;
    this.RANGE = function (data, range, range2) {
        var isNumber = typeof data == 'number';
        var isNumber2 = typeof range == 'number';
        var isNumber3 = typeof range2 == 'number';

        if (isNumber && isNumber2 && isNumber3) {
            if (data > Math.min(range, range2) && data < Math.max(range, range2)) return 1;else return 0;
        }

        var result = [];
        var value = void 0,
            rangeValue = void 0,
            rangValue2 = void 0;
        for (var _i51 = 0; _i51 < data.length; ++_i51) {
            result[_i51] = null;
            value = data[_i51];
            if (!this.IsNumber(value)) continue;

            if (!isNumber2) {
                if (_i51 >= range.length) continue;

                rangeValue = range[_i51];
            } else {
                rangeValue = range;
            }
            if (!this.IsNumber(rangeValue)) continue;

            if (!isNumber3) {
                if (_i51 >= range2.length) continue;

                rangeValue2 = range2[_i51];
            } else {
                rangeValue2 = range2;
            }
            if (!this.IsNumber(rangeValue2)) continue;

            result[_i51] = value > Math.min(rangeValue, rangeValue2) && value < Math.max(rangeValue, rangeValue2) ? 1 : 0;
        }

        return result;
    };

    this.EXIST = function (data, n) {
        n = parseInt(n);

        if (typeof data == 'number') return 0;

        var latestID = null; //最新满足条件的数据索引
        var result = [];
        var value;
        for (var _i52 = 0; _i52 < data.length; ++_i52) {
            result[_i52] = null;
            value = data[_i52];
            if (this.IsNumber(value) && value > 0) latestID = _i52;

            if (latestID != null && _i52 - latestID < n) result[_i52] = 1;else result[_i52] = 0;
        }

        return result;
    };

    this.TFILTER = function (data, data2, n) {
        n = parseInt(n);

        var result = [];

        var isNumber = typeof data == 'number';
        var isNumber2 = typeof range == 'number';

        var count = Math.max(data.length, data2.length);
        for (var _i53 = 0; _i53 < count; ++_i53) {}

        return result;
    };

    /*
    过滤连续出现的信号.
    用法:FILTER(X,N):X满足条件后,将其后N周期内的数据置为0,N为常量.
    例如:
    FILTER(CLOSE>OPEN,5)查找阳线,5天内再次出现的阳线不被记录在内
    */
    this.FILTER = function (data, n) {
        var result = [];
        for (var _i54 = 0, _j3 = 0; _i54 < data.length; ++_i54) {
            if (data[_i54]) {
                result[_i54] = 1;
                for (_j3 = 0; _j3 < n && _j3 + _i54 + 1 < data.length; ++_j3) {
                    result[_j3 + _i54 + 1] = 0;
                }
                _i54 += n;
            } else {
                result[_i54] = 0;
            }
        }

        return result;
    };

    this.BARSLAST = function (data) {
        var result = [];
        if (!data) return result;

        var day = null;
        for (var _i55 = 0; _i55 < data.length; ++_i55) {
            result[_i55] = null;

            if (data[_i55] > 0) day = 0;else if (day != null) ++day;

            if (day != null) result[_i55] = day;
        }

        return result;
    };

    /*
    N周期内第一个条件成立到当前的周期数.
    用法:
    BARSSINCEN(X,N):N周期内第一次X不为0到现在的天数,N为常量
    例如:
    BARSSINCEN(HIGH>10,10)表示10个周期内股价超过10元时到当前的周期数
    */
    this.BARSSINCEN = function (data, n) {
        var result = [];
        var day = null;
        for (var _i56 = 0; _i56 < data.length; ++_i56) {
            result[_i56] = null;
            if (day == null) {
                if (data[_i56]) day = 0;
            } else {
                if (data[_i56]) {
                    if (day + 1 < n) ++day;
                } else {
                    day = null;
                }
            }

            if (day) result[_i56] = day;
        }

        return result;
    };

    /*
    第一个条件成立到当前的周期数.
    用法:
    BARSSINCE(X):第一次X不为0到现在的天数
    例如:
    BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数
    */
    this.BARSSINCE = function (data) {
        var result = [];
        var day = null;

        for (var _i57 = 0; _i57 < data.length; ++_i57) {
            result[_i57] = null;
            if (day == null) {
                if (data[_i57]) day = 0;
            } else {
                ++day;
            }

            if (day) result[_i57] = day;
        }

        return result;
    };

    /*三角函数调用 func 三角函数 
    反正切值. 用法: ATAN(X)返回X的反正切值
    反余弦值. 用法: ACOS(X)返回X的反余弦值
    反正弦值. 用法: ASIN(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 (var _i58 in data) {
                var item = data[_i58];
                if (this.IsNumber(item)) result[_i58] = func(item);else result[_i58] = null;
            }

            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 (var _i59 = 0, _j4 = 0; _i59 < data.length; ++_i59) {
            result[_i59] = 0;
            day = 0;
            var start = _i59 - n;
            var end = _i59 - n2;
            if (start < 0 || end < 0) continue;

            for (_j4 = start; _j4 < data.length && _j4 <= end; ++_j4, ++day) {
                if (!data[_j4]) break;
            }

            if (day == end - start + 1) //[start,end]
                result[_i59] = 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;
            }
        }

        var bFirstPoint = false;
        var bSecondPont = false;
        var firstData = {},
            secondData = {},
            thridData = {};
        var lastData = {};
        for (var _i60 in data) {
            result[_i60] = null;
            var item = data[_i60];
            if (!this.IsNumber(item)) continue;

            if (bFirstPoint == false) {
                bFirstPoint = true;
                firstData = { ID: parseInt(_i60), Value: item }; //第1个点
            } else if (bFirstPoint == true && bSecondPont == false) {
                var temp = (item - firstData.Value) / firstData.Value * 100;
                if (temp > n) {
                    secondData = { ID: parseInt(_i60), Value: item, Up: true };
                    lastData = { ID: parseInt(_i60), Value: item };
                    bSecondPont = true;
                } else if (temp < -n) {
                    secondData = { ID: parseInt(_i60), Value: item, Up: false };
                    lastData = { ID: parseInt(_i60), Value: item };
                    bSecondPont = true;
                }
            } else if (bFirstPoint == true && bSecondPont == true) {
                var temp = (item - lastData.Value) / lastData.Value * 100;
                if (secondData.Up == true) //找下跌的点
                    {
                        if (temp < -n) {
                            thridData = { ID: parseInt(_i60), Value: item, Up: false };
                            this.CalculateZIGLine(firstData, secondData, thridData, data, result);
                            lastData = { ID: parseInt(_i60), Value: item };
                        } else {
                            if (item > lastData.Value) lastData = { ID: parseInt(_i60), Value: item };
                        }
                    } else {
                    if (temp > n) {
                        thridData = { ID: parseInt(_i60), Value: item, Up: true };
                        this.CalculateZIGLine(firstData, secondData, thridData, data, result);
                        lastData = { ID: parseInt(_i60), Value: item };
                    } else {
                        if (item < lastData.Value) lastData = { ID: parseInt(_i60), Value: item };
                    }
                }
            }
        }

        //计算最后1组数据
        thridData = { ID: data.length - 1, Value: data[data.length - 1], Up: !secondData.Up };
        this.CalculateZIGLine(firstData, secondData, thridData, data, result);

        return result;
    };

    this.JSDraw = null;
    this.CalculateZIGLine = function (firstData, secondData, thridData, data, result) {
        if (this.JSDraw == null) this.JSDraw = new JSDraw(this.ErrorHandler);
        var isUp = secondData.Up;
        var findData = firstData;
        if (isUp) {
            for (var i = firstData.ID + 1; i < thridData.ID; ++i) //查找最高点
            {
                var subItem = data[i];
                if (!this.IsNumber(subItem)) continue;
                if (findData.Value < subItem) findData = { ID: i, Value: subItem };
            }
        } else {
            for (var i = firstData.ID + 1; i < thridData.ID; ++i) //查找最低点
            {
                var subItem = data[i];
                if (!this.IsNumber(subItem)) continue;
                if (findData.Value > 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 i = 0,
            result = [];
        for (i = 0; i < zigData.length; ++i) {
            result[i] = null;
            if (this.IsNumber(zigData[i])) break;
        }

        var trough = [];
        var start = i,
            j = 0;
        for (; i < zigData.length; ++i) //第1个波谷
        {
            if (i + 1 < zigData.length && i - 1 >= 0 && zigData[i] < zigData[i - 1] && zigData[i] < zigData[i + 1]) //波谷
                {
                    trough[0] = i;
                    break;
                }
        }

        for (i += 1; i < zigData.length; ++i) {
            result[i] = null;
            if (i + 1 < zigData.length && i - 1 >= 0 && zigData[i] < zigData[i - 1] && zigData[i] < zigData[i + 1]) //波谷
                {
                    console.log('[TROUGHBARS] i', i, zigData[i]);
                    ++j;
                    trough[j] = i;
                    if (j + 1 == n2) {
                        result[i] = i - start;
                    } else if (j + 1 > n2) {
                        trough.shift(); //大于计算的波谷数,去掉第1个波谷
                        start = trough[0];
                        --j;
                        result[i] = i - start;
                    }
                } else {
                if (j + 1 === n2) result[i] = i - start;
            }
        }

        return result;
    };

    /*
    属于未来函数,前M个ZIG转向波峰到当前距离.
    用法:
    PEAKBARS(K,N,M)表示之字转向ZIG(K,N)的前M个波峰到当前的周期数,M必须大于等于1
    例如:
    PEAKBARS(0,5,1)表示%5开盘价ZIG转向的上一个波峰到当前的周期数
    */
    this.PEAKBARS = function (data, n, n2) {
        var zigData = this.ZIG(data, n); //计算ZIG
        var i = 0,
            result = [];
        for (i = 0; i < zigData.length; ++i) {
            result[i] = null;
            if (this.IsNumber(zigData[i])) break;
        }

        var trough = [];
        var start = i,
            j = 0;
        for (; i < zigData.length; ++i) //第1个波峰
        {
            if (i + 1 < zigData.length && i - 1 >= 0 && zigData[i] > zigData[i - 1] && zigData[i] > zigData[i + 1]) //波峰
                {
                    trough[0] = i;
                    break;
                }
        }

        for (i += 1; i < zigData.length; ++i) {
            result[i] = null;
            if (i + 1 < zigData.length && i - 1 >= 0 && zigData[i] > zigData[i - 1] && zigData[i] > zigData[i + 1]) //波峰
                {
                    console.log('[TROUGHBARS] i', i, zigData[i]);
                    ++j;
                    trough[j] = i;
                    if (j + 1 == n2) {
                        result[i] = i - start;
                    } else if (j + 1 > n2) {
                        trough.shift(); //大于计算的波谷数,去掉第1个波谷
                        start = trough[0];
                        --j;
                        result[i] = i - start;
                    }
                } else {
                if (j + 1 === n2) result[i] = i - start;
            }
        }

        return result;
    };

    /*
    一直存在.
    例如:
    EVERY(CLOSE>OPEN,N) 
    表示N日内一直阳线(N应大于0,小于总周期数,N支持变量)
    */
    this.EVERY = function (data, n) {
        var result = [];
        if (n < 1) return result;
        var i = 0;
        for (; i < data.length; ++i) {
            result[i] = null;
            if (this.IsNumber(data[i])) break;
        }

        var flag = 0;
        for (; i < data.length; ++i) {
            if (data[i]) flag += 1;else flag = 0;

            if (flag == n) {
                result[i] = 1;
                --flag;
            } else {
                result[i] = 0;
            }
        }
        return result;
    };

    /*
    成本分布情况.
    用法:
    COST(10),表示10%获利盘的价格是多少,即有10%的持仓量在该价格以下,其余90%在该价格以上,为套牢盘
    该函数仅对日线分析周期有效
    */
    this.COST = function (data) {
        var result = [];
        var exchangeID = 201;
        var exchangeData = this.SymbolData.GetFinanceCacheData(exchangeID); //换手率
        if (!exchangeData) return result;

        var isNumber = Array.isArray(data) ? false : true;
        var singleData = null;
        if (isNumber) singleData = parseFloat(data);
        var compareData = null;

        for (var _i61 = this.SymbolData.Data.Data.length - 1, _j5 = 0, k = 0; _i61 >= 0; --_i61) {
            result[_i61] = null;
            var chipData = this.CalculateChip(_i61, exchangeData, this.SymbolData.Data.Data, 1);
            if (chipData.Max == null || chipData.Min == null || chipData.Max <= 0 || chipData.Min <= 0) continue;

            var max = parseInt(chipData.Max * 100);
            var min = parseInt(chipData.Min * 100);

            if (singleData != null) {
                compareData = singleData;
            } else {
                if (_i61 >= data.length) continue;
                compareData = data[_i61];
            }

            var totalVol = 0,
                vol = 0;
            var aryMap = new Map();
            for (_j5 = _i61; _j5 >= 0; --_j5) {
                var item = chipData.Data[_j5];
                var start = parseInt(item.Low * 100);
                var end = parseInt(item.High * 100);
                if (end - start + 1 <= 0) continue;

                var iAverageVolume = item.Vol;
                iAverageVolume = iAverageVolume / (end - start + 1);
                if (iAverageVolume <= 0) continue;

                for (k = start; k <= end && k <= max; ++k) {
                    if (aryMap.has(k)) {
                        vol = aryMap.get(k);
                        aryMap.set(k, vol + iAverageVolume);
                    } else {
                        aryMap.set(k, iAverageVolume);
                    }
                }

                totalVol += item.Vol;
            }

            //计算获利盘
            vol = 0;
            var _iteratorNormalCompletion7 = true;
            var _didIteratorError7 = false;
            var _iteratorError7 = undefined;

            try {
                for (var _iterator7 = aryMap[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
                    var priceData = _step7.value;

                    vol += priceData[1];
                    result[_i61] = priceData[0] / 100;
                    if (vol / totalVol * 100 > compareData) break;
                }
            } catch (err) {
                _didIteratorError7 = true;
                _iteratorError7 = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion7 && _iterator7.return) {
                        _iterator7.return();
                    }
                } finally {
                    if (_didIteratorError7) {
                        throw _iteratorError7;
                    }
                }
            }
        }

        return result;
    };

    /*
    获利盘比例.
    用法:
    WINNER(CLOSE),表示以当前收市价卖出的获利盘比例,例如返回0.1表示10%获利盘;WINNER(10.5)表示10.5元价格的获利盘比例
    该函数仅对日线分析周期有效
    !!!!计算比较耗时间
    */
    this.WINNER = function (data) {
        var result = [];
        var exchangeID = 201;
        var exchangeData = this.SymbolData.GetFinanceCacheData(exchangeID); //换手率
        if (!exchangeData) return result;

        var isNumber = Array.isArray(data) ? false : true;
        var singleData = null;
        if (isNumber) singleData = parseInt(parseFloat(data) * 100);
        var compareData = null;

        for (var _i62 = this.SymbolData.Data.Data.length - 1, _j6 = 0, k = 0; _i62 >= 0; --_i62) {
            result[_i62] = null;
            var chipData = this.CalculateChip(_i62, exchangeData, this.SymbolData.Data.Data, 1);
            if (chipData.Max == null || chipData.Min == null || chipData.Max <= 0 || chipData.Min <= 0) continue;

            var max = parseInt(chipData.Max * 100);
            var min = parseInt(chipData.Min * 100);

            if (singleData != null) {
                compareData = singleData;
            } else {
                if (_i62 >= data.length) continue;
                compareData = parseInt(data[_i62] * 100);
            }

            var totalVol = 0,
                vol = 0;
            for (_j6 = _i62; _j6 >= 0; --_j6) {
                var item = chipData.Data[_j6];
                var start = parseInt(item.Low * 100);
                var end = parseInt(item.High * 100);
                if (end - start + 1 <= 0) continue;

                var iAverageVolume = item.Vol;
                iAverageVolume = iAverageVolume / (end - start + 1);
                if (iAverageVolume <= 0) continue;

                var profitVol = 0; //获利的成交量
                if (compareData > end) profitVol = item.Vol;else if (compareData < start) profitVol = 0;else profitVol = item.Vol * (compareData - start + 1) / (end - start + 1);

                vol += profitVol;
                totalVol += item.Vol;
            }

            if (totalVol > 0) result[_i62] = vol / totalVol;
        }

        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 (var _i63 = index; _i63 >= 0; --_i63) {
            var _item = {}; //Vol:量 High:最高 Low:最低
            var kData = hisData[_i63];
            if (_i63 == index) _item.Vol = kData.Vol * exchangeData[_i63];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[_i63] = _item;

            seed *= 1 - exchangeData[_i63] / 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 (var _i64 = 0; _i64 < data.length; ++_i64) {
            result[_i64] = 0;
            if (_i64 - 1 < 0) continue;
            if (!this.IsNumber(data[_i64]) || !this.IsNumber(data[_i64 - 1])) //无效数都不算连涨
                {
                    days = 0;
                    continue;
                }

            if (data[_i64] > data[_i64 - 1]) ++days;else days = 0;

            if (days == n) {
                result[_i64] = 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 (var _i65 = 0; _i65 < data.length; ++_i65) {
            result[_i65] = 0;
            if (_i65 - 1 < 0) continue;
            if (!this.IsNumber(data[_i65]) || !this.IsNumber(data[_i65 - 1])) //无效数都不算连涨
                {
                    days = 0;
                    continue;
                }

            if (data[_i65] < data[_i65 - 1]) ++days;else days = 0;

            if (days == n) {
                result[_i65] = 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 (var _i66 = 0; _i66 < count; ++_i66) {
                result[_i66] = 0;
                if (_i66 >= data.length || _i66 >= data2.length) continue;
                if (!this.IsNumber(data[_i66]) || !this.IsNumber(data2[_i66])) {
                    days = 0;
                    continue;
                }

                if (data[_i66] > data2[_i66]) ++days;else days = 0;

                if (days == n) {
                    result[_i66] = 1;
                    --days;
                }
            }
        } else if (Array.isArray(data) && !Array.isArray(data2)) {
            if (n >= data.length || !this.IsNumber(data2)) return;
            var days = 0;
            for (var _i67 in data) {
                result[_i67] = 0;
                if (!this.IsNumber(data[_i67])) {
                    days = 0;
                    continue;
                }

                if (data[_i67] > data2) ++days;else days = 0;

                if (days == n) {
                    result[_i67] = 1;
                    --days;
                }
            }
        } else if (!Array.isArray(data) && Array.isArray(data2)) {
            if (n >= data2.length || !this.IsNumber(data)) return;
            var days = 0;
            for (var _i68 in data2) {
                result[_i68] = 0;
                if (!this.IsNumber(data2[_i68])) {
                    days = 0;
                    continue;
                }

                if (data > data2[_i68]) ++days;else days = 0;

                if (days == n) {
                    result[_i68] = 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 (var _i69 = 0; _i69 < count; ++_i69) {
            result[_i69] = 0;
            if (_i69 - 1 < 0) continue;
            if (_i69 >= data.length || _i69 >= data2.length) continue;
            if (!this.IsNumber(data[_i69]) || !this.IsNumber(data2[_i69]) || !this.IsNumber(data[_i69 - 1]) || !this.IsNumber(data2[_i69 - 1])) continue;

            if (data[_i69] > data2[_i69] && data[_i69 - 1] < data2[_i69 - 1]) result[_i69] = 1;
        }

        for (var _i70 = 0, _j7 = 0; _i70 < count; ++_i70) {
            if (!result[_i70]) continue;

            for (_j7 = 1; _j7 <= n && _i70 - _j7 >= 0; ++_j7) {
                if (data[_i70 - _j7] >= data2[_i70 - _j7]) {
                    result[_i70] = 0;
                    break;
                }
            }
        }

        return result;
    };

    /*
    EXISTR(X,A,B):是否存在(前几日到前几日间).
    例如: EXISTR(CLOSE>OPEN,10,5) 
    表示从前10日内到前5日内存在着阳线
    若A为0,表示从第一天开始,B为0,表示到最后日止
    */
    this.EXISTR = function (data, n, n2) {
        var result = [];
        if (!Array.isArray(data)) return result;

        n = parseInt(n);
        n2 = parseInt(n2);
        if (n <= 0) n = data.length;
        if (n2 <= 0) n2 = 1;
        if (n2 > n) return result;

        var result = [];
        var value;
        for (var _i71 = 0, _j8 = 0; _i71 < data.length; ++_i71) {
            result[_i71] = null;
            if (_i71 - n < 0 || _i71 - n2 < 0) continue;

            result[_i71] = 0;
            for (_j8 = n; _j8 >= n2; --_j8) {
                var value = data[_i71 - _j8];
                if (this.IsNumber(value) && value) {
                    result[_i71] = 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 (var _i72 = 0, _j9 = 0; _i72 < count; ++_i72) {
            result[_i72] = null;

            if (_i72 >= data.length || _i72 >= data2.length || _i72 >= dataAverage.length || _i72 >= data2Average.length) continue;

            var average = dataAverage[_i72];
            var average2 = data2Average[_i72];

            var total = 0,
                total2 = 0,
                total3 = 0;
            for (_j9 = _i72 - n + 1; _j9 <= _i72; ++_j9) {
                total += (data[_j9] - average) * (data2[_j9] - average2); //∑[(Xi-Avg(X))(Yi-Avg(y))])
                total2 += Math.pow(data[_j9] - average, 2); //∑(Xi-Avg(X))^2
                total3 += Math.pow(data2[_j9] - average2, 2); //∑(Yi-Avg(Y))^2)
            }

            result[_i72] = total / n / (Math.sqrt(total2 / n) * Math.sqrt(total3 / n));
        }

        return result;
    };

    //计算数组n周期内的均值
    this.CalculateAverage = function (data, n) {
        var result = [];
        if (n < 1) return result;

        var total = 0;

        for (var i = 0; i < data.length; ++i) //去掉开始的无效数
        {
            if (this.IsNumber(data[i])) break;
        }

        for (; i < data.length && i < n; ++i) //计算第1个周期的数据
        {
            result[i] = null;
            var value = data[i];
            if (!this.IsNumber(value)) continue;
            total += value;
        }
        result[i - 1] = total / n;

        for (; i < data.length; ++i) //计算后面的周期数据
        {
            var value = data[i];
            var preValue = data[i - n]; //上一个周期的第1个数据
            if (!this.IsNumber(value)) value = 0;
            if (!this.IsNumber(preValue)) preValue = 0;

            total = total - preValue + value; //当前周期的数据 等于上一个周期数据 去掉上一个周期的第1个数据 加上这个周期的最后1个数据
            result[i] = total / n;
        }

        return result;
    };

    /*
    COVAR(X,Y,N) 返回X和Y的N周期的协方差
    */
    this.COVAR = function (data, data2, n) {
        var result = [];
        if (n < 1) n = 1;

        if (!Array.isArray(data) || !Array.isArray(data2)) return result;

        var dataAverage = this.CalculateAverage(data, n);
        var data2Average = this.CalculateAverage(data2, n);

        var count = Math.max(data.length, data2.length);

        var count = Math.max(data.length, data2.length);
        for (var _i73 = 0, _j10 = 0; _i73 < count; ++_i73) {
            result[_i73] = null;

            if (_i73 >= data.length || _i73 >= data2.length || _i73 >= dataAverage.length || _i73 >= data2Average.length) continue;

            var average = dataAverage[_i73];
            var average2 = data2Average[_i73];

            var total = 0;
            for (_j10 = _i73 - n + 1; _j10 <= _i73; ++_j10) {
                total += (data[_j10] - average) * (data2[_j10] - average2);
            }

            result[_i73] = 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 (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 (n < 1) n = data.length;

        var nMin = null; //最小值索引
        for (var i = 0; i < data.length; ++i) {
            result[i] = null;
            if (this.IsNumber(data[i])) {
                nMin = i;
                break;
            }
        }

        var j = 0;
        for (i = nMin + 1; i < data.length && j < n; ++i, ++j) //求第1个最大值
        {
            if (data[i] <= data[nMin]) nMin = i;
            if (n == data.length) result[i] = i - nMin;
        }

        for (; i < data.length; ++i) {
            if (i - nMin < n) {
                if (data[i] <= data[nMin]) nMin = i;
            } else {
                nMin = i - n + 1;
                for (j = nMin; j <= i; ++j) //计算区间最小值
                {
                    if (data[j] <= data[nMin]) nMin = j;
                }
            }

            result[i] = i - nMin;
        }

        return result;
    };

    /*
    β(Beta)系数
    BETA(N) 返回当前证券N周期收益与对应大盘指数收益相比的贝塔系数
    需要下载上证指数历史数据
    涨幅(X)=(现价-上一个交易日收盘价)/上一个交易日收盘价
    公式=股票和指数协方差/指数方差
    */
    this.BETA = function (n) {
        var result = [];
        var stockData = this.SymbolData.Data;
        var indexData = this.SymbolData.IndexData;
        if (n <= 0) n = 1;

        var stockProfit = []; //股票涨幅
        var indexProfit = []; //指数涨幅

        for (var _i74 = 0; _i74 < stockData.Data.length; ++_i74) {
            stockProfit[_i74] = 0;
            indexProfit[_i74] = 0;

            var stockItem = stockData.Data[_i74];
            var indexItem = indexData.Data[_i74];

            if (stockItem.Close > 0 && stockItem.YClose > 0) stockProfit[_i74] = (stockItem.Close - stockItem.YClose) / stockItem.YClose;
            if (indexItem.Close > 0 && indexItem.YClose > 0) indexProfit[_i74] = (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;
        }

        var 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;

        for (var i = 0; i < dataCount; ++i) //初始化0
        {
            result[i] = 0;
        }

        for (var pos = 0; pos < dataCount; ++pos) {
            if (this.IsNumber(condition[pos])) break;
        }
        if (pos == dataCount) return result;

        var num = Math.min(dataCount - pos, Math.max(n, 1));

        for (var i = dataCount - 1, j = 0; i >= 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;
    };

    //函数调用
    this.CallFunction = function (name, args, node, symbolData) {
        switch (name) {
            case 'MAX':
                return this.MAX(args[0], args[1]);
            case 'MIN':
                return this.MIN(args[0], args[1]);
            case 'REF':
                return this.REF(args[0], args[1]);
            case 'ABS':
                return this.ABS(args[0]);
            case 'MA':
                return this.MA(args[0], args[1]);
            case "EMA":
                return this.EMA(args[0], args[1]);
            case "SMA":
                return this.SMA(args[0], args[1], args[2]);
            case "DMA":
                return this.DMA(args[0], args[1]);
            case 'EXPMA':
                return this.EXPMA(args[0], args[1]);
            case 'EXPMEMA':
                return this.EXPMEMA(args[0], args[1]);
            case 'COUNT':
                return this.COUNT(args[0], args[1]);
            case 'LLV':
                return this.LLV(args[0], args[1]);
            case 'LLVBARS':
                return this.LLVBARS(args[0], args[1]);
            case 'HHV':
                return this.HHV(args[0], args[1]);
            case 'HHVBARS':
                return this.HHVBARS(args[0], args[1]);
            case 'MULAR':
                return this.MULAR(args[0], args[1]);
            case 'CROSS':
                return this.CROSS(args[0], args[1]);
            case 'LONGCROSS':
                return this.LONGCROSS(args[0], args[1], args[2]);
            case 'AVEDEV':
                return this.AVEDEV(args[0], args[1]);
            case 'STD':
                return this.STD(args[0], args[1]);
            case 'IF':
            case 'IFF':
                return this.IF(args[0], args[1], args[2]);
            case 'IFN':
                return this.IFN(args[0], args[1], args[2]);
            case 'NOT':
                return this.NOT(args[0]);
            case 'SUM':
                return this.SUM(args[0], args[1]);
            case 'RANGE':
                return this.RANGE(args[0], args[1], args[2]);
            case 'EXIST':
                return this.EXIST(args[0], args[1]);
            case 'EXISTR':
                return this.EXISTR(args[0], args[1], args[2]);
            case 'FILTER':
                return this.FILTER(args[0], args[1]);
            case 'TFILTER':
                return this.TFILTER(args[0], args[1], args[2]);
            case 'SLOPE':
                return this.SLOPE(args[0], args[1]);
            case 'BARSLAST':
                return this.BARSLAST(args[0]);
            case 'BARSCOUNT':
                return this.BARSCOUNT(args[0]);
            case 'BARSSINCEN':
                return this.BARSSINCEN(args[0], args[1]);
            case 'BARSSINCE':
                return this.BARSSINCE(args[0]);
            case 'LAST':
                return this.LAST(args[0], args[1], args[2]);
            case 'EVERY':
                return this.EVERY(args[0], args[1]);
            case 'DEVSQ':
                return this.DEVSQ(args[0], args[1]);
            case 'ZIG':
                return this.ZIG(args[0], args[1]);
            case 'TROUGHBARS':
                return this.TROUGHBARS(args[0], args[1], args[2]);
            case 'PEAKBARS':
                return this.PEAKBARS(args[0], args[1], args[2]);
            case 'COST':
                return this.COST(args[0]);
            case 'WINNER':
                return this.WINNER(args[0]);
            case 'FORCAST':
                return this.FORCAST(args[0], args[1]);
            case 'STDP':
                return this.STDP(args[0], args[1]);
            case 'VAR':
                return this.VAR(args[0], args[1]);
            case 'VARP':
                return this.VARP(args[0], args[1]);
            case 'UPNDAY':
                return this.UPNDAY(args[0], args[1]);
            case 'DOWNNDAY':
                return this.DOWNNDAY(args[0], args[1]);
            case 'NDAY':
                return this.NDAY(args[0], args[1], args[2]);
            case 'RELATE':
                return this.RELATE(args[0], args[1], args[2]);
            case 'COVAR':
                return this.COVAR(args[0], args[1], args[2]);
            case 'BETA':
                return this.BETA(args[0]);
            case 'BETA2':
                return this.BETA2(args[0], args[1], args[2]);
            case 'WMA':
                return this.WMA(args[0], args[1]);
            case 'MEMA':
                return this.MEMA(args[0], args[1]);
            case 'SUMBARS':
                return this.SUMBARS(args[0], args[1]);
            case 'REVERSE':
                return this.REVERSE(args[0]);
            case 'SAR':
                return this.SAR(args[0], args[1], args[2]);
            case 'SARTURN':
                return this.SARTURN(args[0], args[1], args[2]);
            case 'BACKSET':
                return this.BACKSET(args[0], args[1]);
            //三角函数
            case 'ATAN':
                return this.Trigonometric(args[0], Math.atan);
            case 'ACOS':
                return this.Trigonometric(args[0], Math.acos);
            case 'ASIN':
                return this.Trigonometric(args[0], Math.asin);
            case 'COS':
                return this.Trigonometric(args[0], Math.cos);
            case 'SIN':
                return this.Trigonometric(args[0], Math.sin);
            case 'TAN':
                return this.Trigonometric(args[0], Math.tan);
            case 'LN':
                return this.Trigonometric(args[0], Math.log);
            case 'LOG':
                return this.Trigonometric(args[0], Math.log10);
            case 'EXP':
                return this.Trigonometric(args[0], Math.exp);
            case 'SQRT':
                return this.Trigonometric(args[0], Math.sqrt);
            default:
                this.ThrowUnexpectedNode(node, '函数' + name + '不存在');
        }
    };

    this.ThrowUnexpectedNode = function (node, message) {
        var marker = node.Marker;
        var msg = message || "执行异常";

        return this.ErrorHandler.ThrowError(marker.Index, marker.Line, marker.Column, msg);
    };
}

//是否有是有效的数字
JSAlgorithm.prototype.IsNumber = function (value) {
    if (value == null) return false;
    if (isNaN(value)) return false;

    return true;
};

//是否有是有效的除数
JSAlgorithm.prototype.IsDivideNumber = function (value) {
    if (value == null) return false;
    if (isNaN(value)) return false;
    if (value == 0) return false;

    return true;
};

/*
   绘图函数 
*/
function JSDraw(errorHandler, symbolData) {
    this.ErrorHandler = errorHandler;
    this.SymbolData = symbolData;

    this.DRAWTEXT = function (condition, price, text) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWTEXT', Text: text };
        if (condition.length <= 0) return result;

        var IsNumber = typeof price == "number";

        for (var i in condition) {
            drawData[i] = null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber) {
                drawData[i] = price;
            } else {
                if (this.IsNumber(price[i])) drawData[i] = price[i];
            }
        }

        return result;
    };

    //direction 文字Y轴位置 0=middle 1=价格的顶部 2=价格的底部
    //offset 文字Y轴偏移
    this.SUPERDRAWTEXT = function (condition, price, text, direction, offset) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'SUPERDRAWTEXT', Text: text, YOffset: offset, Direction: direction, TextAlign: 'center' };
        if (condition.length <= 0) return result;

        var IsNumber = typeof price == "number";

        for (var i in condition) {
            drawData[i] = null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber) {
                drawData[i] = price;
            } else {
                if (this.IsNumber(price[i])) drawData[i] = price[i];
            }
        }

        return result;
    };

    /*
    STICKLINE 绘制柱线
    在图形上绘制柱线。
    用法: STICKLINE(COND,PRICE1,PRICE2,WIDTH,EMPTY),当COND条件满足时,在PRICE1和PRICE2位置之间画柱状线,宽度为WIDTH(10为标准间距),EMPTH不为0则画空心柱。
    例如: STICKLINE(CLOSE>OPEN,CLOSE,OPEN,0.8,1)表示画K线中阳线的空心柱体部分。
    */
    this.STICKLINE = function (condition, data, data2, width, type) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'STICKLINE', Width: width, Type: type };

        if (condition.length <= 0) return result;

        var IsNumber = typeof data == "number";
        var IsNumber2 = typeof data2 == "number";

        for (var i in condition) {
            drawData[i] = null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber && IsNumber2) {
                drawData[i] = { Value: data, Value2: data2 };
            } else if (IsNumber && !IsNumber2) {
                if (isNaN(data2[i])) continue;
                drawData[i] = { Value: data, Value2: data2[i] };
            } else if (!IsNumber && IsNumber2) {
                if (isNaN(data[i])) continue;
                drawData[i] = { Value: data[i], Value2: data2 };
            } else {
                if (isNaN(data[i]) || isNaN(data2[i])) continue;
                drawData[i] = { Value: data[i], Value2: data2[i] };
            }
        }

        return result;
    };

    /*
    DRAWLINE 绘制直线段
    在图形上绘制直线段。
    用法: DRAWLINE(COND1,PRICE1,COND2,PRICE2,EXPAND)
    当COND1条件满足时,在PRICE1位置画直线起点,当COND2条件满足时,在PRICE2位置画直线终点,EXPAND为延长类型。
    例如: DRAWLINE(HIGH>=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1) 表示在创20天新高与创20天新低之间画直线并且向右延长。
    */
    this.DRAWLINE = function (condition, data, condition2, data2, expand) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWLINE', Expand: expand };

        if (condition.length <= 0) return result;
        var count = Math.max(condition.length, condition2.length);

        var bFirstPoint = false;
        var bSecondPont = false;
        var lineCache = { Start: {}, End: {}, List: new Array() };

        for (var _i75 = 0; _i75 < count; ++_i75) {
            drawData[_i75] = null;
            if (_i75 < condition.length && _i75 < condition2.length) {
                if (bFirstPoint == false && bSecondPont == false) {
                    if (condition[_i75] == null || !condition[_i75]) continue;

                    bFirstPoint = true;
                    lineCache.Start = { ID: _i75, Value: data[_i75] }; //第1个点
                } else if (bFirstPoint == true && bSecondPont == false) {
                    var bCondition = condition[_i75] != null && condition[_i75]; //条件1
                    var bCondition2 = condition2[_i75] != null && condition2[_i75]; //条件2

                    if (!bCondition && !bCondition2) continue;

                    if (bCondition) {
                        lineCache.Start = { ID: _i75, Value: data[_i75] }; //移动第1个点
                    } else if (bCondition2) {
                        bSecondPont = true;
                        lineCache.End = { ID: _i75, Value: data2[_i75] }; //第2个点
                    }
                } else if (bFirstPoint == true && bSecondPont == true) //2个点都有了, 等待下一次的点出现
                    {
                        var bCondition = condition[_i75] != null && condition[_i75]; //条件1
                        var bCondition2 = condition2[_i75] != null && condition2[_i75]; //条件2

                        if (bCondition) {
                            var lineData = this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值

                            for (var _j11 in lineData) {
                                var _item2 = lineData[_j11];
                                drawData[_item2.ID] = _item2.Value;
                            }

                            bFirstPoint = bSecondPont = false;
                            lineCache = { Start: {}, End: {} };
                        } else if (bCondition2) {
                            lineCache.End = { ID: _i75, Value: data2[_i75] }; //移动第2个点
                        }
                    }
            }
        }

        if (bFirstPoint == true && bSecondPont == true) //最后一组数据
            {
                var _lineData = this.CalculateDrawLine(lineCache);
                for (var _j12 in _lineData) {
                    var _item3 = _lineData[_j12];
                    drawData[_item3.ID] = _item3.Value;
                }
            }

        return result;
    };

    /*
    画出带状线.
    用法: DRAWBAND(VAL1,COLOR1,VAL2,COLOR2),当VAL1>VAL2时,在VAL1和VAL2之间填充COLOR1;当VAL1<VAL2时,填充COLOR2,这里的颜色均使用RGB函数计算得到.
    例如: DRAWBAND(OPEN,RGB(0,224,224),CLOSE,RGB(255,96,96));
    */
    this.DRAWBAND = function (data, color, data2, color2) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWBAND', Color: [color.toLowerCase(), color2.toLowerCase()] }; //颜色使用小写字符串
        var count = Math.max(data.length, data2.length);

        for (var _i76 = 0; _i76 < count; ++_i76) {
            var _item4 = { Value: null, Value2: null };
            if (_i76 < data.length) _item4.Value = data[_i76];
            if (_i76 < data2.length) _item4.Value2 = data2[_i76];

            drawData.push(_item4);
        }

        return result;
    };

    this.DRAWKLINE = function (high, open, low, close) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWKLINE' };
        var count = Math.max(high.length, open.length, low.length, close.length);

        for (var _i77 = 0; _i77 < count; ++_i77) {
            var _item5 = { Open: null, High: null, Low: null, Close: null };

            if (_i77 < high.length && _i77 < open.length && _i77 < low.length && _i77 < close.length) {
                _item5.Open = open[_i77];
                _item5.High = high[_i77];
                _item5.Low = low[_i77];
                _item5.Close = close[_i77];
            }

            drawData[_i77] = _item5;
        }

        return result;
    };

    //满足条件画一根K线
    this.DRAWKLINE_IF = function (condition, high, open, low, close) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWKLINE_IF' };
        var count = Math.max(condition.length, high.length, open.length, low.length, close.length);

        for (var _i78 = 0; _i78 < count; ++_i78) {
            var _item6 = { Open: null, High: null, Low: null, Close: null };

            if (_i78 < high.length && _i78 < open.length && _i78 < low.length && _i78 < close.length && _i78 < condition.length) {
                if (condition[_i78]) {
                    _item6.Open = open[_i78];
                    _item6.High = high[_i78];
                    _item6.Low = low[_i78];
                    _item6.Close = close[_i78];
                }
            }

            drawData[_i78] = _item6;
        }

        return result;
    };

    /*
    PLOYLINE 折线段
    在图形上绘制折线段。
    用法: PLOYLINE(COND,PRICE),当COND条件满足时,以PRICE位置为顶点画折线连接。
    例如: PLOYLINE(HIGH>=HHV(HIGH,20),HIGH)表示在创20天新高点之间画折线。
    */
    this.POLYLINE = function (condition, data) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'POLYLINE' };
        var isNumber = typeof data == 'number';

        var bFirstPoint = false;
        var bSecondPont = false;
        if (isNumber) {
            for (var _i79 in condition) {
                drawData[_i79] = null;
                if (bFirstPoint == false) {
                    if (!condition[_i79]) continue;

                    drawData[_i79] = data;
                    bFirstPoint = true;
                } else {
                    drawData[_i79] = data;
                }
            }
        } else {
            var lineCache = { Start: {}, End: {}, List: new Array() };
            for (var _i80 in condition) {
                drawData[_i80] = null;
                if (bFirstPoint == false && bSecondPont == false) {
                    if (condition[_i80] == null || !condition[_i80]) continue;
                    if (_i80 >= data.length || !this.IsNumber(data[_i80])) continue;

                    bFirstPoint = true;
                    lineCache.Start = { ID: parseInt(_i80), Value: data[_i80] }; //第1个点
                } else if (bFirstPoint == true && bSecondPont == false) {
                    if (condition[_i80] == null || !condition[_i80]) continue;
                    if (_i80 >= data.length || !this.IsNumber(data[_i80])) continue;

                    lineCache.End = { ID: parseInt(_i80), Value: data[_i80] }; //第2个点
                    //根据起始点和结束点 计算中间各个点的数据
                    var lineData = this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值

                    for (var _j13 in lineData) {
                        var _item7 = lineData[_j13];
                        drawData[_item7.ID] = _item7.Value;
                    }

                    var 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) {
        var drawData = { Value: new Array(), Text: new Array() };
        var result = { DrawData: drawData, DrawType: 'DRAWNUMBER' };
        var isNumber = typeof data2 == 'number';
        var text = void 0;
        if (isNumber) text = data2.toFixed(2);

        for (var _i81 in condition) {
            drawData.Value[_i81] = null;
            if (!condition[_i81]) continue;
            if (_i81 >= data.length || !this.IsNumber(data[_i81])) continue;

            if (isNumber) {
                drawData.Value[_i81] = data[_i81];
                drawData.Text[_i81] = text;
            } else {
                if (_i81 >= data2.length || !data2[_i81]) continue;
                drawData.Value[_i81] = data[_i81];
                if (typeof data2[_i81] == 'number') drawData.Text[_i81] = data2[_i81].toFixed(2);else drawData.Text[_i81] = data2[_i81].toString();
            }
        }

        return result;
    };

    /*
    在图形上绘制小图标.
    用法:
    DRAWICON(COND,PRICE,TYPE),当COND条件满足时,在PRICE位置画TYPE号图标(TYPE为1--41).
    例如:
    DRAWICON(CLOSE>OPEN,LOW,1)表示当收阳时在最低价位置画1号图标.
    */
    this.DRAWICON = function (condition, data, type) {
        var icon = void 0;
        if (g_JSComplierResource.DrawIcon && g_JSComplierResource.DrawIcon.Data && g_JSComplierResource.DrawIcon.Data.has(type)) {
            var iconfont = g_JSComplierResource.DrawIcon.Data.get(type);
            icon = { Symbol: iconfont.Text, Color: iconfont.Color, Family: g_JSComplierResource.DrawIcon.Family, IconFont: true, ID: type };
        }

        if (!icon) {
            //图标对应的字符代码
            var 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)' }]]);

            icon = mapIcon.get(type);
        }

        if (!icon) icon = { Symbol: '🚩' };
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWICON', Icon: icon };
        if (condition.length <= 0) return result;

        var IsNumber = typeof data == "number";
        if (typeof condition == 'number') {
            if (!condition) return result;

            for (var i = 0; i < this.SymbolData.Data.Data.length; ++i) {
                if (IsNumber) {
                    drawData[i] = data;
                } else {
                    if (i < data.length && this.IsNumber(data[i])) drawData[i] = data[i];else drawData[i] = null;
                }
            }
            return result;
        }

        for (var i in condition) {
            drawData[i] = null;

            if (!condition[i]) continue;

            if (IsNumber) {
                drawData[i] = data;
            } else {
                if (this.IsNumber(data[i])) drawData[i] = data[i];
            }
        }

        return result;
    };

    /*
    绘制通道
    condition:条件
    data,data2:通道顶部和底部
    borderColor: 通道顶部和底部线段颜色RGB(24,30,40) 不填就不画
    borderWidth: 通道顶部和底部线段宽度
    areaColor: 通道面积颜色 RGB(200,30,44) 不填使用默认颜色
    dotted: 通道顶部和底部虚线设置 '3,4' , 不填默认 3,3
    */
    this.DRAWCHANNEL = function (condition, data, data2, borderColor, borderWidth, dotted, areaColor) {
        var drawData = [];
        var result = { DrawData: drawData, DrawType: 'DRAWCHANNEL', Border: {} };
        if (condition.length <= 0) return result;

        if (borderColor) result.Border.Color = borderColor;
        if (areaColor) result.AreaColor = areaColor;
        if (borderWidth > 0) result.Border.Width = borderWidth;
        if (dotted) {
            var ary = dotted.split(',');
            result.Border.Dotted = [];
            for (var i in ary) {
                var item = ary[i];
                if (!item) continue;
                var value = parseInt(ary[i]);
                if (value <= 0) continue;
                result.Border.Dotted.push(value);
            }
            if (result.Border.Dotted.length <= 0) result.Border.Dotted = null;
        }

        var IsNumber = typeof data == "number";
        var IsNumber2 = typeof data2 == "number";
        if (typeof condition == 'number') {
            if (!condition) return result; //条件是否

            for (var i = 0; i < this.SymbolData.Data.Data.length; ++i) {
                drawData[i] = null;

                if (IsNumber && IsNumber2) {
                    drawData[i] = { Value: data, Value2: data2 };
                } else if (IsNumber && !IsNumber2) {
                    if (isNaN(data2[i])) continue;
                    drawData[i] = { Value: data, Value2: data2[i] };
                } else if (!IsNumber && IsNumber2) {
                    if (isNaN(data[i])) continue;
                    drawData[i] = { Value: data[i], Value2: data2 };
                } else {
                    if (isNaN(data[i]) || isNaN(data2[i])) continue;
                    drawData[i] = { Value: data[i], Value2: data2[i] };
                }
            }
        } else {
            for (var i = 0; i < condition.length; ++i) {
                drawData[i] = null;

                if (isNaN(condition[i]) || !condition[i]) continue;

                if (IsNumber && IsNumber2) {
                    drawData[i] = { Value: data, Value2: data2 };
                } else if (IsNumber && !IsNumber2) {
                    if (isNaN(data2[i])) continue;
                    drawData[i] = { Value: data, Value2: data2[i] };
                } else if (!IsNumber && IsNumber2) {
                    if (isNaN(data[i])) continue;
                    drawData[i] = { Value: data[i], Value2: data2 };
                } else {
                    if (isNaN(data[i]) || isNaN(data2[i])) continue;
                    drawData[i] = { Value: data[i], Value2: data2[i] };
                }
            }
        }

        return result;
    };
}

JSDraw.prototype.CalculateDrawLine = function (lineCache) {
    lineCache.List = [];
    for (var _i82 = lineCache.Start.ID; _i82 <= lineCache.End.ID; ++_i82) {
        lineCache.List.push(_i82);
    }var height = Math.abs(lineCache.Start.Value - lineCache.End.Value);
    var width = lineCache.List.length - 1;

    var result = [];
    result.push({ ID: lineCache.Start.ID, Value: lineCache.Start.Value }); //第1个点

    if (lineCache.Start.Value > lineCache.End.Value) {
        for (var _i83 = 1; _i83 < lineCache.List.length - 1; ++_i83) {
            var value = height * (lineCache.List.length - 1 - _i83) / width + lineCache.End.Value;
            result.push({ ID: lineCache.List[_i83], Value: value });
        }
    } else {
        for (var _i84 = 1; _i84 < lineCache.List.length - 1; ++_i84) {
            var value = height * _i84 / width + lineCache.Start.Value;
            result.push({ ID: lineCache.List[_i84], Value: value });
        }
    }

    result.push({ ID: lineCache.End.ID, Value: lineCache.End.Value }); //最后一个点

    return result;
};

//是否有是有效的数字
JSDraw.prototype.IsNumber = function (value) {
    if (value == null) return false;
    if (isNaN(value)) return false;

    return true;
};

JSDraw.prototype.IsDrawFunction = function (name) {
    var setFunctionName = new Set(["STICKLINE", "DRAWTEXT", 'SUPERDRAWTEXT', 'DRAWLINE', 'DRAWBAND', 'DRAWKLINE', 'DRAWKLINE_IF', 'PLOYLINE', 'POLYLINE', 'DRAWNUMBER', 'DRAWICON', 'DRAWCHANNEL']);
    if (setFunctionName.has(name)) return true;

    return false;
};

//http://www.newone.com.cn/helpcontroller/index?code=zszy_pc
var DYNAINFO_ARGUMENT_ID = {
    YCLOSE: 3,
    OPEN: 4,
    HIGH: 5,
    LOW: 6,
    CLOSE: 7,
    VOL: 8,
    AMOUNT: 10,
    AMPLITUDE: 13, //振幅
    INCREASE: 14, //涨幅
    EXCHANGERATE: 37 //换手率
};

function JSSymbolData(ast, option, jsExecute) {
    this.AST = ast; //语法树
    this.Execute = jsExecute;

    this.Symbol = '600000.sh';
    this.Name;
    this.Data = null; //个股数据
    this.SourceData = null; //不复权的个股数据
    this.MarketValue = null; //总市值
    this.Period = 0; //周期
    this.Right = 0; //复权
    this.DataType = 0; //默认K线数据 2=分钟走势图数据 3=多日分钟走势图
    this.IsBeforeData = false; //当日走势图数据是否包含开盘前数据

    this.KLineApiUrl = g_JSComplierResource.Domain + "/API/KLine2"; //日线
    this.MinuteKLineApiUrl = g_JSComplierResource.Domain + '/API/KLine3'; //分钟K线
    this.RealtimeApiUrl = g_JSComplierResource.Domain + '/API/stock'; //实时行情
    this.StockHistoryDayApiUrl = g_JSComplierResource.Domain + '/API/StockHistoryDay'; //历史财务数据
    this.StockHistoryDay3ApiUrl = g_JSComplierResource.Domain + '/API/StockHistoryDay3'; //历史财务数据
    this.StockNewsAnalysisApiUrl = g_JSComplierResource.CacheDomain + '/cache/newsanalyze'; //新闻分析数据
    this.HKToSHSZApiUrl = //北上资金
    [g_JSComplierResource.CacheDomain + '/cache/historyday/all/hk2shsz.json', //日线数据
    g_JSComplierResource.CacheDomain + '/cache/analyze/hk2shsz/hk2shsz.json', //最新分钟
    g_JSComplierResource.Domain + '/API/HKToSHSZMinuteData' //多日分时分钟
    ];

    this.MaxRequestDataCount = 1000;
    this.MaxRequestMinuteDayCount = 5;

    this.LatestData; //最新行情
    this.IndexData; //大盘指数
    this.FinanceData = new Map(); //财务数据
    this.MarginData = new Map(); //融资融券
    this.HKToSHSZData = new Map(); //北上资金
    this.NewsAnalysisData = new Map(); //新闻统计
    this.ExtendData = new Map(); //其他的扩展数据


    //使用option初始化
    if (option) {
        if (option.HQDataType) this.DataType = option.HQDataType;
        if (option.Data) {
            this.Data = option.Data;
            if (this.DataType != HQ_DATA_TYPE.MINUTE_ID && this.DataType != HQ_DATA_TYPE.MULTIDAY_MINUTE_ID && this.DataType != HQ_DATA_TYPE.HISTORY_MINUTE_ID) //分钟走势图数据 没有周期和复权
                {
                    this.Period = option.Data.Period; //周期
                    this.Right = option.Data.Right; //复权
                }
            //this.Data=null;
        }

        if (option.SourceData) this.SourceData = option.SourceData;
        if (option.Symbol) this.Symbol = option.Symbol;
        if (option.Name) this.Name = option.Name;
        if (option.MaxReqeustDataCount > 0) this.MaxRequestDataCount = option.MaxReqeustDataCount;
        if (option.MaxRequestMinuteDayCount > 0) this.MaxRequestMinuteDayCount = option.MaxRequestMinuteDayCount;
        if (option.KLineApiUrl) this.KLineApiUrl = option.KLineApiUrl;
        if (option.Right) this.Right = option.Right;
        if (option.Period) this.Period = option.Period;
        if (option.IsBeforeData === true) this.IsBeforeData = option.IsBeforeData;
    }

    this.RecvError = function (request) {
        console.log('[JSSymbolData::RecvError] ajax error.', request.status);
        throw { FunctionName: 'RecvError', Request: request };
    };

    //最新行情
    this.GetLatestData = function () {
        if (this.LatestData) return this.Execute.RunNextJob();

        var self = this;
        JSNetwork.HttpReqeust({
            url: self.RealtimeApiUrl,
            data: {
                "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", "amount", "date", "time", "increase", "exchangerate", "amplitude"],
                "symbol": [this.Symbol]
            },
            type: "post",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvLatestData(recvData);
                self.Execute.RunNextJob();
            },
            error: function error(request) {
                self.RecvError(request);
            }
        });
    };

    this.RecvLatestData = function (data) {
        if (!data.stock || data.stock.length != 1) return;

        var stock = data.stock[0];
        this.LatestData = { Symbol: stock.symbol, Name: stock.name, Date: stock.date, Time: stock.time,
            YClose: stock.yclose, Price: stock.price, Open: stock.open, High: stock.high, Low: stock.low, Vol: stock.vol, Amount: stock.amount,
            Increase: stock.increase, Exchangerate: stock.exchangerate, Amplitude: stock.amplitude };

        console.log('[JSSymbolData::RecvLatestData]', this.LatestData);
    };

    this.GetLatestCacheData = function (dataname) {
        if (!this.LatestData) return null;

        switch (dataname) {
            case DYNAINFO_ARGUMENT_ID.YCLOSE:
                return this.LatestData.YClose;
            case DYNAINFO_ARGUMENT_ID.OPEN:
                return this.LatestData.Open;
            case DYNAINFO_ARGUMENT_ID.HIGH:
                return this.LatestData.High;
            case DYNAINFO_ARGUMENT_ID.LOW:
                return this.LatestData.Low;
            case DYNAINFO_ARGUMENT_ID.VOL:
                return this.LatestData.Vol;
            case DYNAINFO_ARGUMENT_ID.AMOUNT:
                return this.LatestData.Amount;
            case DYNAINFO_ARGUMENT_ID.INCREASE:
                return this.LatestData.Increase;
            case DYNAINFO_ARGUMENT_ID.EXCHANGERATE:
                return this.LatestData.Exchangerate;
            case DYNAINFO_ARGUMENT_ID.AMPLITUDE:
                return this.LatestData.Amplitude;
            case DYNAINFO_ARGUMENT_ID.CLOSE:
                return this.LatestData.Price;
            default:
                return null;
        }
    };

    this.GetVolRateData = function (job, node) {
        var volrKey = job.ID.toString() + '-VolRate-' + this.Symbol;
        if (this.ExtendData.has(volrKey)) return this.Execute.RunNextJob();

        var self = this;
        $.ajax({
            url: self.RealtimeApiUrl,
            data: {
                "field": ["name", "symbol", "avgvol5", 'date'],
                "symbol": [this.Symbol]
            },
            type: "post",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvVolRateData(recvData, volrKey);
                self.Execute.RunNextJob();
            },
            error: function error(request) {
                self.RecvError(request);
            }
        });
    };

    this.RecvVolRateData = function (data, key) {
        if (!data.stock || data.stock.length != 1) return;
        var avgVol5 = data.stock[0].avgvol5;
        var date = data.stock[0].date;
        var item = { AvgVol5: avgVol5, Date: date };
        this.ExtendData.set(key, item);

        console.log('[JSSymbolData::RecvVolRateData]', item);
    };

    this.GetVolRateCacheData = function (node) {
        var key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA.toString() + '-VolRate-' + this.Symbol;
        if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node, '不支持VOLR');

        var result = [];
        var value = this.ExtendData.get(key);
        var avgVol5 = value.AvgVol5 / 241;
        var totalVol = 0;
        //5日成交总量只取了最新一天的,历史的暂时没有取,所以数据计算的时候只计算最新的一天, 其他都空
        for (var i = 0, j = 0; i < this.Data.Data.length; ++i) {
            result[i] = null;
            var item = this.Data.Data[i];
            var dateTime = item.DateTime; //日期加时间
            if (!dateTime) continue;
            var aryValue = dateTime.split(' ');
            if (aryValue.length != 2) continue;
            var date = parseInt(aryValue[0]);
            if (date != value.Date) continue;

            totalVol += item.Vol;
            if (avgVol5 > 0) result[i] = totalVol / (j + 1) / avgVol5 * 100;
            ++j;
        }

        return result;
    };

    //获取大盘指数数据
    this.GetIndexData = function () {
        if (this.IndexData) return this.Execute.RunNextJob();

        var self = this;
        if (this.Period <= 3) //请求日线数据
            {
                JSNetwork.HttpReqeust({
                    url: self.KLineApiUrl,
                    data: {
                        "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", 'up', 'down', 'stop', 'unchanged'],
                        "symbol": '000001.sh',
                        "start": -1,
                        "count": self.MaxRequestDataCount + 500 //多请求2年的数据 确保股票剔除停牌日期以后可以对上
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(recvData) {
                        self.RecvIndexHistroyData(recvData);
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            } else //请求分钟数据
            {
                JSNetwork.HttpReqeust({
                    url: self.MinuteKLineApiUrl,
                    data: {
                        "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"],
                        "symbol": '000001.sh',
                        "start": -1,
                        "count": self.MaxRequestMinuteDayCount + 5
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(data) {
                        self.RecvIndexMinuteHistroyData(data);
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            }
    };

    this.RecvIndexHistroyData = function (data) {
        console.log('[JSSymbolData::RecvIndexHistroyData] recv data', data);

        var hisData = this.JsonDataToHistoryData(data);
        this.IndexData = new ChartData();
        this.IndexData.DataType = 0; /*日线数据 */
        this.IndexData.Data = hisData;

        var aryOverlayData = this.SourceData.GetOverlayData(this.IndexData.Data); //和主图数据拟合以后的数据
        this.IndexData.Data = aryOverlayData;

        if (this.Period > 0 && this.Period <= 3) //周期数据
            {
                var periodData = this.IndexData.GetPeriodData(this.Period);
                this.IndexData.Data = periodData;
            }
    };

    this.RecvIndexMinuteHistroyData = function (data) {
        console.log('[JSSymbolData::RecvIndexMinuteHistroyData] recv data', data);

        var hisData = this.JsonDataToMinuteHistoryData(data);
        this.IndexData = new ChartData();
        this.IndexData.DataType = 1; /*分钟线数据 */
        this.IndexData.Data = hisData;

        if (this.Period >= 5) //周期数据
            {
                var 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;
        if (this.DataType === HQ_DATA_TYPE.MINUTE_ID || this.DataType === HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) //走势图数据
            {
                var apiUrl = g_JSComplierResource.CacheDomain + '/cache/analyze/increaseanalyze/' + symbol + '.json';
                console.log('[JSSymbolData::GetIndexIncreaseData] minute Get url=', apiUrl);
                $.ajax({
                    url: apiUrl,
                    type: "get",
                    dataType: "json",
                    async: true,
                    success: function success(data) {
                        self.RecvMinuteIncreaseData(data, { UpKey: upKey, DownKey: downKey });
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            } else if (this.DataType === HQ_DATA_TYPE.KLINE_ID && this.Period === 0) //K线图 日线
            {
                console.log('[JSSymbolData::GetIndexIncreaseData] K day Get url=', self.KLineApiUrl);
                $.ajax({
                    url: self.KLineApiUrl,
                    data: {
                        "symbol": symbol,
                        "start": -1,
                        "count": self.MaxRequestDataCount
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(data) {
                        self.RecvHistoryIncreaseData(data, { UpKey: upKey, DownKey: downKey });
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            }
    };

    this.RecvHistoryIncreaseData = function (data, key) {
        console.log('[JSSymbolData::RecvHistoryIncreaseData] recv data', data);

        var upData = [],
            downData = [];
        for (var i in data.data) {
            var item = data.data[i];
            var upItem = new SingleData();
            var downItem = new SingleData();
            upItem.Date = item[0];
            upItem.Value = item[8];
            upData[i] = upItem;
            downItem.Date = item[0];
            downItem.Value = item[9];
            downData[i] = downItem;
        }

        var aryFixedData = this.Data.GetFittingData(upData);
        var bindData = new ChartData();
        bindData.Data = aryFixedData;
        bindData.Period = this.Period; //只支持日线
        var data = bindData.GetValue();
        this.ExtendData.set(key.UpKey, data);

        aryFixedData = this.Data.GetFittingData(downData);
        bindData = new ChartData();
        bindData.Data = aryFixedData;
        bindData.Period = this.Period; //只支持日线
        data = bindData.GetValue();
        this.ExtendData.set(key.DownKey, data);
    };

    this.RecvMinuteIncreaseData = function (data, key) {
        console.log('[JSSymbolData::RecvMinuteIncreaseData] recv data', data);
        if (!data.minute) return;
        var minuteData = data.minute;
        if (!minuteData.time || !minuteData.up || !minuteData.down) return;
        var upData = [],
            downData = [];

        if (this.IsBeforeData) {
            for (var i = 0, j = 0; i < this.Data.Data.length; ++i) {
                upData[i] = null;
                downData[i] = null;
                var item = this.Data.Data[i];
                if (item.Before) continue; //盘前数据
                var dateTime = item.DateTime; //日期加时间
                if (!dateTime) continue;
                var aryValue = dateTime.split(' ');
                if (aryValue.length != 2) continue;
                var date = parseInt(aryValue[0]);
                if (date != data.date) continue;

                upData[i] = minuteData.up[j];
                downData[i] = minuteData.down[j];
                ++j;
            }
        } else {
            for (var i = 0, j = 0; i < this.Data.Data.length; ++i) {
                upData[i] = null;
                downData[i] = null;
                var item = this.Data.Data[i];
                var dateTime = item.DateTime; //日期加时间
                if (!dateTime) continue;
                var aryValue = dateTime.split(' ');
                if (aryValue.length != 2) continue;
                var date = parseInt(aryValue[0]);
                if (date != data.date) continue;

                upData[i] = minuteData.up[j];
                downData[i] = minuteData.down[j];
                ++j;
            }
        }

        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();

        var self = this;

        if (this.DataType === HQ_DATA_TYPE.MINUTE_ID) //当天分钟数据
            {
                JSNetwork.HttpReqeust({
                    url: self.RealtimeApiUrl,
                    data: {
                        "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", "amount", "date", "minute", "time", "minutecount"],
                        "symbol": [self.Symbol],
                        "start": -1
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(recvData) {
                        self.RecvMinuteData(recvData);
                        self.Execute.RunNextJob();
                    }
                });
                return;
            }

        if (this.Period <= 3) //请求日线数据
            {
                JSNetwork.HttpReqeust({
                    url: self.KLineApiUrl,
                    data: {
                        "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"],
                        "symbol": self.Symbol,
                        "start": -1,
                        "count": self.MaxRequestDataCount
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(recvData) {
                        self.RecvHistroyData(recvData);
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            } else //请求分钟数据
            {
                JSNetwork.HttpReqeust({
                    url: this.MinuteKLineApiUrl,
                    data: {
                        "field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol"],
                        "symbol": self.Symbol,
                        "start": -1,
                        "count": self.MaxRequestMinuteDayCount
                    },
                    type: "post",
                    dataType: "json",
                    async: true,
                    success: function success(data) {
                        self.RecvMinuteHistroyData(data);
                        self.Execute.RunNextJob();
                    },
                    error: function error(request) {
                        self.RecvError(request);
                    }
                });
            }
    };

    this.RecvHistroyData = function (data) {
        console.log('[JSSymbolData::RecvHistroyData] recv data', data);

        var hisData = this.JsonDataToHistoryData(data);
        this.Data = new ChartData();
        this.Data.DataType = 0; /*日线数据 */
        this.Data.Data = hisData;
        this.SourceData = new ChartData();
        this.SourceData.Data = hisData;

        if (this.Right > 0) //复权
            {
                var rightData = this.Data.GetRightDate(this.Right);
                this.Data.Data = rightData;
            }

        if (this.Period > 0 && this.Period <= 3) //周期数据
            {
                var periodData = this.Data.GetPeriodData(this.Period);
                this.Data.Data = periodData;
            }

        this.Data.Right = this.Right;
        this.Data.Period = this.Period;
        this.Name = data.name;
    };

    this.RecvMinuteHistroyData = function (data) {
        console.log('[JSSymbolData::RecvMinuteHistroyData] recv data', data);

        var hisData = this.JsonDataToMinuteHistoryData(data);
        this.Data = new ChartData();
        this.Data.DataType = 1; /*分钟线数据 */
        this.Data.Data = hisData;
        this.SourceData = new ChartData();
        this.SourceData.Data = hisData;

        if (this.Period >= 5) //周期数据
            {
                var periodData = this.Data.GetPeriodData(this.Period);
                this.Data.Data = periodData;
            }

        this.Data.Period = this.Period;
        this.Name = data.name;
    };

    //最新的分钟数据走势图
    this.RecvMinuteData = function (data) {
        console.log('[JSSymbolData::RecvMinuteData] recv data', data);

        var aryMinuteData = this.JsonDataToMinuteData(data);
        this.Data = new ChartData();
        this.Data.DataType = 2; /*分钟走势图数据 */
        this.Data.Data = aryMinuteData;

        this.Name = data.stock[0].name;
    };

    this.GetSymbolCacheData = function (dataName) {
        if (!this.Data) return new Array();

        switch (dataName) {
            case 'CLOSE':
            case 'C':
                return this.Data.GetClose();
            case 'VOL':
            case 'V':
                return this.Data.GetVol();
            case 'OPEN':
            case 'O':
                return this.Data.GetOpen();
            case 'HIGH':
            case 'H':
                return this.Data.GetHigh();
            case 'LOW':
            case 'L':
                return this.Data.GetLow();
            case 'AMOUNT':
                return this.Data.GetAmount();
        }
    };

    this.GetCurrBarsCount = function () {
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return new Array();

        var lCount = this.Data.Data.length;
        var result = [];
        for (var _i85 = lCount - 1; _i85 >= 0; --_i85) {
            result.push(_i85);
        }return result;
    };

    //财务函数
    this.GetFinanceCacheData = function (id, node) {
        var jobID = JS_EXECUTE_JOB_ID.GetFinnanceJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持FINANCE(' + id + ')');
        if (this.FinanceData.has(jobID)) return this.FinanceData.get(jobID);

        return [];
    };

    this.GetCompanyReleaseDate = function (jobID) {
        if (this.FinanceData.has(jobID)) return this.Execute.RunNextJob();

        var self = this;
        $.ajax({
            url: self.RealtimeApiUrl,
            data: {
                "field": ["name", "symbol", "company.releasedate"], //公司上司日期
                "symbol": [this.Symbol]
            },
            type: "post",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvCompanyReleaseDate(jobID, recvData);
                self.Execute.RunNextJob();
            },
            error: function error(request) {
                self.RecvError(request);
            }
        });
    };

    this.RecvCompanyReleaseDate = function (jobID, data) {
        if (!data.stock || data.stock.length != 1) return;
        var value = data.stock[0].company.releasedate;
        var releaseDate = new Date(value / 10000, value % 10000 / 100, value % 100);

        var aryData = [];
        for (var _i86 in this.Data.Data) {
            value = this.Data.Data[_i86].Date;
            var _date = new Date(value / 10000, value % 10000 / 100, value % 100);
            var _item8 = new SingleData();
            _item8.Date = value;
            _item8.Value = parseInt((_date - releaseDate) / (1000 * 60 * 60 * 24));
            aryData[_i86] = _item8;
        }

        console.log('[JSSymbolData::RecvCompanyReleaseDate] jobID=', jobID, aryData);

        var bindData = new ChartData();
        bindData.Data = aryData;
        bindData.Period = this.Period; //周期

        if (bindData.Period > 0) //周期数据
            {
                var periodData = bindData.GetPeriodSingleData(bindData.Period);
                bindData.Data = periodData;
            }

        var stockData = bindData.GetValue();
        this.FinanceData.set(jobID, stockData);
    };

    //下载财务数据
    this.GetFinanceData = function (jobID) {
        if (this.FinanceData.has(jobID)) return this.Execute.RunNextJob();

        console.log('[JSSymbolData::GetFinanceData] jobID=', jobID);
        var self = this;
        var fieldList = ["name", "date", "symbol"];

        switch (jobID) {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA: //流通股本(万股)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA: //流通股本(手)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA: //流通市值
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
                //换手率
                fieldList.push("capital.a");
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA: //总股本(万股)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
                //总市值
                fieldList.push('capital.total');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
                fieldList.push('finance.peruprofit');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
                fieldList.push('finance.pernetasset');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
                fieldList.push('finance.percreserve');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
                fieldList.push('finance.persearning');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
                fieldList.push('finance.nprofit');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
                fieldList.push('finance.alratio');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
                fieldList.push('finance.profityoy');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
                //过去4个季度现金分红总额
                fieldList.push('execdividend.quarter4');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                fieldList.push('shareholder'); //股东信息
                break;
        }

        //请求数据
        $.ajax({
            url: this.StockHistoryDayApiUrl,
            data: {
                "field": fieldList,
                "symbol": [this.Symbol],
                "orderfield": "date"
            },
            type: "post",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvFinanceData(recvData, jobID);
                self.Execute.RunNextJob();
            }
        });
    };

    this.RecvFinanceData = function (recvData, jobID) {
        switch (jobID) {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                return this.RecvStockDayData(recvData, jobID);
        }
    };

    this.RecvStockDayData = function (recvData, jobID) {
        //console.log(recvData);
        if (!recvData.stock || recvData.stock.length != 1) return;

        var stock = recvData.stock[0];
        var bFinanceData = false; //是否是定期的财务数据
        var bMarketValue = false; //是否计算市值
        var aryData = new Array();
        for (var _i87 in stock.stockday) {
            var item = stock.stockday[_i87];
            var indexData = new SingleData();
            indexData.Date = item.date;

            switch (jobID) {
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value = financeData.a / 100; //流通股本(手)
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.total)) continue;
                    indexData.Value = financeData.total / 10000; //总股本(万股)
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value = financeData.a / 10000; //流通股本(万股)
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:
                    //流通市值
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value = financeData.a; //流通股本
                    bMarketValue = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
                    //总市值
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.total)) continue;
                    indexData.Value = financeData.total; //总股本
                    bMarketValue = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
                    //换手率
                    var financeData = item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value = financeData.a; //流通股本
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.peruprofit)) continue;
                    indexData.Value = financeData.Finance.peruprofit; //每股未分配利润
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.pernetasset)) continue;
                    indexData.Value = financeData.Finance.pernetasset; //每股净资产
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.percreserve)) continue;
                    indexData.Value = financeData.Finance.percreserve; //每股资本公积金
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.persearning)) continue;
                    indexData.Value = financeData.Finance.persearning; //每股收益
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.persearning)) continue;
                    indexData.Value = financeData.Finance.persearning / financeData.Announcement.quarter * 4; //每股收益(折算为全年收益)  报告期每股收益/报告期*4
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.nprofit)) continue;
                    indexData.Value = financeData.Finance.nprofit; //净利润
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.alratio)) continue;
                    indexData.Value = financeData.Finance.alratio; //资产负债率
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
                    var financeData = this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.profityoy)) continue;
                    indexData.Value = financeData.Finance.profityoy; //净利润同比增长率
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
                    var financeData = item.execdividend;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.quarter4)) continue;
                    indexData.Value = financeData.quarter4; //过去4个季度现金分红总额
                    bFinanceData = true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                    //股东人数
                    var shareHolder = item.shareholder;
                    if (!shareHolder || !this.IsNumber(shareHolder.count)) continue;
                    indexData.Value = shareHolder.count;
                    bFinanceData = true;
                    break;
                default:
                    continue;
            }

            aryData.push(indexData);
        }

        var aryFixedData = void 0;
        if (bFinanceData) {
            if (this.SourceData) aryFixedData = this.SourceData.GetFittingFinanceData(aryData);else aryFixedData = this.Data.GetFittingFinanceData(aryData);
        } else if (bMarketValue) {
            if (this.SourceData) aryFixedData = this.SourceData.GetFittingMarketValueData(aryData); //总市值用不复权的价格计算
            else aryFixedData = this.Data.GetFittingMarketValueData(aryData);

            if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA) this.MarketValue = aryFixedData; //总市值保存下 算其他数据可能要用
        } else if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA) //股息率TTM:过去4个季度现金分红总额/总市值 * 100%
            {
                aryFixedData = this.CalculateDividendYield(aryData);
            } else {
            aryFixedData = this.SourceData.GetFittingData(aryData);
        }

        //console.log('[JSSymbolData::RecvStockDayData] jobID=' + jobID, aryFixedData)

        var bindData = new ChartData();
        bindData.Data = aryFixedData;
        bindData.Period = this.Period; //周期

        if (bindData.Period > 0) //周期数据
            {
                var periodData = bindData.GetPeriodSingleData(bindData.Period);
                bindData.Data = periodData;
            }

        if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA) //计算换手率 成交量/流通股本*100
            {
                for (var _i88 in this.Data.Data) {
                    if (!bindData.Data[_i88]) continue;

                    if (this.IsNumber(bindData.Data[_i88].Value) && bindData.Data[_i88].Value != 0) bindData.Data[_i88].Value = this.Data.Data[_i88].Vol / bindData.Data[_i88].Value * 100;else bindData.Data[_i88].Value = 1;
                }
            }

        var data = bindData.GetValue();
        this.FinanceData.set(jobID, data);
    };

    //计算股息率 股息率TTM:过去4个季度现金分红总额/总市值 * 100%
    this.CalculateDividendYield = function (cashData) {
        var dividendYield = [];
        if (!this.MarketValue) return dividendYield;

        for (var _i89 = 0, _j14 = 0; _i89 < this.Data.Data.length; ++_i89) {
            var day = this.Data.Data[_i89];
            var market = this.MarketValue[_i89];
            if (!day || !market) continue;

            var _item9 = new SingleData();
            _item9.Date = day.Date;
            _item9.Value = 0;

            if (_j14 + 1 < cashData.length) {
                if (cashData[_j14].Date < day.Date && cashData[_j14 + 1].Date <= day.Date) {
                    ++_j14;
                    --_i89;
                    continue;
                }
            }

            if (_j14 < cashData.length) {
                var cash = cashData[_j14];
                var endDate = cash.Date + 10000; //1年有效

                if (day.Date >= cash.Date && day.Date <= endDate && this.IsDivideNumber(market.Value) && this.IsNumber(cash.Value)) {
                    _item9.Value = cash.Value / market.Value * 100;
                }
            }

            dividendYield.push(_item9);
        }

        return dividendYield;
    };

    //融资融券函数
    this.GetMarginCacheData = function (id, node) {
        var jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持MARGIN(' + id + ')');
        if (this.MarginData.has(jobID)) return this.MarginData.get(jobID);

        return [];
    };

    //下融资融券
    this.GetMarginData = function (jobID) {
        if (this.MarginData.has(jobID)) return this.Execute.RunNextJob();

        console.log('[JSSymbolData::GetMarginData] jobID=', jobID);
        var self = this;
        var fieldList = ["name", "date", "symbol"];

        switch (jobID) {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
                //融资融券余额
                fieldList.push("margin.balance");
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
                //融资占比
                fieldList.push("margin.rate");
                break;

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET:
                //买入信息-融资净买入
                fieldList.push("margin.buy");
                break;

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:
                //卖出信息-融券净卖出
                fieldList.push("margin.sell");
                break;
        }

        //请求数据
        $.ajax({
            url: this.StockHistoryDayApiUrl,
            data: {
                "field": fieldList,
                "symbol": [this.Symbol],
                "orderfield": "date"
            },
            type: "post",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvMarginData(recvData, jobID);
                self.Execute.RunNextJob();
            }
        });
    };

    this.RecvMarginData = function (data, jobID) {
        console.log(data);
        if (!data.stock || data.stock.length != 1) return;

        var stock = data.stock[0];
        var aryData = new Array();
        var aryData2 = [],
            aryData3 = [],
            aryData4 = []; //其他3个数据
        for (var _i90 in stock.stockday) {
            var item = stock.stockday[_i90];
            var marginData = item.margin;
            if (!marginData) continue;

            var indexData = new SingleData();
            indexData.Date = item.date;

            switch (jobID) {
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
                    if (!this.IsNumber(marginData.balance)) continue;
                    indexData.Value = marginData.balance; //融资融券余额
                    aryData.push(indexData);
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
                    if (!this.IsNumber(marginData.rate)) continue;
                    indexData.Value = marginData.rate; //融资占比
                    aryData.push(indexData);
                    break;

                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET:
                    //买入信息-融资净买入
                    var buyData = marginData.buy;
                    if (!buyData) continue;
                    if (!this.IsNumber(buyData.balance) || !this.IsNumber(buyData.amount) || !this.IsNumber(buyData.repay) || !this.IsNumber(buyData.net)) continue;

                    indexData.Value = buyData.balance;
                    var indexData2 = new SingleData();
                    indexData2.Date = item.date;
                    indexData2.Value = buyData.amount;
                    var indexData3 = new SingleData();
                    indexData3.Date = item.date;
                    indexData3.Value = buyData.repay;
                    var indexData4 = new SingleData();
                    indexData4.Date = item.date;
                    indexData4.Value = buyData.net;

                    aryData.push(indexData);
                    aryData2.push(indexData2);
                    aryData3.push(indexData3);
                    aryData4.push(indexData4);
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:
                    //卖出信息-融券净卖出
                    var sellData = marginData.sell;
                    if (!sellData) continue;
                    if (!this.IsNumber(sellData.balance) || !this.IsNumber(sellData.volume) || !this.IsNumber(sellData.repay) || !this.IsNumber(sellData.net)) continue;

                    indexData.Value = buyData.balance;
                    var indexData2 = new SingleData();
                    indexData2.Date = item.date;
                    indexData2.Value = buyData.volume;
                    var indexData3 = new SingleData();
                    indexData3.Date = item.date;
                    indexData3.Value = buyData.repay;
                    var indexData4 = new SingleData();
                    indexData4.Date = item.date;
                    indexData4.Value = buyData.net;

                    aryData.push(indexData);
                    aryData2.push(indexData2);
                    aryData3.push(indexData3);
                    aryData4.push(indexData4);
                    break;
                default:
                    continue;
            }
        }

        var allData = [];
        if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE) {
            allData.push({ JobID: jobID, Data: aryData });
        } else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET) {
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE, Data: aryData });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT, Data: aryData2 });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY, Data: aryData3 });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET, Data: aryData4 });
        } else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET) {
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE, Data: aryData });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME, Data: aryData2 });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY, Data: aryData3 });
            allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET, Data: aryData4 });
        }

        for (var _i91 in allData) {
            var aryFixedData = this.SourceData.GetFittingData(allData[_i91].Data);

            var bindData = new ChartData();
            bindData.Data = aryFixedData;
            bindData.Period = this.Period; //周期

            if (bindData.Period > 0) //周期数据
                {
                    var periodData = bindData.GetPeriodSingleData(bindData.Period);
                    bindData.Data = periodData;
                }

            var _data = bindData.GetValue();
            this.MarginData.set(allData[_i91].JobID, _data);
        }
    };

    this.GetNewsAnalysisCacheData = function (id, node) {

        var 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.GetHKToSHSZData = function (jobID) {
        if (this.HKToSHSZData.has(jobID)) return this.Execute.RunNextJob();

        var url,
            dataType = 0;
        if (this.DataType === HQ_DATA_TYPE.MINUTE_ID) dataType = 1;else if (this.DataType == HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) dataType = 2;else if (this.Period <= 3) dataType = 0;

        url = this.HKToSHSZApiUrl[dataType];
        var self = this;
        console.log("[JSSymbolData::GetHKToSHSZData] jobID=" + jobID + ", url=" + url + ", dataType=" + dataType);

        if (dataType === 2) {
            //请求数据
            $.ajax({
                url: url,
                data: {
                    "symbol": this.Symbol,
                    'daycount': this.MaxRequestMinuteDayCount
                },
                type: "post",
                dataType: "json",
                async: true,
                success: function success(recvData) {
                    self.RecvMulitMinuteHKToSHSZData(recvData, jobID);
                    self.Execute.RunNextJob();
                }
            });
        } else {
            //请求数据
            $.ajax({
                url: url,
                type: "get",
                dataType: "json",
                async: true,
                success: function success(recvData) {
                    if (dataType == 0) self.RecvHKToSHSZData(recvData, jobID);else if (dataType == 1) self.RecvMinuteHKToSHSZData(recvData, jobID);
                    self.Execute.RunNextJob();
                }
            });
        }
    };

    this.RecvMinuteHKToSHSZData = function (data, jobID) {
        var arySHSZData = [],
            arySHData = [],
            arySZData = [];
        if (this.IsBeforeData) {
            for (i in this.SourceData.Data) {
                var item = this.SourceData.Data[i];
                if (item.Before) {
                    arySHSZData.push(null);
                    arySHData.push(null);
                    arySZData.push(null);
                } else {
                    break;
                }
            }

            for (var i = 0; i < data.time.length; ++i) {
                var time = data.time[i];
                if (time === 925) continue;

                var SHValue = data.hk2sh[i];
                var SZValue = data.hk2sz[i];
                var total = SHValue + SZValue;

                arySHSZData.push(total);
                arySHData.push(SHValue);
                arySZData.push(SZValue);
            }
        } else {
            for (var i = 0; i < data.time.length; ++i) {
                var time = data.time[i];
                var SHValue = data.hk2sh[i];
                var SZValue = data.hk2sz[i];
                var total = SHValue + SZValue;

                arySHSZData.push(total);
                arySHData.push(SHValue);
                arySZData.push(SZValue);
            }
        }

        var allData = [{ Data: arySHSZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ }, { Data: arySHData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH }, { Data: arySZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ }];

        for (var i in allData) {
            var item = allData[i];
            this.HKToSHSZData.set(item.JobID, item.Data);
        }
    };

    this.RecvHKToSHSZData = function (data, jobID) {
        var arySHSZData = [],
            arySHData = [],
            arySZData = [];
        for (var i = 0; i < data.date.length; ++i) {
            var date = data.date[i];
            var SHValue = data.hk2sh[i] * 1000000; //单位是百万
            var SZValue = data.hk2sz[i] * 1000000; //单位是百万
            var total = SHValue + SZValue;

            var itemSHSZData = new SingleData();
            itemSHSZData.Date = date;
            itemSHSZData.Value = total;

            var itemSHData = new SingleData();
            itemSHData.Date = date;
            itemSHData.Value = SHValue;

            var itemSZData = new SingleData();
            itemSZData.Date = date;
            itemSZData.Value = SZValue;

            arySHSZData.push(itemSHSZData);
            arySHData.push(itemSHData);
            arySZData.push(itemSZData);
        }

        var allData = [{ Data: arySHSZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ }, { Data: arySHData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH }, { Data: arySZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ }];

        for (var _i92 in allData) {
            var aryFixedData = this.Data.GetFittingData(allData[_i92].Data);

            var bindData = new ChartData();
            bindData.Data = aryFixedData;
            bindData.Period = this.Period; //周期

            if (bindData.Period > 0) //周期数据
                {
                    var periodData = bindData.GetPeriodSingleData(bindData.Period);
                    bindData.Data = periodData;
                }

            var _data2 = bindData.GetValue();
            this.HKToSHSZData.set(allData[_i92].JobID, _data2);
        }
    };

    this.RecvMulitMinuteHKToSHSZData = function (data, jobID) //多日分时图北上资金
    {
        if (!data.data || data.data.length <= 0) return;

        var arySHSZData = [],
            arySHData = [],
            arySZData = [];
        for (var i = 0, j = 0; i < this.Data.Data.length && j < data.data.length;) {
            arySHSZData[i] = null;
            arySHData[i] = null;
            arySZData[i] = null;
            var item = this.Data.Data[i];
            var dateTime = item.DateTime; //日期加时间
            if (!dateTime) {
                ++i;
                continue;
            }
            var aryValue = dateTime.split(' ');
            if (aryValue.length != 2) {
                ++i;
                continue;
            }
            var date = parseInt(aryValue[0]);
            var day = data.data[j];
            if (!day.minute || day.minute.length <= 0) {
                ++j;
                continue;
            }

            if (day.date > date) {
                ++i;
                continue;
            } else if (day.date < date) {
                ++j;
                continue;
            }

            for (var k in day.minute) {
                var timeItem = day.minute[k];

                var SHValue = timeItem[1];
                var SZValue = timeItem[2];
                var total = SHValue + SZValue;

                arySHSZData[i] = total;
                arySHData[i] = SHValue;
                arySZData[i] = SZValue;
                ++i;
            }

            ++j;
        }

        var allData = [{ Data: arySHSZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ }, { Data: arySHData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH }, { Data: arySZData, JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ }];

        for (var i in allData) {
            var item = allData[i];
            this.HKToSHSZData.set(item.JobID, item.Data);
        }
    };

    //北上资金函数
    this.GetHKToSHSZCacheData = function (id, node) {
        var jobID = JS_EXECUTE_JOB_ID.GetHK2SHSZJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持HK2SHSZ(' + id + ')');
        if (this.HKToSHSZData.has(jobID)) return this.HKToSHSZData.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']]);

        if (!mapFolder.has(jobID)) {
            this.Execute.RunNextJob();
            return;
        }
        var folderName = mapFolder.get(jobID);
        var url = this.StockNewsAnalysisApiUrl + '/' + folderName + '/' + this.Symbol + '.json';

        //请求数据
        $.ajax({
            url: url,
            type: "get",
            dataType: "json",
            async: true,
            success: function success(recvData) {
                self.RecvNewsAnalysisData(recvData, jobID);
                self.Execute.RunNextJob();
            },
            error: function error(request, textStatus) {
                self.RecvNewsAnalysisDataError(request, textStatus, jobID);
                self.Execute.RunNextJob();
            }
        });
    };

    this.RecvNewsAnalysisDataError = function (request, textStatus, jobID) {
        console.log('[JSSymbolData::RecvNewsAnalysisDataError] ajax error.', request.status);

        //没有新闻使用0数据填充
        var aryData = [];
        for (var i = 0; i < this.Data.Data.length; ++i) {
            var item = new SingleData();
            item.Date = this.Data.Data[i].Date;
            item.Value = 0;
            aryData.push(item);
        }

        var bindData = new ChartData();
        bindData.Data = aryData;
        this.NewsAnalysisData.set(jobID, bindData.GetValue());
    };

    this.RecvNewsAnalysisData = function (data, jobID) {
        if (!data.data || !data.date) return;
        if (data.data.length <= 0 || data.data.length != data.date.length) return;

        console.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 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 SingleData();
                    item.Date = data.date[i];
                    item.Value = data.data2[i];
                    if (this.IsNumber(item.Value)) aryData2.push(item);
                }
            }

            var aryFixedData = this.Data.GetFittingData2(aryData, 0);
            var bindData = new 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 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 SingleData();
                item.Date = data.date[i];
                item.Value = data.data[i];
                aryData.push(item);
            }

            var _aryFixedData = this.Data.GetFittingData2(aryData, 0);
            var bindData = new ChartData();
            bindData.Data = _aryFixedData;
            this.NewsAnalysisData.set(jobID, bindData.GetValue());
        }
    };

    this.JsonDataToHistoryData = function (data) {
        var list = data.data;
        var aryDayData = new Array();
        var date = 0,
            yclose = 1,
            open = 2,
            high = 3,
            low = 4,
            close = 5,
            vol = 6,
            amount = 7;
        var up = 8,
            down = 9,
            stop = 10,
            unchanged = 11;
        for (var i = 0; i < list.length; ++i) {
            var item = new HistoryData();

            item.Date = list[i][date];
            item.Open = list[i][open];
            item.YClose = list[i][yclose];
            item.Close = list[i][close];
            item.High = list[i][high];
            item.Low = list[i][low];
            item.Vol = list[i][vol]; //原始单位股
            item.Amount = list[i][amount];
            //上涨 下跌家数
            if (list[i].length > up) item.Up = list[i][up];
            if (list[i].length > down) item.Down = list[i][down];
            if (list[i].length > stop) item.Stop = list[i][stop];
            if (list[i].length > unchanged) item.Unchanged = list[i][unchanged];

            if (isNaN(item.Open) || item.Open <= 0) continue; //停牌的数据剔除

            aryDayData.push(item);
        }

        return aryDayData;
    };

    this.JsonDataToMinuteHistoryData = function (data) {
        var list = data.data;
        var aryDayData = new Array();
        var date = 0,
            yclose = 1,
            open = 2,
            high = 3,
            low = 4,
            close = 5,
            vol = 6,
            amount = 7,
            time = 8;
        for (var i = 0; i < list.length; ++i) {
            var _item10 = new HistoryData();

            _item10.Date = list[i][date];
            _item10.Open = list[i][open];
            _item10.YClose = list[i][yclose];
            _item10.Close = list[i][close];
            _item10.High = list[i][high];
            _item10.Low = list[i][low];
            _item10.Vol = list[i][vol]; //原始单位股
            _item10.Amount = list[i][amount];
            _item10.Time = list[i][time];

            // if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除
            aryDayData.push(_item10);
        }

        // 无效数据处理
        for (var _i93 = 0; _i93 < aryDayData.length; ++_i93) {
            var minData = aryDayData[_i93];
            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 (_i93 == 0) {
                    if (minData.YClose > 0) {
                        minData.Open = minData.YClose;
                        minData.High = minData.YClose;
                        minData.Low = minData.YClose;
                        minData.Close = minData.YClose;
                    }
                } else // 用前一个有效数据填充
                    {
                        for (var _j15 = _i93 - 1; _j15 >= 0; --_j15) {
                            var minData2 = aryDayData[_j15];
                            if (minData2 == null) coninue;
                            if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0) {
                                if (minData.YClose <= 0) minData.YClose = minData2.Close;
                                minData.Open = minData2.Open;
                                minData.High = minData2.High;
                                minData.Low = minData2.Low;
                                minData.Close = minData2.Close;
                                break;
                            }
                        }
                    }
            }
        }
        return aryDayData;
    };

    //API 返回数据 转化为array[]
    this.JsonDataToMinuteData = function (data) {
        var aryMinuteData = new Array();
        for (var i in data.stock[0].minute) {
            var jsData = data.stock[0].minute[i];
            var item = new MinuteData();

            item.Close = jsData.price;
            item.Open = jsData.open;
            item.High = jsData.high;
            item.Low = jsData.low;
            item.Vol = jsData.vol; //股
            item.Amount = jsData.amount;
            if (i == 0) //第1个数据 写死9:25
                item.DateTime = data.stock[0].date.toString() + " 0925";else item.DateTime = data.stock[0].date.toString() + " " + jsData.time.toString();
            item.Increase = jsData.increase;
            item.Risefall = jsData.risefall;
            item.AvPrice = jsData.avprice;

            aryMinuteData[i] = item;
        }

        return aryMinuteData;
    };

    //CODELIKE 模糊股票代码
    this.CODELIKE = function (value) {
        if (this.Symbol && this.Symbol.indexOf(value) == 0) return 1;
        return 0;
    };

    this.NAMELIKE = function (value) {
        if (this.Name && this.Name.indexOf(value) == 0) return 1;
        return 0;
    };

    /*
    SETCODE 市场类型
    0:深圳 1:上海,47:中金所期货 28:郑州商品 29:大连商品 30:上海商品,27:香港指数 31:香港主板,48:香港创业板... 
    */
    this.SETCODE = function () {
        if (this.Symbol.indexOf('.sh')) return 1;
        if (this.Symbol.indexOf('.sz')) return 0;

        return 0;
    };

    this.DATE = function () {
        var result = [];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for (var _i94 in this.Data.Data) {
            var item = this.Data.Data[_i94];
            result[_i94] = item.Date;
        }

        return result;
    };

    this.YEAR = function () {
        var result = [];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for (var _i95 in this.Data.Data) {
            var item = this.Data.Data[_i95];
            if (this.IsNumber(item.Date)) result[_i95] = parseInt(item.Date / 10000);else result[_i95] = null;
        }

        return result;
    };

    this.MONTH = function () {
        var result = [];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for (var _i96 in this.Data.Data) {
            var item = this.Data.Data[_i96];
            if (this.IsNumber(item.Date)) result[_i96] = parseInt(item.Date % 10000 / 100);else result[_i96] = null;
        }

        return result;
    };

    //星期 1-7
    this.WEEK = function () {
        var result = [];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        var tempDate = new Date();
        for (var _i97 in this.Data.Data) {
            var item = this.Data.Data[_i97];
            result[_i97] = null;
            if (!this.IsNumber(item.Date)) continue;

            var year = parseInt(item.Date / 10000);
            var month = parseInt(item.Date % 10000 / 100);
            var day = item.Date % 100;

            tempDate.setFullYear(year);
            tempDate.setMonth(month - 1);
            tempDate.setDate(day);

            result[_i97] = tempDate.getDay();
        }

        return result;
    };

    this.REFDATE = function (data, date) {
        var result = null;
        var index = null;
        for (var _i98 in this.Data.Data) //查找日期对应的索引
        {
            if (this.Data.Data[_i98].Date == date) {
                index = parseInt(_i98);
                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=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟
        var PERIOD_MAP = [5, 6, 7, 11, 0, 1, 2, 3, 4, 5];
        return PERIOD_MAP[this.Period];
    };
}

//是否有是有效的数字
JSSymbolData.prototype.IsNumber = function (value) {
    if (value == null) return false;
    if (isNaN(value)) return false;

    return true;
};

JSSymbolData.prototype.IsDivideNumber = function (value) {
    if (value == null) return false;
    if (isNaN(value)) return false;
    if (value == 0) return false;

    return true;
};

JSSymbolData.prototype.JsonDataToFinance = function (data) {
    var financeData;

    for (var _i99 = 1; _i99 <= 4; ++_i99) {
        switch (_i99) {
            case 1:
                var finance = data.finance1;
                var announcement = data.announcement1;
                break;
            case 2:
                var finance = data.finance2;
                var announcement = data.announcement2;
                break;
            case 3:
                var finance = data.finance3;
                var announcement = data.announcement3;
                break;
            case 4:
                var finance = data.finance4;
                var announcement = data.announcement4;
                break;
            default:
                break;
        }

        if (!finance || !announcement || !this.IsNumber(announcement.year) || !this.IsNumber(announcement.quarter)) continue;
        if (financeData) //如果存在1天公布多个报告期数据 只取最新的一个公告期数据
            {
                if (financeData.Announcement.year < announcement.year) financeData = { Date: data.date, Finance: finance, Announcement: announcement };
            } else {
            financeData = { Date: data.date, Finance: finance, Announcement: announcement };
        }
    }

    return financeData;
};

var JS_EXECUTE_DEBUG_LOG = false;

var JS_EXECUTE_JOB_ID = {
    JOB_DOWNLOAD_SYMBOL_DATA: 1, //下载股票的K线数据
    JOB_DOWNLOAD_INDEX_DATA: 2, //下载大盘的K线数据
    JOB_DOWNLOAD_SYMBOL_LATEST_DATA: 3, //最新的股票行情数据
    JOB_DOWNLOAD_INDEX_INCREASE_DATA: 4, //涨跌股票个数统计数据
    JOB_DOWNLOAD_VOLR_DATA: 5, //5日量比均量下载量比数据

    //财务函数
    JOB_DOWNLOAD_TOTAL_EQUITY_DATA: 100, //总股本(万股)
    JOB_DOWNLOAD_FLOW_EQUITY_DATA: 101, //流通股本(万股)
    JOB_DOWNLOAD_PER_U_PROFIT_DATA: 102, //每股未分配利润
    JOB_DOWNLOAD_PER_NETASSET_DATA: 103, //每股净资产
    JOB_DOWNLOAD_PER_C_RESERVE_DATA: 104, //每股资本公积金
    JOB_DOWNLOAD_PER_S_EARNING_DATA: 105, //每股收益 
    JOB_DOWNLOAD_PER_S_EARNING2_DATA: 106, //每股收益(折算为全年收益),对于沪深品种有效
    JOB_DOWNLOAD_RELEASE_DATE_DATA: 107, //上市的天数
    JOB_DOWNLOAD_N_PROFIT_DATA: 108, //净利润
    JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA: 109, //流通市值
    JOB_DOWNLOAD_MARKETVALUE_DATA: 110, //总市值
    JOB_DOWNLOAD_PROFIT_YOY_DATA: 111, //利润同比 (Profit year on year)
    JOB_DOWNLOAD_AL_RATIO_DATA: 112, //资产负债率 (asset-liability ratio)
    JOB_DOWNLOAD_DIVIDEND_YIELD_DATA: 113, //股息率
    JOB_DOWNLOAD_SHAREHOLDER_DATA: 114, //股东人数    


    JOB_DOWNLOAD_CAPITAL_DATA: 200, //流通股本(手)
    JOB_DOWNLOAD_EXCHANGE_DATA: 201, //换手率 成交量/流通股本*100


    JOB_DOWNLOAD_MARGIN_BALANCE: 1000, //融资融券余额
    JOB_DOWNLOAD_MARGIN_RATE: 1001, //融资占比

    JOB_DOWNLOAD_MARGIN_BUY_BALANCE: 1010, //买入信息-融资余额
    JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: 1011, //买入信息-买入额
    JOB_DOWNLOAD_MARGIN_BUY_REPAY: 1012, //买入信息-偿还额
    JOB_DOWNLOAD_MARGIN_BUY_NET: 1013, //买入信息-融资净买入

    JOB_DOWNLOAD_MARGIN_SELL_BALANCE: 1020, //卖出信息-融券余量
    JOB_DOWNLOAD_MARGIN_SELL_VOLUME: 1021, //卖出信息-卖出量
    JOB_DOWNLOAD_MARGIN_SELL_REPAY: 1022, //卖出信息-偿还量
    JOB_DOWNLOAD_MARGIN_SELL_NET: 1023, //卖出信息-融券净卖出

    JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: 2000, //负面新闻统计
    JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: 2001, //机构调研
    JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: 2002, //互动易
    JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: 2003, //股东增持
    JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: 2004, //股东减持
    JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: 2005, //信托持股
    JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: 2006, //大宗交易
    JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: 2007, //官网新闻
    JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: 2008, //高管要闻
    JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: 2009, //股权质押

    JOB_DOWNLOAD_HK_TO_SH: 2050, //北上流入上证
    JOB_DOWNLOAD_HK_TO_SZ: 2051, //北上流入深证
    JOB_DOWNLOAD_HK_TO_SH_SZ: 2052, //北上流总的


    JOB_RUN_SCRIPT: 10000, //执行脚本

    GetFinnanceJobID: function GetFinnanceJobID(value) {
        var dataMap = new Map([[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA], //FINANCE(1)   总股本(万股)
        [7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA], //FINANCE(7)   流通股本(万股)
        [9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA], //FINANCE(9)   资产负债率 (asset-liability ratio)
        [18, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA], //FINANCE(18)  每股公积金
        [30, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA], //FINANCE(30)  净利润
        [32, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA], //FINANCE(32)  每股未分配利润
        [33, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA], //FINANCE(33)  每股收益(折算为全年收益),对于沪深品种有效
        [34, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA], //FINANCE(34)  每股净资产
        [38, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA], //FINANCE(38)  每股收益(最近一期季报)
        [40, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA], //FINANCE(40)  流通市值 
        [41, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA], //FINANCE(41)  总市值
        [42, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_RELEASE_DATE_DATA], //FINANCE(42)  上市的天数
        [43, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA], //FINANCE(43)  利润同比 (Profit year on year)
        [45, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA], //FINANCE(45)  股息率

        [100, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA], //FINANCE(100) 股东人数

        [200, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA], //流通股本(手)
        [201, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA] //换手率 成交量/流通股本

        ]);

        if (dataMap.has(value)) return dataMap.get(value);

        return null;
    },

    //融资融券
    GetMarginJobID: function GetMarginJobID(value) {
        var dataMap = new Map([[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE], //MARGIN(1)   融资融券余额
        [2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE], //MARGIN(2)   融资占比

        [3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE], //MARGIN(3)   买入信息-融资余额
        [4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT], //MARGIN(4)   买入信息-买入额
        [5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY], //MARGIN(5)   买入信息-偿还额
        [6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET], //MARGIN(6)   买入信息-融资净买入

        [7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE], //MARGIN(7)   卖出信息-融券余量
        [8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME], //MARGIN(8)   卖出信息-卖出量
        [9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY], //MARGIN(9)   卖出信息-偿还量
        [10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET]]);

        if (dataMap.has(value)) return dataMap.get(value);

        return null;
    },

    //北上资金
    GetHK2SHSZJobID: function GetHK2SHSZJobID(value) {
        var dataMap = new Map([[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ], //HK2SHSZ(1)   北上流总的
        [2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH], //HK2SHSZ(2)   北上流入上证
        [3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ]]);

        if (dataMap.has(value)) return dataMap.get(value);

        return null;
    },

    GetNewsAnalysisID: function GetNewsAnalysisID(value) {
        var dataMap = new Map([[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE], //NEWS(1)   负面新闻统计
        [2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH], //NEWS(2)   机构调研统计
        [3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT], //NEWS(3)   互动易
        [4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE], //NEWS(4)   股东增持
        [5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2], //NEWS(5)   股东减持
        [6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER], //NEWS(6)   信托持股
        [7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING], //NEWS(7)   大宗交易
        [8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS], //NEWS(8)   官网新闻
        [9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS], //NEWS(9)   高管要闻
        [10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE]]);

        if (dataMap.has(value)) return dataMap.get(value);

        return null;
    }

};

function JSExecute(ast, option) {
    this.AST = ast; //语法树

    this.ErrorHandler = new ErrorHandler();
    this.VarTable = new Map(); //变量表
    this.OutVarTable = new Array(); //输出变量
    this.Arguments = [];
    this.ErrorCallback; //执行错误回调

    //脚本自动变量表, 只读
    this.ConstVarTable = new Map([
    //个股数据
    ['CLOSE', null], ['VOL', null], ['OPEN', null], ['HIGH', null], ['LOW', null], ['AMOUNT', null], ['C', null], ['V', null], ['O', null], ['H', null], ['L', null], ['VOLR', null],

    //日期类
    ['DATE', null], ['YEAR', null], ['MONTH', null], ['PERIOD', null], ['WEEK', null],

    //大盘数据
    ['INDEXA', null], ['INDEXC', null], ['INDEXH', null], ['INDEXL', null], ['INDEXO', null], ['INDEXV', null], ['INDEXADV', null], ['INDEXDEC', null],

    //到最后交易日的周期数
    ['CURRBARSCOUNT', null],
    //流通股本(手)
    ['CAPITAL', null],
    //换手率
    ['EXCHANGE', null], ['SETCODE', null]]);

    this.SymbolData = new JSSymbolData(this.AST, option, this);
    this.Algorithm = new JSAlgorithm(this.ErrorHandler, this.SymbolData);
    this.Draw = new JSDraw(this.ErrorHandler, this.SymbolData);

    this.JobList = []; //执行的任务队列

    this.UpdateUICallback = null; //回调
    this.CallbackParam = null;

    if (option) {
        if (option.Callback) this.UpdateUICallback = option.Callback;
        if (option.CallbackParam) this.CallbackParam = option.CallbackParam;
        if (option.Arguments) this.Arguments = option.Arguments;
    }

    this.Execute = function () {
        console.log('[JSExecute::Execute] JobList', this.JobList);
        this.RunNextJob();
    };

    this.RunNextJob = function () {
        if (this.JobList.length <= 0) return;

        var jobItem = this.JobList.shift();

        switch (jobItem.ID) {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA:
                return this.SymbolData.GetSymbolData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA:
                return this.SymbolData.GetIndexData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA:
                return this.SymbolData.GetIndexIncreaseData(jobItem);
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA:
                return this.SymbolData.GetLatestData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA:
                //量比
                return this.SymbolData.GetVolRateData(jobItem);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                return this.SymbolData.GetFinanceData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_RELEASE_DATE_DATA:
                return this.SymbolData.GetCompanyReleaseDate(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:
                //卖出信息-融券净卖出
                return this.SymbolData.GetMarginData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: //负面新闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: //机构调研
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: //互动易
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: //股东增持
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: //股东减持
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: //信托持股
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: //大宗交易
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: //官网新闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: //高管要闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE:
                //股权质押
                return this.SymbolData.GetNewsAnalysisData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ: //北上总的
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH: //北上上证
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ:
                //北上深证
                return this.SymbolData.GetHKToSHSZData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT:
                return this.Run();
        }
    };

    this.ReadSymbolData = function (name, node) {
        switch (name) {
            case 'CLOSE':
            case 'C':
            case 'VOL':
            case 'V':
            case 'OPEN':
            case 'O':
            case 'HIGH':
            case 'H':
            case 'LOW':
            case 'L':
            case 'AMOUNT':
                return this.SymbolData.GetSymbolCacheData(name);
            case 'VOLR':
                return this.SymbolData.GetVolRateCacheData(node);

            //大盘数据
            case 'INDEXA':
            case 'INDEXC':
            case 'INDEXH':
            case 'INDEXH':
            case 'INDEXO':
            case 'INDEXV':
            case 'INDEXL':
            case 'INDEXADV':
            case 'INDEXDEC':
                return this.SymbolData.GetIndexCacheData(name);
            case 'CURRBARSCOUNT':
                return this.SymbolData.GetCurrBarsCount();
            case 'CAPITAL':
                return this.SymbolData.GetFinanceCacheData(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA);
            case 'EXCHANGE':
                return this.SymbolData.GetFinanceCacheData(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
            case 'SETCODE':
                return this.SymbolData.SETCODE();

            case 'DATE':
                return this.SymbolData.DATE();
            case 'YEAR':
                return this.SymbolData.YEAR();
            case 'MONTH':
                return this.SymbolData.MONTH();
            case 'WEEK':
                return this.SymbolData.WEEK();
            case 'PERIOD':
                return this.SymbolData.PERIOD();
        }
    };

    //读取变量
    this.ReadVariable = function (name, node) {
        if (this.ConstVarTable.has(name)) {
            var data = this.ConstVarTable.get(name);

            if (data == null) //动态加载,用到再加载
                {
                    data = this.ReadSymbolData(name, node);
                    this.ConstVarTable.set(name, data);
                }

            return data;
        }

        if (this.VarTable.has(name)) return this.VarTable.get(name);

        this.ThrowUnexpectedNode(node, '变量' + name + '不存在');
        return null;
    };

    //单数据转成数组 个数和历史数据一致
    this.SingleDataToArrayData = function (value) {
        var count = this.SymbolData.Data.Data.length;
        var result = [];
        for (var _i100 = 0; _i100 < count; ++_i100) {
            result[_i100] = value;
        }

        return result;
    };

    this.RunAST = function () {
        //预定义的变量
        for (var _i101 in this.Arguments) {
            var _item11 = this.Arguments[_i101];
            this.VarTable.set(_item11.Name, _item11.Value);
        }

        if (!this.AST) this.ThrowError();
        if (!this.AST.Body) this.ThrowError();

        for (var _i102 in this.AST.Body) {
            var _item12 = this.AST.Body[_i102];
            this.VisitNode(_item12);

            //输出变量
            if (_item12.Type == Syntax.ExpressionStatement && _item12.Expression) {
                if (_item12.Expression.Type == Syntax.AssignmentExpression && _item12.Expression.Operator == ':' && _item12.Expression.Left) {
                    var assignmentItem = _item12.Expression;
                    var varName = assignmentItem.Left.Name;
                    var _outVar = this.VarTable.get(varName);
                    if (!Array.isArray(_outVar)) _outVar = this.SingleDataToArrayData(_outVar);

                    this.OutVarTable.push({ Name: varName, Data: _outVar, Type: 0 });
                } else if (_item12.Expression.Type == Syntax.CallExpression) {
                    var callItem = _item12.Expression;
                    if (this.Draw.IsDrawFunction(callItem.Callee.Name)) {
                        var draw = callItem.Draw;
                        draw.Name = callItem.Callee.Name;
                        this.OutVarTable.push({ Name: draw.Name, Draw: draw, Type: 1 });
                    }
                } else if (_item12.Expression.Type == Syntax.SequenceExpression) {
                    var _varName = void 0;
                    var _draw = void 0;
                    var color = void 0;
                    var lineWidth = void 0;
                    var colorStick = false;
                    var pointDot = false;
                    var circleDot = false;
                    var lineStick = false;
                    var stick = false;
                    var volStick = false;
                    var isShow = true;
                    var isExData = false;
                    for (var _j16 in _item12.Expression.Expression) {
                        var itemExpression = _item12.Expression.Expression[_j16];
                        if (itemExpression.Type == Syntax.AssignmentExpression && itemExpression.Operator == ':' && itemExpression.Left) {
                            _varName = itemExpression.Left.Name;
                            var varValue = this.VarTable.get(_varName);
                            if (!Array.isArray(varValue)) {
                                varValue = this.SingleDataToArrayData(varValue);
                                this.VarTable.set(_varName, varValue); //把常量放到变量表里
                            }
                        } else if (itemExpression.Type == Syntax.Identifier) {
                            var value = itemExpression.Name;
                            if (value === 'COLORSTICK') colorStick = true;else if (value === 'POINTDOT') pointDot = true;else if (value === 'CIRCLEDOT') circleDot = true;else if (value === 'LINESTICK') lineStick = true;else if (value === 'STICK') stick = true;else if (value === 'VOLSTICK') volStick = true;else if (value.indexOf('COLOR') == 0) color = value;else if (value.indexOf('LINETHICK') == 0) lineWidth = value;else if (value.indexOf('NODRAW') == 0) isShow = false;else if (value.indexOf('EXDATA') == 0) isExData = true; //扩展数据, 不显示再图形里面
                        } else if (itemExpression.Type == Syntax.Literal) //常量
                            {
                                var aryValue = this.SingleDataToArrayData(itemExpression.Value);
                                _varName = itemExpression.Value.toString();
                                this.VarTable.set(_varName, aryValue); //把常量放到变量表里
                            } else if (itemExpression.Type == Syntax.CallExpression && this.Draw.IsDrawFunction(itemExpression.Callee.Name)) {
                            _draw = itemExpression.Draw;
                            _draw.Name = itemExpression.Callee.Name;
                        }
                    }

                    if (pointDot && _varName) //圆点
                        {
                            var _outVar2 = this.VarTable.get(_varName);
                            if (!Array.isArray(_outVar2)) _outVar2 = this.SingleDataToArrayData(_outVar2);
                            var _value = { Name: _varName, Data: _outVar2, Radius: 2, Type: 3 };
                            if (color) _value.Color = color;
                            if (lineWidth) _value.LineWidth = lineWidth;
                            this.OutVarTable.push(_value);
                        } else if (circleDot && _varName) //圆点
                        {
                            var _outVar3 = this.VarTable.get(_varName);
                            if (!Array.isArray(_outVar3)) _outVar3 = this.SingleDataToArrayData(_outVar3);
                            var _value2 = { Name: _varName, Data: _outVar3, Radius: 1.3, Type: 3 };
                            if (color) _value2.Color = color;
                            if (lineWidth) _value2.LineWidth = lineWidth;
                            this.OutVarTable.push(_value2);
                        } else if (lineStick && _varName) //LINESTICK  同时画出柱状线和指标线
                        {
                            var _outVar4 = this.VarTable.get(_varName);
                            var _value3 = { Name: _varName, Data: _outVar4, Type: 4 };
                            if (color) _value3.Color = color;
                            if (lineWidth) _value3.LineWidth = lineWidth;
                            this.OutVarTable.push(_value3);
                        } else if (stick && _varName) //STICK 画柱状线
                        {
                            var _outVar5 = this.VarTable.get(_varName);
                            var _value4 = { Name: _varName, Data: _outVar5, Type: 5 };
                            if (color) _value4.Color = color;
                            if (lineWidth) _value4.LineWidth = lineWidth;
                            this.OutVarTable.push(_value4);
                        } else if (volStick && _varName) //VOLSTICK   画彩色柱状线
                        {
                            var _outVar6 = this.VarTable.get(_varName);
                            var _value5 = { Name: _varName, Data: _outVar6, Type: 6 };
                            if (color) _value5.Color = color;
                            this.OutVarTable.push(_value5);
                        } else if (_varName && color) {
                        var _outVar7 = this.VarTable.get(_varName);
                        if (!Array.isArray(_outVar7)) _outVar7 = this.SingleDataToArrayData(_outVar7);
                        var _value6 = { Name: _varName, Data: _outVar7, Color: color, Type: 0 };
                        if (lineWidth) _value6.LineWidth = lineWidth;
                        if (isShow == false) _value6.IsShow = false;
                        if (isExData == true) _value6.IsExData = true;
                        this.OutVarTable.push(_value6);
                    } else if (_draw && color) {
                        this.OutVarTable.push({ Name: _draw.Name, Draw: _draw, Color: color, Type: 1 });
                    } else if (colorStick && _varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子
                        {
                            var _outVar8 = this.VarTable.get(_varName);
                            var _value7 = { Name: _varName, Data: _outVar8, Color: color, Type: 2 };
                            this.OutVarTable.push(_value7);
                        } else if (_varName) {
                        var _outVar9 = this.VarTable.get(_varName);
                        var _value8 = { Name: _varName, Data: _outVar9, Type: 0 };
                        if (color) _value8.Color = color;
                        if (lineWidth) _value8.LineWidth = lineWidth;
                        if (isShow == false) _value8.IsShow = false;
                        if (isExData == true) _value8.IsExData = true;
                        this.OutVarTable.push(_value8);
                    }
                }
            }
        }

        console.log('[JSExecute::Run]', this.VarTable);

        return this.OutVarTable;
    };

    this.Run = function () {
        try {
            var data = this.RunAST(); //执行脚本
            console.log('[JSComplier.Run] execute finish', data);

            if (this.UpdateUICallback) {
                console.log('[JSComplier.Run] invoke UpdateUICallback.');
                if (this.CallbackParam && this.CallbackParam.Self && this.CallbackParam.Self.ClassName === 'ScriptIndexConsole') this.CallbackParam.JSExecute = this;
                this.UpdateUICallback(data, this.CallbackParam);
            }
        } catch (error) {
            console.log(error);
            if (this.ErrorCallback) this.ErrorCallback(error);
        }
    };

    this.VisitNode = function (node) {
        switch (node.Type) {
            case Syntax.SequenceExpression:
                this.VisitSequenceExpression(node);
                break;
            case Syntax.ExpressionStatement:
                this.VisitNode(node.Expression);
                break;
            case Syntax.AssignmentExpression:
                this.VisitAssignmentExpression(node);
                break;
            case Syntax.BinaryExpression:
            case Syntax.LogicalExpression:
                this.VisitBinaryExpression(node);
                break;
            case Syntax.CallExpression:
                this.VisitCallExpression(node);
                break;
        }
    };

    this.VisitSequenceExpression = function (node) {
        for (var _i103 in node.Expression) {
            var _item13 = node.Expression[_i103];
            this.VisitNode(_item13);
        }
    };

    //函数调用
    this.VisitCallExpression = function (node) {
        var funcName = node.Callee.Name;
        var args = [];
        for (var _i104 in node.Arguments) {
            var _item14 = node.Arguments[_i104];
            var value = void 0;
            if (_item14.Type == Syntax.BinaryExpression || _item14.Type == Syntax.LogicalExpression) value = this.VisitBinaryExpression(_item14);else if (_item14.Type == Syntax.CallExpression) value = this.VisitCallExpression(_item14);else value = this.GetNodeValue(_item14);
            args.push(value);
        }

        if (JS_EXECUTE_DEBUG_LOG) console.log('[JSExecute::VisitCallExpression]', funcName, '(', args.toString(), ')');

        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 'DRAWICON':
                node.Draw = this.Draw.DRAWICON(args[0], args[1], args[2]);
                node.Out = [];
                break;
            case 'DRAWLINE':
                node.Draw = this.Draw.DRAWLINE(args[0], args[1], args[2], args[3], args[4]);
                node.Out = node.Draw.DrawData;
                break;
            case 'DRAWBAND':
                node.Draw = this.Draw.DRAWBAND(args[0], args[1], args[2], args[3]);
                node.Out = [];
                break;
            case 'DRAWKLINE':
                node.Draw = this.Draw.DRAWKLINE(args[0], args[1], args[2], args[3]);
                node.Out = [];
                break;
            case 'DRAWKLINE_IF':
                node.Draw = this.Draw.DRAWKLINE_IF(args[0], args[1], args[2], args[3], args[4]);
                node.Out = [];
                break;
            case 'PLOYLINE':
            case 'POLYLINE':
                node.Draw = this.Draw.POLYLINE(args[0], args[1]);
                node.Out = node.Draw.DrawData;
                break;
            case 'DRAWNUMBER':
                node.Draw = this.Draw.DRAWNUMBER(args[0], args[1], args[2]);
                node.Out = node.Draw.DrawData.Value;
                break;
            case "DRAWCHANNEL":
                node.Draw = this.Draw.DRAWCHANNEL(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
                node.Out = [];
                break;
            case 'CODELIKE':
                node.Out = this.SymbolData.CODELIKE(args[0]);
                break;
            case 'NAMELIKE':
                node.Out = this.SymbolData.NAMELIKE(args[1]);
                break;
            case 'REFDATE':
                node.Out = this.SymbolData.REFDATE(args[0], args[1]);
                break;
            case 'FINANCE':
                node.Out = this.SymbolData.GetFinanceCacheData(args[0], node);
                break;
            case "MARGIN":
                node.Out = this.SymbolData.GetMarginCacheData(args[0], node);
                break;
            case "HK2SHSZ":
                node.Out = this.SymbolData.GetHKToSHSZCacheData(args[0], node);
                break;
            case "NEWS":
                node.Out = this.SymbolData.GetNewsAnalysisCacheData(args[0], node);
                break;
            case 'UPCOUNT':
            case 'DOWNCOUNT':
                node.Out = this.SymbolData.GetIndexIncreaseCacheData(funcName, args[0], node);
                break;
            default:
                node.Out = this.Algorithm.CallFunction(funcName, args, node);
                break;
        }

        return node.Out;
    };

    //赋值
    this.VisitAssignmentExpression = function (node) {
        var left = node.Left;
        if (left.Type != Syntax.Identifier) this.ThrowUnexpectedNode(node);

        var varName = left.Name;

        var right = node.Right;
        var value = null;
        if (right.Type == Syntax.BinaryExpression || right.Type == Syntax.LogicalExpression) value = this.VisitBinaryExpression(right);else if (right.Type == Syntax.CallExpression) value = this.VisitCallExpression(right);else if (right.Type == Syntax.Literal) value = right.Value;else if (right.Type == Syntax.Identifier) //右值是变量
            value = this.ReadVariable(right.Name, right);

        if (JS_EXECUTE_DEBUG_LOG) console.log('[JSExecute::VisitAssignmentExpression]', varName, ' = ', value);
        this.VarTable.set(varName, value);
    };

    //逻辑运算
    this.VisitBinaryExpression = function (node) {
        var stack = [];
        stack.push(node);
        var 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 {
                var value = stack.pop();
                if (value.Type == Syntax.BinaryExpression) //只遍历操作符就可以
                    {
                        var leftValue = this.GetNodeValue(value.Left);
                        var rightValue = this.GetNodeValue(value.Right);

                        if (JS_EXECUTE_DEBUG_LOG) console.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 '==':
                                value.Out = this.Algorithm.EQ(leftValue, rightValue);
                                break;
                            case '!=':
                            case '<>':
                                value.Out = this.Algorithm.NEQ(leftValue, rightValue);
                                break;
                        }

                        if (JS_EXECUTE_DEBUG_LOG) console.log('[JSExecute::VisitBinaryExpression] BinaryExpression', value);
                    } else if (value.Type == Syntax.LogicalExpression) {
                    var _leftValue = this.GetNodeValue(value.Left);
                    var _rightValue = this.GetNodeValue(value.Right);

                    if (JS_EXECUTE_DEBUG_LOG) console.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) console.log('[JSExecute::VisitBinaryExpression] LogicalExpression', value);
                }

                node = temp;
            }
        }

        return node.Out;
    };

    this.GetNodeValue = function (node) {
        switch (node.Type) {
            case Syntax.Literal:
                //数字
                return node.Value;
            case Syntax.UnaryExpression:
                if (node.Operator == '-') {
                    var _value9 = this.GetNodeValue(node.Argument);
                    return this.Algorithm.Subtract(0, _value9);
                }
                return node.Argument.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);
            default:
                this.ThrowUnexpectedNode(node);
        }
    };

    this.ThrowUnexpectedNode = function (node, message) {
        var marker = node.Marker;
        var msg = message || "执行异常";

        return this.ErrorHandler.ThrowError(marker.Index, marker.Line, marker.Column, msg);
    };

    this.ThrowError = function () {};
}

//对外导出类
function JSComplier() {}

//词法分析
JSComplier.Tokenize = function (code) {
    console.log('[JSComplier.Tokenize]', code);
    var tokenizer = new Tokenizer(code);
    var tokens = [];
    try {
        while (true) {
            var token = tokenizer.GetNextToken();
            if (!token) break;

            tokens.push(token);
        }
    } catch (e) {}

    return tokens;
};

//语法解析 生成抽象语法树(Abstract Syntax Tree)
JSComplier.Parse = function (code) {
    console.log('[JSComplier.Parse]', code);

    var parser = new JSParser(code);
    parser.Initialize();
    var program = parser.ParseScript();
    var ast = program;
    return ast;
};

/*
    执行
    option.Symbol=股票代码
    option.Name=股票名称
    option.Data=这个股票的ChartData
    option.Right=复权
    option.MaxReqeustDataCount=请求数据的最大个数
*/

function timeout(ms) {
    return new Promise(function (resolve) {
        setTimeout(resolve, ms);
    });
}

JSComplier.Execute = function (code, option, errorCallback) {
    //异步调用
    //var asyncExecute= async function() es5不能执行 去掉异步
    var asyncExecute = function asyncExecute() {
        try {
            console.log('[JSComplier.Execute]', code, option);

            console.log('[JSComplier.Execute] parser .....');
            var parser = new JSParser(code);
            parser.Initialize();
            var program = parser.ParseScript();

            var ast = program;
            console.log('[JSComplier.Execute] parser finish.', ast);

            console.log('[JSComplier.Execute] execute .....');
            var execute = new JSExecute(ast, option);
            execute.ErrorCallback = errorCallback; //执行错误回调
            execute.JobList = parser.Node.GetDataJobList();
            execute.JobList.push({ ID: JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT });

            var _result = execute.Execute();
        } catch (error) {
            console.log(error);

            if (errorCallback) errorCallback(error);
        }
    };

    asyncExecute();

    console.log('[JSComplier.Execute] async execute.');
};

JSComplier.SetDomain = function (domain, cacheDomain) //修改API地址
{
    if (domain) g_JSComplierResource.Domain = domain;
    if (cacheDomain) g_JSComplierResource.CacheDomain = cacheDomain;
};

var HQ_DATA_TYPE = {
    KLINE_ID: 0, //K线
    MINUTE_ID: 2, //当日走势图
    HISTORY_MINUTE_ID: 3, //历史分钟走势图
    MULTIDAY_MINUTE_ID: 4 //多日走势图
};

//脚本指标
//name=指标名字 args=参数名字 参数值
function ScriptIndex(name, script, args, option) {
    this.newMethod = BaseIndex; //派生
    this.newMethod(name);
    delete this.newMethod;

    this.Script = script;
    this.Arguments = [];
    this.OutVar = [];
    this.ID; //指标ID
    this.FloatPrecision = 2; //小数位数
    this.KLineType = null; //K线显示类型
    this.InstructionType; //五彩K线, 交易指标
    this.YSpecificMaxMin = null; //最大最小值
    this.YSplitScale = null; //固定刻度
    this.Condition = null; //限制条件

    //指标上锁配置信息
    this.IsLocked = false; //是否锁住指标
    this.LockCallback = null;
    this.LockID = null;
    this.LockBG = null; //锁背景色
    this.LockTextColor = null;
    this.LockText = null;
    this.LockFont = null;
    this.LockCount = 20;

    if (option) {
        if (option.FloatPrecision >= 0) this.FloatPrecision = option.FloatPrecision;
        if (option.ID) this.ID = option.ID;
        if (option.KLineType >= 0) this.KLineType = option.KLineType;
        if (option.InstructionType) this.InstructionType = option.InstructionType;
        if (option.YSpecificMaxMin) this.YSpecificMaxMin = option.YSpecificMaxMin;
        if (option.YSplitScale) this.YSplitScale = option.YSplitScale;
        if (option.Condition) this.Condition = option.Condition;
    }

    if (option && option.Lock) {
        if (option.Lock.IsLocked == true) this.IsLocked = true; //指标上锁
        if (option.Lock.Callback) this.LockCallback = option.Lock.Callback; //锁回调
        if (option.Lock.ID) this.LockID = option.Lock.ID; //锁ID
        if (option.Lock.BG) this.LockBG = option.Lock.BG;
        if (option.Lock.TextColor) this.LockTextColor = option.Lock.TextColor;
        if (option.Lock.Text) this.LockText = option.Lock.Text;
        if (option.Lock.Font) this.LockFont = option.Lock.Font;
        if (option.Lock.Count) this.LockCount = option.Lock.Count;
    }

    if (args) this.Arguments = args;

    this.SetLock = function (lockData) {
        if (lockData.IsLocked == true) {
            this.IsLocked = true; //指标上锁
            if (lockData.Callback) this.LockCallback = lockData.Callback; //锁回调
            if (lockData.ID) this.LockID = lockData.ID; //锁ID
            if (lockData.BG) this.LockBG = lockData.BG;
            if (lockData.TextColor) this.LockTextColor = lockData.TextColor;
            if (lockData.Text) this.LockText = lockData.Text;
            if (lockData.Font) this.LockFont = lockData.Font;
            if (lockData.Count) this.LockCount = lockData.Count;
        } else {
            //清空锁配置信息
            this.IsLocked = false; //是否锁住指标
            this.LockCallback = null;
            this.LockID = null;
            this.LockBG = null; //锁背景色
            this.LockTextColor = null;
            this.LockText = null;
            this.LockFont = null;
            this.LockCount = 20;
        }
    };

    this.ExecuteScript = function (hqChart, windowIndex, hisData) {
        this.OutVar = [];
        var self = this;
        var param = {
            HQChart: hqChart,
            WindowIndex: windowIndex,
            HistoryData: hisData,
            Self: this
        };

        //数据类型
        var hqDataType = HQ_DATA_TYPE.KLINE_ID; //默认K线
        if (hqChart.ClassName === 'MinuteChartContainer' || hqChart.ClassName === 'MinuteChartHScreenContainer') {
            if (hqChart.DayCount > 1) hqDataType = HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟
            else hqDataType = HQ_DATA_TYPE.MINUTE_ID; //分钟数据
        } else if (hqChart.ClassName === 'HistoryMinuteChartContainer') {
            hqDataType = HQ_DATA_TYPE.HISTORY_MINUTE_ID; //历史分钟
        }
        var option = {
            HQDataType: hqDataType,
            Symbol: hqChart.Symbol,
            Name: hqChart.Name,
            Data: hisData,
            SourceData: hqChart.SourceData,
            Callback: this.RecvResultData, CallbackParam: param,
            Async: true,
            MaxRequestDataCount: hqChart.MaxReqeustDataCount,
            MaxRequestMinuteDayCount: hqChart.MaxRequestMinuteDayCount,
            Arguments: this.Arguments,
            Condition: this.Condition,
            IsBeforeData: hqChart.IsBeforeData
        };

        if (hqDataType === 3) option.TrateDate = hqChart.TradeDate;

        if (this.Condition && !this.IsMeetCondition(param, option)) {
            this.ShowConditionError(param);
            return;
        }

        var code = this.Script;
        var run = JSComplier.Execute(code, option, hqChart.ScriptErrorCallback);
    };

    //是否符合限制条件
    this.IsMeetCondition = function (param, option) {
        console.log('[ScriptIndex::IsMeetCondition] ', this.Condition);
        if (this.Condition.Period) //周期是否满足
            {
                if (!this.IsMeetPeriodCondition(param, option)) return false;
            }

        return true;
    };

    //周期是否满足条件
    this.IsMeetPeriodCondition = function (param, option) {
        if (!this.Condition.Period) return true;

        for (var i in this.Condition.Period) {
            var item = this.Condition.Period[i];
            switch (item) {
                case CONDITION_PERIOD.MINUTE_ID:
                    if (option.HQDataType == HQ_DATA_TYPE.MINUTE_ID) return true;
                    break;
                case CONDITION_PERIOD.MULTIDAY_MINUTE_ID:
                    if (option.HQDataType == HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) return true;
                    break;
                case CONDITION_PERIOD.KLINE_DAY_ID:
                case CONDITION_PERIOD.KLINE_WEEK_ID:
                case CONDITION_PERIOD.KLINE_MONTH_ID:
                case CONDITION_PERIOD.KLINE_YEAR_ID:

                case CONDITION_PERIOD.KLINE_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_5_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_15_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_30_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_60_MINUTE_ID:
                    if (param.HQChart.Period == item) return true;
                    break;
            }
        }

        return false;
    };

    //显示指标不符合条件
    this.ShowConditionError = function (param) {
        var hqChart = param.HQChart;
        var windowIndex = param.WindowIndex;

        hqChart.DeleteIndexPaint(windowIndex);
        if (windowIndex == 0) hqChart.ShowKLine(true);

        var message = '指标不支持当前品种或周期';

        var line = new ChartLine();
        line.Canvas = hqChart.Canvas;
        line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        line.NotSupportMessage = message;
        hqChart.ChartPaint.push(line);
        hqChart.Draw();
    };

    this.RecvResultData = function (outVar, param) {
        var hqChart = param.HQChart;
        var windowIndex = param.WindowIndex;
        var hisData = param.HistoryData;
        param.Self.OutVar = outVar;
        param.Self.BindData(hqChart, windowIndex, hisData);

        if (param.Self.IsLocked == false) //不上锁
            {
                param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(null);
            } else //上锁
            {
                var lockData = { IsLocked: true, Callback: param.Self.LockCallback, IndexName: param.Self.Name, ID: param.Self.LockID,
                    BG: param.Self.LockBG, Text: param.Self.LockText, TextColor: param.Self.LockTextColor, Font: param.Self.LockFont, Count: param.Self.LockCount };
                param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData);
            }

        param.HQChart.UpdataDataoffset(); //更新数据偏移
        param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值
        param.HQChart.Draw();

        var event = hqChart.GetIndexEvent(); //指标计算完成回调
        if (event) {
            var self = param.Self;
            var data = { OutVar: self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData,
                Stock: { Symbol: hqChart.Symbol, Name: hqChart.Name } };
            event.Callback(event, data, self);
        }
    };

    this.CreateLine = function (hqChart, windowIndex, varItem, id) {
        var line = new ChartLine();
        line.Canvas = hqChart.Canvas;
        line.DrawType = 1;
        line.Name = varItem.Name;
        line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color = this.GetColor(varItem.Color);else line.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) line.LineWidth = width;
        }

        if (varItem.IsShow == false) line.IsShow = false;

        var titleIndex = windowIndex + 1;
        line.Data.Data = varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(line.Data, varItem.Name, line.Color);

        hqChart.ChartPaint.push(line);
    };

    //创建柱子
    this.CreateBar = function (hqChart, windowIndex, varItem, id) {
        var bar = new ChartStickLine();
        bar.Canvas = hqChart.Canvas;
        if (varItem.Draw.Width > 0) bar.LineWidth = varItem.Draw.Width;else bar.LineWidth = 1;

        bar.Name = varItem.Name;
        bar.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        bar.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) bar.Color = this.GetColor(varItem.Color);else bar.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        bar.Data.Data = varItem.Draw.DrawData;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(bar);
    };

    //创建文本
    this.CreateText = function (hqChart, windowIndex, varItem, id) {
        var chartText = new ChartSingleText();
        chartText.Canvas = hqChart.Canvas;

        chartText.Name = varItem.Name;
        chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chartText.Color = this.GetColor(varItem.Color);else chartText.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chartText.Data.Data = varItem.Draw.DrawData;
        chartText.Text = varItem.Draw.Text;
        if (varItem.Draw.Direction > 0) chartText.Direction = varItem.Draw.Direction;
        if (varItem.Draw.YOffset > 0) chartText.YOffset = varItem.Draw.YOffset;
        if (varItem.Draw.TextAlign) chartText.TextAlign = varItem.Draw.TextAlign;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    };

    //COLORSTICK 
    this.CreateMACD = function (hqChart, windowIndex, varItem, id) {
        var chartMACD = new ChartMACD();
        chartMACD.Canvas = hqChart.Canvas;

        chartMACD.Name = varItem.Name;
        chartMACD.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartMACD.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        var titleIndex = windowIndex + 1;
        chartMACD.Data.Data = varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chartMACD.Data, varItem.Name, this.GetDefaultColor(id));

        hqChart.ChartPaint.push(chartMACD);
    };

    this.CreatePointDot = function (hqChart, windowIndex, varItem, id) {
        var pointDot = new ChartPointDot();
        pointDot.Canvas = hqChart.Canvas;
        pointDot.Name = varItem.Name;
        pointDot.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        pointDot.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) pointDot.Color = this.GetColor(varItem.Color);else pointDot.Color = this.GetDefaultColor(id);

        if (varItem.Radius) pointDot.Radius = varItem.Radius;

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) pointDot.Radius = width;
        }

        var titleIndex = windowIndex + 1;
        pointDot.Data.Data = varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(pointDot.Data, varItem.Name, pointDot.Color);

        hqChart.ChartPaint.push(pointDot);
    };

    this.CreateStick = function (hqChart, windowIndex, varItem, id) {
        var chart = new ChartStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        hqChart.ChartPaint.push(chart);
    };

    this.CreateLineStick = function (hqChart, windowIndex, varItem, id) {
        var chart = new ChartLineStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        hqChart.ChartPaint.push(chart);
    };

    this.CreateStraightLine = function (hqChart, windowIndex, varItem, id) {
        var line = new ChartLine();
        line.DrawType = 1;
        line.Canvas = hqChart.Canvas;
        line.Name = varItem.Name;
        line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color = this.GetColor(varItem.Color);else line.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) line.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        line.Data.Data = varItem.Draw.DrawData;
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        hqChart.ChartPaint.push(line);
    };

    this.CreateVolStick = function (hqChart, windowIndex, varItem, id, hisData) {
        var chart = new ChartVolStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        chart.KLineDrawType = hqChart.KLineDrawType; //设置K线显示类型
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        chart.HistoryData = hisData;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        hqChart.ChartPaint.push(chart);
    };

    this.CreateBand = function (hqChart, windowIndex, varItem, id) {
        var chart = new ChartBand();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.FirstColor = varItem.Draw.Color[0];
        chart.SecondColor = varItem.Draw.Color[1];
        chart.Data.Data = varItem.Draw.DrawData;

        hqChart.ChartPaint.push(chart);
    };

    //创建K线图
    this.CreateKLine = function (hqChart, windowIndex, varItem, id) {
        var chart = new ChartKLine();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data.Data = varItem.Draw.DrawData;
        chart.IsShowMaxMinPrice = false;
        chart.IsShowKTooltip = false;

        if (varItem.Color) //如果设置了颜色,使用外面设置的颜色
            chart.UnchagneColor = chart.DownColor = chart.UpColor = this.GetColor(varItem.Color);

        hqChart.ChartPaint.push(chart);
    };

    this.CreatePolyLine = function (hqChart, windowIndex, varItem, id) {
        var line = new ChartLine();
        line.Canvas = hqChart.Canvas;
        line.Name = varItem.Name;
        line.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color = this.GetColor(varItem.Color);else line.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) line.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        line.Data.Data = varItem.Draw.DrawData;
        hqChart.TitlePaint[titleIndex].Data[id] = new DynamicTitleData(line.Data, ' ', line.Color); //给一个空的标题

        hqChart.ChartPaint.push(line);
    };

    this.CreateNumberText = function (hqChart, windowIndex, varItem, id) {
        var chartText = new ChartSingleText();
        chartText.Canvas = hqChart.Canvas;

        chartText.Name = varItem.Name;
        chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chartText.Color = this.GetColor(varItem.Color);else chartText.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chartText.Data.Data = varItem.Draw.DrawData.Value;
        chartText.Text = varItem.Draw.DrawData.Text;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    };

    //创建图标
    this.CreateIcon = function (hqChart, windowIndex, varItem, id) {
        var chartText = new ChartSingleText();
        chartText.Canvas = hqChart.Canvas;
        chartText.TextAlign = 'center';

        chartText.Name = varItem.Name;
        chartText.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        var titleIndex = windowIndex + 1;
        chartText.Data.Data = varItem.Draw.DrawData;
        var icon = varItem.Draw.Icon;
        if (icon.IconFont == true) {
            chartText.IconFont = { Family: icon.Family, Text: icon.Symbol, Color: icon.Color };
        } else {
            chartText.Text = icon.Symbol;
            if (varItem.Color) chartText.Color = this.GetColor(varItem.Color);else if (icon.Color) chartText.Color = icon.Color;else chartText.Color = 'rgb(0,0,0)';
        }

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    };

    //创建通道
    this.CreateChannel = function (hqChart, windowIndex, varItem, id) {
        var chart = new ChartChannel();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        if (varItem.Draw.AreaColor) chart.AreaColor = varItem.Draw.AreaColor;else if (varItem.Color) chart.AreaColor = this.GetColor(varItem.Color);else chart.AreaColor = this.GetDefaultColor(id);

        if (varItem.Draw.Border.Color) chart.LineColor = varItem.Draw.Border.Color;else chart.LineColor = null;

        if (varItem.Draw.Border.Dotted) chart.LineDotted = varItem.Draw.Border.Dotted;
        if (varItem.Draw.Border.Width > 0) chart.LineWidth = varItem.Draw.Border.Width;

        //let titleIndex=windowIndex+1;
        chart.Data.Data = varItem.Draw.DrawData;
        hqChart.ChartPaint.push(chart);
    };

    //创建K线
    this.CreateSelfKLine = function (hqChart, windowIndex, hisData) {
        var chart = new ChartKLine();
        chart.Canvas = hqChart.Canvas;
        chart.Name = "Self Kline";
        chart.ChartBorder = hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame = hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data = hisData;
        chart.IsShowMaxMinPrice = false;
        chart.IsShowKTooltip = false;
        chart.DrawType = this.KLineType;

        hqChart.ChartPaint.push(chart);
    };

    this.BindInstructionData = function (hqChart, windowIndex, hisData) //绑定指示指标
    {
        if (this.OutVar == null || this.OutVar.length < 0) return;
        if (this.InstructionType == 2) //五彩K线
            {
                var varItem = this.OutVar[this.OutVar.length - 1]; //取最后一组数据作为指示数据
                hqChart.SetInstructionData(this.InstructionType, { Data: varItem.Data, Name: this.Name, ID: this.ID }); //设置指示数据
                return true;
            } else if (this.InstructionType == 1) //交易系统
            {
                var buyData, sellData;
                for (var i in this.OutVar) {
                    var _item15 = this.OutVar[i];
                    if (_item15.Name == 'ENTERLONG') buyData = _item15.Data;else if (_item15.Name == 'EXITLONG') sellData = _item15.Data;
                }

                hqChart.SetInstructionData(this.InstructionType, { Buy: buyData, Sell: sellData, Name: this.Name, ID: this.ID }); //设置指示数据
                return true;
            }
    };

    this.BindData = function (hqChart, windowIndex, hisData) {
        if (windowIndex == 0 && this.InstructionType) {
            this.BindInstructionData(hqChart, windowIndex, hisData);
            return;
        }

        //清空主指标图形
        hqChart.DeleteIndexPaint(windowIndex);
        if (windowIndex == 0) hqChart.ShowKLine(true);

        if (this.OutVar == null || this.OutVar.length < 0) return;

        //叠加一个K线背景
        if (this.KLineType != null) {
            if (this.KLineType === 0 || this.KLineType === 1 || this.KLineType === 2) this.CreateSelfKLine(hqChart, windowIndex, hisData);else if (this.KLineType === -1 && windowIndex == 0) hqChart.ShowKLine(false);
        }

        if (windowIndex >= 1 && hqChart.Frame) {
            hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision = this.FloatPrecision;
            if (this.YSpecificMaxMin) hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = this.YSpecificMaxMin; //最大最小值
            if (this.YSplitScale) hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale = this.YSplitScale; //固定刻度
        }

        for (var _i105 in this.OutVar) {
            var _item16 = this.OutVar[_i105];
            if (_item16.IsExData === true) continue; //扩展数据不显示图形

            if (_item16.Type == 0) {
                this.CreateLine(hqChart, windowIndex, _item16, _i105);
            } else if (_item16.Type == 1) {
                switch (_item16.Draw.DrawType) {
                    case 'STICKLINE':
                        this.CreateBar(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWTEXT':
                    case 'SUPERDRAWTEXT':
                        this.CreateText(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWLINE':
                        this.CreateStraightLine(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWBAND':
                        this.CreateBand(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWKLINE':
                        this.CreateKLine(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWKLINE_IF':
                        this.CreateKLine(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'POLYLINE':
                        this.CreatePolyLine(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWNUMBER':
                        this.CreateNumberText(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWICON':
                        this.CreateIcon(hqChart, windowIndex, _item16, _i105);
                        break;
                    case 'DRAWCHANNEL':
                        this.CreateChannel(hqChart, windowIndex, _item16, _i105);
                        break;
                }
            } else if (_item16.Type == 2) {
                this.CreateMACD(hqChart, windowIndex, _item16, _i105);
            } else if (_item16.Type == 3) {
                this.CreatePointDot(hqChart, windowIndex, _item16, _i105);
            } else if (_item16.Type == 4) {
                this.CreateLineStick(hqChart, windowIndex, _item16, _i105);
            } else if (_item16.Type == 5) {
                this.CreateStick(hqChart, windowIndex, _item16, _i105);
            } else if (_item16.Type == 6) {
                this.CreateVolStick(hqChart, windowIndex, _item16, _i105, hisData);
            }
        }

        var titleIndex = windowIndex + 1;
        hqChart.TitlePaint[titleIndex].Title = this.Name;

        var indexParam = '';
        for (var _i106 in this.Arguments) {
            var _item17 = this.Arguments[_i106];
            if (indexParam.length > 0) indexParam += ',';
            indexParam += _item17.Value.toString();
        }

        if (indexParam.length > 0) hqChart.TitlePaint[titleIndex].Title = this.Name + '(' + indexParam + ')';

        return true;
    };

    //给一个默认的颜色
    this.GetDefaultColor = function (id) {
        var COLOR_ARRAY = ["rgb(255,174,0)", "rgb(25,199,255)", "rgb(175,95,162)", "rgb(236,105,65)", "rgb(68,114,196)", "rgb(229,0,79)", "rgb(0,128,255)", "rgb(252,96,154)", "rgb(42,230,215)", "rgb(24,71,178)"];

        var number = parseInt(id);
        return COLOR_ARRAY[number % (COLOR_ARRAY.length - 1)];
    };

    //获取颜色
    this.GetColor = function (colorName) {
        var COLOR_MAP = new Map([['COLORBLACK', 'rgb(0,0,0)'], ['COLORBLUE', 'rgb(18,95,216)'], ['COLORGREEN', 'rgb(25,158,0)'], ['COLORCYAN', 'rgb(0,255,198)'], ['COLORRED', 'rgb(238,21,21)'], ['COLORMAGENTA', 'rgb(255,0,222)'], ['COLORBROWN', 'rgb(149,94,15)'], ['COLORLIGRAY', 'rgb(218,218,218)'], //画淡灰色
        ['COLORGRAY', 'rgb(133,133,133)'], //画深灰色
        ['COLORLIBLUE', 'rgb(94,204,255)'], //淡蓝色
        ['COLORLIGREEN', 'rgb(183,255,190)'], //淡绿色
        ['COLORLICYAN', 'rgb(154,255,242)'], //淡青色
        ['COLORLIRED', 'rgb(255,172,172)'], //淡红色
        ['COLORLIMAGENTA', 'rgb(255,145,241)'], //淡洋红色
        ['COLORWHITE', 'rgb(255,255,255)'], //白色
        ['COLORYELLOW', 'rgb(255,198,0)']]);

        if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName);

        //COLOR 自定义色
        //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。
        //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。
        if (colorName.indexOf('COLOR') == 0) return '#' + colorName.substr(5);

        return 'rgb(30,144,255)';
    };
}

function OverlayScriptIndex(name, script, args, option) {
    this.newMethod = ScriptIndex; //派生
    this.newMethod(name, script, args, option);
    delete this.newMethod;

    //叠加指标
    this.OverlayIndex = null; // { IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }

    this.BindData = function (hqChart, windowIndex, hisData) {
        if (!this.OverlayIndex || this.OverlayIndex.IsOverlay != true) return;

        this.OverlayIndex.Frame.ChartPaint = [];
        if (this.OutVar == null || this.OutVar.length < 0) return;

        /*叠加一个K线背景
        if (this.KLineType!=null)
        {
            if (this.KLineType===0 || this.KLineType===1 || this.KLineType===2) this.CreateSelfKLine(hqChart,windowIndex,hisData);
            else if (this.KLineType===-1 && windowIndex==0) hqChart.ShowKLine(false);
        }
          if (windowIndex>=1 && hqChart.Frame)
        {
            hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision=this.FloatPrecision;
            if (this.YSpecificMaxMin)  hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=this.YSpecificMaxMin;  //最大最小值
            if (this.YSplitScale)   hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale=this.YSplitScale;             //固定刻度
        }
        */

        //指标名字
        var titleInfo = { Data: [], Title: this.Name };
        var indexParam = '';
        for (var i in this.Arguments) {
            var _item18 = this.Arguments[i];
            if (indexParam.length > 0) indexParam += ',';
            indexParam += _item18.Value.toString();
        }
        if (indexParam.length > 0) titleInfo.Title = this.Name + '(' + indexParam + ')';

        var titleIndex = windowIndex + 1;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.set(this.OverlayIndex.Identify, titleInfo);
        this.OverlayIndex.Frame.Frame.Title = titleInfo.Title; //给子框架设置标题

        for (var i in this.OutVar) {
            var _item19 = this.OutVar[i];
            if (_item19.IsExData === true) continue; //扩展数据不显示图形

            if (_item19.Type == 0) {
                this.CreateLine(hqChart, windowIndex, _item19, i);
            } else if (_item19.Type == 1) {
                switch (_item19.Draw.DrawType) {
                    case 'STICKLINE':
                        this.CreateBar(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWTEXT':
                    case 'SUPERDRAWTEXT':
                        this.CreateText(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWLINE':
                        this.CreateStraightLine(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWBAND':
                        this.CreateBand(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWKLINE':
                        this.CreateKLine(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWKLINE_IF':
                        this.CreateKLine(hqChart, windowIndex, _item19, i);
                        break;
                    case 'POLYLINE':
                        this.CreatePolyLine(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWNUMBER':
                        this.CreateNumberText(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWICON':
                        this.CreateIcon(hqChart, windowIndex, _item19, i);
                        break;
                    case 'DRAWCHANNEL':
                        this.CreateChannel(hqChart, windowIndex, _item19, i);
                        break;
                }
            } else if (_item19.Type == 2) {
                this.CreateMACD(hqChart, windowIndex, _item19, i);
            } else if (_item19.Type == 3) {
                this.CreatePointDot(hqChart, windowIndex, _item19, i);
            } else if (_item19.Type == 4) {
                this.CreateLineStick(hqChart, windowIndex, _item19, i);
            } else if (_item19.Type == 5) {
                this.CreateStick(hqChart, windowIndex, _item19, i);
            } else if (_item19.Type == 6) {
                this.CreateVolStick(hqChart, windowIndex, _item19, i, hisData);
            }
        }

        /*
        hqChart.TitlePaint[titleIndex].Title=this.Name;
          let indexParam='';
        for(let i in this.Arguments)
        {
            let item=this.Arguments[i];
            if (indexParam.length>0) indexParam+=',';
            indexParam+=item.Value.toString();
        }
          if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')';
        */

        return true;
    };

    //指标执行完成
    this.RecvResultData = function (outVar, param) {
        var hqChart = param.HQChart;
        var windowIndex = param.WindowIndex;
        var hisData = param.HistoryData;
        param.Self.OutVar = outVar;
        param.Self.BindData(hqChart, windowIndex, hisData);

        param.HQChart.UpdataDataoffset(); //更新数据偏移
        param.HQChart.UpdateFrameMaxMin(); //调整坐标最大 最小值
        param.HQChart.Draw();

        var event = hqChart.GetOverlayIndexEvent(); //指标计算完成回调
        if (event) {
            var self = param.Self;
            var data = { OutVar: self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData,
                Identify: self.OverlayIndex.Identify,
                Stock: { Symbol: hqChart.Symbol, Name: hqChart.Name } };
            event.Callback(event, data, self);
        }
    };

    //////////////////////////////////////////////////////////////////////////////////////
    //  图形创建
    /////////////////////////////////////////////////////////////////////////////////////

    this.CreateLine = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartLine();
        chart.Canvas = hqChart.Canvas;
        chart.DrawType = 1;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) line.LineWidth = width;
        }

        if (varItem.IsShow == false) line.IsShow = false;

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        frame.ChartPaint.push(chart);
    };

    //创建柱子
    this.CreateBar = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartStickLine();
        chart.Canvas = hqChart.Canvas;
        if (varItem.Draw.Width > 0) chart.LineWidth = varItem.Draw.Width;else chart.LineWidth = 1;

        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    };

    //创建文本
    this.CreateText = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartSingleText();
        chart.Canvas = hqChart.Canvas;

        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData;
        chart.Text = varItem.Draw.Text;
        if (varItem.Draw.Direction > 0) chart.Direction = varItem.Draw.Direction;
        if (varItem.Draw.YOffset > 0) chart.YOffset = varItem.Draw.YOffset;
        if (varItem.Draw.TextAlign) chart.TextAlign = varItem.Draw.TextAlign;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    };

    //COLORSTICK 
    this.CreateMACD = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartMACD();
        chart.Canvas = hqChart.Canvas;

        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, this.GetDefaultColor(id));

        frame.ChartPaint.push(chart);
    };

    this.CreatePointDot = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartPointDot();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.Radius) chart.Radius = varItem.Radius;

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.Radius = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreateStick = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreateLineStick = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartLineStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreateStraightLine = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartLine();
        chart.DrawType = 1;
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData;
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreateVolStick = function (hqChart, windowIndex, varItem, id, hisData) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartVolStick();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        chart.KLineDrawType = hqChart.KLineDrawType; //设置K线显示类型
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Data;
        chart.HistoryData = hisData;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(chart.Data, varItem.Name, chart.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreateBand = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartBand();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        chart.FirstColor = varItem.Draw.Color[0];
        chart.SecondColor = varItem.Draw.Color[1];
        chart.Data.Data = varItem.Draw.DrawData;

        frame.ChartPaint.push(chart);
    };

    //创建K线图
    this.CreateKLine = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartKLine();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        chart.Data.Data = varItem.Draw.DrawData;
        chart.IsShowMaxMinPrice = false;
        chart.IsShowKTooltip = false;

        if (varItem.Color) //如果设置了颜色,使用外面设置的颜色
            chart.UnchagneColor = chart.DownColor = chart.UpColor = this.GetColor(varItem.Color);

        frame.ChartPaint.push(chart);
    };

    this.CreatePolyLine = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartLine();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        if (varItem.LineWidth) {
            var width = parseInt(varItem.LineWidth.replace("LINETHICK", ""));
            if (!isNaN(width) && width > 0) chart.LineWidth = width;
        }

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData;
        var titlePaint = hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id] = new DynamicTitleData(line.Data, ' ', line.Color); //给一个空的标题

        frame.ChartPaint.push(chart);
    };

    this.CreateNumberText = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartSingleText();
        chart.Canvas = hqChart.Canvas;

        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;
        if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else chart.Color = this.GetDefaultColor(id);

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData.Value;
        chart.Text = varItem.Draw.DrawData.Text;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    };

    //创建图标
    this.CreateIcon = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartSingleText();
        chart.Canvas = hqChart.Canvas;
        chart.TextAlign = 'center';

        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        var titleIndex = windowIndex + 1;
        chart.Data.Data = varItem.Draw.DrawData;
        var icon = varItem.Draw.Icon;
        if (icon.IconFont == true) {
            chart.IconFont = { Family: icon.Family, Text: icon.Symbol, Color: icon.Color };
        } else {
            chart.Text = icon.Symbol;
            if (varItem.Color) chart.Color = this.GetColor(varItem.Color);else if (icon.Color) chart.Color = icon.Color;else chart.Color = 'rgb(0,0,0)';
        }

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    };

    //创建通道
    this.CreateChannel = function (hqChart, windowIndex, varItem, id) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartChannel();
        chart.Canvas = hqChart.Canvas;
        chart.Name = varItem.Name;
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        if (varItem.Draw.AreaColor) chart.AreaColor = varItem.Draw.AreaColor;else if (varItem.Color) chart.AreaColor = this.GetColor(varItem.Color);else chart.AreaColor = this.GetDefaultColor(id);

        if (varItem.Draw.Border.Color) chart.LineColor = varItem.Draw.Border.Color;else chart.LineColor = null;

        if (varItem.Draw.Border.Dotted) chart.LineDotted = varItem.Draw.Border.Dotted;
        if (varItem.Draw.Border.Width > 0) chart.LineWidth = varItem.Draw.Border.Width;

        //let titleIndex=windowIndex+1;
        chart.Data.Data = varItem.Draw.DrawData;
        frame.ChartPaint.push(chart);
    };

    //创建K线
    this.CreateSelfKLine = function (hqChart, windowIndex, hisData) {
        var overlayIndex = this.OverlayIndex;
        var frame = overlayIndex.Frame;
        var chart = new ChartKLine();
        chart.Canvas = hqChart.Canvas;
        chart.Name = "Self Kline";
        chart.ChartBorder = frame.Frame.ChartBorder;
        chart.ChartFrame = frame.Frame;
        chart.Identify = overlayIndex.Identify;

        chart.Data = hisData;
        chart.IsShowMaxMinPrice = false;
        chart.IsShowKTooltip = false;
        chart.DrawType = this.KLineType;

        frame.ChartPaint.push(chart);
    };

    //给一个默认的颜色
    this.GetDefaultColor = function (id) {
        var COLOR_ARRAY = ["rgb(24,71,178)", "rgb(42,230,215)", "rgb(252,96,154)", "rgb(0,128,255)", "rgb(229,0,79)", "rgb(68,114,196)", "rgb(255,174,0)", "rgb(25,199,255)", "rgb(175,95,162)", "rgb(236,105,65)"];

        var number = parseInt(id);
        return COLOR_ARRAY[number % (COLOR_ARRAY.length - 1)];
    };
}

//后台执行指标
function APIScriptIndex(name, script, args, option) {
    this.newMethod = ScriptIndex; //派生
    this.newMethod(name, script, args, option);
    delete this.newMethod;

    this.ExecuteScript = function (hqChart, windowIndex, hisData) {};

    this.RecvAPIData = function (data) {};
}

// 本地json数据指标
function LocalJsonDataIndex(name, args, option) {
    this.newMethod = ScriptIndex; //派生
    this.newMethod(name, null, args, null);
    delete this.newMethod;

    this.JsonData; //json格式数据
    if (option.JsonData) this.JsonData = option.JsonData;

    this.RequestData = function (hqChart, windowIndex, hisData) {
        if (!this.JsonData) {
            console.warn("[LocalJsonDataIndex::RequestData] JsonData is null");
            if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback('json 数据不能为空');
            return;
        }

        this.OutVar = this.FittingData(this.JsonData, hisData);
        this.BindData(hqChart, windowIndex, hisData);
    };

    this.FittingData = function (jsonData, hisData) {
        outVar = jsonData.OutVar;
        date = jsonData.Date;
        time = jsonData.Time;

        result = [];

        for (i in outVar) {
            item = outVar[i];
            var indexData = [];
            outVarItem = { Name: item.Name, Type: item.Type };
            for (j in item.Data) {
                var indexItem = new SingleData(); //单列指标数据
                indexItem.Date = date[j];
                if (time && j < time.length) indexItem.Time = time[j];
                indexItem.Value = item.Data[j];
                indexData.push(indexItem);
            }

            if (hisData.Period < 4) var aryFittingData = hisData.GetFittingData(indexData); //数据和主图K线拟合
            else var aryFittingData = hisData.GetMinuteFittingData(indexData); //数据和主图K线拟合
            var bindData = new ChartData();
            bindData.Data = aryFittingData;
            outVarItem.Data = bindData.GetValue();
            result.push(outVarItem);
        }

        return result;
    };
}

////////////////////////////////////////////////////////////////////////
//  无UI指标执行
// obj: { Name:指标名字 , ID:指标ID , Script:指标脚本, Args:指标参数, ErrorCallback:错误回调 }
//
///////////////////////////////////////////////////////////////////////
function ScriptIndexConsole(obj) {
    this.ID; //指标ID
    this.Name; //指标名字
    this.Script; //脚本
    this.Arguments = []; //参数
    this.ClassName = 'ScriptIndexConsole';
    this.ErrorCallback; //执行错误回调
    this.FinishCallback; //执行完成回调

    if (obj) {
        if (obj.Name) this.Name = obj.Name;
        if (obj.Script) this.Script = obj.Script;
        if (obj.ID) this.ID = obj.ID;
        if (obj.Args) this.Arguments = obj.Args;
        if (obj.ErrorCallback) this.ErrorCallback = obj.ErrorCallback;
        if (obj.FinishCallback) this.FinishCallback = obj.FinishCallback;
    }

    //执行脚本
    //obj:
    //     HQDataType:(HQ_DATA_TYPE), Period:周期, Right:复权
    //     Stock: {Name:, Symbol:}, Request: { MaxDataCount:请求K线数据个数, MaxMinuteDayCount:请求分钟数据天数, TradeDate: 历史走势图才用}, :
    //     Data: 当前计算数据(周期|复权以后), Source:  原始股票数据 
    this.ExecuteScript = function (obj) {
        this.OutVar = [];
        var param = { Self: this };

        var option = {
            HQDataType: obj.HQDataType,
            Symbol: obj.Stock.Symbol,
            Name: obj.Stock.Name,
            Period: obj.Period,
            Right: obj.Right,
            Callback: this.RecvResultData, CallbackParam: param,
            Async: true,
            MaxRequestDataCount: obj.Request.MaxDataCount,
            MaxRequestMinuteDayCount: obj.Request.MaxMinuteDayCount,
            Arguments: this.Arguments
        };

        if (obj.HQDataType === HQ_DATA_TYPE.HISTORY_MINUTE_ID) option.TrateDate = obj.Request.TradeDate;

        var code = this.Script;
        var run = JSComplier.Execute(code, option, this.ErrorCallback);
    };

    this.RecvResultData = function (outVar, param) {
        var self = param.Self;
        var jsExec = param.JSExecute;

        var date = jsExec.SymbolData.Data.GetDate();

        var result = {
            Out: outVar,
            Date: date, //日期对应 上面的数据
            Stock: { Name: jsExec.SymbolData.Name, Symbol: jsExec.SymbolData.Symbol }
        };
        if (jsExec.SymbolData.Period >= 4) result.Time = jsExec.SymbolData.Data.GetTime();
        //console.log('[ScriptIndexConsole::RecvResultData] outVar ', outVar);
        if (self.FinishCallback) self.FinishCallback(result, param.JSExecute);
    };
}

ScriptIndexConsole.SetDomain = function (domain, cacheDomain) //修改API地址
{
    JSComplier.SetDomain(domain, cacheDomain);
};

function JSNetwork() {}

JSNetwork.HttpReqeust = function (obj) //对请求进行封装
{
    $.ajax({
        url: obj.url, data: obj.data,
        type: obj.type, dataType: obj.dataType, async: obj.async,
        success: obj.success,
        error: obj.error
    });
};

/* 测试例子
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;';

console.log(code1+code2)
var tokens=JSComplier.Tokenize(code1+code2);
var ast=JSComplier.Parse(code2+code1);

console.log(ast);
*/