You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

10644 lines
350 KiB

/*
copyright (c) 2018 jones
http://www.apache.org/licenses/LICENSE-2.0
开源项目 https://github.com/jones2000/HQChart
jones_2000@163.com
分析家语法编译器
*/
//日志
import { JSConsole } from "./umychart.console.wechat.js"
import { JSCommonData } from "./umychart.data.wechat.js"; //行情数据结构体 及涉及到的行情算法(复权,周期等)
//配色资源
import {
JSCommonResource_Global_JSChartResource as g_JSChartResource,
JSCommonResource_JSCHART_LANGUAGE_ID as JSCHART_LANGUAGE_ID,
JSCommonResource_Global_JSChartLocalization as g_JSChartLocalization,
} from './umychart.resource.wechat.js'
import
{
JSCommonSplit_IFrameSplitOperator as IFrameSplitOperator,
} from './umychart.framesplit.wechat.js'
var g_JSComplierResource=
{
Domain : "https://opensource.zealink.com", //API域名
CacheDomain : "https://opensourcecache.zealink.com", //缓存域名
CustomFunction: //定制函数
{
Data:new Map() //自定义函数 key=函数名, Value:{ID:函数名, Callback: }
},
CustomVariant: //自定义变量
{
Data:new Map() //自定义函数 key=变量名, Value:{ Name:变量名, Description:描述信息 }
},
IsCustomFunction:function(name)
{
if (g_JSComplierResource.CustomFunction.Data.has(name)) return true;
return false;
},
IsCustomVariant:function(name)
{
if (g_JSComplierResource.CustomVariant.Data.has(name)) return true;
return false;
}
}
var Messages = {
BadGetterArity: 'Getter must not have any formal parameters',
BadSetterArity: 'Setter must have exactly one formal parameter',
BadSetterRestParameter: 'Setter function argument must not be a rest parameter',
ConstructorIsAsync: 'Class constructor may not be an async method',
ConstructorSpecialMethod: 'Class constructor may not be an accessor',
DeclarationMissingInitializer: 'Missing initializer in %0 declaration',
DefaultRestParameter: 'Unexpected token =',
DuplicateBinding: 'Duplicate binding %0',
DuplicateConstructor: 'A class may only have one constructor',
DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals',
ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer',
GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts',
IllegalBreak: 'Illegal break statement',
IllegalContinue: 'Illegal continue statement',
IllegalExportDeclaration: 'Unexpected token',
IllegalImportDeclaration: 'Unexpected token',
IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list',
IllegalReturn: 'Illegal return statement',
InvalidEscapedReservedWord: 'Keyword must not contain escaped characters',
InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence',
InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
InvalidLHSInForIn: 'Invalid left-hand side in for-in',
InvalidLHSInForLoop: 'Invalid left-hand side in for-loop',
InvalidModuleSpecifier: 'Unexpected token',
InvalidRegExp: 'Invalid regular expression',
LetInLexicalBinding: 'let is disallowed as a lexically bound name',
MissingFromClause: 'Unexpected token',
MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
NewlineAfterThrow: 'Illegal newline after throw',
NoAsAfterImportNamespace: 'Unexpected token',
NoCatchOrFinally: 'Missing catch or finally after try',
ParameterAfterRestParameter: 'Rest parameter must be last formal parameter',
Redeclaration: '%0 \'%1\' has already been declared',
StaticPrototype: 'Classes may not have static property named prototype',
StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
StrictDelete: 'Delete of an unqualified identifier in strict mode.',
StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block',
StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
StrictModeWith: 'Strict mode code may not include a with statement',
StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
StrictReservedWord: 'Use of future reserved word in strict mode',
StrictVarName: 'Variable name may not be eval or arguments in strict mode',
TemplateOctalLiteral: 'Octal literals are not allowed in template strings.',
UnexpectedEOS: 'Unexpected end of input',
UnexpectedIdentifier: 'Unexpected identifier',
UnexpectedNumber: 'Unexpected number',
UnexpectedReserved: 'Unexpected reserved word',
UnexpectedString: 'Unexpected string',
UnexpectedTemplate: 'Unexpected quasi %0',
UnexpectedToken: 'Unexpected token %0',
UnexpectedTokenIllegal: 'Unexpected token ILLEGAL',
UnknownLabel: 'Undefined label \'%0\'',
UnterminatedRegExp: 'Invalid regular expression: missing /'
};
var Regex = {
// Unicode v8.0.0 NonAsciiIdentifierStart:
NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/,
// Unicode v8.0.0 NonAsciiIdentifierPart:
NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/
}
var Character =
{
FromCodePoint: function (cp) {
return (cp < 0x10000) ? String.fromCharCode(cp) :
String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) +
String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023));
},
//是否是空格 https://tc39.github.io/ecma262/#sec-white-space
IsWhiteSpace:function(cp)
{
return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) ||
(cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0);
},
//是否换行 https://tc39.github.io/ecma262/#sec-line-terminators
IsLineTerminator:function(cp)
{
return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029);
},
// https://tc39.github.io/ecma262/#sec-names-and-keywords
IsIdentifierStart:function(cp)
{
return (cp === 0x24) || (cp === 0x5F) ||
(cp >= 0x41 && cp <= 0x5A) ||
(cp >= 0x61 && cp <= 0x7A) ||
(cp === 0x5C) ||
//【】
(cp===0x3010 || cp===0x3011) ||
((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(Character.FromCodePoint(cp)));
},
IsIdentifierPart: function (cp)
{
return (cp === 0x24) || (cp === 0x5F) ||
(cp >= 0x41 && cp <= 0x5A) ||
(cp >= 0x61 && cp <= 0x7A) ||
(cp >= 0x30 && cp <= 0x39) ||
(cp === 0x5C) ||
//【】
(cp===0x3010 || cp===0x3011) ||
((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(Character.FromCodePoint(cp)));
},
// https://tc39.github.io/ecma262/#sec-literals-numeric-literals
IsDecimalDigit: function (cp)
{
return (cp >= 0x30 && cp <= 0x39); // 0..9
},
IsHexDigit: function (cp)
{
return (cp >= 0x30 && cp <= 0x39) || (cp >= 0x41 && cp <= 0x46) || (cp >= 0x61 && cp <= 0x66); // a..f
},
isOctalDigit: function (cp)
{
return (cp >= 0x30 && cp <= 0x37); // 0..7
}
}
var TOKEN_NAME={};
TOKEN_NAME[1 /* BooleanLiteral */] = 'Boolean';
TOKEN_NAME[2 /* EOF */] = '<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)
{
let error=new Error(msg);
//通过自己抛异常并自己截获 来获取调用堆栈信息
try
{
throw error;
}
catch(base)
{
if (Object.create && Object.defineProperties)
{
error=Object.create(base);
error.Column=column;
}
}
return error;
}
this.CreateError=function(index, line, col, description)
{
let msg='Line ' + line + ': ' + description;
let error=this.ConstructError(msg,col);
error.Index=index;
error.LineNumber=line;
error.Description=description;
return error;
}
this.ThrowError=function(index, line, col, description)
{
let error=this.CreateError(index,line,col,description);
throw error;
}
}
//扫描类
function Scanner(code, ErrorHandler)
{
this.Source=code;
this.ErrorHandler=ErrorHandler;
this.Length=code.length;
this.Index=0;
this.LineNumber=(code.length>0)?1:0;
this.LineStart=0;
this.CurlyStack=[];
this.SaveState=function() //保存当前扫描状态
{
return { Index:this.Index, LineNumber:this.LineNumber, LineStart:this.LineStart };
}
this.RestoreState=function(state) //还原扫描状态
{
this.Index=state.Index;
this.LineNumber=state.LineNumber;
this.LineStart=state.LineStart;
}
this.IsEOF=function() //否是已经结束
{
return this.Index>=this.Length;
}
this.IsKeyword=function(id)
{
return false;
}
this.CodePointAt = function (i)
{
let cp = this.Source.charCodeAt(i);
if (cp >= 0xD800 && cp <= 0xDBFF)
{
let second = this.Source.charCodeAt(i + 1);
if (second >= 0xDC00 && second <= 0xDFFF) {
var first = cp;
cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return cp;
}
this.Lex=function()
{
if (this.IsEOF()) return { Type:2/*EOF*/, Value:'', LineNumber:this.LineNumber, LineStart:this.LineStart, Start:this.Index, End:this.Index };
let cp=this.Source.charCodeAt(this.Index);
//变量名 或 关键字
if (Character.IsIdentifierStart(cp)) return this.ScanIdentifier();
//( ) ; 开头 操作符扫描
if (cp === 0x28 || cp === 0x29 || cp === 0x3B) return this.ScanPunctuator();
//' " 开头 字符串扫描
if (cp === 0x27 || cp === 0x22) return this.ScanStringLiteral();
//. 开头 浮点型
if (cp==0x2E)
{
if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index + 1)))
return this.ScanNumericLiteral();
return this.ScanPunctuator();
}
//数字
if (Character.IsDecimalDigit(cp)) return this.ScanNumericLiteral();
if (cp >= 0xD800 && cp < 0xDFFF)
{
if (Character.IsIdentifierStart(this.CodePointAt(this.Index))) return this.ScanIdentifier();
}
return this.ScanPunctuator();
}
//关键字 变量名 https://tc39.github.io/ecma262/#sec-names-and-keywords
this.ScanIdentifier=function()
{
let type;
let start=this.Index;
//0x5C 反斜杠
let id=(this.Source.charCodeAt(start)=== 0x5C) ? this.GetComplexIdentifier() : this.GetIdentifier();
if (id.length) type=3; //Identifier
else if (this.IsKeyword(id)) type=4; //Keyword
else if (id==null) type=5; //NullLiteral
else if (id=='true' || id=='false') type=1; //BooleanLiteral
else type=3; //Identifier
if (type!=3 && (start+id.length!=this.Index))
{
let restore=this.Index;
this.Index=start;
throw Messages.InvalidEscapedReservedWord;
this.Index=restore;
}
if (id=='AND' || id=='OR') type=7 /*Punctuator*/;
return { Type:type, Value:id, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index};
}
this.GetIdentifier=function()
{
let start=this.Index++; //start 保存进来的位置
while(!this.IsEOF())
{
let ch=this.Source.charCodeAt(this.Index);
if (ch==0x5C)
{
this.Index=start;
return this.GetComplexIdentifier();
}
else if (ch >= 0xD800 && ch < 0xDFFF)
{
this.Index=start;
return this.GetComplexIdentifier();
}
if (Character.IsIdentifierPart(ch)) ++this.Index;
else break;
}
return this.Source.slice(start,this.Index);
}
//操作符 https://tc39.github.io/ecma262/#sec-punctuators
this.ScanPunctuator=function()
{
let start=this.Index;
let str=this.Source[this.Index];
switch(str)
{
case '(':
++this.Index;
break;
case ')':
case ';':
case ',':
++this.Index;
break;
case '.':
++this.Index;
/*if (this.Source[this.Index] === '.' && this.Source[this.Index + 1] === '.')
{
//Spread operator: ...
this.Index += 2;
str = '...';
}
*/
break;
default:
str=this.Source.substr(this.Index,3);
if (str=='AND')
{
this.Index+=3;
}
else
{
str = this.Source.substr(this.Index, 2);
if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '<>' || str === '<=' || str === '>=' || str === '=>' || str==':=' || str=='OR')
{
this.Index += 2;
}
else
{
str=this.Source[this.Index];
if ('<>=!+-*%&|^/:'.indexOf(str) >= 0) ++this.Index;
}
}
}
if (this.Index==start)
this.ThrowUnecpectedToken();
return { Type:7/*Punctuator*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index };
}
//字符串 https://tc39.github.io/ecma262/#sec-literals-string-literals
this.ScanStringLiteral=function()
{
let start=this.Index;
let quote=this.Source[this.Index];
++this.Index;
var octal=false;
let str='';
while(!this.IsEOF())
{
let ch=this.Source[this.Index++];
if (ch==quote)
{
quote='';
break;
}
else if (ch=='\\') //字符串转义
{
throw "not complete";
}
else if (Character.IsLineTerminator(ch.charCodeAt(0)))
{
break;
}
else
{
str+=ch;
}
}
if (quote!='')
{
this.Index=start;
this.ThrowUnecpectedToken();
}
return {Type:8/*StringLiteral*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index};
}
this.ScanNumericLiteral=function()
{
let start=this.Index;
let ch=this.Source[this.Index];
let num='';
if (ch!='.')
{
num=this.Source[this.Index++];
ch=this.Source[this.Index];
// Hex number starts with '0x'. 16进制
if (num=='0')
{
if (ch=='x' || ch=='X')
{
++this.Index;
return this.ScanHexLiteral(start);
}
}
while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
{
num+=this.Source[this.Index++];
}
ch=this.Source[this.Index];
}
if (ch=='.')
{
num+=this.Source[this.Index++];
while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
{
num+=this.Source[this.Index++];
}
ch=this.Source[this.Index];
}
//科学计数法
if (ch=='e' || ch=='E')
{
num+=this.Source[this.Index++];
ch=this.Source[this.Index];
if (ch=='+' || ch=='-') num+=this.Source[this.Index];
if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
{
while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
{
num+=this.Source[this.Index++];
}
}
else
{
this.ThrowUnecpectedToken();
}
}
if (Character.IsIdentifierStart(this.Source.charCodeAt(this.Index)))
{
this.ThrowUnecpectedToken();
}
return { Type:6/*NumericLiteral*/, Value:parseFloat(num), LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index };
}
//空格 或 注释
this.ScanComments=function()
{
let comments;
let start=(this.Index==0);
while(!this.IsEOF())
{
let ch=this.Source.charCodeAt(this.Index);
if (Character.IsWhiteSpace(ch)) //过滤掉空格
{
++this.Index;
}
else if (Character.IsLineTerminator(ch))
{
++this.Index;
if (ch==0x0D && this.Source.charCodeAt(this.Index)==0x0A) ++this.Index; //回车+换行
++this.LineNumber;
this.LineStart=this.Index;
start=true;
}
else if (ch==0x2F) // //注释
{
ch=this.Source.charCodeAt(this.Index+1);
if (ch==0x2F)
{
this.Index+=2;
let comment=this.SkipSingleLineComment(2);
start=true;
}
else
{
break;
}
}
else if (ch == 0x7B) //{ } 注释
{
this.Index += 1;
let comment = this.SkipMultiLineComment();
}
else
{
break;
}
}
return comments;
}
this.SkipMultiLineComment = function ()
{
var comments = [];
while (!this.IsEOF())
{
var ch = this.Source.charCodeAt(this.Index);
if (Character.IsLineTerminator(ch))
{
++this.LineNumber;
++this.Index;
this.LineStart = this.Index;
}
else if (ch == 0x7D)
{
this.Index += 1;
return comments;
}
else
{
++this.Index;
}
}
return comments;
}
//单行注释 https://tc39.github.io/ecma262/#sec-comments
this.SkipSingleLineComment=function(offset)
{
let comments=[];
while(!this.IsEOF())
{
let ch=this.Source.charCodeAt(this.Index);
++this.Index;
if (Character.IsLineTerminator(ch))
{
if (ch === 13 && this.Source.charCodeAt(this.Index) === 10)
++this.Index;
++this.LineNumber;
this.LineStart=this.Index;
return comments;
}
}
return comments;
}
this.ThrowUnecpectedToken=function(message)
{
if (!message) message = Messages.UnexpectedTokenIllegal;
return this.ErrorHandler.ThrowError(this.Index, this.LineNumber, this.Index - this.LineStart + 1, message);
}
}
function Tokenizer(code)
{
this.ErrorHandler=new ErrorHandler(); //错误信息处理类
this.Scanner=new Scanner(code,this.ErrorHandler);
this.Buffer=[];
this.GetNextToken=function()
{
if (this.Buffer.length==0)
{
let comments=this.Scanner.ScanComments();
if (!this.Scanner.IsEOF())
{
let token=this.Scanner.Lex();
let entry={ Type:TOKEN_NAME[token.Type], Value:this.Scanner.Source.slice(token.Start, token.End)};
this.Buffer.push(entry);
}
}
return this.Buffer.shift();
}
}
var Syntax = {
AssignmentExpression: 'AssignmentExpression',
AssignmentPattern: 'AssignmentPattern',
ArrayExpression: 'ArrayExpression',
ArrayPattern: 'ArrayPattern',
ArrowFunctionExpression: 'ArrowFunctionExpression',
AwaitExpression: 'AwaitExpression',
BlockStatement: 'BlockStatement',
BinaryExpression: 'BinaryExpression',
BreakStatement: 'BreakStatement',
CallExpression: 'CallExpression',
CatchClause: 'CatchClause',
ClassBody: 'ClassBody',
ClassDeclaration: 'ClassDeclaration',
ClassExpression: 'ClassExpression',
ConditionalExpression: 'ConditionalExpression',
ContinueStatement: 'ContinueStatement',
DoWhileStatement: 'DoWhileStatement',
DebuggerStatement: 'DebuggerStatement',
EmptyStatement: 'EmptyStatement',
ExportAllDeclaration: 'ExportAllDeclaration',
ExportDefaultDeclaration: 'ExportDefaultDeclaration',
ExportNamedDeclaration: 'ExportNamedDeclaration',
ExportSpecifier: 'ExportSpecifier',
ExpressionStatement: 'ExpressionStatement',
ForStatement: 'ForStatement',
ForOfStatement: 'ForOfStatement',
ForInStatement: 'ForInStatement',
FunctionDeclaration: 'FunctionDeclaration',
FunctionExpression: 'FunctionExpression',
Identifier: 'Identifier',
IfStatement: 'IfStatement',
ImportDeclaration: 'ImportDeclaration',
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
ImportSpecifier: 'ImportSpecifier',
Literal: 'Literal',
LabeledStatement: 'LabeledStatement',
LogicalExpression: 'LogicalExpression',
MemberExpression: 'MemberExpression',
MetaProperty: 'MetaProperty',
MethodDefinition: 'MethodDefinition',
NewExpression: 'NewExpression',
ObjectExpression: 'ObjectExpression',
ObjectPattern: 'ObjectPattern',
Program: 'Program',
Property: 'Property',
RestElement: 'RestElement',
ReturnStatement: 'ReturnStatement',
SequenceExpression: 'SequenceExpression',
SpreadElement: 'SpreadElement',
Super: 'Super',
SwitchCase: 'SwitchCase',
SwitchStatement: 'SwitchStatement',
TaggedTemplateExpression: 'TaggedTemplateExpression',
TemplateElement: 'TemplateElement',
TemplateLiteral: 'TemplateLiteral',
ThisExpression: 'ThisExpression',
ThrowStatement: 'ThrowStatement',
TryStatement: 'TryStatement',
UnaryExpression: 'UnaryExpression',
UpdateExpression: 'UpdateExpression',
VariableDeclaration: 'VariableDeclaration',
VariableDeclarator: 'VariableDeclarator',
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
YieldExpression: 'YieldExpression'
};
function Node()
{
this.IsNeedIndexData=false; //是否需要大盘数据
this.IsNeedLatestData=false; //是否需要最新的个股行情数据
this.IsNeedSymbolData=false; //是否需要下载股票数据
this.IsNeedMarginData = new Set();
this.IsNeedNewsAnalysisData = new Set(); //新闻统计数据
this.IsNeedBlockIncreaseData = new Set(); //是否需要市场涨跌股票数据统计
this.IsNeedSymbolExData = new Set(); //下载股票行情的其他数据
this.FunctionData=[]; //{ID:, Args:, FunctionName: }
//FINVALUE(ID),FINONE(ID,Y,MMDD), FINANCE(ID)
this.IsAPIData = [] //加载API数据
this.GetDataJobList=function() //下载数据任务列表
{
let jobs=[];
if (this.IsNeedSymbolData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA});
if (this.IsNeedIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA});
if (this.IsNeedLatestData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA});
//涨跌停家数统计
for (var blockSymbol of this.IsNeedBlockIncreaseData)
{
jobs.push({ ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA, Symbol: blockSymbol });
}
//加载融资融券
for (var jobID of this.IsNeedMarginData)
{
jobs.push({ID:jobID});
}
//加载新闻统计
for (var jobID of this.IsNeedNewsAnalysisData)
{
jobs.push({ID:jobID});
}
for (var i in this.IsAPIData)
{
var item = this.IsAPIData[i];
jobs.push(item);
}
//行情其他数据
for (var jobID of this.IsNeedSymbolExData)
{
jobs.push({ ID:jobID });
}
for(var i in this.FunctionData)
{
var item=this.FunctionData[i];
jobs.push(item);
}
return jobs;
}
this.VerifySymbolVariable = function (varName, token)
{
let setIndexName = new Set(['INDEXA', 'INDEXC', 'INDEXH', 'INDEXL', "INDEXO", "INDEXV", 'INDEXDEC', 'INDEXADV']);
if (setIndexName.has(varName))
{
this.IsNeedIndexData=true;
return;
}
let setSymbolDataName=new Set(['CLOSE','C','VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT']);
if (setSymbolDataName.has(varName))
{
this.IsNeedSymbolData=true;
return;
}
if (varName === 'VOLR')
{
if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA))
this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA);
return;
}
//CAPITAL流通股本(手), EXCHANGE 换手率, TOTALCAPITAL 总股本(手)
let setVariantName=new Set(
[
"CAPITAL","TOTALCAPITAL","EXCHANGE",
"HYBLOCK","DYBLOCK","GNBLOCK","FGBLOCK","ZSBLOCK","ZHBLOCK","ZDBLOCK","HYZSCODE",
"GNBLOCKNUM","FGBLOCKNUM","ZSBLOCKNUM","ZHBLOCKNUM","ZDBLOCKNUM",
"HYSYL","HYSJL"
]);
if (setVariantName.has(varName))
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:varName };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (g_JSComplierResource.IsCustomVariant(varName)) //自定义函数
{
var item={ VariantName:varName, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
}
this.VerifySymbolFunction = function (callee, args, token)
{
//自定义函数 可以覆盖系统内置函数
if (g_JSComplierResource.IsCustomFunction(callee.Name))
{
var item={FunctionName:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA, Args:args}
if (token) item.Token={ Index:token.Start, Line:token.LineNumber};
this.FunctionData.push(item);
return;
}
if (callee.Name=='DYNAINFO')
{
this.IsNeedLatestData=true;
return;
}
//财务函数
if (callee.Name=='FINANCE')
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:args, FunctionName:callee.Name };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name=="FINVALUE")
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE, Args:args, FunctionName:callee.Name };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name=="FINONE")
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE, Args:args, FunctionName:callee.Name };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name=='GPJYVALUE')
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE, Args:args, FunctionName:callee.Name };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name === 'MARGIN')
{
let jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(args[0].Value);
if (jobID && !this.IsNeedMarginData.has(jobID)) this.IsNeedMarginData.add(jobID);
return;
}
if (callee.Name === 'NEWS')
{
let jobID = JS_EXECUTE_JOB_ID.GetNewsAnalysisID(args[0].Value);
if (jobID && !this.IsNeedNewsAnalysisData.has(jobID)) this.IsNeedNewsAnalysisData.add(jobID);
return;
}
if (callee.Name == 'COST' || callee.Name == 'WINNER') //筹码都需要换手率
{
//下载流通股
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE, Args:[7], FunctionName:"FINANCE", FunctionName2:callee.Name };
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name=="INBLOCK")
{
var item={ ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT, VariantName:"INBLOCK" }; //下载所有板块
if (token) item.Token={ Index:token.Start, Line:token.LineNumber };
this.FunctionData.push(item);
return;
}
if (callee.Name === 'BETA') //beta需要下载上证指数
{
this.IsNeedIndexData = true;
return;
}
if (callee.Name == 'UPCOUNT' || callee.Name == 'DOWNCOUNT') //上涨下跌个数
{
var blockSymbol = args[0].Value;
if (!this.IsNeedBlockIncreaseData.has(blockSymbol)) this.IsNeedBlockIncreaseData.add(blockSymbol);
return;
}
if (callee.Name == "LOADAPIDATA") //加载API数据
{
var item = { Name: callee.Name, ID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA, Args: args };
if (token) item.Token = { Index: token.Start, Line: token.LineNumber };
this.IsAPIData.push(item);
return;
}
}
this.ExpressionStatement=function(expression)
{
return { Type:Syntax.ExpressionStatement, Expression:expression };
}
this.Script=function(body)
{
return {Type:Syntax.Program, Body:body, SourceType:'通达信脚本' };
}
this.SequenceExpression=function(expression)
{
return {Type:Syntax.SequenceExpression, Expression:expression };
}
this.BinaryExpression=function(operator, left, right)
{
let logical = (operator === '||' || operator === '&&' || operator=='AND' || operator=='OR');
let type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression;
return { Type:type, Operator:operator, Left:left, Right:right };
}
this.Literal=function(value,raw)
{
return { Type:Syntax.Literal, Value:value, Raw:raw };
}
this.Identifier = function (name, token)
{
this.VerifySymbolVariable(name, token);
return { Type:Syntax.Identifier, Name:name};
}
this.AssignmentExpression=function (operator, left, right)
{
return { Type:Syntax.AssignmentExpression, Operator:operator, Left:left, Right:right };
}
this.UnaryExpression=function(operator, argument)
{
return { Type:Syntax.UnaryExpression, Operator:operator, Argument:argument, Prefix:true };
}
this.EmptyStatement=function()
{
return { Type:Syntax.EmptyStatement };
}
this.CallExpression = function (callee, args, token)
{
this.VerifySymbolFunction(callee, args, token);
return { Type:Syntax.CallExpression, Callee:callee, Arguments:args };
}
this.StaticMemberExpression = function (object, property)
{
return { Type: Syntax.MemberExpression, Computed: false, Object: object, Property: property };
}
}
function JSParser(code)
{
this.ErrorHandler=new ErrorHandler();
this.Scanner=new Scanner(code, this.ErrorHandler);
this.Node=new Node(); //节点创建
this.LookAhead={Type:2, Value:'', LineNumber:this.Scanner.LineNumber, LineStart:0, Start:0, End:0 };
this.HasLineTerminator=false;
this.Context = {
IsModule: false,
await: false,
allowIn: true,
allowStrictDirective: true,
allowYield: true,
FirstCoverInitializedNameError: null,
IsAssignmentTarget: false,
IsBindingElement: false,
InFunctionBody: false,
inIteration: false,
inSwitch: false,
labelSet: {},
Strict: false
};
this.PeratorPrecedence =
{
')': 0,
';': 0,
',': 0,
']': 0,
'||': 1,
'OR':1,
'&&': 2,
'AND':2,
'|': 3,
'^': 4,
'&': 5,
'=': 6,
'==': 6,
'!=': 6,
'<>': 6,
'===': 6,
'!==': 6,
'<': 7,
'>': 7,
'<=': 7,
'>=': 7,
'<<': 8,
'>>': 8,
'>>>': 8,
'+': 9,
'-': 9,
'*': 11,
'/': 11,
'%': 11
};
this.StartMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 };
this.LastMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 };
this.Initialize=function()
{
this.NextToken();
this.LastMarker={ Index:this.Scanner.Index, Line:this.Scanner.LineNumber, Column:this.Scanner.Index-this.Scanner.LineStart };
}
this.CreateNode=function()
{
return { Index:this.StartMarker.Index, Line:this.StartMarker.Line, Column:this.StartMarker.Column };
}
this.StartNode=function(token, lastLineStart)
{
if (lastLineStart==void 0) { lastLineStart=0; }
let column = token.Start - token.LineStart;
let line = token.LineNumber;
if (column < 0)
{
column += lastLineStart;
line--;
}
return { Index: token.Start, Line: line, Column: column };
}
this.Match=function(value)
{
return this.LookAhead.Type==7 /*Punctuator*/ && this.LookAhead.Value==value;
}
this.Expect=function(value)
{
let token=this.NextToken();
if (token.Type!=7 /*Punctuator*/ || token.Value!=value)
this.ThrowUnexpectedToken(token);
}
//是否是赋值操作符
this.MatchAssign=function()
{
if (this.LookAhead.Type!=7 /*Punctuator*/) return false;
let op=this.LookAhead.Value;
return op==':' || op==':=';
}
this.GetTokenRaw=function(token)
{
return this.Scanner.Source.slice(token.Start, token.End);
}
this.NextToken=function()
{
let token=this.LookAhead;
this.LastMarker.Index=this.Scanner.Index;
this.LastMarker.Line=this.Scanner.LineNumber;
this.LastMarker.Column=this.Scanner.Index-this.Scanner.LineStart;
this.CollectComments(); //过滤注释 空格
if (this.Scanner.Index !== this.StartMarker.Index)
{
this.StartMarker.Index = this.Scanner.Index;
this.StartMarker.Line = this.Scanner.LineNumber;
this.StartMarker.Column = this.Scanner.Index - this.Scanner.LineStart;
}
let next=this.Scanner.Lex();
this.HasLineTerminator=(token.LineNumber!=next.LineNumber);
if (next && this.Context.Strict && next.Type==3/*Identifier */)
{
//TODO:
}
this.LookAhead=next;
return token;
}
this.CollectComments=function()
{
this.Scanner.ScanComments();
}
this.ParseScript=function()
{
let node=this.CreateNode();
let body=this.ParseDirectivePrologues();
while(this.LookAhead.Type!=2 /*EOF*/)
{
body.push(this.ParseStatementListItem())
}
return this.Finalize(node,this.Node.Script(body));
}
//https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive
this.ParseDirective=function()
{
let token=this.LookAhead;
let node=this.CreateNode();
let expr=this.ParseExpression();
}
this.ParseDirectivePrologues=function()
{
let firstRestricted=null;
let body=[];
while(true)
{
let token=this.LookAhead;
if (token.Type!=8 /*StringLiteral*/) break;
let statement=this.ParseDirective();
body.push(statement);
}
return body;
}
// https://tc39.github.io/ecma262/#sec-block
this.ParseStatementListItem=function()
{
let statement;
this.Context.IsAssignmentTarget=true;
this.Context.IsBindingElement=true;
if (this.LookAhead.Type==4 /*Keyword*/)
{
}
else
{
statement=this.ParseStatement();
}
return statement;
}
// https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations
this.ParseStatement=function()
{
let statement;
switch(this.LookAhead.Type)
{
case 1 /* BooleanLiteral */:
case 5 /* NullLiteral */:
case 6 /* NumericLiteral */:
case 8 /* StringLiteral */:
case 10 /* Template */:
case 9 /* RegularExpression */:
statement = this.ParseExpressionStatement();
break;
case 7 /* Punctuator */:
let value = this.LookAhead.Value;
if (value === '(') statement = this.ParseExpressionStatement();
else if (value === ';') statement = this.ParseEmptyStatement();
else statement = this.ParseExpressionStatement();
break;
case 3 /* Identifier */:
statement = this.ParseLabelledStatement();
break;
case 4 /* Keyword */:
break;
default:
statement="error";
}
return statement;
}
// https://tc39.github.io/ecma262/#sec-empty-statement
this.ParseEmptyStatement=function()
{
let node=this.CreateNode();
this.Expect(';');
return this.Finalize(node, this.Node.EmptyStatement());
}
//https://tc39.github.io/ecma262/#sec-labelled-statements
this.ParseLabelledStatement=function()
{
let node=this.CreateNode();
let expr=this.ParseExpression();
this.ConsumeSemicolon();
let statement = new this.Node.ExpressionStatement(expr);
return this.Finalize(node, statement);
}
// https://tc39.github.io/ecma262/#sec-comma-operator
this.ParseExpression=function()
{
let startToken=this.LookAhead;
let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
if (this.Match(','))
{
let expressions=[];
expressions.push(expr);
while(this.LookAhead.Type!=2 /*EOF*/)
{
if (!this.Match(',')) break;
this.NextToken();
expressions.push(this.IsolateCoverGrammar(this.ParseAssignmentExpression));
}
expr=this.Finalize(this.StartNode(startToken),this.Node.SequenceExpression(expressions));
}
return expr;
}
this.ParseAssignmentExpression=function()
{
let expr;
let startToken=this.LookAhead;
let token=startToken;
expr=this.ParseConditionalExpression();
if (this.MatchAssign())
{
if (!this.Context.IsAssignmentTarget)
{
let marker=expr.Marker;
this.ThrowUnexpectedError(marker.Index,marker.Line,marker.Column,Messages.InvalidLHSInAssignment);
}
if (!this.Match('=') && !this.Match(':'))
{
this.Context.IsAssignmentTarget=false;
this.Context.IsBindingElement=false;
}
else
{
this.ReinterpretExpressionAsPattern(expr);
}
token=this.NextToken();
let operator=token.Value;
let right=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
expr=this.Finalize(this.StartNode(startToken), this.Node.AssignmentExpression(operator, expr, right));
this.Context.FirstCoverInitializedNameError=null;
}
return expr;
}
this.ParseConditionalExpression=function()
{
let startToken=this.LookAhead;
let expr=this.InheritCoverGrammar(this.ParseBinaryExpression);
return expr;
}
this.ParseBinaryExpression=function()
{
let startToken=this.LookAhead;
let expr=this.InheritCoverGrammar(this.ParseExponentiationExpression);
let token=this.LookAhead;
var prec=this.BinaryPrecedence(token);
if (prec>0)
{
this.NextToken();
this.Context.IsAssignmentTarget=false;
this.Context.IsBindingElement=false;
let markers=[startToken,this.LookAhead];
let left=expr;
let right=this.IsolateCoverGrammar(this.ParseExponentiationExpression);
let stack=[left,token.Value,right];
let precedences = [prec];
while(true)
{
prec=this.BinaryPrecedence(this.LookAhead);
if (prec<=0) break;
while(stack.length>2 && prec<=precedences[precedences.length-1])
{
right=stack.pop();
let operator=stack.pop();
precedences.pop();
left=stack.pop();
markers.pop();
let node=this.StartNode(markers[markers.length - 1]);
stack.push(this.Finalize(node, this.Node.BinaryExpression(operator, left, right)));
}
//Shift
stack.push(this.NextToken().Value);
precedences.push(prec);
markers.push(this.LookAhead);
stack.push(this.IsolateCoverGrammar(this.ParseExponentiationExpression));
}
let i=stack.length-1;
expr=stack[i];
let lastMarker=markers.pop();
while(i>1)
{
let marker=markers.pop();
let lastLineStart=lastMarker && lastMarker.LineStart;
let node=this.StartNode(marker, lastLineStart);
let operator=stack[i-1];
expr=this.Finalize(node, this.Node.BinaryExpression(operator, stack[i - 2], expr));
i-=2;
lastMarker=marker;
}
}
return expr;
}
this.ParseExponentiationExpression=function()
{
let startToken=this.LookAhead;
let expr=this.InheritCoverGrammar(this.ParseUnaryExpression);
return expr;
}
this.ParseUnaryExpression=function()
{
let expr;
if (this.Match('+') || this.Match('-'))
{
let node=this.StartNode(this.LookAhead);
let token=this.NextToken();
expr=this.InheritCoverGrammar(this.ParseUnaryExpression);
expr=this.Finalize(node, this.Node.UnaryExpression(token.Value, expr));
this.Context.IsAssignmentTarget=false;
this.Context.IsBindingElement=false;
}
else
{
expr=this.ParseUpdateExpression();
}
return expr;
}
// https://tc39.github.io/ecma262/#sec-update-expressions
this.ParseUpdateExpression=function()
{
let expr;
let startToken=this.LookAhead;
expr=this.InheritCoverGrammar(this.ParseLeftHandSideExpressionAllowCall);
return expr;
}
this.ParseLeftHandSideExpressionAllowCall=function()
{
let startToken=this.LookAhead;
let expr;
expr=this.InheritCoverGrammar(this.ParsePrimaryExpression);
while(true)
{
if (this.Match('.'))
{
this.Context.IsBindingElement = false;
this.Context.IsAssignmentTarget = true;
this.Expect('.');
const property = this.ParseIdentifierName();
expr = this.Finalize(this.StartNode(startToken), this.Node.StaticMemberExpression(expr, property));
}
else if (this.Match('('))
{
this.Context.IsBindingElement=false;
this.Context.IsAssignmentTarget=false;
var args=this.ParseArguments(); //解析 调用参数
expr = this.Finalize(this.StartNode(startToken), this.Node.CallExpression(expr, args, startToken));
}
else
{
break;
}
}
return expr;
}
/*
BooleanLiteral = 1,
EOF=2,
Identifier=3,
Keyword=4,
NullLiteral=5,
NumericLiteral=6,
Punctuator=7,
StringLiteral=9,
RegularExpression=9,
Template=10
*/
this.IsIdentifierName = function (token)
{
return token.Type === 3 //Identifier
|| token.Type === 4 //Keyword
|| token.Type === 1 //BooleanLiteral
|| token.Type === 5;//NullLiteral;
}
this.ParseIdentifierName = function ()
{
const node = this.CreateNode();
const token = this.NextToken();
if (!this.IsIdentifierName(token))
{
this.ThrowUnexpectedToken(token);
}
return this.Finalize(node, this.Node.Identifier(token.Value, token));
}
// https://tc39.github.io/ecma262/#sec-left-hand-side-expressions
this.ParseArguments=function()
{
this.Expect('(');
var args=[];
if (!this.Match(')'))
{
while(true)
{
let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
args.push(expr);
if (this.Match(')')) break;
this.ExpectCommaSeparator();
if (this.Match(')')) break;
}
}
this.Expect(')');
return args;
}
// Quietly expect a comma when in tolerant mode, otherwise delegates to expect().
this.ExpectCommaSeparator=function()
{
this.Expect(',');
}
// https://tc39.github.io/ecma262/#sec-primary-expression
this.ParsePrimaryExpression=function()
{
let node=this.CreateNode();
let expr;
var token, raw;
switch(this.LookAhead.Type)
{
case 3:/* Identifier */
token = this.NextToken();
expr = this.Finalize(node, this.Node.Identifier(token.Value, token));
break;
case 6:/* NumericLiteral */
case 8:/* StringLiteral */
this.Context.IsAssignmentTarget=false;
this.Context.IsBindingElement=false;
token=this.NextToken();
raw=this.GetTokenRaw(token);
expr=this.Finalize(node, this.Node.Literal(token.Value,raw));
break;
case 7:/* Punctuator */
switch(this.LookAhead.Value)
{
case '(':
this.Context.IsBindingElement=false;
expr=this.InheritCoverGrammar(this.ParseGroupExpression);
break;
default:
expr=this.ThrowUnexpectedToken(this.NextToken())
}
break;
default:
expr = this.ThrowUnexpectedToken(this.NextToken());
}
return expr;
}
this.ParseGroupExpression=function()
{
let expr;
this.Expect('(');
if (this.Match(')'))
{
this.NextToken();
}
else
{
let startToken=this.LookAhead;
let params=[];
let arrow=false;
this.Context.IsBindingElement=true;
expr=this.InheritCoverGrammar(this.ParseAssignmentExpression);
if (this.Match(','))
{
let expressions=[];
this.Context.IsAssignmentTarget=false;
expressions.push(expr);
while(this.LookAhead.Type!=2 /* EOF */)
{
if (!this.Match(',')) break;
this.NextToken();
if (this.Match(')'))
{
}
}
}
if (!arrow)
{
this.Expect(')');
this.Context.IsBindingElement=false;
}
}
return expr;
}
// https://tc39.github.io/ecma262/#sec-expression-statement
this.ParseExpressionStatement=function()
{
let node=this.CreateNode();
let expr=this.ParseExpression();
this.ConsumeSemicolon();
return this.Finalize(node,this.Node.ExpressionStatement(expr));
}
this.ConsumeSemicolon=function()
{
if (this.Match(';'))
{
this.NextToken();
}
else if (!this.HasLineTerminator)
{
//if (this.LookAhead.Type!=2/*EOF*/ && !this.Match('}'))
this.LastMarker.Index=this.StartMarker.Index;
this.LastMarker.Line=this.StartMarker.Line;
this.LastMarker.Column=this.StartMarker.Column;
}
}
this.ReinterpretExpressionAsPattern=function(expr)
{
switch(expr.Type)
{
case Syntax.Identifier:
case Syntax.MemberExpression:
case Syntax.AssignmentExpression:
break;
default:
break;
}
}
this.Finalize=function(marker,node)
{
node.Marker={ Line:marker.Line, Column:marker.Column, Index:marker.Index };
return node;
}
this.BinaryPrecedence = function (token)
{
let op = token.Value;
let precedence;
if (token.Type === 7 /* Punctuator */) precedence = this.PeratorPrecedence[op] || 0;
else precedence = 0;
return precedence;
};
this.IsolateCoverGrammar=function(parseFunction)
{
let previousIsBindingElement=this.Context.IsBindingElement;
let previousIsAssignmentTarget=this.Context.IsAssignmentTarget;
let previousFirstCoverInitializedNameError=this.Context.FirstCoverInitializedNameError;
this.Context.IsBindingElement=true;
this.Context.IsAssignmentTarget=true;
this.Context.FirstCoverInitializedNameError=null;
let result=parseFunction.call(this);
if (this.Context.FirstCoverInitializedNameError!=null)
{
//错误 this.throwUnexpectedToken(this.context.firstCoverInitializedNameError);
}
this.Context.IsBindingElement=previousIsBindingElement;
this.Context.IsAssignmentTarget=previousIsAssignmentTarget;
this.Context.FirstCoverInitializedNameError=previousFirstCoverInitializedNameError;
return result;
}
this.InheritCoverGrammar = function (parseFunction)
{
let previousIsBindingElement = this.Context.IsBindingElement;
let previousIsAssignmentTarget = this.Context.IsAssignmentTarget;
let previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError;
this.Context.IsBindingElement = true;
this.Context.IsAssignmentTarget = true;
this.Context.FirstCoverInitializedNameError = null;
let result = parseFunction.call(this);
this.Context.IsBindingElement = this.Context.IsBindingElement && previousIsBindingElement;
this.Context.IsAssignmentTarget = this.Context.IsAssignmentTarget && previousIsAssignmentTarget;
this.Context.FirstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.Context.FirstCoverInitializedNameError;
return result;
};
this.ThrowUnexpectedToken=function(token,message)
{
throw this.UnexpectedTokenError(token,message);
}
this.ThrowUnexpectedError=function(index,line,column,message)
{
let msg=message || "执行异常";
return this.ErrorHandler.ThrowError(index,line,column,msg);
}
this.UnexpectedTokenError=function(token,message)
{
let msg=message || Messages.UnexpectedToken;
let value='ILLEGAL';
if (token)
{
if (!message)
{
}
value=token.Value;
}
msg=msg.replace("%0",value);
if (token && typeof(token.LineNumber)=='number')
{
let index=token.Start;
let line=token.LineNumber;
let lastMarkerLineStart=this.LastMarker.Index-this.LastMarker.Column;
let column=token.Start-lastMarkerLineStart+1;
return this.ErrorHandler.CreateError(index,line,column,msg);
}
else
{
let index=this.LastMarker.Index;
let line=this.LastMarker.Line;
let column=this.LastMarker.Column+1;
return this.ErrorHandler.CreateError(index,line,column,msg);
}
}
}
/*
算法类
*/
function JSAlgorithm(errorHandler, symbolData)
{
this.ErrorHandler=errorHandler;
this.SymbolData = symbolData; //股票数据
//相加
this.Add=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值相加
if (isNumber && isNumber2) return data+data2;
//都是数组相加
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=data[i]+data2[i];
}
}
return result;
}
//单数据和数组相加
let value;
let aryData;
if (isNumber)
{
value=data;
aryData=data2;
}
else
{
value=data2;
aryData=data;
}
for(let i in aryData)
{
result[i]=null;
if (!isNaN(aryData[i]) && !isNaN(value)) result[i]=value+aryData[i];
}
return result;
}
//相减
this.Subtract=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值相减
if (isNumber && isNumber2) return data-data2;
//都是数组相减
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=data[i]-data2[i];
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if (!isNaN(data) && !isNaN(data2[i])) result[i]=data-data2[i];
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if (!isNaN(data[i]) && !isNaN(data2)) result[i]=data[i]-data2;
}
}
return result;
}
//相乘
this.Multiply=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值相乘
if (isNumber && isNumber2) return data*data2;
//都是数组相乘
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=data[i]*data2[i];
}
}
return result;
}
//单数据和数组相乘
let value;
let aryData;
if (isNumber)
{
value=data;
aryData=data2;
}
else
{
value=data2;
aryData=data;
}
for(let i in aryData)
{
result[i]=null;
if (!isNaN(aryData[i]) && !isNaN(value)) result[i]=value*aryData[i];
}
return result;
}
//相除
this.Divide=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值相除
if (isNumber && isNumber2)
{
if (data2==0) return null; //除0判断
return data/data2;
}
//都是数组相除
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( this.IsNumber(data[i]) && this.IsDivideNumber(data2[i]) ) result[i]=data[i]/data2[i];
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( this.IsNumber(data) && this.IsDivideNumber(data2[i]) ) result[i]=data/data2[i];
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( this.IsNumber(data[i]) && this.IsDivideNumber(data2) ) result[i]=data[i]/data2;
}
}
return result;
}
//大于
this.GT=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值比较
if (isNumber && isNumber2) return (data>data2 ? 1 : 0);
//都是数组比较
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]>data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>data2 ? 1 : 0);
}
}
return result;
}
//大于等于
this.GTE=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值比较
if (isNumber && isNumber2) return (data>=data2 ? 1 : 0);
//都是数组比较
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]>=data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>=data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>=data2 ? 1 : 0);
}
}
return result;
}
//小于
this.LT=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值比较
if (isNumber && isNumber2) return (data<data2 ? 1 : 0);
//都是数组比较
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]<data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data<data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]<data2 ? 1 : 0);
}
}
return result;
}
//小于等于
this.LTE=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值比较
if (isNumber && isNumber2) return (data>=data2 ? 1 : 0);
//都是数组比较
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]<=data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data<=data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]<=data2 ? 1 : 0);
}
}
return result;
}
//等于
this.EQ=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值比较
if (isNumber && isNumber2) return (data==data2 ? 1 : 0);
//都是数组比较
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]==data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data==data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]==data2 ? 1 : 0);
}
}
return result;
}
//不等于
this.NEQ = function (data, data2)
{
let isNumber = typeof (data) == 'number';
let isNumber2 = typeof (data2) == 'number';
//单数值比较
if (isNumber && isNumber2) return (data != data2 ? 1 : 0);
//都是数组比较
let result = [];
if (!isNumber && !isNumber2)
{
let count = Math.max(data.length, data2.length);
for (let i = 0; i < count; ++i)
{
result[i] = null; //初始化
if (i < data.length && i < data2.length)
{
if (!isNaN(data[i]) && !isNaN(data2[i])) result[i] = (data[i] != data2[i] ? 1 : 0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for (let i in data2)
{
result[i] = null;
if (!isNaN(data) && !isNaN(data2[i])) result[i] = (data != data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for (let i in data)
{
result[i] = null;
if (!isNaN(data[i]) && !isNaN(data2)) result[i] = (data[i] != data2 ? 1 : 0);
}
}
return result;
}
//AND &&
this.And=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值 &&
if (isNumber && isNumber2) return (data && data2 ? 1 : 0);
//都是数组 &&
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i] && data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data && data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i] && data2 ? 1 : 0);
}
}
return result;
}
//OR ||
this.Or=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值 &&
if (isNumber && isNumber2) return (data || data2 ? 1 : 0);
//都是数组 &&
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i] || data2[i] ? 1:0);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data || data2[i] ? 1 : 0);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i] || data2 ? 1 : 0);
}
}
return result;
}
this.IF=function(data,trueData,falseData)
{
let isNumber=this.IsNumber(data);
let isNumber2=this.IsNumber(trueData);
let isNumber3=this.IsNumber(falseData);
var isArray2=Array.isArray(trueData);
var isArray3=Array.isArray(falseData);
//单数值
if (isNumber)
{
if (isNumber2 && isNumber3) return data?trueData:falseData;
return data? trueData:falseData;
}
//都是数组
let result=[];
for(let i in data)
{
if (data[i])
{
if (isNumber2) result[i]=trueData;
else if (isArray2) result[i]=trueData[i];
else result[i]=null;
}
else
{
if (isNumber3) result[i]=falseData;
else if (isArray3) result[i]=falseData[i];
else result[i]=null;
}
}
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)
{
let result=[];
if (typeof(n)=='number')
{
if (data.length<=0) return result;
if (n>=data.length) return result;
result=data.slice(0,data.length-n);
for(let i=0;i<n;++i)
result.unshift(null);
}
else //n 为数组的情况
{
for(let i=0;i<data.length;++i)
{
result[i]=null;
if (i>=n.length) continue;
var value=n[i];
if (value>0 && value<=i) result[i]=data[i-value];
else if (i) result[i]=result[i-1];
else result[i]=data[i];
}
}
return result;
}
//引用若干周期前的数据(未作平滑处理).
//用法: REFV(X,A),引用A周期前的X值.A可以是变量.
//平滑处理:当引用不到数据时进行的操作.
//例如: REFV(CLOSE,BARSCOUNT(C)-1)表示第二根K线的收盘价.
this.REFV=function(data,n)
{
let result=[];
if (typeof(n)=='number')
{
if (data.length<=0) return result;
if (n>=data.length) return result;
result=data.slice(0,data.length-n);
for(let i=0;i<n;++i) //不作平滑处理
result.unshift(null);
}
else //n 为数组的情况
{
for(let i=0;i<data.length;++i)
{
result[i]=null;
if (i>=n.length) continue;
var value=n[i];
if (value>=0 && value<=i) result[i]=data[i-value];
}
}
return result;
}
//属于未来函数,引用若干周期后的数据(平滑处理).
//用法: REFX(X,A),引用A周期后的X值.A可以是变量.
//平滑处理:当引用不到数据时进行的操作.此函数中,平滑时使用上一个周期的引用值.
//例如: TT:=IF(C>O,1,2);
// REFX(CLOSE,TT);表示阳线引用下一周期的收盘价,阴线引用日后第二周期的收盘价.
this.REFX=function(data,n)
{
let result=[];
if (typeof(n)=='number')
{
if (data.length<=0) return result;
if (n>=data.length) return result;
result=data.slice(n,data.length);
//平滑处理
var lastData=data[data.length-1];
for(let i=0;i<n;++i)
result.push(lastData);
}
else //n 为数组的情况
{
var dataCount=data.length;
for(let i=0;i<data.length;++i)
{
result[i]=null;
if (i>=n.length) continue;
var value=n[i];
if (value>=0 && value+i<dataCount) result[i]=data[i+value];
else if (i) result[i]=result[i-1];
else result[i]=data[i];
}
}
return result;
}
//属于未来函数,引用若干周期后的数据(未作平滑处理).
//用法:REFXV(X,A),引用A周期后的X值.A可以是变量.
//平滑处理:当引用不到数据时进行的操作.
//例如: REFXV(CLOSE,1)表示下一周期的收盘价,在日线上就是明天收盘价
this.REFXV=function(data,n)
{
let result=[];
if (typeof(n)=='number')
{
if (data.length<=0) return result;
if (n>=data.length) return result;
result=data.slice(n,data.length);
//平滑处理
for(let i=0;i<n;++i)
result.push(null);
}
else //n 为数组的情况
{
var dataCount=data.length;
for(let i=0;i<data.length;++i)
{
result[i]=null;
if (i>=n.length) continue;
var value=n[i];
if (value>=0 && value+i<dataCount) result[i]=data[i+value];
}
}
return result;
}
this.MAX=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值
if (isNumber && isNumber2) return Math.max(data,data2);
//都是数组
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=Math.max(data[i], data2[i]);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=Math.max(data, data2[i]);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=Math.max(data[i], data2);
}
}
return result;
}
this.MIN=function(data,data2)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(data2)=='number';
//单数值
if (isNumber && isNumber2) return Math.min(data,data2);
//都是数组
let result=[];
if (!isNumber && !isNumber2)
{
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
result[i]=null; //初始化
if (i<data.length && i<data2.length)
{
if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=Math.min(data[i], data2[i]);
}
}
return result;
}
if (isNumber) //单数据-数组
{
for(let i in data2)
{
result[i]=null;
if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=Math.min(data, data2[i]);
}
}
else //数组-单数据
{
for(let i in data)
{
result[i]=null;
if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=Math.min(data[i], data2);
}
}
return result;
}
//取正数
this.ABS=function(data)
{
let result=[];
for(let i in data)
{
result[i]=null;
if (!isNaN(data[i])) result[i]=Math.abs(data[i]);
}
return result;
}
this.MA=function(data,dayCount)
{
if (dayCount <= 0) return [];
let 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); //复制一份数据出来 需要把data数据里的null数据用前一个数据覆盖
for (var days = 0; i < data.length; ++i, ++days)
{
if (days < dayCount - 1)
{
result[i] = null;
continue;
}
let preValue = data[i - (dayCount - 1)];
let sum = 0;
for (let 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;
}
this.XMA = function (data, n)
{
var result = [];
var offset = 0;
for (; offset < data.length; ++offset)
{
if (this.IsNumber(data[offset])) break;
}
var p = parseInt((n - 2) / 2);
var sum = 0;
var count = 0, start = 0, end = 0;
for (var i = offset, j=0; i < data.length; ++i)
{
start = i - p - 1;
end = i + (n - p) - 1;
for (j = start; j < end; ++j)
{
if (j >= 0 && j < data.length)
{
if (this.IsNumber(data[j]))
{
sum += data[j];
++count;
}
}
}
if (count != 0) result[i] = (sum / count);
else result[i] = null;
sum = 0;
count = 0;
}
return result;
}
/*
SMA 移动平均
返回移动平均。
用法: SMA(X,N,M) X的M日移动平均,M为权重,如Y=(X*M+Y'*(N-M))/N
*/
this.SMA=function(data,n,m)
{
var result = [];
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 (data[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)
{
let 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)
{
let result = [];
if (!data || !data.length) return result;
var i = 0, j = 0;
for (j = 0; j < data.length && !this.IsNumber(data[j]); ++j)
{
result[j] = null;
}
i = j;
if (dayCount < 1 || i + dayCount >= data.length) return result;
var sum = 0;
var data = data.slice(0);
for (; i < j + dayCount; ++i)
{
result[i] = null;
if (!this.IsNumber(data[i]) && i - 1 >= 0)
data[i] = data[i - 1];
sum += data[i];
}
result[i - 1] = sum / dayCount;
for (; i < data.length; ++i)
{
if (this.IsNumber(result[i - 1]) && this.IsNumber(data[i]))
result[i] = (data[i] + result[i - 1] * (dayCount - 1)) / dayCount;
else if (i - 1 > -1 && this.IsNumber(result[i - 1]))
result[i] = result[i - 1];
else
result[i] = null;
}
return result;
}
/*
加权移动平均
返回加权移动平均
用法:EXPMA(X,M):X的M日加权移动平均
EXPMA[i]=buffer[i]*para+(1-para)*EXPMA[i-1] para=2/(1+__para)
*/
this.EXPMA=function(data,dayCount)
{
let result=[];
if (dayCount>=data.length) return result;
let i=dayCount;
for(;i<data.length;++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;
}
var total = 0;
for (i = data.length - 1; i >= start; --i)
{
for (j = i, total = 0; j >= start && total < data2[i]; --j)
total += data[j];
if (j < start) result[i] = null;
else result[i] = i - j;
}
for (i = start + 1; i < data.length; ++i)
{
if (result[i] == null)
result[i] = result[i - 1];
}
return result;
}
/*
求相反数.
用法:REVERSE(X)返回-X.
例如:REVERSE(CLOSE)返回-CLOSE
*/
this.REVERSE = function (data)
{
var result = [];
var i = 0;
for (; i < data.length && !this.isNumber(data[i]); ++i)
{
result[i] = null;
}
for (; i < data.length; ++i)
{
if (!this.isNumber(data[i]))
result[i] = null;
else
result[i] = 0 - data[i];
}
return result;
}
this.COUNT=function(data,n)
{
if (Array.isArray(n))
{
var start=null;
var dataCount=data.length;
for(var i=0;i<dataCount;++i)
{
if (this.IsNumber(data[i]))
{
start=i;
break;
}
}
if (start==null) return [];
var result=[];
var count=0;
for(var i=0;i<n.length;++i)
{
var period=n[i];
if (!this.IsNumber(period)) continue;
if (period<1) period=i+1;
count=0;
for(var j=i, k=0 ;j>=0 && k<period ;--j,++k) //当前往前period天 统计
{
if (data[j]) ++count;
}
result[i]=count;
}
return result;
}
else
{
var period=n;
var dataCount=data.length;
var period=period<1?dataCount:period;
var i=0,j=0;
for(;i<dataCount;++i) // 取第1个有效数据
{
if (this.IsNumber(data[i])) break;
}
var result=[];
var days=0;
for(;i<dataCount && j<period; ++i,++j)
{
days=data[i]?days+1:days;
result[i]=days;
}
for(;i<dataCount;++i)
{
if (data[i-period] && days) days--;
days=data[i] ? days+1 : days;
result[i]=days;
}
return result;
}
}
/*
HHV 最高值
求最高值。
用法: HHV(X,N) 求N周期内X最高值,N=0则从第一个有效值开始。
例如: HHV(HIGH,30) 表示求30日最高价。
*/
this.HHV=function(data,n)
{
let 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 + 1); j <= i; ++j)
{
nMax = data[j] < data[nMax] ? nMax : j;
}
}
result[i] = data[nMax];
}
}
return result;
}
/*
LLV 最低值
求最低值。
用法: LLV(X,N) 求N周期内X最低值,N=0则从第一个有效值开始。
例如: LLV(LOW,0) 表示求历史最低价。
*/
this.LLV=function(data,n)
{
var result = [];
if (Array.isArray(n))
{
for (var i = 0; i < data.length; ++i)
{
result[i] = null;
if (i >= n.length) continue;
var min = null;
var count = n[i];
if (count > 0 && count <= i)
{
for (var j = i - count; j <= i; ++j)
{
if (min == null || min > data[j]) min = data[j];
}
}
else
{
count = i;
for (var j = 0; j <= i; ++j)
{
if (min == null || min > data[j]) min = data[j];
}
}
result[i] = min;
}
}
else
{
if (n>data.length) return result;
if (n<=0) n=data.length-1;
var nMin=0;
for(nMin=0;nMin<data.length;++nMin)
{
if (this.IsNumber(data[nMin])) break;
}
if (nMin<data.length) result[nMin]=data[nMin];
for(var i=nMin+1,j=2;i<data.length && j<n;++i,++j)
{
if (data[i]<=data[nMin]) nMin=i;
result[i]=data[nMin];
}
for(;i<data.length;++i)
{
if (i-nMin<n)
{
nMin=data[i]>data[nMin]?nMin:i;
}
else
{
for(j=nMin=(i-n+1);j<=i;++j)
{
nMin=data[j]>data[nMin]?nMin:j;
}
}
result[i]=data[nMin];
}
}
return result;
}
this.STD=function(data,n)
{
var result=[];
var total=0;
var averageData=[]; //平均值
for(var i=n-1;i<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)
{
let result=[];
let days=null;
for(let i in data)
{
result[i]=0;
if (days==null)
{
if (!this.IsNumber(data[i])) contnue;
days=0;
}
result[i]=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)
{
let isNumber=typeof(data)=='number';
if (isNumber) return data? 0:1;
let result=[];
for(let i in data)
{
result[i]=null;
if (this.IsNumber(data[i])) result[i]=data[i]?0:1;
}
return result;
}
//FORCAST 线性回归预测值
//FORCAST(X,N)  返回线性回归预测值。
this.FORCAST=function(data,n)
{
var result=[];
if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
var num = n;
var datanum = data.length;
if (num < 1 || num >= datanum)
return result;
var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope;
var i, j,x;
for(j = 0; j < datanum && !this.IsNumber(data[j]); ++j)
{
result[j] = null;
}
for(i = j+num-1; i < datanum; ++i)
{
Ex = Ey = Sxy = Sxx = 0;
for (j = 0, x = num; j < num && j <= i; ++j,--x)
{
Ex +=x;
Ey += data[i - j];
}
Ex /= num;
Ey /= num;
for (j = 0, x = num; j < num && j <= i; ++j, --x)
{
Sxy += (x-Ex)*(data[i-j]-Ey);
Sxx += (x-Ex)*(x-Ex);
}
Slope = Sxy / Sxx;
Const = Ey - Ex*Slope;
result[i] = Slope * num + Const;
}
return result;
}
//SLOPE 线性回归斜率
//SLOPE(X,N)  返回线性回归斜率。
this.SLOPE=function(data,n)
{
let result=[];
if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
if (n<1 || !data.length) return result;
if (n>=data.length) return result;
let start=0;
for(let i=0;i<data.length;++i,++start)
{
result[i]=null;
if (this.IsNumber(data[i])) break;
}
let x,y,xy,xx;
for(let i=start+n-1;i<data.length;++i)
{
result[i]=null;
x=y=xy=xx=0;
for(var j=0;j<n && j<=i; ++j)
{
x+=(i-j); //数据索引相加
y+=data[i-j]; //数据相加
}
x=x/n; y=y/n;
for(j=0;j<n && j<=i; ++j)
{
xy+=(i-j-x)*(data[i-j]-y);
xx+=(i-j-x)*(i-j-x);
}
if (xx) result[i]= xy/xx;
else if (i) result[i]=result[i-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;
}
}
//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)
{
let isNumber=typeof(data)=='number';
let isNumber2=typeof(range)=='number';
let isNumber3=typeof(range2)=='number';
if (isNumber && isNumber2 && isNumber3)
{
if (data>Math.min(range,range2) && data<Math.max(range,range2)) return 1;
else return 0;
}
let result=[];
let value, rangeValue,rangValue2;
for(let i=0; i<data.length; ++i)
{
result[i]=null;
value=data[i];
if (!this.IsNumber(value)) continue;
if (!isNumber2)
{
if (i>=range.length) continue;
rangeValue=range[i];
}
else
{
rangeValue=range;
}
if (!this.IsNumber(rangeValue)) continue;
if (!isNumber3)
{
if (i>=range2.length) continue;
rangeValue2=range2[i];
}
else
{
rangeValue2=range2;
}
if (!this.IsNumber(rangeValue2)) continue;
result[i]= (value>Math.min(rangeValue,rangeValue2) && 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; //最新满足条件的数据索引
var result=[];
var value;
for(let i=0;i<data.length;++i)
{
result[i]=null;
value=data[i];
if (this.IsNumber(value) && value>0) latestID==i;
if (i-latestID<n) result[i]=1;
else result[i]=0;
}
return result;
}
this.TFILTER=function(data,data2,n)
{
n=parseInt(n);
var result=[];
let isNumber=typeof(data)=='number';
let isNumber2=typeof(range)=='number';
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
}
return result;
}
/*
过滤连续出现的信号.
用法:FILTER(X,N):X满足条件后,将其后N周期内的数据置为0,N为常量.
例如:
FILTER(CLOSE>OPEN,5)查找阳线,5天内再次出现的阳线不被记录在内
*/
this.FILTER = function (data, n)
{
var result = [];
for (let i = 0, j = 0; i < data.length; ++i)
{
if (data[i])
{
result[i] = 1;
for (j = 0; j < n && j + i + 1 < data.length; ++j)
{
result[j + i + 1] = 0;
}
i += n;
}
else
{
result[i] = 0;
}
}
return result;
}
this.BARSLAST=function(data)
{
var result=[];
if (!data) return result;
let day=null;
for(let i=0;i<data.length;++i)
{
result[i]=null;
if (data[i]>0) day=0;
else if (day!=null) ++day;
if (day!=null) result[i]=day;
}
return result;
}
/*
N周期内第一个条件成立到当前的周期数.
用法:
BARSSINCEN(X,N):N周期内第一次X不为0到现在的天数,N为常量
例如:
BARSSINCEN(HIGH>10,10)表示10个周期内股价超过10元时到当前的周期数
*/
this.BARSSINCEN = function (data, n)
{
var result=[];
if (this.IsNumber(n) && Array.isArray(data))
{
var nPeriod=n;
if (nPeriod<1) nPeriod=data.length;
var i=this.GetFirstVaildIndex(data);
if (i>=data.length) return result;
var j=0;
if (i <= nPeriod - 1) j = nPeriod - 1;
else j = i;
result[j] = j - i;
for (; j < data.length; ++j)
{
if (this.IsNumber(result[j - 1]))
{
if (result[j - 1] + 1 < nPeriod)
{
result[j] = result[j - 1] + 1;
}
else
{
for (var k = j - nPeriod+1; k <= j; ++k)
{
if (!(Math.abs(data[k]) < 0.000001))
{
result[j] = j - k;
break;
}
}
}
}
else
{
if (!(Math.abs(data[j]) < 0.000001))
result[j] = 0;
}
}
}
return result;
}
/*
第一个条件成立到当前的周期数.
用法:
BARSSINCE(X):第一次X不为0到现在的天数
例如:
BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数
*/
this.BARSSINCE = function (data)
{
var result = [];
var day = null;
for (let i = 0; i < data.length; ++i)
{
result[i] = null;
if (day == null)
{
if (data[i]) day = 0;
}
else
{
++day;
}
if (day) result[i] = day;
}
return result;
}
/*三角函数调用 func 三角函数
反正切值. 用法: ATAN(X)返回X的反正切值
余弦值. 用法: COS(X)返回X的余弦值
正弦值. 用法: SIN(X)返回X的正弦值
正切值. 用法: TAN(X)返回X的正切值
求自然对数. 用法: LN(X)以e为底的对数 例如: LN(CLOSE)求收盘价的对数
求10为底的对数. 用法: LOG(X)取得X的对数 例如: LOG(100)等于2
指数. 用法: EXP(X)为e的X次幂 例如: EXP(CLOSE)返回e的CLOSE次幂
开平方. 用法: SQRT(X)为X的平方根 例如: SQRT(CLOSE)收盘价的平方根
*/
this.Trigonometric = function (data, func)
{
if (!Array.isArray(data))
{
if (this.IsNumber(data)) return func(data);
return null;
}
else
{
var result = [];
for (let i in data)
{
var item = data[i];
if (this.IsNumber(item)) result[i] = func(item);
else result[i] = null;
}
return result;
}
}
//反正弦值. 用法: ASIN(X)返回X的反正弦值
this.ASIN = function (data)
{
if (!Array.isArray(data))
{
if (this.IsNumber(data)) return Math.acos(data);
return null;
}
else
{
var result = [];
for (let i in data)
{
var item = data[i];
result[i] = null;
if (this.IsNumber(item))
{
if (item >= -1 && item <= 1)
{
result[i] = Math.asin(item);
}
else if (i - 1 >= 0)
{
var preItem = result[i - 1];
if (this.IsNumber(preItem)) result[i] = preItem;
}
}
}
return result;
}
}
//反余弦值. 用法: ACOS(X)返回X的反余弦值
this.ACOS = function (data)
{
if (!Array.isArray(data))
{
if (this.IsNumber(data)) return Math.acos(data);
return null;
}
else
{
var result = [];
for (let i in data)
{
var item = data[i];
result[i] = null;
if (this.IsNumber(item))
{
if (item >= -1 && item <= 1)
{
result[i] = Math.acos(item);
}
else if (i - 1 >= 0) //超出范围使用上一个数值
{
var preItem = result[i - 1];
if (this.IsNumber(preItem)) result[i] = preItem;
}
}
}
return result;
}
}
/*
LAST(X,A,B):持续存在.
用法:
LAST(CLOSE>OPEN,10,5)
表示从前10日到前5日内一直阳线
若A为0,表示从第一天开始,B为0,表示到最后日止
*/
this.LAST = function (data, n, n2)
{
var result = [];
if (n2 <= 0) n2 = data.length - 1;
if (n2 > n) return result;
var day = 0;
for (let i = 0, j = 0; i < data.length; ++i) {
result[i] = 0;
day = 0;
var start = i - n;
var end = i - n2;
if (start < 0 || end < 0) continue;
for (j = start; j < data.length && j <= end; ++j, ++day) {
if (!data[j]) break;
}
if (day == end - start + 1) //[start,end]
result[i] = 1;
}
return result;
}
/*
属于未来函数,之字转向.
用法: ZIG(K,N),当价格变化量超过N%时转向,K表示0:开盘价,1:最高价,2:最低价,3:收盘价,其余:数组信息
例如: ZIG(3,5)表示收盘价的5%的ZIG转向
*/
this.ZIG=function(data,n)
{
var hisData=this.SymbolData.Data;
var result=[];
if (typeof(data)=='number')
{
switch(data)
{
case 0:
data=hisData.GetOpen();
break;
case 1:
data=hisData.GetHigh();
break;
case 2:
data=hisData.GetLow();
break;
case 3:
data=hisData.GetClose();
break;
default:
return result;
}
}
return this.ZIG_Calculate(data,n);
}
this.ZIG_Calculate=function(data,dRate)
{
var dest=[];
var nDataCount=data.length;
var m=this.GetFirstVaildIndex(data);
var i = 0, lLastPos = 0, lState = 0, j = 0;
var dif = 0;
for (i = m + 1, lLastPos = lState = m; i<nDataCount - 1 && lState == m; ++i)
{
lState = Math.abs(data[i] - data[m]) * 100 >= dRate*data[m] ? (data[i]>data[m] ? i : -i) : m;
}
for (; i<nDataCount - 1; ++i)
{
if (data[i] >= data[i - 1] && data[i] >= data[i + 1])
{
if (lState<0)
{
if ((data[i] - data[-lState]) * 100<dRate*data[-lState]) continue;
else
{
dif = (data[lLastPos] - data[j = -lState]) / (-lState - lLastPos);
dest[j--]=data[-lState];
for (; j >= lLastPos; j--)
dest[j]=data[-lState] + (-lState - j)*dif;
lLastPos = -lState;
lState = i;
}
}
else if (data[i]>data[lState]) lState = i;
}
else if (data[i] <= data[i - 1] && data[i] <= data[i + 1])
{
if (lState>0)
{
if ((data[lState] - data[i]) * 100<dRate*data[lState]) continue;
else
{
dif = (data[lState] - data[j = lLastPos]) / (lState - lLastPos);
dest[j++]=data[lLastPos];
for (; j <= lState; ++j)
dest[j]=data[lLastPos] + (j - lLastPos)*dif;
lLastPos = lState;
lState = -i;
}
}
else if (data[i]<data[-lState]) lState = -i;
}
}
if (Math.abs(lState) >= nDataCount - 2)
{
if (lState>0 && data[nDataCount - 1] >= data[lState]) lState = nDataCount - 1;
if (lState<0 && data[nDataCount - 1] <= data[-lState]) lState = 1 - nDataCount;
}
if (lState>0)
{
dif = (data[lState] - data[j = lLastPos]) / (lState - lLastPos );
dest[j++]=data[lLastPos];
for (; j <= lState; ++j)
dest[j]=data[lLastPos] + (j - lLastPos)*dif;
}
else
{
dif = (data[lLastPos] - data[j = -lState]) / (-lState - lLastPos);
dest[j--]=data[-lState];
for (; j >= lLastPos; j--)
dest[j]=(data[-lState] + (-lState - j)*dif);
}
if ((lState = Math.abs(lState))<nDataCount - 1)
{
if (data[nDataCount - 1] >= data[lState])
{
dif = (data[nDataCount - 1] - data[j = lState]) / (nDataCount - lState);
dest[j++]=(data[lState]);
for (; j<nDataCount; ++j)
dest[j]=(data[lState] + (j - lState)*dif);
}
else
{
dif = (data[lState] - data[j = nDataCount - 1]) / (nDataCount - lState);
dest[j--]=(data[nDataCount - 1]);
for (; j >= lState; j--)
dest[j]=(data[nDataCount - 1] + (nDataCount - j)*dif);
}
}
return dest;
}
this.GetFirstVaildIndex=function(data)
{
for (var i = 0; i <data.length; ++i)
{
if (this.IsNumber(data[i]))
return i;
}
return data.length;
}
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 dest=[];
var lEnd =n2;
if (lEnd<1) return dest;
var nDataCount = zigData.length;
var trough = [];
for(var i=0;i<lEnd;++i) trough[i]=0;
var lFlag = 0;
var i = this.GetFirstVaildIndex(zigData) + 1;
for (lEnd--; i<nDataCount && zigData[i]>zigData[i - 1]; ++i);
for (; i<nDataCount && zigData[i]<zigData[i - 1]; ++i);
for (trough[0] = --i; i<nDataCount - 1; ++i)
{
if (zigData[i]<zigData[i + 1])
{
if (lFlag)
{
if (lEnd)
{
var tempTrough=trough.slice(0);
for(var j=0;j<lEnd;++j)
{
trough[j+1]=tempTrough[j];
}
}
trough[lFlag = 0] = i;
}
}
else lFlag = 1;
if (trough[lEnd]) dest[i]=(i - trough[lEnd]);
}
if (trough[lEnd]) dest[i]=(i - trough[lEnd]);
return dest;
}
this.TROUGH=function(data,n,n2)
{
var zigData=this.ZIG(data,n); //计算ZIG
var dest=[];
var End=n2;
if(End<1) return dest;
var nDataCount = zigData.length;
var trough=[];
for(var i=0;i<End;++i) trough[i]=0;
var i=1,Flag=0;
var i = this.GetFirstVaildIndex(zigData) + 1;
for(End--; i<nDataCount && zigData[i]>zigData[i-1]; ++i);
for(; i<nDataCount && zigData[i]<zigData[i-1]; ++i);
for(trough[0]=--i;i<nDataCount-1;++i)
{
if(zigData[i]<zigData[i+1])
{
if(Flag)
{
if(End)
{
var tempTrough=trough.slice(0);
for(var j=0;j<End;++j)
{
trough[j+1]=tempTrough[j];
}
}
trough[Flag=0]=i;
}
}
else Flag=1;
if(trough[End]) dest[i]=zigData[trough[End]];
}
if(trough[End]) dest[i]=zigData[trough[End]];
return dest;
}
/*
属于未来函数,前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 dest=[];
var nDataCount = zigData.length;
var lEnd = n2;
if (lEnd < 1) return dest;
var peak = [];
for(var i=0;i<lEnd;++i) peak[i]=0;
var lFlag = 0;
var i = this.GetFirstVaildIndex(zigData) + 1;
for (lEnd--; i<nDataCount && zigData[i]<zigData[i - 1]; ++i);
for (; i<nDataCount && zigData[i]>zigData[i - 1]; ++i);
for (peak[0] = --i; i<nDataCount - 1; ++i)
{
if (zigData[i]>zigData[i + 1])
{
if (lFlag)
{
if (lEnd)
{
var tempPeak=peak.slice(0);
for(var j=0;j<lEnd;++j)
{
peak[j+1]=tempPeak[j];
}
}
peak[lFlag = 0] = i;
}
}
else lFlag = 1;
if (peak[lEnd]) dest[i]=(i - peak[lEnd]);
}
if (peak[lEnd])dest[i]=(i - peak[lEnd]);
return dest;
}
this.PEAK=function(data,n,n2)
{
var zigData=this.ZIG(data,n); //计算ZIG
var dest=[];
var nDataCount = zigData.length;
var lEnd = n2;
if (lEnd < 1) return dest;
var lFlag = 0;
var peak = [];
for(var i=0;i<lEnd;++i) peak[i]=0;
var i = this.GetFirstVaildIndex(zigData) + 1;
for (lEnd--; i<nDataCount && zigData[i]<zigData[i - 1]; ++i);
for (; i<nDataCount && zigData[i]>zigData[i - 1]; ++i);
for (peak[0] = --i; i<nDataCount - 1; ++i)
{
if (zigData[i]>zigData[i + 1])
{
if (lFlag)
{
if (lEnd)
{
var tempPeak=peak.slice(0);
for(var j=0;j<lEnd;++j)
{
peak[j+1]=tempPeak[j];
}
}
peak[lFlag = 0] = i;
}
}
else lFlag = 1;
if (peak[lEnd]) dest[i]=(zigData[peak[lEnd]]);
}
if (peak[lEnd]) dest[i]=(zigData[peak[lEnd]]);
return dest;
}
/*
一直存在.
例如:
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, node)
{
var result=[];
var rate=data/100;
if(rate<0.000001 || rate>1) return result;
var kData=this.SymbolData.Data.Data;
if (!kData || kData.length<=0) return result;
var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本
var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low;
for(var i=0;i<kData.length;++i)
{
var item=kData[i];
dMinPrice = Math.min(dMinPrice,item.Low);
dMaxPrice = Math.max(dMaxPrice,item.High);
}
if (dMinPrice > 2000 || dMinPrice < 0 || dMaxPrice>2000 || dMinPrice < 0)
this.ThrowUnexpectedNode(node,'COST() 历史K线最大最小值错误, 超出(0,1000)范围');
var lMaxPrice = parseInt(dMaxPrice * 100 + 1);
var lMinPrice = parseInt(dMinPrice * 100 - 1);
var lLow = 0, lHigh = 0, lClose = 0;
//去掉小数
dMaxPrice = lMaxPrice / 100.0;
dMinPrice = lMinPrice / 100.0;
var lSpeed = lMaxPrice - lMinPrice + 1;
if (lSpeed < 1) return result;
var aryVolPrice=[],aryPerVol=[];
for(var i=0;i<lSpeed;++i)
{
aryVolPrice[i]=0;
aryPerVol[i]=0;
}
var dHSL = 0, dTotalVol = 0, dVol = 0, dCost=0;
for(var i=0;i<kData.length;++i)
{
if (i >= aryCapital.length) continue;
if (aryCapital[i]>1)
{
var kItem=kData[i]
dHSL = kItem.Vol/aryCapital[i];
for( var j=0;j<lSpeed;j++)
aryVolPrice[j]*=(1-dHSL);
lLow=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.Low *100)))-lMinPrice;
lHigh=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.High*100)))-lMinPrice;
lClose=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.Close*100)))-lMinPrice;
for(var j=0;j<lSpeed;++j) aryPerVol[j]=0;
var lHalf =parseInt((lLow + lHigh + 2 * lClose) / 4);
if (lHalf == lHigh || lHalf == lLow)
{
aryPerVol[lHalf] += kItem.Vol;
}
else
{
var dVH = kItem.Vol / (lHalf - lLow);
for (var k = lLow; k<lHalf; ++k)
{
aryPerVol[k] += (k - lLow)*(dVH / (lHalf - lLow));
}
for (k; k <= lHigh; ++k)
{
aryPerVol[k] += (k - lHigh)*(dVH / (lHalf - lHigh));
}
}
var dTotalVol = 0;
for (var j = lLow; j <= lHigh; j++)
{
aryVolPrice[j] += aryPerVol[j];
}
for (var j = 0; j < lSpeed; j++)
{
dTotalVol += aryVolPrice[j];
}
for(j=0,dCost=dVol=0;j<lSpeed;++j)
{
dVol+=aryVolPrice[j];
if(dVol>=dTotalVol*rate)
{
dCost=(dMaxPrice-dMinPrice)*j/lSpeed+dMinPrice;
break;
}
}
}
result[i]=dCost;
}
return result;
}
/*
获利盘比例.
用法:
WINNER(CLOSE),表示以当前收市价卖出的获利盘比例,例如返回0.1表示10%获利盘;WINNER(10.5)表示10.5元价格的获利盘比例
该函数仅对日线分析周期有效
!!!!计算比较耗时间
*/
this.WINNER = function (data,node)
{
var result=[];
var kData=this.SymbolData.Data.Data;
if (!kData || kData.length<=0) return result;
var aryCapital=this.SymbolData.GetStockCacheData({ FunctionName:"FINANCE", Args:[7], ArgCount:1, Node:node } ); //流通股本
var dMaxPrice=kData[0].High,dMinPrice=kData[0].Low;
for(var i=0;i<kData.length;++i)
{
var item=kData[i];
dMinPrice = Math.min(dMinPrice,item.Low);
dMaxPrice = Math.max(dMaxPrice,item.High);
}
if (dMinPrice > 1000 || dMinPrice < 0 || dMaxPrice>1000 || dMinPrice < 0)
this.ThrowUnexpectedNode(node,'WINNER() 历史K线最大最小值错误, 超出(0,1000)范围');
var lMaxPrice = parseInt(dMaxPrice * 100 + 1);
var lMinPrice = parseInt(dMinPrice * 100 - 1);
var lLow = 0, lHigh = 0, lClose = 0;
//去掉小数
dMaxPrice = lMaxPrice / 100.0;
dMinPrice = lMinPrice / 100.0;
var lSpeed = lMaxPrice - lMinPrice + 1;
if (lSpeed < 1) return result;
var aryVolPrice=[],aryPerVol=[];
for(var i=0;i<lSpeed;++i)
{
aryVolPrice[i]=0;
aryPerVol[i]=0;
}
var dHSL = 0, dTotalVol = 0, dVol = 0;
for(var i=0;i<kData.length;++i)
{
if (i >= aryCapital.length) continue;
if (!(aryCapital[i]>1)) continue;
var kItem=kData[i]
dHSL = kItem.Vol/aryCapital[i];
for( var j=0;j<lSpeed;j++)
aryVolPrice[j]*=(1-dHSL);
lLow=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.Low *100)))-lMinPrice;
lHigh=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.High*100)))-lMinPrice;
lClose=parseInt(Math.min(lMaxPrice,Math.max(lMinPrice,kItem.Close*100)))-lMinPrice;
for(var j=0;j<lSpeed;++j) aryPerVol[j]=0;
var lHalf =parseInt((lLow + lHigh + 2 * lClose) / 4);
if (lHalf == lHigh || lHalf == lLow)
{
aryPerVol[lHalf] += kItem.Vol;
}
else
{
var dVH = kItem.Vol / (lHalf - lLow);
for (var k = lLow; k<lHalf; ++k)
{
aryPerVol[k] += (k - lLow)*(dVH / (lHalf - lLow));
}
for (k; k <= lHigh; ++k)
{
aryPerVol[k] += (k - lHigh)*(dVH / (lHalf - lHigh));
}
}
var dTotalVol = 0;
for (var j = lLow; j <= lHigh; j++)
{
aryVolPrice[j] += aryPerVol[j];
}
for (var j = 0; j < lSpeed; j++)
{
dTotalVol += aryVolPrice[j];
}
if (Array.isArray(data))
lHigh = parseInt(Math.min((data[i] * 100) - lMinPrice, lSpeed - 1));
else
lHigh = parseInt(Math.min((data * 100) - lMinPrice, lSpeed - 1));
for (var j = 0, dVol = 0; j <= lHigh; j++)
{
dVol += aryVolPrice[j];
}
if (dTotalVol > 0) result[i]=dVol / dTotalVol;
else if (i - 1 >= 0) result[i] = result[i - 1];
}
return result;
}
//计算截至到某一天的历史所有筹码
this.CalculateChip = function (index, exchangeData, hisData, dRate)
{
var result = { Min: null, Max: null, Data: [] };
var seed = 1;//筹码历史衰减换手系数
var max = null, min = null;
for (let i = index; i >= 0; --i)
{
let item = {}; //Vol:量 High:最高 Low:最低
var kData = hisData[i];
if (i == index) item.Vol = kData.Vol * exchangeData[i];
else item.Vol = kData.Vol * seed;
item.Date = kData.Date;
item.High = kData.High;
item.Low = kData.Low;
if (max == null) max = item.High;
else if (max < item.High) max = item.High;
if (min == null) min = item.Low;
else if (min < item.Low) min = item.Low;
result.Data[i] = item;
seed *= (1 - (exchangeData[i] / 100) * dRate); //换手率累乘
}
result.Max = max;
result.Min = min;
return result;
}
/*
返回是否连涨周期数.
用法:
UPNDAY(CLOSE,M)
表示连涨M个周期,M为常量
*/
this.UPNDAY = function (data, n)
{
var result = [];
if (n < 1) return result;
if (data == null || n > data.length) return result;
var days = 0;
for (let i = 0; i < data.length; ++i)
{
result[i] = 0;
if (i - 1 < 0) continue;
if (!this.IsNumber(data[i]) || !this.IsNumber(data[i - 1])) //无效数都不算连涨
{
days = 0;
continue;
}
if (data[i] > data[i - 1])++days;
else days = 0;
if (days == n)
{
result[i] = 1;
--days;
}
}
return result;
}
/*
返回是否连跌周期.
用法:
DOWNNDAY(CLOSE,M)
表示连跌M个周期,M为常量
*/
this.DOWNNDAY = function (data, n)
{
var result = [];
if (n < 1) return result;
if (data == null || n > data.length) return result;
var days = 0;
for (let i = 0; i < data.length; ++i)
{
result[i] = 0;
if (i - 1 < 0) continue;
if (!this.IsNumber(data[i]) || !this.IsNumber(data[i - 1])) //无效数都不算连涨
{
days = 0;
continue;
}
if (data[i] < data[i - 1])++days;
else days = 0;
if (days == n)
{
result[i] = 1;
--days;
}
}
return result;
}
/*
返回是否持续存在X>Y
用法:
NDAY(CLOSE,OPEN,3)
表示连续3日收阳线
*/
this.NDAY = function (data, data2, n)
{
var result = [];
if (n < 1) return result;
if (!Array.isArray(data) && !Array.isArray(data2)) return result;
if (data == null || data2 == null) return result;
if (Array.isArray(data) && Array.isArray(data2))
{
if (n >= data.length || n >= data2.length) return result;
var count = Math.max(data.length, data2.length);
var days = 0;
for (let i = 0; i < count; ++i)
{
result[i] = 0;
if (i >= data.length || i >= data2.length) continue;
if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]))
{
days = 0;
continue;
}
if (data[i] > data2[i])++days;
else days = 0;
if (days == n)
{
result[i] = 1;
--days;
}
}
}
else if (Array.isArray(data) && !Array.isArray(data2))
{
if (n >= data.length || !this.IsNumber(data2)) return;
var days = 0;
for (let i in data)
{
result[i] = 0;
if (!this.IsNumber(data[i]))
{
days = 0;
continue;
}
if (data[i] > data2)++days;
else days = 0;
if (days == n)
{
result[i] = 1;
--days;
}
}
}
else if (!Array.isArray(data) && Array.isArray(data2))
{
if (n >= data2.length || !this.IsNumber(data)) return;
var days = 0;
for (let i in data2)
{
result[i] = 0;
if (!this.IsNumber(data2[i]))
{
days = 0;
continue;
}
if (data > data2[i])++days;
else days = 0;
if (days == n)
{
result[i] = 1;
--days;
}
}
}
return result;
}
/*
两条线维持一定周期后交叉.
用法:LONGCROSS(A,B,N)表示A在N周期内都小于B,本周期从下方向上穿过B时返回1,否则返回0
*/
this.LONGCROSS = function (data, data2, n)
{
var result = [];
var count = Math.max(data.length, data2.length);
for (let i = 0; i < count; ++i)
{
result[i] = 0;
if (i - 1 < 0) continue;
if (i >= data.length || i >= data2.length) continue;
if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]) || !this.IsNumber(data[i - 1]) || !this.IsNumber(data2[i - 1])) continue;
if (data[i] > data2[i] && data[i - 1] < data2[i - 1]) result[i] = 1;
}
for (let i = 0, j = 0; i < count; ++i)
{
if (!result[i]) continue;
for (j = 1; j <= n && i - j >= 0; ++j)
{
if (data[i - j] >= data2[i - j])
{
result[i] = 0;
break;
}
}
}
return result;
}
/*
EXISTR(X,A,B):是否存在(前几日到前几日间).
例如: EXISTR(CLOSE>OPEN,10,5)
表示从前10日内到前5日内存在着阳线
若A为0,表示从第一天开始,B为0,表示到最后日止
*/
this.EXISTR = function (data, n, n2)
{
var result = [];
if (!Array.isArray(data)) return result;
n = parseInt(n);
n2 = parseInt(n2);
if (n <= 0) n = data.length;
if (n2 <= 0) n2 = 1;
if (n2 > n) return result;
var result = [];
var value;
for (let i = 0, j = 0; i < data.length; ++i)
{
result[i] = null;
if (i - n < 0 || i - n2 < 0) continue;
result[i] = 0;
for (j = n; j >= n2; --j)
{
var value = data[i - j];
if (this.IsNumber(value) && value)
{
result[i] = 1;
break;
}
}
}
return result;
}
/*
RELATE(X,Y,N) 返回X和Y的N周期的相关系数
RELATE(X,Y,N)=(∑[(Xi-Avg(X))(Yi-Avg(y))])/N ÷ √((∑(Xi-Avg(X))^2)/N * (∑(Yi-Avg(Y))^2)/N)
其中 avg(x)表示x的N周期均值: avg(X) = (∑Xi)/N
√(...)表示开平方
*/
this.RELATE = function (data, data2, n)
{
var result = [];
if (n < 1) n = 1;
if (!Array.isArray(data) || !Array.isArray(data2)) return result;
var dataAverage = this.CalculateAverage(data, n);
var data2Average = this.CalculateAverage(data2, n);
var count = Math.max(data.length, data2.length);
for (let i = 0, j = 0; i < count; ++i)
{
result[i] = null;
if (i >= data.length || i >= data2.length || i >= dataAverage.length || i >= data2Average.length) continue;
var average = dataAverage[i];
var average2 = data2Average[i];
var total = 0, total2 = 0, total3 = 0;
for (j = i - n + 1; j <= i; ++j)
{
total += (data[j] - average) * (data2[j] - average2); //∑[(Xi-Avg(X))(Yi-Avg(y))])
total2 += Math.pow(data[j] - average, 2); //∑(Xi-Avg(X))^2
total3 += Math.pow(data2[j] - average2, 2); //∑(Yi-Avg(Y))^2)
}
result[i] = (total / n) / (Math.sqrt(total2 / n) * Math.sqrt(total3 / n));
}
return result;
}
//计算数组n周期内的均值
this.CalculateAverage = function (data, n)
{
var result = [];
if (n < 1) return result;
var total = 0;
for (var i = 0; i < data.length; ++i) //去掉开始的无效数
{
if (this.IsNumber(data[i])) break;
}
for (; i < data.length && i < n; ++i) //计算第1个周期的数据
{
result[i] = null;
var value = data[i];
if (!this.IsNumber(value)) continue;
total += value;
}
result[i - 1] = total / n;
for (; i < data.length; ++i) //计算后面的周期数据
{
var value = data[i];
var preValue = data[i - n]; //上一个周期的第1个数据
if (!this.IsNumber(value)) value = 0;
if (!this.IsNumber(preValue)) preValue = 0;
total = total - preValue + value; //当前周期的数据 等于上一个周期数据 去掉上一个周期的第1个数据 加上这个周期的最后1个数据
result[i] = total / n;
}
return result;
}
/*
COVAR(X,Y,N) 返回X和Y的N周期的协方差
*/
this.COVAR = function (data, data2, n)
{
var result = [];
if (n < 1) n = 1;
if (!Array.isArray(data) || !Array.isArray(data2)) return result;
var dataAverage = this.CalculateAverage(data, n);
var data2Average = this.CalculateAverage(data2, n);
var count = Math.max(data.length, data2.length);
var count = Math.max(data.length, data2.length);
for (let i = 0, j = 0; i < count; ++i)
{
result[i] = null;
if (i >= data.length || i >= data2.length || i >= dataAverage.length || i >= data2Average.length) continue;
var average = dataAverage[i];
var average2 = data2Average[i];
var total = 0;
for (j = i - n + 1; j <= i; ++j)
{
total += (data[j] - average) * (data2[j] - average2);
}
result[i] = (total / n);
}
return result;
}
/*
求上一高点到当前的周期数.
用法:
HHVBARS(X,N):求N周期内X最高值到当前周期数,N=0表示从第一个有效值开始统计
例如:
HHVBARS(HIGH,0)求得历史新高到到当前的周期数
*/
this.HHVBARS = function (data, n)
{
var result = [];
if (!Array.isArray(data)) return result;
if (Array.isArray(n))
{
for(var i=0;i<n.length;++i)
{
result[i]=null;
var period=n[i];
if (!this.IsNumber(period)) continue;
var start=i-period;
if (start<0) start=0;
var nMax=null;
var j=start;
for(; j<data.length;++j)
{
if (this.IsNumber(data[j]))
{
nMax=j;
break;
}
}
for(var k=0; j<data.length && k<period;++k, ++j)
{
if (data[j]>=data[nMax]) nMax=j;
}
if (nMax!=null)
result[i]=(i-nMax);
}
}
else
{
if (n < 1) n = data.length;
var nMax = null; //最大值索引
for (var i = 0; i < data.length; ++i)
{
result[i] = null;
if (this.IsNumber(data[i])) {
nMax = i;
break;
}
}
var j = 0;
for (i = nMax + 1; i < data.length && j < n; ++i, ++j) //求第1个最大值
{
if (data[i] >= data[nMax]) nMax = i;
if (n == data.length) result[i] = (i - nMax);
}
for (; i < data.length; ++i)
{
if (i - nMax < n)
{
if (data[i] >= data[nMax]) nMax = i;
}
else
{
nMax = i - n + 1;
for (j = nMax; j <= i; ++j) //计算区间最大值
{
if (data[j] >= data[nMax]) nMax = j;
}
}
result[i] = i - nMax;
}
}
return result;
}
/*
求上一低点到当前的周期数.
用法: LLVBARS(X,N):求N周期内X最低值到当前周期数,N=0表示从第一个有效值开始统计
例如: LLVBARS(HIGH,20)求得20日最低点到当前的周期数
*/
this.LLVBARS = function (data, n)
{
var result = [];
if (!Array.isArray(data)) return result;
if (Array.isArray(n))
{
for(var i=0;i<n.length;++i)
{
result[i]=null;
var period=n[i];
if (!this.IsNumber(period)) continue;
var start=i-period;
if (start<0) start=0;
var nMin=null;
var j=start;
for(; j<data.length;++j)
{
if (this.IsNumber(data[j]))
{
nMin=j;
break;
}
}
for(var k=0; j<data.length && k<period;++k, ++j)
{
if (data[j]<=data[nMin]) nMin=j;
}
if (nMin!=null)
result[i]=(i-nMin);
}
}
else
{
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 (let i = 0; i < stockData.Data.length; ++i)
{
stockProfit[i] = 0;
indexProfit[i] = 0;
var stockItem = stockData.Data[i];
var indexItem = indexData.Data[i];
if (stockItem.Close > 0 && stockItem.YClose > 0) stockProfit[i] = (stockItem.Close - stockItem.YClose) / stockItem.YClose;
if (indexItem.Close > 0 && indexItem.YClose > 0) indexProfit[i] = (indexItem.Close - indexItem.YClose) / indexItem.YClose;
}
//计算均值数组
var averageStockProfit = this.CalculateAverage(stockProfit, n);
var averageIndexProfit = this.CalculateAverage(indexProfit, n);
for (var i = 0, j = 0; i < stockData.Data.length; ++i)
{
result[i] = null;
if (i >= stockProfit.length || i >= indexProfit.length || i >= averageStockProfit.length || i >= averageIndexProfit.length) continue;
var averageStock = averageStockProfit[i];
var averageIndex = averageIndexProfit[i];
var covariance = 0; //协方差
var variance = 0; //方差
for (j = i - n + 1; j <= i; ++j)
{
var value = (indexProfit[j] - averageIndex);
var value2 = (stockProfit[j] - averageStock);
covariance += value * value2;
variance += value * value;
}
if (this.IsDivideNumber(variance) && this.IsNumber(covariance))
result[i] = covariance / variance; //(covariance/n)/(variance/n)=covariance/variance;
}
return result;
}
/*
用法:BETA2(X,Y,N)为X与Y的N周期相关放大系数,表示Y变化1%,则X将变化N%
例如:BETA2(CLOSE,INDEXC,10)表示收盘价与大盘指数之间的10周期相关放大率
*/
this.BETA2 = function (x, y, n)
{
var result = [];
if (n <= 0) n = 1;
var xProfit = [null]; //x数据的涨幅
var yProfit = [null]; //y数据的涨幅
var count = Math.max(x.length, y.length);
var lastItem = { X: x[0], Y: y[0] };
for (var i = 1; i < count; ++i)
{
xProfit[i] = 0;
yProfit[i] = 0;
var xItem = x[i];
var yItem = y[i];
if (lastItem.X > 0) xProfit[i] = (xItem - lastItem.X) / lastItem.X;
if (lastItem.Y > 0) yProfit[i] = (yItem - lastItem.Y) / lastItem.Y;
lastItem = { X: xItem, Y: yItem };
}
//计算均值数组
var averageXProfit = this.CalculateAverage(xProfit, n);
var averageYProfit = this.CalculateAverage(yProfit, n);
for (var i = 0, j = 0; i < count; ++i)
{
result[i] = null;
if (i >= xProfit.length || i >= yProfit.length || i >= averageXProfit.length || i >= averageYProfit.length) continue;
var averageX = averageXProfit[i];
var averageY = averageYProfit[i];
var covariance = 0; //协方差
var variance = 0; //方差
for (j = i - n + 1; j <= i; ++j)
{
var value = (xProfit[j] - averageX);
var value2 = (yProfit[j] - averageY);
covariance += value * value2;
variance += value * value;
}
if (this.IsDivideNumber(variance) && this.IsNumber(covariance))
result[i] = covariance / variance; //(covariance/n)/(variance/n)=covariance/variance;
}
return result;
}
/*
抛物转向.
用法:
SAR(N,S,M),N为计算周期,S为步长,M为极值
例如:
SAR(10,2,20)表示计算10日抛物转向,步长为2%,极限值为20%
*/
this.SAR = function (n, step, exValue)
{
var result = [];
var stockData = this.SymbolData.Data;
if (n >= stockData.Data.length) return result;
var high = null, low = null;
for (var i = 0; i < n; ++i)
{
var item = stockData.Data[i];
if (high == null) high = item.High;
else if (high < item.High) high = item = high;
if (low == null) low = item.Low;
else if (low > item.Low) low = item.Low;
}
const SAR_LONG = 0, SAR_SHORT = 1;
var position = SAR_LONG;
result[n - 1] = low;
var nextSar = low, sip = stockData.Data[0].High, af = exValue / 100;
for (var i = n; i < stockData.Data.length; ++i)
{
var ysip = sip;
var item = stockData.Data[i];
var yitem = stockData.Data[i - 1];
if (position == SAR_LONG)
{
if (item.Low < result[i - 1])
{
position = SAR_SHORT;
sip = item.Low;
af = step / 100;
nextSar = Math.max(item.High, yitem.High);
nextSar = Math.max(nextSar, ysip + af * (sip - ysip));
}
else
{
position = SAR_LONG;
if (item.High > ysip)
{
sip = item.High;
af = Math.min(af + step / 100, exValue / 100);
}
nextSar = Math.min(item.Low, yitem.Low);
nextSar = Math.min(nextSar, result[i - 1] + af * (sip - result[i - 1]));
}
}
else if (position == SAR_SHORT)
{
if (item.High > result[i - 1])
{
position = SAR_LONG;
sip = item.High;
af = step / 100;
nextSar = Math.min(item.Low, yitem.Low);
nextSar = Math.min(nextSar, result[i - 1] + af * (sip - ysip));
}
else
{
position = SAR_SHORT;
if (item.Low < ysip)
{
sip = item.Low;
af = Math.min(af + step / 100, exValue / 100);
}
nextSar = Math.max(item.High, yitem.High);
nextSar = Math.max(nextSar, result[i - 1] + af * (sip - result[i - 1]));
}
}
result[i] = nextSar;
}
return result;
}
/*
抛物转向点.
用法:
SARTURN(N,S,M),N为计算周期,S为步长,M为极值,若发生向上转向则返回1,若发生向下转向则返回-1,否则为0
其用法与SAR函数相同
*/
this.SARTURN = function (n, step, exValue)
{
var result = [];
var sar = this.SAR(n, step, exValue);
var stockData = this.SymbolData.Data;
var index = 0;
for (index = 0; index < sar.length; ++index)
{
if (this.IsNumber(sar[index])) break;
}
var flag = 0;
if (index < stockData.Data.length) flag = stockData.Data[index].Close > sar[index];
for (var i = index + 1; i < stockData.Data.length; ++i)
{
var item = stockData.Data[i];
if (item.Close < sar[i] && flag) result[i] = -1;
else result[i] = (item.Close > sar[i] && !flag) ? 1 : 0;
flag = item.Close > sar[i];
}
return result;
}
/*
属于未来函数,将当前位置到若干周期前的数据设为1.
用法:
BACKSET(X,N),若X非0,则将当前位置到N周期前的数值设为1.
例如:
BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0
*/
this.BACKSET = function (condition, n)
{
var result = [];
if (!condition) return result;
var dataCount = condition.length;
if (!this.IsNumber(dataCount) || dataCount <= 0) return result;
if (Array.isArray(n))
{
for(var i=0;i<dataCount;++i) //初始化0
{
result[i]=0;
}
for(var i=0;i<dataCount;++i)
{
var value=condition[i];
var period=n[i];
if (this.IsNumber(value) && value && this.IsNumber(period))
{
for(var j=i,k=0; j>=0 && k<period; --j,++k)
{
result[j]=1;
}
}
}
}
else
{
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;
}
//STRCAT(A,B):将两个字符串A,B(非序列化)相加成一个字符串C.
//用法: STRCAT('多头','开仓')将两个字符串'多头','开仓'相加成一个字符串'多头开仓'
this.STRCAT = function (str1, str2)
{
var result=[];
if (this.IsString(str1) && this.IsString(str2))
result=str1+str2;
return result;
}
//VARCAT(A,B):将两个字符串A,B相加成一个字符串C.
//用法: DRAWTEXT(CLOSE>OPEN,LOW,VARCAT('多头',VAR2STR(C,2))) 将两个字符串相加成一个字符串并按条件显示出来
this.VARCAT=function(data,data2)
{
var result=[];
if (Array.isArray(data) && Array.isArray(data2))
{
var nCount=Math.max(data.length, data2.length);
var strValue="";
for(var i=0;i<nCount;++i)
{
result[i]=null;
strValue="";
if (i<data.length)
{
var item=data[i];
if (this.IsString(item))
strValue+=item;
}
if (i<data2.length)
{
var item=data2[i];
if (this.IsString(item))
strValue+=item;
}
if (strValue!="")
result[i]=strValue;
}
}
else if (this.IsString(data) && Array.isArray(data2))
{
for(var i=0;i<data2.length;++i)
{
result[i]=null;
var item=data2[i];
if (this.IsString(item))
{
result[i]=data+item;
}
}
}
else if (Array.isArray(data) && this.IsString(data2))
{
for(var i=0;i<data.length;++i)
{
result[i]=null;
var item=data[i];
if (this.IsString(item))
{
result[i]=item+data2;
}
}
}
else if (this.IsString(data) && this.IsString(data2))
{
result=data+data2;
}
return result;
}
//STRSPACE(A):字符串附带一空格
this.STRSPACE=function(data)
{
var result=[];
if (Array.isArray(data))
{
for(var i=0;i<data.length;++i)
{
result[i]=null;
var item=data[i];
if (TouchList.IsString(item))
result[i]=item+' ';
}
}
else
{
if (this.IsString(data))
result=data+" ";
}
return result;
}
//CON2STR(A,N):取A最后的值(非序列值)转为字符串,小数位数N.
//用法: CON2STR(FINANCE(20),3)表示取营业收入,以3位小数转为字符串
this.CON2STR = function (data, n)
{
var result = [];
if (Array.isArray(data))
{
for (var i = data.length - 1; i >= 0; --i)
{
var item = data[i];
if (this.IsNumber(item))
{
result = item.toFixed(n);
return result;
}
}
}
else
{
if (this.IsNumber(data))
result = data.toFixed(n);
}
return result;
}
//VAR2STR(A,N):取A的每一个值转为字符串,小数位数N.
//用法: VAR2STR(C,3)表示取收盘价,以3位小数转为字符串
this.VAR2STR=function(data,n)
{
var result=[];
if (Array.isArray(data))
{
for(var i=0;i<data.length;++i)
{
result[i]=null;
var item=data[i];
if (this.IsNumber(item))
result[i]=item.toFixed(n);
}
}
else
{
if (this.IsNumber(data))
result=data.toFixed(n);
}
return result;
}
this.ZTPRICE = function (data, rate)
{
if (!this.IsNumber(rate)) return null;
if (Array.isArray(data))
{
var result = [];
for (var i in data)
{
var item = data[i];
if (this.IsNumber(item)) result[i] = (1 + rate) * item;
else result[i] = null;
}
return result;
}
else if (this.IsNumber(data))
{
var result = (1 + rate) * data;
return result;
}
}
this.DTPRICE = function (data, rate)
{
if (!this.IsNumber(rate)) return null;
if (Array.isArray(data))
{
var result = [];
for (var i in data)
{
var item = data[i];
if (this.IsNumber(item)) result[i] = (1 - rate) * item;
else result[i] = null;
}
return result;
}
else if (this.IsNumber(data))
{
var result = (1 - rate) * data;
return result;
}
}
/*
FRACPART(A) 取得小数部分
含义:FRACPART(A)返回数值的小数部分
阐释:例如FRACPART(12.3)求得0.3,FRACPART(-3.5)求得-0.5
*/
this.FRACPART = function (data)
{
if (Array.isArray(data))
{
var result = [];
var integer = 0;
for (var i in data)
{
var item = data[i];
if (this.IsNumber(item))
{
integer = parseInt(item);
result[i] = item - integer;
}
else result[i] = null;
}
return result;
}
else if (this.IsNumber(data))
{
integer = parseInt(data);
var result = data - integer;
return result;
}
}
/*
统计连续满足条件的周期数.
用法: BARSLASTCOUNT(X),统计连续满足X条件的周期数.
例如: BARSLASTCOUNT(CLOSE>OPEN)表示统计连续收阳的周期数
*/
this.BARSLASTCOUNT=function(data)
{
var result=null;
if (Array.isArray(data))
{
result=[];
if (data.length>0)
{
var count=0;
for(var i=data.length-1;i>=0;--i)
{
count=0;
for(var j=i;j>=0;--j)
{
if (data[j]) ++count;
else break;
}
result[i]=count;
}
}
}
else
{
if (data) result=1;
else result=0;
}
return result;
}
//取整.
//用法: INTPART(A)返回沿A绝对值减小方向最接近的整数
//例如:INTPART(12.3)求得12,INTPART(-3.5)求得-3
this.INTPART=function(data)
{
var result=null;
if (Array.isArray(data))
{
result=[];
for(var i in data)
{
var item=data[i];
if (this.IsNumber(item)) result[i]=parseInt(item);
else result[i]=null;
}
}
else if (this.IsNumber(data))
{
result=parseInt(data);
}
return result;
}
//函数调用
this.CallFunction=function(name,args,node)
{
switch(name)
{
case 'MAX':
return this.MAX(args[0], args[1]);
case 'MIN':
return this.MIN(args[0], args[1]);
case 'REF':
return this.REF(args[0], args[1]);
case "REFV":
return this.REFV(args[0], args[1]);
case 'REFX':
return this.REFX(args[0], args[1]);
case "REFXV":
return this.REFXV(args[0], args[1]);
case 'ABS':
return this.ABS(args[0]);
case 'MA':
return this.MA(args[0], args[1]);
case "EMA":
return this.EMA(args[0], args[1]);
case "SMA":
return this.SMA(args[0], args[1],args[2]);
case "DMA":
return this.DMA(args[0], args[1]);
case "XMA":
return this.XMA(args[0], args[1]);
case 'EXPMA':
return this.EXPMA(args[0], args[1]);
case 'EXPMEMA':
return this.EXPMEMA(args[0], args[1]);
case 'COUNT':
return this.COUNT(args[0], args[1]);
case 'LLV':
return this.LLV(args[0], args[1]);
case 'LLVBARS':
return this.LLVBARS(args[0], args[1]);
case 'HHV':
return this.HHV(args[0], args[1]);
case 'HHVBARS':
return this.HHVBARS(args[0], args[1]);
case 'MULAR':
return this.MULAR(args[0], args[1]);
case 'CROSS':
return this.CROSS(args[0], args[1]);
case 'LONGCROSS':
return this.LONGCROSS(args[0], args[1], args[2]);
case 'AVEDEV':
return this.AVEDEV(args[0], args[1]);
case 'STD':
return this.STD(args[0], args[1]);
case 'IF':
case 'IFF':
return this.IF(args[0], args[1], args[2]);
case 'IFN':
return this.IFN(args[0], args[1], args[2]);
case 'NOT':
return this.NOT(args[0]);
case 'SUM':
return this.SUM(args[0], args[1]);
case 'RANGE':
return this.RANGE(args[0],args[1],args[2]);
case 'EXIST':
return this.EXIST(args[0],args[1]);
case 'EXISTR':
return this.EXISTR(args[0], args[1], args[2]);
case 'FILTER':
return this.FILTER(args[0], args[1]);
case 'TFILTER':
return this.TFILTER(args[0],args[1],args[2]);
case 'SLOPE':
return this.SLOPE(args[0],args[1]);
case 'BARSLAST':
return this.BARSLAST(args[0]);
case 'BARSCOUNT':
return this.BARSCOUNT(args[0]);
case 'BARSSINCEN':
return this.BARSSINCEN(args[0], args[1]);
case 'BARSSINCE':
return this.BARSSINCE(args[0]);
case 'LAST':
return this.LAST(args[0], args[1], args[2]);
case 'EVERY':
return this.EVERY(args[0], args[1]);
case 'ZIG':
return this.ZIG(args[0], args[1]);
case 'TROUGHBARS':
return this.TROUGHBARS(args[0], args[1], args[2]);
case "TROUGH":
return this.TROUGH(args[0],args[1],args[2]);
case 'PEAKBARS':
return this.PEAKBARS(args[0], args[1], args[2]);
case 'PEAK':
return this.PEAK(args[0],args[1],args[2]);
case 'COST':
return this.COST(args[0], node);
case 'WINNER':
return this.WINNER(args[0], node);
case 'UPNDAY':
return this.UPNDAY(args[0], args[1]);
case 'DOWNNDAY':
return this.DOWNNDAY(args[0], args[1]);
case 'NDAY':
return this.NDAY(args[0], args[1], args[2]);
case 'DEVSQ':
return this.DEVSQ(args[0], args[1]);
case 'FORCAST':
return this.FORCAST(args[0], args[1]);
case 'STDP':
return this.STDP(args[0], args[1]);
case 'VAR':
return this.VAR(args[0], args[1]);
case 'VARP':
return this.VARP(args[0], args[1]);
case 'RELATE':
return this.RELATE(args[0], args[1], args[2]);
case 'COVAR':
return this.COVAR(args[0], args[1], args[2]);
case 'BETA':
return this.BETA(args[0]);
case 'BETA2':
return this.BETA2(args[0], args[1], args[2]);
case 'WMA':
return this.WMA(args[0], args[1]);
case 'MEMA':
return this.MEMA(args[0], args[1]);
case 'SUMBARS':
return this.SUMBARS(args[0], args[1]);
case 'REVERSE':
return this.REVERSE(args[0]);
case 'SAR':
return this.SAR(args[0], args[1], args[2]);
case 'SARTURN':
return this.SARTURN(args[0], args[1], args[2]);
case 'BACKSET':
return this.BACKSET(args[0], args[1]);
case 'STRCAT':
return this.STRCAT(args[0], args[1]);
case "VARCAT":
return this.VARCAT(args[0], args[1]);
case "VAR2STR":
return this.VAR2STR(args[0], args[1]);
case 'CON2STR':
return this.CON2STR(args[0], args[1]);
case "STRSPACE":
return this.STRSPACE(args[0]);
case 'DTPRICE':
return this.DTPRICE(args[0], args[1]);
case 'ZTPRICE':
return this.ZTPRICE(args[0], args[1]);
case 'FRACPART':
return this.FRACPART(args[0]);
case 'BARSLASTCOUNT':
return this.BARSLASTCOUNT(args[0]);
case 'INTPART':
return this.INTPART(args[0]);
//三角函数
case 'ATAN':
return this.Trigonometric(args[0], Math.atan);
case 'ACOS':
return this.ACOS(args[0]);
case 'ASIN':
return this.ASIN(args[0]);
case 'COS':
return this.Trigonometric(args[0], Math.cos);
case 'SIN':
return this.Trigonometric(args[0], Math.sin);
case 'TAN':
return this.Trigonometric(args[0], Math.tan);
case 'LN':
return this.Trigonometric(args[0], Math.log);
case 'LOG':
return this.Trigonometric(args[0], Math.log10);
case 'EXP':
return this.Trigonometric(args[0], Math.exp);
case 'SQRT':
return this.Trigonometric(args[0], Math.sqrt);
default:
this.ThrowUnexpectedNode(node,'函数'+name+'不存在');
}
}
//调用自定义函数 返回数据格式{Out:输出数据, Draw:绘图数据(可选)}
this.CallCustomFunction=function(name, args, symbolData, node)
{
var functionInfo=g_JSComplierResource.CustomFunction.Data.get(name);
var dwonloadData=symbolData.GetStockCacheData({ CustomName:name, Node:node });
if (!functionInfo.Invoke)
return { Out: dwonloadData }
JSConsole.Complier.Log('[JSAlgorithm::CallCustomFunction] call custom function functionInfo=',functionInfo);
var self=this;
var obj=
{
Name:name,
Args:args,
Symbol:symbolData.Symbol, Period:symbolData.Period, Right:symbolData.Right,
KData:symbolData.Data, //K线数据
DownloadData:dwonloadData,
ThrowError:function(error)
{
self.ThrowUnexpectedNode(node, error);
}
};
return functionInfo.Invoke(obj);
}
this.ThrowUnexpectedNode=function(node,message)
{
let marker=node.Marker;
let msg=message || "执行异常";
return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg);
}
}
//是否有是有效的数字
JSAlgorithm.prototype.IsNumber=function(value)
{
if (value==null) return false;
if (isNaN(value)) return false;
return true;
}
//是否是整形
JSAlgorithm.prototype.IsInteger=function(x)
{
return (typeof x === 'number') && (x % 1 === 0);
}
//是否有是有效的除数
JSAlgorithm.prototype.IsDivideNumber=function(value)
{
if (value==null) return false;
if (isNaN(value)) return false;
if (value==0) return false;
return true;
}
//是否是字符串
JSAlgorithm.prototype.IsString=function(value)
{
if (value && typeof(value)=='string') return true;
return false;
}
/*
绘图函数
*/
function JSDraw(errorHandler, symbolData)
{
this.ErrorHandler=errorHandler;
this.SymbolData = symbolData;
this.DRAWTEXT=function(condition,price,text)
{
let drawData=[];
let result={DrawData:drawData, DrawType:'DRAWTEXT',Text:text};
if (Array.isArray(condition))
{
var IsNumber=this.IsNumber(price);
for(var i in condition)
{
drawData[i]=null;
if (isNaN(condition[i]) || !condition[i]) continue;
if (IsNumber)
{
drawData[i]=price;
}
else
{
if (this.IsNumber(price[i])) drawData[i]=price[i];
}
}
}
else if (this.IsNumber(condition) && condition)
{
var IsNumber=this.IsNumber(price);
for(var i=0;i<this.SymbolData.Data.Data.length;++i)
{
if (IsNumber) drawData[i]=price;
else if (this.IsNumber(price[i])) drawData[i]=price[i];
}
}
return result;
}
this.DRAWTEXT_FIX=function(condition,x,y,type,text)
{
let result={Position:null, DrawType:'DRAWTEXT_FIX',Text:text};
if (condition.length<=0) return result;
for(var i in condition)
{
if (isNaN(condition[i]) || !condition[i]) continue;
result.Position={X:x, Y:y, Type:type};
return result;
}
return result;
}
//direction 文字Y轴位置 0=middle 1=价格的顶部 2=价格的底部
//offset 文字Y轴偏移
this.SUPERDRAWTEXT = function (condition, price, text, direction, offset)
{
let drawData = [];
let 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;
}
this.STICKLINE=function(condition,data,data2,width,type)
{
let drawData=[];
let result={DrawData:drawData, DrawType:'STICKLINE',Width:width, Type:type};
var IsNumber=typeof(data)=="number";
var IsNumber2=typeof(data2)=="number";
if (Array.isArray(condition)) //数组
{
if(condition.length<=0) return result;
for(var i in condition)
{
drawData[i]=null;
if (isNaN(condition[i]) || !condition[i]) continue;
if (IsNumber && IsNumber2)
{
drawData[i]={Value:data,Value2:data2};
}
else if (IsNumber && !IsNumber2)
{
if (isNaN(data2[i])) continue;
drawData[i]={Value:data,Value2:data2[i]};
}
else if (!IsNumber && IsNumber2)
{
if (isNaN(data[i])) continue;
drawData[i]={Value:data[i],Value2:data2};
}
else
{
if (isNaN(data[i]) || isNaN(data2[i])) continue;
drawData[i]={Value:data[i],Value2:data2[i]};
}
}
}
else
{
if(!condition) return result;
for(var i=0;i<this.SymbolData.Data.Data.length;++i) //以K线长度为数据长度
{
drawData[i]=null;
if (IsNumber && IsNumber2)
{
drawData[i]={Value:data,Value2:data2};
}
else if (IsNumber && !IsNumber2)
{
if (!this.IsNumber(data2[i])) continue;
drawData[i]={Value:data,Value2:data2[i]};
}
else if (!IsNumber && IsNumber2)
{
if (!this.IsNumber(data[i])) continue;
drawData[i]={Value:data[i],Value2:data2};
}
else
{
if (!this.IsNumber(data[i]) || !this.IsNumber(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)
{
let drawData=[];
let result={DrawData:drawData, DrawType:'DRAWLINE', Expand:expand};
if(condition.length<=0) return result;
let count=Math.max(condition.length,condition2.length);
let bFirstPoint=false;
let bSecondPont=false;
let lineCache={Start:{ },End:{ }, List:new Array()};
for(let i=0;i<count;++i)
{
drawData[i]=null;
if (i<condition.length && i<condition2.length)
{
if (bFirstPoint==false && bSecondPont==false)
{
if (condition[i]==null || !condition[i]) continue;
bFirstPoint=true;
lineCache.Start={ID:i, Value:data[i]}; //第1个点
}
else if (bFirstPoint==true && bSecondPont==false)
{
var bCondition2=(condition2[i]!=null && condition2[i]); //条件2
if (!bCondition2) continue;
if (bCondition2)
{
bSecondPont=true;
lineCache.End={ID:i, Value:data2[i]}; //第2个点
}
}
if (bFirstPoint==true && bSecondPont==true) //2个点都有了, 等待下一次的点出现
{
let lineData=this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值
for(let j in lineData)
{
let item=lineData[j];
drawData[item.ID]=item.Value;
}
bFirstPoint=bSecondPont=false;
lineCache={Start:{ },End:{ }};
}
}
}
if (expand==1) //右延长线
{
var x2=null;
for(var i=drawData.length-1;i>=0;--i)
{
if (this.IsNumber(drawData[i]))
{
x2=i;
break;
}
}
//y3=(y1-y2)*(x3-x1)/(x2-x1)
if (x2!=null && x2-1>=0)
{
var x1=x2-1;
for(var i=x2+1;i<drawData.length;++i)
{
var y1=drawData[x1];
var y2=drawData[x2];
var y3=(y1-y2)*(i-x1)/(x2-x1);
drawData[i]=y1-y3;
}
}
}
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)
{
let drawData=[];
let result = { DrawData: drawData, DrawType: 'DRAWBAND', Color: [color.toLowerCase(), color2.toLowerCase()]};
let count=Math.max(data.length, data2.length);
for(let i=0;i<count;++i)
{
let item={Value:null, Value2:null};
if (i<data.length) item.Value=data[i];
if (i<data2.length) item.Value2=data2[i];
drawData.push(item);
}
return result;
}
this.DRAWKLINE = function (high, open, low, close)
{
let drawData = [];
let result = { DrawData: drawData, DrawType: 'DRAWKLINE' };
let count = Math.max(high.length, open.length, low.length, close.length);
for (let i = 0; i < count; ++i)
{
let item = { Open: null, High: null, Low: null, Close: null };
if (i < high.length && i < open.length && i < low.length && i < close.length)
{
item.Open = open[i];
item.High = high[i];
item.Low = low[i];
item.Close = close[i];
}
drawData[i] = item;
}
return result;
}
//满足条件画一根K线
this.DRAWKLINE_IF = function (condition, high, open, low, close)
{
let drawData = [];
let result = { DrawData: drawData, DrawType: 'DRAWKLINE_IF' };
let count = Math.max(condition.length, high.length, open.length, low.length, close.length);
for (let i = 0; i < count; ++i) {
let item = { Open: null, High: null, Low: null, Close: null };
if (i < high.length && i < open.length && i < low.length && i < close.length && i < condition.length)
{
if (condition[i])
{
item.Open = open[i];
item.High = high[i];
item.Low = low[i];
item.Close = close[i];
}
}
drawData[i] = item;
}
return result;
}
/*
PLOYLINE 折线段
在图形上绘制折线段。
用法: PLOYLINE(COND,PRICE),当COND条件满足时,以PRICE位置为顶点画折线连接。
例如: PLOYLINE(HIGH>=HHV(HIGH,20),HIGH)表示在创20天新高点之间画折线。
*/
this.POLYLINE = function (condition, data)
{
let drawData = [];
let result = { DrawData: drawData, DrawType: 'POLYLINE' };
let isNumber = typeof (data) == 'number';
let bFirstPoint = false;
let bSecondPont = false;
if (isNumber)
{
for (let i in condition)
{
drawData[i] = null;
if (bFirstPoint == false)
{
if (!condition[i]) continue;
drawData[i] = data;
bFirstPoint = true;
}
else {
drawData[i] = data;
}
}
}
else
{
let lineCache = { Start: {}, End: {}, List: new Array() };
for (let i in condition)
{
drawData[i] = null;
if (bFirstPoint == false && bSecondPont == false)
{
if (condition[i] == null || !condition[i]) continue;
if (i >= data.length || !this.IsNumber(data[i])) continue;
bFirstPoint = true;
lineCache.Start = { ID: parseInt(i), Value: data[i] }; //第1个点
}
else if (bFirstPoint == true && bSecondPont == false)
{
if (condition[i] == null || !condition[i]) continue;
if (i >= data.length || !this.IsNumber(data[i])) continue;
lineCache.End = { ID: parseInt(i), Value: data[i] }; //第2个点
//根据起始点和结束点 计算中间各个点的数据
let lineData = this.CalculateDrawLine(lineCache); //计算2个点的线上 其他点的数值
for (let j in lineData)
{
let item = lineData[j];
drawData[item.ID] = item.Value;
}
let start = { ID: lineCache.End.ID, Value: lineCache.End.Value };
lineCache = { Start: start, End: {} };
}
}
}
return result
}
/*
画出数字.
用法:
DRAWNUMBER(COND,PRICE,NUMBER),当COND条件满足时,在PRICE位置书写数字NUMBER.
例如:
DRAWNUMBER(CLOSE/OPEN>1.08,LOW,C)表示当日实体阳线大于8%时在最低价位置显示收盘价.
*/
this.DRAWNUMBER = function (condition, data, data2)
{
let drawData = { Value: new Array(), Text: new Array() };
let result = { DrawData: drawData, DrawType: 'DRAWNUMBER' };
var isArrayData=Array.isArray(data);
let isNumber = typeof (data2) == 'number';
let text;
if (isNumber)
{
if (this.IsInteger(data2)) text=data2.toString();
else text=data2.toFixed(2);
}
for (let i in condition)
{
drawData.Value[i] = null;
if (!condition[i]) continue;
if (isArrayData)
{
if (i >= data.length || !this.IsNumber(data[i])) continue;
if (isNumber)
{
drawData.Value[i] = data[i];
drawData.Text[i] = text;
}
else
{
if (i >= data2.length || !data2[i]) continue;
drawData.Value[i] = data[i];
if (typeof(data2[i])=='number')
drawData.Text[i] = data2[i].toFixed(2);
else
drawData.Text[i] = data2[i].toString();
}
}
else if (this.IsNumber(data))
{
if (isNumber)
{
drawData.Value[i]=data;
drawData.Text[i]=text;
}
else
{
if (i>=data2.length || !data2[i]) continue;
drawData.Value[i]=data;
if (this.IsNumber(data2[i]))
drawData.Text[i] = data2[i].toFixed(2);
else
drawData.Text[i] = data2[i].toString();
}
}
}
return result;
}
/*
在图形上绘制小图标.
用法:
DRAWICON(COND,PRICE,TYPE),当COND条件满足时,在PRICE位置画TYPE号图标(TYPE为1--41).
例如:
DRAWICON(CLOSE>OPEN,LOW,1)表示当收阳时在最低价位置画1号图标.
*/
this.DRAWICON = function (condition, data, type)
{
//图标对应的字符代码
let mapIcon = new Map([
[1, { Symbol: '↑', Color: 'rgb(238,44,44)' }], [2, { Symbol: '↓', Color: 'rgb(0,139,69)' }],
[3, { Symbol: '😧' }], [4, { Symbol: '😨' }], [5, { Symbol: '😁' }], [6, { Symbol: '😱' }],
[7, { Symbol: '◼', Color: 'rgb(238,44,44)' }], [8, { Symbol: '◆', Color: 'rgb(0,139,69)' }],
[9, { Symbol: '💰' }], [10, { Symbol: '📪' }], [11, { Symbol: '👆' }], [12, { Symbol: '👇' }],
[13, { Symbol: 'B', Color: 'rgb(178,34,34)' },], [14, { Symbol: 'S', Color: 'rgb(0,139,69)' }],
[36, { Symbol: 'Χ', Color: 'rgb(238,44,44)' }], [37, { Symbol: 'X', Color: 'rgb(0,139,69)' }],
[38, { Symbol: '▲', Color: 'rgb(238,44,44)' }], [39, { Symbol: '▼', Color: 'rgb(0,139,69)' }],
]);
let icon = mapIcon.get(type);
if (!icon) icon = { Symbol: '●', Color: 'rgb(0,139,69)'};
let drawData = [];
let result = { DrawData: drawData, DrawType: 'DRAWICON', Icon: icon };
if (condition.length <= 0) return result;
var IsNumber = typeof (data) == "number";
if (typeof (condition) == 'number')
{
if (!condition) return result;
for (var i = 0; i < this.SymbolData.Data.Data.length; ++i)
{
if (IsNumber)
{
drawData[i] = data;
}
else
{
if (i < data.length && this.IsNumber(data[i])) drawData[i] = data[i];
else drawData[i] = null;
}
}
return result;
}
for (var i in condition)
{
drawData[i] = null;
if (!condition[i]) continue;
if (IsNumber)
{
drawData[i] = data;
}
else
{
if (this.IsNumber(data[i])) drawData[i] = data[i];
}
}
return result;
}
// 相对位置上画矩形.
//用法: DRAWRECTREL(LEFT,TOP,RIGHT,BOTTOM,COLOR),以图形窗口(LEFT,TOP)为左上角,(RIGHT,BOTTOM)为右下角绘制矩形,坐标单位是窗口沿水平和垂直方向的1/1000,取值范围是0—999,超出范围则可能显示在图形窗口外,矩形中间填充颜色COLOR,COLOR为0表示不填充.
//例如: DRAWRECTREL(0,0,500,500,RGB(255,255,0))表示在图形最左上部1/4位置用黄色绘制矩形
this.DRAWRECTREL = function (left, top, right, bottom, color)
{
let drawData =
{
Rect:
{
Left: Math.min(left,right), Top: Math.min(top,bottom),
Right: Math.max(left,right), Bottom: Math.max(top,bottom)
},
Color: color
};
if (color == 0) drawData.Color = null;
let result = { DrawData: drawData, DrawType: 'DRAWRECTREL' };
return result;
}
//填充背景.
//用法: DRAWGBK(COND,COLOR1,COLOR2,colorAngle) colorAngle=渐近色角度
//例如: DRAWGBK(O>C,RGB(0,255,0),RGB(255,0,0),0);
this.DRAWGBK=function(condition, color, color2, colorAngle)
{
let drawData={ Color:[], Angle:colorAngle };
if (color) drawData.Color.push(color);
if (color2) drawData.Color.push(color2);
let result={DrawData:null, DrawType:'DRAWGBK'};
if (Array.isArray(condition))
{
for(var i in condition)
{
var item=condition[i];
if (item)
{
result.DrawData=drawData;
break;
}
}
}
else
{
if (condition) result.DrawData=drawData;
}
return result;
}
this.DRAWGBK2=function(condition, color, color2, colorAngle)
{
let drawData={ Color:[], Angle:colorAngle };
if (color) drawData.Color.push(color);
if (color2) drawData.Color.push(color2);
let result={DrawData:null, DrawType:'DRAWGBK2'};
if (Array.isArray(condition))
{
drawData.Data=[];
for(var i in condition)
{
var item=condition[i];
drawData.Data[i]=item ? 1:0;
}
result.DrawData=drawData;
}
else
{
if (condition)
{
result.DrawData=drawData;
result.DrawType="DRAWGBK";
}
}
return result;
}
this.RGB = function (r, g, b)
{
var rgb = `rgb(${r},${g},${b})`;
return rgb;
}
this.RGBA = function (r, g, b, a)
{
var rgba = `rgba(${r},${g},${b},${a})`;
return rgba;
}
}
JSDraw.prototype.CalculateDrawLine=function(lineCache)
{
lineCache.List=[];
for(let i=lineCache.Start.ID; i<=lineCache.End.ID; ++i) lineCache.List.push(i);
let height=Math.abs(lineCache.Start.Value-lineCache.End.Value);
let width=lineCache.List.length-1;
var result=[];
result.push({ID:lineCache.Start.ID, Value:lineCache.Start.Value}); //第1个点
if (lineCache.Start.Value>lineCache.End.Value)
{
for(let i=1;i<lineCache.List.length-1;++i)
{
var value=height*(lineCache.List.length-1-i)/width+lineCache.End.Value;
result.push({ID:lineCache.List[i], Value:value});
}
}
else
{
for(let i=1;i<lineCache.List.length-1;++i)
{
var value=height*i/width+lineCache.Start.Value;
result.push({ID:lineCache.List[i], 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.IsInteger=function(x)
{
return (typeof x === 'number') && (x % 1 === 0);
}
JSDraw.prototype.IsDrawFunction=function(name)
{
let setFunctionName = new Set(["STICKLINE", "DRAWTEXT", 'SUPERDRAWTEXT', "DRAWTEXT_FIX", 'DRAWLINE', 'DRAWBAND', 'DRAWKLINE', 'DRAWKLINE_IF', 'PLOYLINE', 'POLYLINE', 'DRAWNUMBER', 'DRAWICON','DRAWRECTREL', "DRAWGBK", "DRAWGBK2"]);
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=分钟走势图数据
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.MaxReqeustDataCount=1000;
this.MaxRequestMinuteDayCount=5;
this.LatestData; //最新行情
this.IndexData; //大盘指数
this.MarginData = new Map(); //融资融券
this.NewsAnalysisData = new Map(); //新闻统计
this.ExtendData = new Map(); //其他的扩展数据
//股票数据缓存 key=函数名(参数) { Data: value=拟合的数据 , Error: }
//FinValue(id)
this.StockData=new Map();
this.NetworkFilter; //网络请求回调 function(data, callback);
//使用option初始化
if (option)
{
if (option.HQDataType) this.DataType = option.HQDataType;
if (option.Data)
{
this.Data=option.Data;
if (this.DataType != 2) //2=分钟走势图数据 没有周期和复权
{
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.Symbol) this.Symbol = option.Symbol;
if (option.MaxReqeustDataCount>0) this.MaxReqeustDataCount=option.MaxReqeustDataCount;
if (option.MaxRequestMinuteDayCount>0) this.MaxRequestMinuteDayCount=option.MaxRequestMinuteDayCount;
if (option.KLineApiUrl) this.KLineApiUrl=option.KLineApiUrl;
if (option.NetworkFilter) this.NetworkFilter = option.NetworkFilter;
}
//最新行情
this.GetLatestData=function()
{
if (this.LatestData) return this.Execute.RunNextJob();
var self=this;
wx.request({
url: self.RealtimeApiUrl,
data:
{
"field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"],
"symbol": [this.Symbol]
},
method:"POST",
dataType: "json",
success: function (recvData)
{
self.RecvLatestData(recvData);
self.Execute.RunNextJob();
},
error: function(request)
{
self.RecvError(request);
}
});
}
this.RecvLatestData = function (recvData)
{
let data=recvData.data;
if (!data.stock || data.stock.length!=1) return;
let stock=data.stock[0];
this.LatestData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time,
YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount,
Increase:stock.increase, Exchangerate:stock.exchangerate, Amplitude:stock.amplitude};
JSConsole.Complier.Log('[JSSymbolData::RecvLatestData]', this.LatestData);
}
this.GetLatestCacheData=function(dataname)
{
if (!this.LatestData) return null;
switch(dataname)
{
case DYNAINFO_ARGUMENT_ID.YCLOSE:
return this.LatestData.YClose;
case DYNAINFO_ARGUMENT_ID.OPEN:
return this.LatestData.Open;
case DYNAINFO_ARGUMENT_ID.HIGH:
return this.LatestData.High;
case DYNAINFO_ARGUMENT_ID.LOW:
return this.LatestData.Low;
case DYNAINFO_ARGUMENT_ID.VOL:
return this.LatestData.Vol;
case DYNAINFO_ARGUMENT_ID.AMOUNT:
return this.LatestData.Amount;
case DYNAINFO_ARGUMENT_ID.INCREASE:
return this.LatestData.Increase;
case DYNAINFO_ARGUMENT_ID.EXCHANGERATE:
return this.LatestData.Exchangerate;
case DYNAINFO_ARGUMENT_ID.AMPLITUDE:
return this.LatestData.Amplitude;
case DYNAINFO_ARGUMENT_ID.CLOSE:
return this.LatestData.Price;
default:
return null;
}
}
this.GetVolRateData = function (job, node) {
var volrKey = job.ID.toString() + '-VolRate-' + this.Symbol;
if (this.ExtendData.has(volrKey)) return this.Execute.RunNextJob();
var self = this;
wx.request({
url: self.RealtimeApiUrl,
data:
{
"field": ["name", "symbol", "avgvol5", 'date'],
"symbol": [this.Symbol]
},
method: "POST",
dataType: "json",
async: true,
success: function (recvData)
{
self.RecvVolRateData(recvData, volrKey);
self.Execute.RunNextJob();
},
error: function (request)
{
self.RecvError(request);
}
});
}
this.RecvVolRateData = function (recvData, key)
{
var data=recvData.data;
if (!data.stock || data.stock.length != 1) return;
var avgVol5 = data.stock[0].avgvol5;
var date = data.stock[0].date;
var item = { AvgVol5: avgVol5, Date: date };
this.ExtendData.set(key, item);
JSConsole.Complier.Log('[JSSymbolData::RecvVolRateData]', item);
}
this.GetVolRateCacheData = function (node)
{
var key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA.toString() + '-VolRate-' + this.Symbol;
if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node, '不支持VOLR');
var result = [];
var value = this.ExtendData.get(key);
var avgVol5 = value.AvgVol5 / 241;
var totalVol = 0;
//5日成交总量只取了最新一天的,历史的暂时没有取,所以数据计算的时候只计算最新的一天, 其他都空
for (var i = 0, j = 0; i < this.Data.Data.length; ++i)
{
result[i] = null;
var item = this.Data.Data[i];
var dateTime = item.DateTime; //日期加时间
if (!dateTime) continue;
var aryValue = dateTime.split(' ');
if (aryValue.length != 2) continue;
var date = parseInt(aryValue[0]);
if (date != value.Date) continue;
totalVol += item.Vol;
if (avgVol5 > 0) result[i] = totalVol / (j + 1) / avgVol5 * 100;
++j;
}
return result;
}
//获取大盘指数数据
this.GetIndexData=function()
{
if (this.IndexData) return this.Execute.RunNextJob();
var self=this;
if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) //请求日线数据
{
wx.request({
url: self.KLineApiUrl,
data:
{
"field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", 'up', 'down', 'stop', 'unchanged'],
"symbol": '000001.sh',
"start": -1,
"count": self.MaxReqeustDataCount+500 //多请求2年的数据 确保股票剔除停牌日期以后可以对上
},
method: 'POST',
dataType: "json",
success: function (recvData)
{
self.RecvIndexHistroyData(recvData);
self.Execute.RunNextJob();
},
error: function(request)
{
self.RecvError(request);
}
});
}
else if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据
{
wx.request({
url: self.MinuteKLineApiUrl,
data:
{
"field": ["name","symbol","yclose","open","price","high","low","vol"],
"symbol": '000001.sh',
"start": -1,
"count": self.MaxRequestMinuteDayCount+5
},
method: 'POST',
dataType: "json",
success: function (data)
{
self.RecvIndexMinuteHistroyData(data);
self.Execute.RunNextJob();
},
error: function(request)
{
self.RecvError(request);
}
});
}
}
this.RecvIndexHistroyData=function(recvData)
{
let data = recvData.data;
JSConsole.Complier.Log('[JSSymbolData::RecvIndexHistroyData] recv data' , data);
let hisData=this.JsonDataToHistoryData(data);
this.IndexData = new JSCommonData.ChartData();
this.IndexData.DataType=0; /*日线数据 */
this.IndexData.Data=hisData;
var aryOverlayData = this.SourceData.GetOverlayData(this.IndexData.Data); //和主图数据拟合以后的数据
this.IndexData.Data=aryOverlayData;
if (JSCommonData.ChartData.IsDayPeriod(this.Period, false)) //周期数据
{
let periodData=this.IndexData.GetPeriodData(this.Period);
this.IndexData.Data=periodData;
}
}
this.RecvIndexMinuteHistroyData = function (recvData)
{
let data = recvData.data;
JSConsole.Complier.Log('[JSSymbolData::RecvIndexMinuteHistroyData] recv data' , data);
let hisData=this.JsonDataToMinuteHistoryData(data);
this.IndexData = new JSCommonData.ChartData();
this.IndexData.DataType=1; /*分钟线数据 */
this.IndexData.Data=hisData;
if (JSCommonData.ChartData.IsMinutePeriod(this.Period, false)) //周期数据
{
let periodData=this.IndexData.GetPeriodData(this.Period);
this.IndexData.Data=periodData;
}
}
//获取大盘指数缓存数据
this.GetIndexCacheData=function(dataName)
{
if (!this.IndexData) return new Array();
switch(dataName)
{
case 'INDEXA':
return this.IndexData.GetAmount();
case 'INDEXC':
return this.IndexData.GetClose();
case 'INDEXH':
return this.IndexData.GetHigh();
case 'INDEXL':
return this.IndexData.GetLow();
case 'INDEXO':
return this.IndexData.GetOpen();
case 'INDEXV':
return this.IndexData.GetVol();
case 'INDEXADV':
return this.IndexData.GetUp();
case 'INDEXDEC':
return this.IndexData.GetDown();
}
}
//分钟涨幅股票个数统计数据下载
this.GetIndexIncreaseData = function (job)
{
var upKey = job.ID.toString() + '-UpCount-' + job.Symbol;
var downKey = job.ID.toString() + '-DownCount-' + job.Symbol;
if (this.ExtendData.has(upKey) && this.ExtendData.has(downKey)) return this.Execute.RunNextJob();
var symbol = job.Symbol;
symbol = symbol.replace('.CI', '.ci');
var self = this;
var apiUrl = g_JSComplierResource.CacheDomain + '/cache/analyze/increaseanalyze/' + symbol + '.json';
JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] Get url=', apiUrl);
wx.request({
url: apiUrl,
method: "GET",
dataType: "json",
success: function (data)
{
self.RecvMinuteIncreaseData(data, { UpKey: upKey, DownKey: downKey });
self.Execute.RunNextJob();
},
error: function (request)
{
self.RecvError(request);
}
});
}
this.RecvMinuteIncreaseData = function (recvData, key)
{
JSConsole.Complier.Log('[JSSymbolData::RecvMinuteIncreaseData] recv data', recvData);
var data=recvData.data;
if (!data.minute) return;
var minuteData = data.minute;
if (!minuteData.time || !minuteData.up || !minuteData.down) return;
var upData = [], downData = [];
for (var i = 0; i < minuteData.time.length; ++i) {
upData[i] = minuteData.up[i];
downData[i] = minuteData.down[i];
}
this.ExtendData.set(key.UpKey, upData);
this.ExtendData.set(key.DownKey, downData);
}
//分钟涨幅股票个数统计数据
this.GetIndexIncreaseCacheData = function (funcName, symbol, node) {
var key;
if (funcName == 'UPCOUNT') key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString() + '-UpCount-' + symbol;
else if (funcName == 'DOWNCOUNT') key = JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString() + '-DownCount-' + symbol;
if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node, '不支持函数' + funcName + '(' + symbol + ')');
return this.ExtendData.get(key);
}
this.GetSymbolData=function()
{
if (this.Data) return this.Execute.RunNextJob();
let self=this;
if (this.DataType === 2) //当天分钟数据
{
wx.request({
url: self.RealtimeApiUrl,
data:
{
"field": ["name", "symbol", "yclose", "open", "price", "high", "low", "vol", "amount", "date", "minute", "time", "minutecount"],
"symbol": [self.Symbol],
"start": -1
},
method: 'POST',
dataType: "json",
async: true,
success: function (recvData) {
self.RecvMinuteData(recvData);
self.Execute.RunNextJob();
}
});
return;
}
if (JSCommonData.ChartData.IsDayPeriod(this.Period,true)) //请求日线数据
{
wx.request({
url: self.KLineApiUrl,
data:
{
"field": [ "name", "symbol","yclose","open","price","high","low","vol"],
"symbol": self.Symbol,
"start": -1,
"count": self.MaxReqeustDataCount
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
self.RecvHistroyData(recvData);
self.Execute.RunNextJob();
},
error: function(request)
{
self.RecvError(request);
}
});
}
else if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true)) //请求分钟数据
{
wx.request({
url: this.MinuteKLineApiUrl,
data:
{
"field": ["name","symbol","yclose","open","price","high","low","vol"],
"symbol": self.Symbol,
"start": -1,
"count": self.MaxRequestMinuteDayCount
},
method: 'POST',
dataType: "json",
async:true,
success: function (data)
{
self.RecvMinuteHistroyData(data);
self.Execute.RunNextJob();
},
error: function(request)
{
self.RecvError(request);
}
});
}
}
this.RecvHistroyData=function(recvData)
{
let data=recvData.data;
JSConsole.Complier.Log('[JSSymbolData::RecvHistroyData] recv data' , data);
let hisData=this.JsonDataToHistoryData(data);
this.Data=new JSCommonData.ChartData();
this.Data.DataType=0; /*日线数据 */
this.Data.Data=hisData;
this.SourceData = new JSCommonData.ChartData;
this.SourceData.Data = hisData;
if (this.Right>0) //复权
{
let rightData=this.Data.GetRightDate(this.Right);
this.Data.Data=rightData;
}
if (JSCommonData.ChartData.IsDayPeriod(this.Period, false)) //周期数据
{
let periodData=this.Data.GetPeriodData(this.Period);
this.Data.Data=periodData;
}
this.Name = data.name;
}
this.RecvMinuteHistroyData = function (recvData)
{
let data = recvData.data;
JSConsole.Complier.Log('[JSSymbolData::RecvMinuteHistroyData] recv data' , data);
let hisData=this.JsonDataToMinuteHistoryData(data);
this.Data = new JSCommonData.ChartData();
this.Data.DataType=1; /*分钟线数据 */
this.Data.Data=hisData;
this.SourceData = new JSCommonData.ChartData;
this.SourceData.Data = hisData;
if (JSCommonData.ChartData.IsMinutePeriod(this.Period, false)) //周期数据
{
let periodData=this.Data.GetPeriodData(this.Period);
this.Data.Data=periodData;
}
this.Name = data.name;
}
//最新的分钟数据走势图
this.RecvMinuteData = function (recvData)
{
let data = recvData.data;
JSConsole.Complier.Log('[JSSymbolData::RecvMinuteData] recv data', data);
var aryMinuteData = this.JsonDataToMinuteData(data);
this.Data = new JSCommonData.ChartData();
this.Data.DataType = 2; /*分钟走势图数据 */
this.Data.Data = aryMinuteData;
this.Name = data.stock[0].name;
}
this.GetSymbolCacheData=function(dataName)
{
if (!this.Data) return new Array();
switch(dataName)
{
case 'CLOSE':
case 'C':
return this.Data.GetClose();
case 'VOL':
case 'V':
return this.Data.GetVol();
case 'OPEN':
case 'O':
return this.Data.GetOpen();
case 'HIGH':
case 'H':
return this.Data.GetHigh();
case 'LOW':
case 'L':
return this.Data.GetLow();
case 'AMOUNT':
case 'AMO':
return this.Data.GetAmount();
case 'VOLINSTK':
return this.Data.GetPosition();
}
}
this.GetCurrBarsCount=function()
{
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return new Array();
let lCount=this.Data.Data.length;
let result=[];
for(let i=lCount-1;i>=0;--i)
result.push(i);
return result;
}
this.GetIsLastBar = function ()
{
let result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result
let lCount = this.Data.Data.length;
for (let i = 0; i < lCount; ++i)
{
if (i == lCount - 1) result.push(1);
else result.push(0);
}
return result;
}
//融资融券函数
this.GetMarginCacheData = function (id, node)
{
let jobID = JS_EXECUTE_JOB_ID.GetMarginJobID(id);
if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持MARGIN(' + id + ')');
if (this.MarginData.has(jobID)) return this.MarginData.get(jobID);
return [];
}
//下融资融券
this.GetMarginData = function (jobID)
{
if (this.MarginData.has(jobID)) return this.Execute.RunNextJob();
JSConsole.Complier.Log('[JSSymbolData::GetMarginData] jobID=', jobID);
var self = this;
let fieldList = ["name", "date", "symbol"];
switch (jobID)
{
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE: //融资融券余额
fieldList.push("margin.balance");
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE: //融资占比
fieldList.push("margin.rate");
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入
fieldList.push("margin.buy");
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出
fieldList.push("margin.sell");
break;
}
//请求数据
wx.request({
url: this.StockHistoryDayApiUrl,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"orderfield": "date"
},
method: 'POST',
dataType: "json",
async: true,
success: function (recvData) {
self.RecvMarginData(recvData, jobID);
self.Execute.RunNextJob();
}
});
}
this.RecvMarginData = function (recvData, jobID)
{
var data = recvData.data;
//JSConsole.Complier.Log(data);
if (!data.stock || data.stock.length != 1) return;
let stock = data.stock[0];
var aryData = new Array();
var aryData2 = [], aryData3 = [], aryData4 = []; //其他3个数据
for (let i in stock.stockday)
{
var item = stock.stockday[i];
var marginData = item.margin;
if (!marginData) continue;
let indexData = new JSCommonData.SingleData();
indexData.Date = item.date;
switch (jobID)
{
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
if (!this.IsNumber(marginData.balance)) continue;
indexData.Value = marginData.balance; //融资融券余额
aryData.push(indexData);
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
if (!this.IsNumber(marginData.rate)) continue;
indexData.Value = marginData.rate; //融资占比
aryData.push(indexData);
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入
var buyData = marginData.buy;
if (!buyData) continue;
if (!this.IsNumber(buyData.balance) || !this.IsNumber(buyData.amount) || !this.IsNumber(buyData.repay) || !this.IsNumber(buyData.net)) continue;
indexData.Value = buyData.balance;
var indexData2 = new JSCommonData.SingleData();
indexData2.Date = item.date;
indexData2.Value = buyData.amount;
var indexData3 = new JSCommonData.SingleData();
indexData3.Date = item.date;
indexData3.Value = buyData.repay;
var indexData4 = new JSCommonData.SingleData();
indexData4.Date = item.date;
indexData4.Value = buyData.net;
aryData.push(indexData);
aryData2.push(indexData2);
aryData3.push(indexData3);
aryData4.push(indexData4);
break;
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出
var sellData = marginData.sell;
if (!sellData) continue;
if (!this.IsNumber(sellData.balance) || !this.IsNumber(sellData.volume) || !this.IsNumber(sellData.repay) || !this.IsNumber(sellData.net)) continue;
indexData.Value = buyData.balance;
var indexData2 = new JSCommonData.SingleData();
indexData2.Date = item.date;
indexData2.Value = buyData.volume;
var indexData3 = new JSCommonData.SingleData();
indexData3.Date = item.date;
indexData3.Value = buyData.repay;
var indexData4 = new JSCommonData.SingleData();
indexData4.Date = item.date;
indexData4.Value = buyData.net;
aryData.push(indexData);
aryData2.push(indexData2);
aryData3.push(indexData3);
aryData4.push(indexData4);
break;
default:
continue;
}
}
var allData = [];
if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE)
{
allData.push({ JobID: jobID, Data: aryData });
}
else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT ||
jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET)
{
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE, Data: aryData });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT, Data: aryData2 });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY, Data: aryData3 });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET, Data: aryData4 });
}
else if (jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME ||
jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY || jobID === JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET)
{
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE, Data: aryData });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME, Data: aryData2 });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY, Data: aryData3 });
allData.push({ JobID: JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET, Data: aryData4 });
}
for (let i in allData)
{
let aryFixedData = this.Data.GetFittingData(allData[i].Data);
var bindData = new JSCommonData.ChartData();
bindData.Data = aryFixedData;
bindData.Period = this.Period; //周期
if (bindData.Period > 0) //周期数据
{
var periodData = bindData.GetPeriodSingleData(bindData.Period);
bindData.Data = periodData;
}
let data = bindData.GetValue();
this.MarginData.set(allData[i].JobID, data);
}
}
this.GetNewsAnalysisCacheData = function (id, node)
{
let jobID = JS_EXECUTE_JOB_ID.GetNewsAnalysisID(id);
if (!jobID) this.Execute.ThrowUnexpectedNode(node, '不支持NEWS(' + id + ')');
if (this.NewsAnalysisData.has(jobID)) return this.NewsAnalysisData.get(jobID);
return [];
}
//下载新闻统计
this.GetNewsAnalysisData = function (jobID)
{
if (this.NewsAnalysisData.has(jobID)) return this.Execute.RunNextJob();
var self = this;
var mapFolder = new Map([
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE, "negative"],
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH, 'research'],
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT, 'interact'],
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE, 'holderchange'], //NEWS(4) 股东增持
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2, 'holderchange'], //NEWS(5) 股东减持
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER, 'trustholder'], //NEWS(6) 信托持股
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING, 'Blocktrading'], //NEWS(7) 大宗交易
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS, 'companynews'], //NEWS(8) 官网新闻
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS, 'topmanagers'], //NEWS(9) 高管要闻
[JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE, 'Pledge'], //NEWS(10) 股权质押
]);
if (!mapFolder.has(jobID))
{
this.Execute.RunNextJob();
return;
}
var folderName = mapFolder.get(jobID);
var url = this.StockNewsAnalysisApiUrl + '/' + folderName + '/' + this.Symbol + '.json';
//请求数据
wx.request({
url: url,
method: 'GET',
dataType: "json",
async: true,
success: function (recvData)
{
if (recvData.statusCode==200)
self.RecvNewsAnalysisData(recvData, jobID);
else
self.RecvNewsAnalysisDataError(recvData, jobID);
self.Execute.RunNextJob();
},
fail: function (request, textStatus)
{
//self.RecvNewsAnalysisDataError(request, textStatus, jobID);
self.Execute.RunNextJob();
}
});
}
this.RecvNewsAnalysisDataError = function (recvData, jobID)
{
JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisDataError] request error.', recvData.statusCode);
//没有新闻使用0数据填充
var aryData = [];
for (var i = 0; i < this.Data.Data.length; ++i)
{
var item = new JSCommonData.SingleData();
item.Date = this.Data.Data[i].Date;
item.Value = 0
aryData.push(item);
}
var bindData = new JSCommonData.ChartData();
bindData.Data = aryData;
this.NewsAnalysisData.set(jobID, bindData.GetValue());
}
this.RecvNewsAnalysisData = function (recvData, jobID)
{
var data=recvData.data;
if (!data.data || !data.date) return;
if (data.data.length <= 0 || data.data.length != data.date.length) return;
JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisData] jobID', jobID, data.update);
if (jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE || jobID == JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2)
{
var aryData = [], aryData2 = [];
for (var i = 0; i < data.data.length; ++i)
{
var item = new JSCommonData.SingleData();
item.Date = data.date[i];
item.Value = data.data[i];
if (this.IsNumber(item.Value)) aryData.push(item);
if (i < data.data2.length)
{
item = new JSCommonData.SingleData();
item.Date = data.date[i];
item.Value = data.data2[i];
if (this.IsNumber(item.Value)) aryData2.push(item);
}
}
let aryFixedData = this.Data.GetFittingData2(aryData, 0);
var bindData = new JSCommonData.ChartData();
bindData.Data = aryFixedData;
this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE, bindData.GetValue());
aryFixedData = this.Data.GetFittingData2(aryData2, 0);
bindData = new JSCommonData.ChartData();
bindData.Data = aryFixedData;
this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2, bindData.GetValue());
}
else
{
var aryData = [];
for (var i = 0; i < data.data.length; ++i)
{
var item = new JSCommonData.SingleData();
item.Date = data.date[i];
item.Value = data.data[i];
aryData.push(item);
}
let aryFixedData = this.Data.GetFittingData2(aryData, 0);
var bindData = new JSCommonData.ChartData();
bindData.Data = aryFixedData;
this.NewsAnalysisData.set(jobID, bindData.GetValue());
}
}
this.GetStockDataKey=function(jobItem, aryArgs)
{
var key=jobItem.FunctionName;
if (aryArgs.length>0)
{
key+="(";
for(var i=0;i<aryArgs.length;++i)
{
if (i>0) key+=",";
key+=aryArgs[i].toString();
}
key+=")";
}
return key;
}
this.GetFinOne=function(jobItem)
{
var aryArgs=this.JobArgumentsToArray(jobItem, 3);
var key=this.GetStockDataKey(jobItem,aryArgs);
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetFinOne', //类名::
Explain:'财务数据FINONE(ID,Y,MMDD)',
JobID:jobItem.ID,
Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
self.RecvStockValue(recvData,jobItem,key,1);
self.Execute.RunNextJob();
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
var apiDownload=new DownloadFinOneData(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
Args:aryArgs,
DataKey:key,
Callback:function(recvData, jobItem, key)
{
self.RecvStockValue(recvData, jobItem, key,1);
self.Execute.RunNextJob();
},
ErrorCallback:function(strError)
{
self.AddStockValueError(key,strError);
}
});
apiDownload.Download();
}
this.GetFinValue=function(jobItem)
{
var aryArgs=this.JobArgumentsToArray(jobItem, 1);
var lID=aryArgs[0];
var key=this.GetStockDataKey(jobItem,aryArgs);
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetFinValue', //类名::
Explain:'财务数据FINVALUE(ID)',
JobID:jobItem.ID,
Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
self.RecvStockValue(recvData,jobItem,key,0);
self.Execute.RunNextJob();
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
var apiDownload=new DownloadFinValueData(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
Args:aryArgs,
DataKey:key,
Callback:function(recvData, jobItem, key)
{
self.RecvStockValue(recvData, jobItem, key,0);
self.Execute.RunNextJob();
},
ErrorCallback:function(strError)
{
self.AddStockValueError(key,strError);
}
});
apiDownload.Download();
}
this.GetFinance=function(jobItem)
{
var aryArgs=this.JobArgumentsToArray(jobItem, 1);
var lID=aryArgs[0];
var key=this.GetStockDataKey(jobItem,aryArgs);
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetFinance', //类名::
Explain:'财务数据FINANCE(ID)',
JobID:jobItem.ID,
Request:{ Url:self.RealtimeApiUrl, Type:'POST', Data:{ id:lID, symbol: this.Symbol, daterange:dateRange } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
self.RecvStockValue(recvData,jobItem,key,0);
self.Execute.RunNextJob();
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
var apiDownload=new DownloadFinanceData(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
RealtimeUrl:this.RealtimeApiUrl,
Args:aryArgs,
DataKey:key,
Callback:function(recvData, jobItem, key)
{
self.RecvStockValue(recvData, jobItem, key,0);
self.Execute.RunNextJob();
},
ErrorCallback:function(strError)
{
self.AddStockValueError(key,strError);
}
});
apiDownload.Download();
}
this.GetVariantData=function(jobItem)
{
var key=jobItem.VariantName;
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetVariantData', //类名::
Explain:'变量数据下载',
JobID:jobItem.ID,
Request:{ Url:"www.121287.com", Type:'POST', Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
if (recvData.Error)
{
self.AddStockValueError(key,recvData.Error);
}
else
{
var dataType=0;
if (IFrameSplitOperator.IsNumber(recvData.DataType)) dataType=recvData.DataType;
self.RecvStockValue(recvData.Data,jobItem,key,dataType);
}
self.Execute.RunNextJob();
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
var errorCallback=function(strError)
{
self.AddStockValueError(key,strError);
};
var apiDownload;
if (jobItem.VariantName=="CAPITAL" || jobItem.VariantName=="TOTALCAPITAL" || jobItem.VariantName=="EXCHANGE")
{
var callback=function(recvData, jobItem, key)
{
self.RecvStockValue(recvData, jobItem, key,0);
self.Execute.RunNextJob();
};
apiDownload=new DownloadFinanceData(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
RealtimeUrl:this.RealtimeApiUrl,
Args:[jobItem.VariantName],
DataKey:key,
Callback:callback,
ErrorCallback:errorCallback
});
}
else if (jobItem.VariantName=="HYBLOCK" || jobItem.VariantName=="DYBLOCK" || jobItem.VariantName=="GNBLOCK")
{
var callback=function(recvData, jobItem, key, dataType)
{
self.RecvStockValue(recvData, jobItem, key, dataType);
self.Execute.RunNextJob();
};
apiDownload=new DownloadGroupData(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
RealtimeUrl:this.RealtimeApiUrl,
Args:[jobItem.VariantName],
DataKey:key,
Callback:callback,
ErrorCallback:errorCallback
});
}
else if (jobItem.VariantName=="INBLOCK")
{
var errorMessage=`${jobItem.VariantName}, 请对接外部数据.`;
this.AddStockValueError(key,errorMessage);
this.Execute.RunNextJob();
return;
}
else
{
var errorMessage=`不支持变量${jobItem.VariantName}, 请对接外部数据.`;
this.AddStockValueError(key,errorMessage);
this.Execute.RunNextJob();
return;
}
apiDownload.Download();
}
this.GetGPJYValue=function(jobItem)
{
var aryArgs=this.JobArgumentsToArray(jobItem, 3);
var key=this.GetStockDataKey(jobItem,aryArgs);
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var self=this;
//TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理
var dataType=aryArgs[2]==1?0:2;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetGPJYValue', //类名::
Explain:'股票交易类数据GPJYVALUE(ID,N,TYPE)',
JobID:jobItem.ID,
Request:{ Url:self.StockHistoryDayApiUrl, Type:'POST', Data:{ Args:aryArgs, symbol: this.Symbol, daterange:dateRange } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
self.RecvStockValue(recvData,jobItem,key,dataType);
self.Execute.RunNextJob();
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
var apiDownload=new DownloadGPJYValue(
{
Job:jobItem,
Symbol:this.Symbol,
Url:this.StockHistoryDayApiUrl,
Args:aryArgs,
DataKey:key,
Callback:function(recvData, jobItem, key)
{
self.RecvStockValue(recvData, jobItem, key,dataType);
self.Execute.RunNextJob();
},
ErrorCallback:function(strError)
{
self.AddStockValueError(key,strError);
}
});
apiDownload.Download();
}
//自定义变量数据下载
this.GetCustomVariantData=function(jobItem)
{
var key=jobItem.VariantName;
if (this.StockData.has(key)) return this.Execute.RunNextJob();
var variantInfo=g_JSComplierResource.CustomVariant.Data.get(key);
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetCustomVariantData', //类名::函数名
Explain:'自定义变量数据下载',
JobID:jobItem.ID,
Request:{ Url:"www.121287.com", Type:'POST', Data:{ VariantName:jobItem.VariantName, symbol: this.Symbol, daterange:dateRange } },
Self:this,
VariantInfo:variantInfo,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
if (recvData.Error) self.AddStockValueError(key,recvData.Error);
else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType);
self.Execute.RunNextJob();
});
}
else
{
this.AddStockValueError(key, `自定义变量${key}下载失败`);
this.Execute.RunNextJob();
}
}
this.GetCustomFunctionData=function(jobItem)
{
var key=jobItem.FunctionName;
var functionInfo=g_JSComplierResource.CustomFunction.Data.get(key);
if (!functionInfo.IsDownload) return this.Execute.RunNextJob();
if (this.StockData.has(key)) return this.Execute.RunNextJob(); //一个函数只能缓存一个数据, 保存多个外部自己保存
var self=this;
if (this.NetworkFilter)
{
var dateRange=this.Data.GetDateRange();
var obj=
{
Name:'JSSymbolData::GetCustomFunctionData', //类名::函数名
Explain:'自定义函数数据下载',
JobID:jobItem.ID,
Request:
{
Url:"www.121287.com", Type:'POST',
Data:
{
FunctionName:jobItem.FunctionName,
symbol: this.Symbol, daterange:dateRange,
JobItem:jobItem //函数编译信息
}
},
Self:this,
FunctionInfo:functionInfo,
PreventDefault:false
};
this.NetworkFilter(obj, function(recvData)
{
if (recvData.Error) self.AddStockValueError(key,recvData.Error);
else self.RecvStockValue(recvData.Data,jobItem,key,recvData.DataType);
self.Execute.RunNextJob();
});
}
else
{
this.AddStockValueError(key, `自定义函数${key}下载失败`);
this.Execute.RunNextJob();
}
}
this.RecvStockValue=function(recvData,jobItem,key,dataType)
{
if (!recvData)
{
//JSConsole.Complier.Log(`[JSSymbolData::RecvStockValue] key=${key} data is null`);
return;
}
if (dataType==0)
{
if (Array.isArray(recvData))
{
var kdata=this.Data; //K线
var aryFittingData;
if (JSCommonData.ChartData.IsDayPeriod(this.Period,true))
aryFittingData=kdata.GetFittingFinanceData(recvData); //数据和主图K线拟合
else if (JSCommonData.ChartData.IsMinutePeriod(this.Period,true))
aryFittingData=kdata.GetMinuteFittingFinanceData(recvData); //数据和主图K线拟合
else
return;
var bindData=new JSCommonData.ChartData();
bindData.Data=aryFittingData;
var result=bindData.GetValue();
if (key=="EXCHANGE") //计算换手率=成交量/流通股本*100
{
for(var i in result)
{
var kitem=kdata.Data[i];
if (result[i]>0)
result[i]=kitem.Vol/result[i] * 100;
}
}
this.StockData.set(key,{ Data:result });
}
else
{
this.StockData.set(key,{ Data:recvData.Value });
}
}
else if (dataType==1) //单数值
{
this.StockData.set(key,{ Data:recvData.Value });
}
else if (dataType==2) //数据不做平滑处理
{
var kdata=this.Data; //K线
var aryFittingData;
if (JSCommonData.ChartData.IsDayPeriod(this.Period,true))
aryFittingData=kdata.GetFittingTradeData(recvData, 0, false); //数据和主图K线拟合
else if (JSCommonData.ChartData.IsMinutePeriod(this.Period,true))
aryFittingData=kdata.GetMinuteFittingTradeData(recvData, 0, false); //数据和主图K线拟合
else
return;
var bindData=new JSCommonData.ChartData();
bindData.Data=aryFittingData;
var result=bindData.GetValue();
this.StockData.set(key,{ Data:result });
}
}
this.AddStockValueError=function(key, message)
{
this.StockData.set(key,{ Error:message });
}
this.GetStockCacheData=function(obj)
{
var key;
if (obj.FunctionName)
key=this.GetStockDataKey({FunctionName:obj.FunctionName}, obj.Args);
else if (obj.VariantName)
key=obj.VariantName;
else if (obj.CustomName)
key=obj.CustomName; //自定义名字
else
return null;
if (!this.StockData.has(key)) return null;
var data=this.StockData.get(key);
if (data.Error) this.Execute.ThrowUnexpectedNode(obj.Node, data.Error);
return data.Data;
}
this.IsInBlock=function(blockName, node)
{
var data=this.GetStockCacheData({ VariantName:"INBLOCK", Node:node });
if (!data) return 0;
var aryBlock=data.split('|');
for(var i=0; i<aryBlock.length; ++i)
{
var item=aryBlock[i];
if (item==blockName) return 1;
}
return 0;
}
this.JobArgumentsToArray=function(job, lCount)
{
var args=job.Args;
if (args.length!=lCount)
{
var token=job.Token;
this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName}() Error: argument count error.`);
}
var aryValue=[];
for(var i=0;i<args.length;++i)
{
var item=args[i];
if (this.IsNumber(item))
{
aryValue.push(item);
}
else if (item.Type==Syntax.Literal)
{
aryValue.push(item.Value);
}
else if (item.Type==Syntax.Identifier) //变量 !!只支持默认的变量值
{
var isFind=false;
for(var j in this.Arguments)
{
const argItem=this.Arguments[j];
if (argItem.Name==item.Name)
{
aryValue.push(argItem.Value);
isFind=true;
break;
}
}
if (!isFind)
{
var token=job.Token;
this.Execute.ErrorHandler.ThrowError(token.Index,token.Line,0,`${job.FunctionName}() Error: can't read ${item.Name}`);
}
}
}
return aryValue;
}
this.DownloadCustomAPIData = function (job)
{
if (!this.NetworkFilter) return this.Execute.RunNextJob();
var args = [];
for (var i in job.Args)
{
var item = job.Args[i];
if (item.Type == Syntax.Literal)
{
args.push(item.Value);
}
else if (item.Type == Syntax.Identifier) //变量 !!只支持默认的变量值
{
var isFind = false;
for (var j in this.Arguments)
{
const argItem = this.Arguments[j];
if (argItem.Name == item.Name)
{
args.push(argItem.Value);
isFind = true;
break;
}
}
if (!isFind)
{
var token = job.Token;
this.Execute.ErrorHandler.ThrowError(token.Index, token.Line, 0, `LoadAPIData() Error: can't read ${item.Name}`);
}
}
else
{
return this.Execute.RunNextJob();
}
}
var self = this;
var obj =
{
Name: 'JSSymbolData::DownloadCustomAPIData', //类名::函数名
Explain: '下载自定义api数据',
Period: this.Period,
Right: this.Right,
Symbol: this.Symbol,
KData: this.Data, //K线数据
Cache: this.CustomAPIData,
Args: args,
Self: this,
PreventDefault: false
};
this.NetworkFilter(obj, function (data) {
self.RecvCustomAPIData(data, args);
self.Execute.RunNextJob();
});
if (obj.PreventDefault == true) return; //已被上层替换,不调用默认的网络请求
this.Execute.RunNextJob();
}
this.RecvCustomAPIData = function (recvData, args)
{
if (!recvData || !recvData.data) return;
var data = recvData.data;
var apiKey = this.GenerateCustomAPIKey(args);
if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true))
{
if (!data.date || !data.time) return;
var date = data.date;
var time = data.time;
for (var key in data)
{
if (key == 'date' || key == 'time') continue;
var item = data[key];
}
}
else if (JSCommonData.ChartData.IsDayPeriod(this.Period, true))
{
if (!data.date) return;
var date = data.date;
var result = { __Type__: "Object" };
for (var key in data) {
if (key == 'date') continue;
var item = data[key];
if (Array.isArray(item))
{
var value = this.FittingCustomAPIArray(item, date);
result[key] = value;
}
else if (this.IsNumber(item))
{
result[key] = item;
}
}
this.CustomAPIData.set(apiKey, result);
}
}
this.FittingCustomAPIArray = function (data, date, time)
{
var kdata = this.Data; //K线
var arySingleData = [];
for (var i in data)
{
var value = data[i];
var indexItem = new JSCommonData.SingleData(); //单列指标数据
indexItem.Date = date[i];
if (time && i < time.length) indexItem.Time = time[i];
indexItem.Value = value;
arySingleData.push(indexItem);
}
var aryFittingData;
if (JSCommonData.ChartData.IsDayPeriod(this.Period, true))
aryFittingData = kdata.GetFittingData(arySingleData); //数据和主图K线拟合
else if (JSCommonData.ChartData.IsMinutePeriod(this.Period, true))
aryFittingData = kdata.GetMinuteFittingData(arySingleData); //数据和主图K线拟合
else
return null;
var bindData = new JSCommonData.ChartData();
bindData.Data = aryFittingData;
var result = bindData.GetValue();
return result;
}
this.JsonDataToHistoryData=function(data)
{
var list = data.data;
var aryDayData=new Array();
var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7;
var up = 8, down = 9, stop = 10, unchanged = 11;
for (var i = 0; i < list.length; ++i)
{
var item = new JSCommonData.HistoryData();
item.Date = list[i][date];
item.Open = list[i][open];
item.YClose = list[i][yclose];
item.Close = list[i][close];
item.High = list[i][high];
item.Low = list[i][low];
item.Vol = list[i][vol]; //原始单位股
item.Amount = list[i][amount];
if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除
//上涨 下跌家数
if (list[i].length > up) item.Up = list[i][up];
if (list[i].length > down) item.Down = list[i][down];
if (list[i].length > stop) item.Stop = list[i][stop];
if (list[i].length > unchanged) item.Unchanged = list[i][unchanged];
aryDayData.push(item);
}
return aryDayData;
}
this.JsonDataToMinuteHistoryData=function(data)
{
var list = data.data;
var aryDayData=new Array();
var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8;
for (var i = 0; i < list.length; ++i)
{
let item = new JSCommonData.HistoryData();
item.Date = list[i][date];
item.Open = list[i][open];
item.YClose = list[i][yclose];
item.Close = list[i][close];
item.High = list[i][high];
item.Low = list[i][low];
item.Vol = list[i][vol]; //原始单位股
item.Amount = list[i][amount];
item.Time=list[i][time];
// if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除
aryDayData.push(item);
}
// 无效数据处理
for(let i = 0; i < aryDayData.length; ++i)
{
var minData = aryDayData[i];
if (minData == null) coninue;
if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0
|| isNaN(minData.Close) || minData.Close <= 0 || isNaN(minData.YClose) || minData.YClose <= 0)
{
if (i == 0)
{
if (minData.YClose > 0)
{
minData.Open = minData.YClose;
minData.High = minData.YClose;
minData.Low = minData.YClose;
minData.Close = minData.YClose;
}
}
else // 用前一个有效数据填充
{
for(let j = i-1; j >= 0; --j)
{
var minData2 = aryDayData[j];
if (minData2 == null) coninue;
if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0)
{
if (minData.YClose <= 0) minData.YClose = minData2.Close;
minData.Open = minData2.Open;
minData.High = minData2.High;
minData.Low = minData2.Low;
minData.Close = minData2.Close;
break;
}
}
}
}
}
return aryDayData;
}
//API 返回数据 转化为array[]
this.JsonDataToMinuteData = function (data)
{
var aryMinuteData = new Array();
for (var i in data.stock[0].minute)
{
var jsData = data.stock[0].minute[i];
var item = new JSCommonData.MinuteData();
item.Close = jsData.price;
item.Open = jsData.open;
item.High = jsData.high;
item.Low = jsData.low;
item.Vol = jsData.vol; //股
item.Amount = jsData.amount;
if (i == 0) //第1个数据 写死9:25
item.DateTime = data.stock[0].date.toString() + " 0925";
else
item.DateTime = data.stock[0].date.toString() + " " + jsData.time.toString();
item.Date = data.stock[0].date;
item.Time = jsData.time;
item.Increate = jsData.increate;
item.Risefall = jsData.risefall;
item.AvPrice = jsData.avprice;
aryMinuteData[i] = item;
}
return aryMinuteData;
}
//CODELIKE 模糊股票代码
this.CODELIKE=function(value)
{
if (this.Symbol.indexOf(value)==0) return 1;
return 0;
}
this.NAMELIKE = function (value)
{
if (this.Name && this.Name.indexOf(value) == 0) return 1;
return 0;
}
/*
SETCODE 市场类型
0:深圳 1:上海,47:中金所期货 28:郑州商品 29:大连商品 30:上海商品,27:香港指数 31:香港主板,48:香港创业板...
*/
this.SETCODE=function()
{
if (this.Symbol.indexOf('.sh')) return 1;
if (this.Symbol.indexOf('.sz')) return 0;
return 0;
}
this.GetSymbol = function () { return this.Symbol; }
this.GetName = function () { return this.Name; }
this.TIME=function()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
for(let i in this.Data.Data)
{
var item=this.Data.Data[i];
if (this.IsNumber(item.Time))
result[i]=item.Time;
else
result[i]=0;
}
return result;
}
this.DATE = function ()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
for (let i in this.Data.Data)
{
var item = this.Data.Data[i];
result[i] = item.Date - 19000000;;
}
return result;
}
this.YEAR = function ()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
for (let i in this.Data.Data)
{
var item = this.Data.Data[i];
if (this.IsNumber(item.Date))
result[i] = parseInt(item.Date / 10000);
else
result[i] = null;
}
return result;
}
this.MONTH = function ()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
for (let i in this.Data.Data)
{
var item = this.Data.Data[i];
if (this.IsNumber(item.Date))
result[i] = parseInt(item.Date % 10000 / 100);
else
result[i] = null;
}
return result;
}
//星期 1-7
this.WEEK = function ()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
var tempDate = new Date();
for (let i in this.Data.Data)
{
var item = this.Data.Data[i];
result[i] = null;
if (!this.IsNumber(item.Date)) continue;
var year = parseInt(item.Date / 10000);
var month = parseInt(item.Date % 10000 / 100);
var day = item.Date % 100;
tempDate.setFullYear(year);
tempDate.setMonth(month - 1);
tempDate.setDate(day);
result[i] = tempDate.getDay();
}
return result;
}
this.REFDATE = function (data, date)
{
var result = null;
var findDate=null;
if (Array.isArray(date))
{
if (date.length>0) findDate=date[date.length-1];
}
else if (this.IsNumber(date))
{
findDate=date;
}
if (findDate==null) return null;
if (findDate<5000000) findDate+=19000000;
var index = null;
for (let i in this.Data.Data) //查找日期对应的索引
{
if (this.Data.Data[i].Date == findDate)
{
index = parseInt(i);
break;
}
}
if (index == null || index >= data.length) return null;
return data[index];
}
//用法:结果从0到11,依次分别是1/5/15/30/60分钟,日/周/月,多分钟,多日,季,年
this.PERIOD=function()
{
//Period周期 0=日线 1=周线 2=月线 3=年线 9=季线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟
const PERIOD_MAP=[5,6,7,11, 0,1,2,3,4,5, 9];
if (this.Period >= 0 && this.Period <= PERIOD_MAP.length - 1)
return PERIOD_MAP[this.Period];
return this.Period;
}
this.GetDrawNull = function ()
{
var result = [];
if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;
for (let i in this.Data.Data)
{
result[i] = null;
}
return result;
}
}
//是否有是有效的数字
JSSymbolData.prototype.IsNumber = function (value)
{
if (value == null) return false;
if (isNaN(value)) return false;
return true;
}
JSSymbolData.prototype.IsDivideNumber = function (value)
{
if (value == null) return false;
if (isNaN(value)) return false;
if (value == 0) return false;
return true;
}
JSSymbolData.prototype.JsonDataToFinance = function (data)
{
var financeData;
for (let i = 1; i <= 4; ++i)
{
switch (i)
{
case 1:
var finance = data.finance1;
var announcement = data.announcement1;
break;
case 2:
var finance = data.finance2;
var announcement = data.announcement2;
break;
case 3:
var finance = data.finance3;
var announcement = data.announcement3;
break;
case 4:
var finance = data.finance4;
var announcement = data.announcement4;
break;
default:
break;
}
if (!finance || !announcement || !this.IsNumber(announcement.year) || !this.IsNumber(announcement.quarter)) continue;
if (financeData) //如果存在1天公布多个报告期数据 只取最新的一个公告期数据
{
if (financeData.Announcement.year < announcement.year)
financeData = { Date: item.date, Finance: finance, Announcement: announcement };
}
else
{
financeData = { Date: data.date, Finance: finance, Announcement: announcement };
}
}
return financeData;
}
var JS_EXECUTE_DEBUG_LOG=false;
var JS_EXECUTE_JOB_ID=
{
JOB_DOWNLOAD_SYMBOL_DATA:1, //下载股票的K线数据
JOB_DOWNLOAD_INDEX_DATA:2, //下载大盘的K线数据
JOB_DOWNLOAD_SYMBOL_LATEST_DATA:3, //最新的股票行情数据
JOB_DOWNLOAD_INDEX_INCREASE_DATA: 4, //涨跌股票个数统计数据
JOB_DOWNLOAD_VOLR_DATA: 5, //5日量比均量下载量比数据
JOB_DOWNLOAD_FINVALUE:301, //引用专业财务数据 FINVALUE(ID),ID为数据编号
JOB_DOWNLOAD_FINONE:302, //引用指定年和月日的某类型的财务数据 FINONE(ID,Y,MMDD),ID为数据编号,Y和MMDD表示年和月日.
JOB_DOWNLOAD_FINANCE:303, //FINANCE(ID) 基础财务数据
JOB_DOWNLOAD_GPJYVALUE:304, //引用股票交易类数据 GPJYVALUE(ID,N,TYPE),ID为数据编号,N表示第几个数据,TYPE:为1表示做平滑处理,没有数据的周期返回上一周期的值;为0表示不做平滑处理
JOB_DOWNLOAD_VARIANT:305, //CAPITAL , TOTALCAPITAL, EXCHANGE
JOB_CUSTOM_FUNCTION_DATA:6000, //自定义函数
JOB_CUSTOM_VARIANT_DATA:6001, //自定义变量
JOB_DOWNLOAD_MARGIN_BALANCE: 1000, //融资融券余额
JOB_DOWNLOAD_MARGIN_RATE: 1001, //融资占比
JOB_DOWNLOAD_MARGIN_BUY_BALANCE: 1010, //买入信息-融资余额
JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: 1011, //买入信息-买入额
JOB_DOWNLOAD_MARGIN_BUY_REPAY: 1012, //买入信息-偿还额
JOB_DOWNLOAD_MARGIN_BUY_NET: 1013, //买入信息-融资净买入
JOB_DOWNLOAD_MARGIN_SELL_BALANCE: 1020, //卖出信息-融券余量
JOB_DOWNLOAD_MARGIN_SELL_VOLUME: 1021, //卖出信息-卖出量
JOB_DOWNLOAD_MARGIN_SELL_REPAY: 1022, //卖出信息-偿还量
JOB_DOWNLOAD_MARGIN_SELL_NET: 1023, //卖出信息-融券净卖出
JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: 2000, //负面新闻统计
JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: 2001, //机构调研
JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: 2002, //互动易
JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: 2003, //股东增持
JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: 2004, //股东减持
JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: 2005, //信托持股
JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: 2006, //大宗交易
JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: 2007, //官网新闻
JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: 2008, //高管要闻
JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: 2009, //股权质押
JOB_CUSTOM_FUNCTION_DATA: 6000, //自定义函数
JOB_CUSTOM_VARIANT_DATA: 6001, //自定义变量
JOB_DOWNLOAD_CUSTOM_API_DATA: 30000, //自定义数据
JOB_RUN_SCRIPT:10000, //执行脚本
//融资融券
GetMarginJobID: function (value)
{
let dataMap = new Map([
[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE], //MARGIN(1) 融资融券余额
[2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE], //MARGIN(2) 融资占比
[3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE], //MARGIN(3) 买入信息-融资余额
[4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT], //MARGIN(4) 买入信息-买入额
[5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY], //MARGIN(5) 买入信息-偿还额
[6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET], //MARGIN(6) 买入信息-融资净买入
[7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE], //MARGIN(7) 卖出信息-融券余量
[8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME], //MARGIN(8) 卖出信息-卖出量
[9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY], //MARGIN(9) 卖出信息-偿还量
[10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET], //MARGIN(10) 卖出信息-融券净卖出
]);
if (dataMap.has(value)) return dataMap.get(value);
return null;
},
GetNewsAnalysisID: function (value)
{
let dataMap = new Map([
[1, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE], //NEWS(1) 负面新闻统计
[2, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH], //NEWS(2) 机构调研统计
[3, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT], //NEWS(3) 互动易
[4, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE], //NEWS(4) 股东增持
[5, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2], //NEWS(5) 股东减持
[6, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER], //NEWS(6) 信托持股
[7, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING], //NEWS(7) 大宗交易
[8, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS], //NEWS(8) 官网新闻
[9, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS], //NEWS(9) 高管要闻
[10, JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE], //NEWS(10) 股权质押
]);
if (dataMap.has(value)) return dataMap.get(value);
return null;
}
};
function JSExecute(ast,option)
{
this.AST=ast; //语法树
this.ErrorHandler=new ErrorHandler();
this.VarTable=new Map(); //变量表
this.OutVarTable=new Array(); //输出变量
this.Arguments=[];
//脚本自动变量表, 只读
this.ConstVarTable=new Map([
//个股数据
['CLOSE', null], ['VOL', null], ['OPEN', null], ['HIGH', null], ['LOW', null], ['AMOUNT', null], ['AMO', null], ['VOLINSTK',null],
['C', null], ['V', null], ['O', null], ['H', null], ['L', null], ['VOLR', null],
//日期类
['DATE', null], ['YEAR', null], ['MONTH', null], ['PERIOD', null], ['WEEK', null],["TIME",null],
//大盘数据
['INDEXA',null],['INDEXC',null],['INDEXH',null],['INDEXL',null],['INDEXO',null],['INDEXV',null],
['INDEXADV', null], ['INDEXDEC', null],
['CURRBARSCOUNT', null], //到最后交易日的周期数
['ISLASTBAR', null], //判断是否为最后一个周期
["TOTALCAPITAL",null], //总股本
['CAPITAL', null], //流通股本(手)
['EXCHANGE', null], //换手率
['SETCODE', null], //市场类型
['CODE', null], //品种代码
['STKNAME', null], //品种名称
['HYBLOCK', null], //所属行业板块
['DYBLOCK', null], //所属地域板块
['GNBLOCK', null], //所属概念
["FGBLOCK",null], //所属风格板块
["ZSBLOCK",null], //所属指数板块
["ZHBLOCK",null], //所属组合板块
["ZDBLOCK",null], //所属自定义板块
["HYZSCODE",null],
["GNBLOCKNUM",null], //所属概念板块的个数
["FGBLOCKNUM",null], //所属风格板块的个数
["ZSBLOCKNUM",null], //所属指数板块的个数
["ZHBLOCKNUM",null], //所属组合板块的个数
["ZDBLOCKNUM",null], //所属自定义板块的个数
["HYSYL",null], //指数市盈率或个股所属行业的市盈率
["HYSJL",null], //指数市净率或个股所属行业的市净率
['DRAWNULL', null]
]);
this.SymbolData=new JSSymbolData(this.AST,option,this);
this.Algorithm = new JSAlgorithm(this.ErrorHandler, this.SymbolData);
this.Draw = new JSDraw(this.ErrorHandler, this.SymbolData);
this.JobList=[]; //执行的任务队列
this.UpdateUICallback=null; //回调
this.CallbackParam=null;
if (option)
{
if (option.Callback) this.UpdateUICallback=option.Callback;
if (option.CallbackParam) this.CallbackParam=option.CallbackParam;
if (option.Arguments) this.Arguments=option.Arguments;
}
this.Execute=function()
{
JSConsole.Complier.Log('[JSExecute::Execute] JobList', this.JobList);
this.RunNextJob();
}
this.RunNextJob=function()
{
if (this.JobList.length<=0) return;
var jobItem=this.JobList.shift();
switch (jobItem.ID)
{
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA:
return this.SymbolData.GetSymbolData();
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA:
return this.SymbolData.GetIndexData();
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA:
return this.SymbolData.GetIndexIncreaseData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA:
return this.SymbolData.GetLatestData();
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA: //量比
return this.SymbolData.GetVolRateData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINONE:
return this.SymbolData.GetFinOne(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINVALUE:
return this.SymbolData.GetFinValue(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FINANCE:
return this.SymbolData.GetFinance(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GPJYVALUE:
return this.SymbolData.GetGPJYValue(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VARIANT: //CAPITAL, TOTALCAPITAL
return this.SymbolData.GetVariantData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA:
return this.SymbolData.GetCustomVariantData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA:
return this.SymbolData.GetCustomFunctionData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE: //买入信息-融资余额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT: //买入信息-买入额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY: //买入信息-偿还额
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET: //买入信息-融资净买入
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE: //卖出信息-融券余量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME: //卖出信息-卖出量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY: //卖出信息-偿还量
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET: //卖出信息-融券净卖出
return this.SymbolData.GetMarginData(jobItem.ID);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE: //负面新闻
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH: //机构调研
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT: //互动易
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE: //股东增持
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2: //股东减持
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER: //信托持股
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING: //大宗交易
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS: //官网新闻
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS: //高管要闻
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE: //股权质押
return this.SymbolData.GetNewsAnalysisData(jobItem.ID);
case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CUSTOM_API_DATA:
return this.SymbolData.DownloadCustomAPIData(jobItem);
case JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT:
return this.Run();
}
}
this.ReadSymbolData=function(name,node)
{
switch(name)
{
case 'CLOSE':
case 'C':
case 'VOL':
case 'V':
case 'OPEN':
case 'O':
case 'HIGH':
case 'H':
case 'LOW':
case 'L':
case 'AMOUNT':
case 'VOLINSTK':
return this.SymbolData.GetSymbolCacheData(name);
case 'VOLR':
return this.SymbolData.GetVolRateCacheData(node);
//大盘数据
case 'INDEXA':
case 'INDEXC':
case 'INDEXH':
case 'INDEXO':
case 'INDEXV':
case 'INDEXL':
case 'INDEXADV':
case 'INDEXDEC':
return this.SymbolData.GetIndexCacheData(name);
case 'CURRBARSCOUNT':
return this.SymbolData.GetCurrBarsCount();
case 'ISLASTBAR':
return this.SymbolData.GetIsLastBar();
case "TOTALCAPITAL":
case 'CAPITAL':
case 'EXCHANGE':
case "HYBLOCK":
case "DYBLOCK":
case "GNBLOCK":
case "FGBLOCK":
case "ZSBLOCK":
case "ZHBLOCK":
case "ZDBLOCK":
case "HYZSCODE":
case "GNBLOCKNUM":
case "FGBLOCKNUM":
case "ZSBLOCKNUM":
case "ZHBLOCKNUM":
case "ZDBLOCKNUM":
case "HYSYL":
case "HYSJL":
return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node });
case 'SETCODE':
return this.SymbolData.SETCODE();
case 'CODE':
return this.SymbolData.GetSymbol();
case 'STKNAME':
return this.SymbolData.GetName();
case 'TIME':
return this.SymbolData.TIME();
case 'DATE':
return this.SymbolData.DATE();
case 'YEAR':
return this.SymbolData.YEAR();
case 'MONTH':
return this.SymbolData.MONTH();
case 'WEEK':
return this.SymbolData.WEEK();
case 'PERIOD':
return this.SymbolData.PERIOD();
case 'DRAWNULL':
return this.SymbolData.GetDrawNull();
}
}
this.ReadCustomVariant=function(name,node)
{
return this.SymbolData.GetStockCacheData({ VariantName:name, Node:node });
}
//读取变量
this.ReadVariable=function(name,node)
{
if (this.ConstVarTable.has(name))
{
let data=this.ConstVarTable.get(name);
if (data==null) //动态加载,用到再加载
{
data=this.ReadSymbolData(name,node);
this.ConstVarTable.set(name,data);
}
return data;
}
if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量
if (this.VarTable.has(name)) return this.VarTable.get(name);
this.ThrowUnexpectedNode(node, '变量'+name+'不存在');
return null;
}
this.ReadMemberVariable = function (node)
{
var obj = node.Object;
var member = node.Property;
let maiObj;
if (obj.Type == Syntax.BinaryExpression || obj.Type == Syntax.LogicalExpression)
maiObj = this.VisitBinaryExpression(obj);
else if (obj.Type == Syntax.CallExpression)
maiObj = this.VisitCallExpression(obj);
else
maiObj = this.GetNodeValue(obj);
if (!maiObj) return null;
var value = maiObj[member.Name];
if (value) return value;
return null;
}
//单数据转成数组 个数和历史数据一致
this.SingleDataToArrayData = function (value)
{
let count = this.SymbolData.Data.Data.length;
let result = [];
for (let i = 0; i < count; ++i)
{
result[i] = value;
}
return result;
}
this.RunAST=function()
{
//预定义的变量
for(let i in this.Arguments)
{
let item =this.Arguments[i];
this.VarTable.set(item.Name,item.Value);
}
if (!this.AST) this.ThrowError();
if (!this.AST.Body) this.ThrowError();
for(let i in this.AST.Body)
{
let item =this.AST.Body[i];
this.VisitNode(item);
//输出变量
if (item.Type==Syntax.ExpressionStatement && item.Expression)
{
if (item.Expression.Type==Syntax.AssignmentExpression && item.Expression.Operator==':' && item.Expression.Left)
{
let assignmentItem=item.Expression;
let varName=assignmentItem.Left.Name;
let outVar=this.VarTable.get(varName);
var type=0;
if (!Array.isArray(outVar))
{
if (typeof (outVar) == 'string')
{
var floatValue=parseFloat(outVar);
if (IFrameSplitOperator.IsNumber(floatValue))
{
outVar=this.SingleDataToArrayData(floatValue);
}
else
{
outVar=this.SingleDataToArrayData(outVar);
type=1001;
}
}
else outVar = this.SingleDataToArrayData(outVar);
}
this.OutVarTable.push({Name:varName, Data:outVar,Type:type});
}
else if (item.Expression.Type==Syntax.CallExpression)
{
let callItem=item.Expression;
if (this.Draw.IsDrawFunction(callItem.Callee.Name))
{
let draw=callItem.Draw;
draw.Name=callItem.Callee.Name;
this.OutVarTable.push({Name:draw.Name, Draw:draw, Type:1});
}
else
{
let outVar=callItem.Out;
varName=`__temp_c_${callItem.Callee.Name}_${i}__`;
var type=0;
if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
this.OutVarTable.push({Name:varName, Data:outVar,Type:type,NoneName:true});
}
}
else if (item.Expression.Type==Syntax.Identifier)
{
let varName=item.Expression.Name;
let outVar=this.ReadVariable(varName,item.Expression);
var type=0;
if (!Array.isArray(outVar))
{
if (typeof(outVar)=='string') outVar=this.SingleDataToArrayData(parseFloat(outVar));
else outVar=this.SingleDataToArrayData(outVar);
}
varName="__temp_i_"+i+"__";
this.OutVarTable.push({Name:varName, Data:outVar, Type:type, NoneName:true});
}
else if (item.Expression.Type==Syntax.BinaryExpression)
{
var varName="__temp_b_"+i+"__";
let outVar=item.Expression.Out;
var type=0;
if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true});
}
else if (item.Expression.Type==Syntax.LogicalExpression) //逻辑语句 如 T1 AND T2
{
var varName="__temp_l_"+i+"__";
let outVar=item.Expression.Out;
var type=0;
if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
this.OutVarTable.push({Name:varName, Data:outVar,Type:type, NoneName:true});
}
else if (item.Expression.Type==Syntax.SequenceExpression)
{
let varName;
let draw;
let color;
let lineWidth;
let colorStick=false;
let pointDot=false;
let circleDot=false;
let lineStick=false;
let stick=false;
let volStick=false;
let isShow = true;
let isExData = false;
let isDotLine = false;
let isOverlayLine = false; //叠加线
let isNoneName=false;
var isShowTitle=true;
//显示在位置之上,对于DRAWTEXT和DRAWNUMBER等函数有用,放在语句的最后面(不能与LINETHICK等函数共用),比如:
//DRAWNUMBER(CLOSE>OPEN,HIGH,CLOSE),DRAWABOVE;
var isDrawAbove=false;
for(let j in item.Expression.Expression)
{
let itemExpression=item.Expression.Expression[j];
if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left)
{
varName = itemExpression.Left.Name;
let varValue = this.VarTable.get(varName);
if (!Array.isArray(varValue))
{
varValue = this.SingleDataToArrayData(varValue);
this.VarTable.set(varName, varValue); //把常量放到变量表里
}
}
else if (itemExpression.Type==Syntax.Identifier)
{
let value=itemExpression.Name;
if (value==='COLORSTICK') colorStick=true;
else if (value==='POINTDOT') pointDot=true;
else if (value==='CIRCLEDOT') circleDot=true;
else if (value == 'DOTLINE') isDotLine = true;
else if (value==='LINESTICK') lineStick=true;
else if (value==='STICK') stick=true;
else if (value==='VOLSTICK') volStick=true;
else if (value==="DRAWABOVE") isDrawAbove=true;
else if (value.indexOf('COLOR')==0) color=value;
else if (value.indexOf('LINETHICK')==0) lineWidth=value;
else if (value.indexOf('NODRAW') == 0) isShow = false;
else if (value.indexOf('EXDATA') == 0) isExData = true; //扩展数据, 不显示再图形里面
else if (value.indexOf('LINEOVERLAY') == 0) isOverlayLine = true;
else if (value.indexOf("NOTEXT")==0 || value.indexOf("NOTITLE")==0) isShowTitle=false; //标题不显示
else
{
varName=itemExpression.Name;
let varValue=this.ReadVariable(varName,itemExpression);
if (!Array.isArray(varValue)) varValue=this.SingleDataToArrayData(varValue);
varName="__temp_si_"+i+"__";
isNoneName=true;
this.VarTable.set(varName,varValue); //放到变量表里
}
}
else if (itemExpression.Type == Syntax.Literal) //常量
{
let aryValue = this.SingleDataToArrayData(itemExpression.Value);
varName = itemExpression.Value.toString();
this.VarTable.set(varName, aryValue); //把常量放到变量表里
}
else if (itemExpression.Type==Syntax.CallExpression)
{
if (this.Draw.IsDrawFunction(itemExpression.Callee.Name))
{
draw=itemExpression.Draw;
draw.Name=itemExpression.Callee.Name;
}
else
{
let varValue=itemExpression.Out;
varName=`__temp_sc_${itemExpression.Callee.Name}_${i}__`;
isNoneName=true;
this.VarTable.set(varName,varValue);
}
}
else if (itemExpression.Type==Syntax.BinaryExpression)
{
varName="__temp_sb_"+i+"__";
let aryValue=itemExpression.Out;
isNoneName=true;
this.VarTable.set(varName,aryValue);
}
}
if (pointDot && varName) //圆点
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Radius:g_JSChartResource.POINTDOT.Radius, Type:3};
if (color) value.Color=color;
if (lineWidth) value.LineWidth = lineWidth;
this.OutVarTable.push(value);
}
else if (circleDot && varName) //圆点
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Radius:g_JSChartResource.CIRCLEDOT.Radius, Type:3};
if (color) value.Color=color;
if (lineWidth) value.LineWidth = lineWidth;
this.OutVarTable.push(value);
}
else if (lineStick && varName) //LINESTICK 同时画出柱状线和指标线
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Type:4};
if (color) value.Color=color;
if (lineWidth) value.LineWidth=lineWidth;
this.OutVarTable.push(value);
}
else if (stick && varName) //STICK 画柱状线
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Type:5};
if (color) value.Color=color;
if (lineWidth) value.LineWidth=lineWidth;
this.OutVarTable.push(value);
}
else if (volStick && varName) //VOLSTICK 画彩色柱状线
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Type:6};
if (color) value.Color=color;
this.OutVarTable.push(value);
}
else if (colorStick && varName) //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Color:color, Type:2};
if (lineWidth) value.LineWidth=lineWidth;
if (color) value.Color=color;
this.OutVarTable.push(value);
}
else if (varName && color)
{
let outVar=this.VarTable.get(varName);
let value={Name:varName, Data:outVar, Color:color, Type:0};
if (lineWidth) value.LineWidth=lineWidth;
if (isShow == false) value.IsShow = false;
if (isExData == true) value.IsExData = true;
if (isDotLine == true) value.IsDotLine = true;
if (isOverlayLine == true) value.IsOverlayLine = true;
if (isNoneName==true) value.NoneName=true;
if (isShowTitle==false) value.IsShowTitle=false;
this.OutVarTable.push(value);
}
else if (draw)
{
var outVar = { Name: draw.Name, Draw: draw, Type: 1 };
if (color) outVar.Color = color;
if (lineWidth) outVar.LineWidth = lineWidth;
if (isDrawAbove) outVar.IsDrawAbove=true;
this.OutVarTable.push(outVar);
}
else if (varName)
{
let outVar = this.VarTable.get(varName);
let value = { Name: varName, Data: outVar, Type: 0 };
if (color) value.Color = color;
if (lineWidth) value.LineWidth = lineWidth;
if (isShow == false) value.IsShow = false;
if (isExData == true) value.IsExData = true;
if (isDotLine == true) value.IsDotLine = true;
if (isOverlayLine == true) value.IsOverlayLine = true;
if (isShowTitle==false) value.IsShowTitle=false;
this.OutVarTable.push(value);
}
}
}
}
JSConsole.Complier.Log('[JSExecute::Run]', this.VarTable);
return this.OutVarTable;
}
this.Run=function()
{
let data=this.RunAST();//执行脚本
JSConsole.Complier.Log('[JSComplier.Run] execute finish', data);
if (this.UpdateUICallback)
{
JSConsole.Complier.Log('[JSComplier.Run] invoke UpdateUICallback.');
this.UpdateUICallback(data,this.CallbackParam);
}
}
this.VisitNode=function(node)
{
switch(node.Type)
{
case Syntax.SequenceExpression:
this.VisitSequenceExpression(node);
break;
case Syntax.ExpressionStatement:
this.VisitNode(node.Expression);
break;
case Syntax.AssignmentExpression:
this.VisitAssignmentExpression(node);
break;
case Syntax.BinaryExpression:
case Syntax.LogicalExpression:
this.VisitBinaryExpression(node);
break;
case Syntax.CallExpression:
this.VisitCallExpression(node);
break;
}
}
this.VisitSequenceExpression=function(node)
{
for(let i in node.Expression)
{
let item =node.Expression[i];
this.VisitNode(item);
}
}
//函数调用
this.VisitCallExpression=function(node)
{
let funcName=node.Callee.Name;
let args=[];
for(let i in node.Arguments)
{
let item=node.Arguments[i];
let value;
if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression)
value=this.VisitBinaryExpression(item);
else if (item.Type==Syntax.CallExpression)
value=this.VisitCallExpression(item);
else
value=this.GetNodeValue(item);
args.push(value);
}
//if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitCallExpression]' , funcName, '(', args.toString() ,')');
if (g_JSComplierResource.IsCustomFunction(funcName)) //自定义函数
{
var data=this.Algorithm.CallCustomFunction(funcName, args, this.SymbolData, node);
node.Out=[];
node.Draw=null;
if (data)
{
if (data.Out) node.Out=data.Out;
if (data.Draw) node.Draw=data.Draw;
}
return node.Out;
}
switch(funcName)
{
case 'DYNAINFO': //行情最新数据
node.Out=this.SymbolData.GetLatestCacheData(args[0]);
break;
case 'STICKLINE':
node.Draw=this.Draw.STICKLINE(args[0],args[1],args[2],args[3],args[4]);
node.Out=[];
break;
case 'DRAWTEXT':
node.Draw=this.Draw.DRAWTEXT(args[0],args[1],args[2]);
node.Out=[];
break;
case 'SUPERDRAWTEXT':
node.Draw = this.Draw.SUPERDRAWTEXT(args[0], args[1], args[2], args[3], args[4]);
node.Out = [];
break;
case 'DRAWTEXT_FIX':
node.Draw=this.Draw.DRAWTEXT_FIX(args[0],args[1],args[2],args[3],args[4]);
node.Out=[];
break;
case 'DRAWICON':
node.Draw = this.Draw.DRAWICON(args[0], args[1], args[2]);
node.Out = [];
break;
case 'DRAWLINE':
node.Draw=this.Draw.DRAWLINE(args[0],args[1],args[2],args[3],args[4]);
node.Out=node.Draw.DrawData;
break;
case 'DRAWBAND':
node.Draw=this.Draw.DRAWBAND(args[0],args[1],args[2],args[3]);
node.Out=[];
break;
case 'DRAWKLINE':
node.Draw = this.Draw.DRAWKLINE(args[0], args[1], args[2], args[3]);
node.Out = [];
break;
case 'DRAWKLINE_IF':
node.Draw = this.Draw.DRAWKLINE_IF(args[0], args[1], args[2], args[3], args[4]);
node.Out = [];
break;
case 'PLOYLINE':
case 'POLYLINE':
node.Draw = this.Draw.POLYLINE(args[0], args[1]);
node.Out = node.Draw.DrawData;
break;
case 'DRAWNUMBER':
node.Draw = this.Draw.DRAWNUMBER(args[0], args[1], args[2]);
node.Out = node.Draw.DrawData.Value;
break;
case 'RGB':
node.Out = this.Draw.RGB(args[0], args[1], args[2]);
break;
case 'RGBA':
node.Out = this.Draw.RGBA(args[0], args[1], args[2],args[3]);
break;
case 'DRAWRECTREL':
node.Draw = this.Draw.DRAWRECTREL(args[0], args[1], args[2], args[3], args[4]);
node.Out = [];
break;
case 'DRAWGBK':
node.Draw=this.Draw.DRAWGBK(args[0],args[1],args[2],args[3]);
node.Out=[];
break;
case 'DRAWGBK2':
node.Draw=this.Draw.DRAWGBK2(args[0],args[1],args[2],args[3]);
node.Out=[];
break;
case 'CODELIKE':
node.Out=this.SymbolData.CODELIKE(args[0]);
break;
case 'NAMELIKE':
node.Out = this.SymbolData.NAMELIKE(args[1]);
break;
case 'REFDATE':
node.Out = this.SymbolData.REFDATE(args[0], args[1]);
break;
case 'FINANCE':
node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } );
break;
case "FINVALUE":
node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:1, Node:node } );
break;
case "FINONE":
node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } );
break;
case "GPJYVALUE":
node.Out=this.SymbolData.GetStockCacheData( {FunctionName:funcName, Args:args, ArgCount:3, Node:node } );
break;
case "MARGIN":
node.Out = this.SymbolData.GetMarginCacheData(args[0], node);
break;
case "NEWS":
node.Out = this.SymbolData.GetNewsAnalysisCacheData(args[0], node);
break;
case 'UPCOUNT':
case 'DOWNCOUNT':
node.Out = this.SymbolData.GetIndexIncreaseCacheData(funcName, args[0], node);
break;
case 'LOADAPIDATA':
node.Out = this.SymbolData.GetCustomApiData(args);
break;
case "INBLOCK":
node.Out=this.SymbolData.IsInBlock(args[0],node);
break;
default:
node.Out=this.Algorithm.CallFunction(funcName, args,node);
break;
}
return node.Out;
}
//赋值
this.VisitAssignmentExpression=function(node)
{
let left=node.Left;
if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node);
let varName=left.Name;
let right=node.Right;
let value=null;
if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression)
value=this.VisitBinaryExpression(right);
else if (right.Type==Syntax.CallExpression)
value=this.VisitCallExpression(right);
else if (right.Type==Syntax.Literal)
value=right.Value;
else if (right.Type==Syntax.Identifier) //右值是变量
value=this.ReadVariable(right.Name,right);
else if (right.Type == Syntax.MemberExpression)
value = this.ReadMemberVariable(right);
if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitAssignmentExpression]' , varName, ' = ',value);
this.VarTable.set(varName,value);
}
//逻辑运算
this.VisitBinaryExpression=function(node)
{
let stack=[];
stack.push(node);
let temp=null;
while(stack.length!=0)
{
temp=stack[stack.length-1];
if (temp.Left && node!=temp.Left && node!=temp.Right)
{
stack.push(temp.Left);
}
else if (temp.Right && node!=temp.Right)
{
stack.push(temp.Right);
}
else
{
let value=stack.pop();
if (value.Type==Syntax.BinaryExpression) //只遍历操作符就可以
{
let leftValue=this.GetNodeValue(value.Left);
let rightValue=this.GetNodeValue(value.Right);
if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue);
value.Out=null; //保存中间值
switch(value.Operator)
{
case '-':
value.Out=this.Algorithm.Subtract(leftValue,rightValue);
break;
case '*':
value.Out=this.Algorithm.Multiply(leftValue,rightValue);
break;
case '/':
value.Out=this.Algorithm.Divide(leftValue,rightValue)
break;
case '+':
value.Out=this.Algorithm.Add(leftValue,rightValue);
break;
case '>':
value.Out=this.Algorithm.GT(leftValue,rightValue);
break;
case '>=':
value.Out=this.Algorithm.GTE(leftValue,rightValue);
break;
case '<':
value.Out=this.Algorithm.LT(leftValue,rightValue);
break;
case '<=':
value.Out=this.Algorithm.LTE(leftValue,rightValue);
break;
case '==':
case '=':
value.Out=this.Algorithm.EQ(leftValue,rightValue);
break;
case '!=':
case '<>':
value.Out = this.Algorithm.NEQ(leftValue, rightValue);
break;
}
if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value);
}
else if (value.Type==Syntax.LogicalExpression)
{
let leftValue=this.GetNodeValue(value.Left);
let rightValue=this.GetNodeValue(value.Right);
if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue);
value.Out=null; //保存中间值
switch(value.Operator)
{
case '&&':
case 'AND':
value.Out=this.Algorithm.And(leftValue,rightValue);
break;
case '||':
case 'OR':
value.Out=this.Algorithm.Or(leftValue,rightValue);
break;
}
if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value);
}
node=temp;
}
}
return node.Out;
}
this.GetNodeValue=function(node)
{
switch(node.Type)
{
case Syntax.Literal: //数字
return node.Value;
case Syntax.UnaryExpression:
if (node.Operator=='-')
{
let value=this.GetNodeValue(node.Argument);
return this.Algorithm.Subtract(0,value);
}
return node.Argument.Value;
case Syntax.Identifier:
let value=this.ReadVariable(node.Name,node);
return value;
case Syntax.BinaryExpression:
case Syntax.LogicalExpression:
return node.Out;
case Syntax.CallExpression:
return this.VisitCallExpression(node);
default:
this.ThrowUnexpectedNode(node);
}
}
this.ThrowUnexpectedNode=function(node,message)
{
let marker=node.Marker;
let msg=message || "执行异常";
return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg);
}
this.ThrowError=function()
{
}
}
//对外导出类
function JSComplier()
{
}
//词法分析
JSComplier.Tokenize=function(code)
{
JSConsole.Complier.Log('[JSComplier.Tokenize]', code);
let tokenizer=new Tokenizer(code);
let tokens=[];
try
{
while(true)
{
let token=tokenizer.GetNextToken();
if (!token) break;
tokens.push(token);
}
}
catch(e)
{
}
return tokens;
}
//语法解析 生成抽象语法树(Abstract Syntax Tree)
JSComplier.Parse=function(code)
{
JSConsole.Complier.Log('[JSComplier.Parse]',code);
let parser=new JSParser(code);
parser.Initialize();
let program=parser.ParseScript();
let ast=program;
return ast;
}
/*
执行
option.Symbol=股票代码
option.Name=股票名称
option.Data=这个股票的ChartData
option.Right=复权
option.MaxReqeustDataCount=请求数据的最大个数
*/
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
JSComplier.Execute=function(code,option,errorCallback)
{
//异步调用
var asyncExecute= function()
{
try
{
JSConsole.Complier.Log('[JSComplier.Execute] code ',code);
JSConsole.Complier.Log('[JSComplier.Execute] parser .....');
let parser=new JSParser(code);
parser.Initialize();
let program=parser.ParseScript();
let ast=program;
JSConsole.Complier.Log('[JSComplier.Execute] parser finish.', ast);
JSConsole.Complier.Log('[JSComplier.Execute] execute .....');
let execute=new JSExecute(ast,option);
execute.JobList=parser.Node.GetDataJobList();
execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT});
let result=execute.Execute();
}catch(error)
{
JSConsole.Complier.Log(error);
if (errorCallback) errorCallback(error);
}
}
asyncExecute();
JSConsole.Complier.Log('[JSComplier.Execute] async execute.');
}
JSComplier.SetDomain = function (domain, cacheDomain)
{
if (domain) g_JSComplierResource.Domain = domain;
if (cacheDomain) g_JSComplierResource.CacheDomain = cacheDomain;
}
JSComplier.AddFunction=function(obj) //添加函数 { Name:函数名, Description:描述信息, IsDownload:是否需要下载数据, Invoke:函数执行(可选) }
{
if (!obj || !obj.Name) return;
var ID=obj.Name.toUpperCase();
g_JSComplierResource.CustomFunction.Data.set(ID, obj);
}
JSComplier.AddVariant=function(obj) //{ Name:变量名, Description:描述信息 }
{
if (!obj || !obj.Name) return;
var ID=obj.Name.toUpperCase();
g_JSComplierResource.CustomVariant.Data.set(ID, obj);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//数据下载
function DownloadFinanceData(obj)
{
this.Url=obj.Url;
this.RealtimeUrl=obj.RealtimeUrl;
this.Job=obj.Job;
this.Symbol=obj.Symbol;
this.Args=obj.Args;
this.DataKey=obj.DataKey;
this.RecvCallback=obj.Callback;
this.ErrorCallback=obj.ErrorCallback;
this.Download=function()
{
var id=this.Args[0];
switch(id)
{
case 1: //FINANCE(1) 总股本(随时间可能有变化) 股
case 7: //FINANCE(7) 流通股本(随时间可能有变化) 股
case "EXCHANGE": //换手率
this.DownloadHistoryData(id);
break;
case 9: //FINANCE(9) 资产负债率
case 18: //FINANCE(18) 每股公积金
case 30: //FINANCE(30) 净利润
case 32: //FINANCE(32) 每股未分配利润
case 33: //FINANCE(33) 每股收益(折算为全年收益),对于沪深品种有效
case 34: //FINANCE(34) 每股净资产
case 38: //FINANCE(38) 每股收益(最近一期季报)
case 40: //FINANCE(40) 流通市值
case 41: //FINANCE(41) 总市值
case 42: //FINANCE(42) 上市的天数
case 43: //FINANCE(43) 利润同比
case "CAPITAL":
case "TOTALCAPITAL":
//定制
case 100: //股东人数
this.DownloadRealtimeData(id);
break;
default:
this.DownloadRealtimeData(id);
break;
}
}
//最新一期数据
this.DownloadRealtimeData=function(id)
{
var self=this;
var fieldList=this.GetFieldList();
if (!fieldList)
{
if (this.Job.FunctionName2) message=`${this.Job.FunctionName2} can't support.`;
else if (this.Job.FunctionName) message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`;
else message=`${this.Args[0]} can't support.`;
this.ErrorCallback(message);
self.RecvCallback(null, self.Job, self.DataKey);
return;
}
//请求数据
wx.request({
url: this.RealtimeUrl,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"condition":[ ] ,
"start": 0,
"end": 10
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.RealtimeDataToHQChartData(recvData.data);
self.RecvCallback(data, self.Job, self.DataKey);
}
});
}
//历史数据
this.DownloadHistoryData=function(id)
{
var self=this;
var fieldList=this.GetFieldList();
if (!fieldList)
{
message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`;
this.ErrorCallback(message);
self.RecvCallback(null, self.Job, self.DataKey);
return;
}
//请求数据
wx.request({
url: this.Url,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"condition":[ ] ,
"start": 0,
"end": 200
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.ToHQChartData(recvData.data);
if (data) //排序
data.sort(function (a, b) { return (a.Date - b.Date) });
self.RecvCallback(data, self.Job, self.DataKey);
}
});
}
this.GetFieldList=function()
{
var id=this.Args[0];
switch(id)
{
case 1:
return ["capital.total", "capital.date"];
case 7:
return ["capital.a", "capital.date"];
case "EXCHANGE":
return ["capital.a", "capital.date"];
case 9:
return ["finance.peruprofit","symbol","date"];
case 18:
return ["finance.percreserve","symbol","date"];
case 30:
return ["finance.nprofit","symbol","date"];
case 32:
return ["finance.peruprofit","symbol","date"];
case 33:
return ["finance.persearning","symbol","date"];
case 34:
return ["finance.pernetasset","symbol","date"];
case 38:
return ["finance.persearning","symbol","date"];
case 40:
return ["capital.a", "capital.date","symbol","date", "price"];
case 41:
return ["capital.total", "capital.date","symbol","date","price"];
case "CAPITAL":
return ["capital.a", "capital.date","symbol","date"];
case "TOTALCAPITAL":
return ["capital.total", "capital.date","symbol","date"];
case 42:
return ["company.releasedate","symbol","date"];
case 43:
return ["dividendyield","symbol","date"];
case 100:
return ["shareholder","symbol","date"]
default:
return null;
}
}
//最新报告期数据
this.RealtimeDataToHQChartData=function(recvData,id)
{
if (!recvData.stock || recvData.stock.length!=1) return null;
var stock=recvData.stock[0];
var id=this.Args[0];
var date=stock.date;
switch(id)
{
case 9:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.peruprofit };
case 18:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.percreserve };
case 30:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.nprofit };
case 32:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.peruprofit };
case 33:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.persearning };
case 34:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.pernetasset };
case 38:
if (!stock.finance) return null;
return { Date:date, Value:stock.finance.persearning };
case 40: //FINANCE(40) 流通市值
if (!stock.capital) return null;
return { Date:date, Value:stock.capital.a*stock.price }; //流通股*最新价格
case 41: //FINANCE(41) 总市值
if (!stock.capital) return null;
return { Date:date, Value:stock.capital.total*stock.price }; //总股本*最新价格
case 42: //FINANCE(42) 上市的天数
if (!stock.company) return null;
{
var releaseDate=stock.company.releasedate;
var year=parseInt(releaseDate/10000);
var month=parseInt((releaseDate%10000)/100);
var day=releaseDate%100;
var firstDate=new Date(year, month-1, day);
var nowDate=new Date();
var days=parseInt((nowDate.getTime()-firstDate.getTime())/(1000 * 60 * 60 * 24));
return { Date:date, Value:days+1 };
}
case 43:
if (!stock.dividendyield) return null;
return { Date:date, Value:stock.dividendyield.quarter4 };
case 100:
if (!stock.shareholder) return null;
return { Date:date, Value:stock.shareholder.count };
case "CAPITAL":
if (!stock.capital) return null;
return { Date:date, Value:stock.capital.a/100 }; //当前流通股本 手
case "TOTALCAPITAL":
if (!stock.capital) return null;
return { Date:date, Value:stock.capital.total/100 }; //当前流通股本 手
}
}
//历史数据转
this.ToHQChartData=function(recvData)
{
if (!recvData.stock || recvData.stock.length!=1) return null;
var aryData=[];
var setDate=new Set(); //有重复数据 去掉
var stock=recvData.stock[0];
var id=this.Args[0];
for(var i in stock.stockday)
{
var item=stock.stockday[i];
var hqchartItem=this.ToHQChartItemData(item,id);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
return aryData;
}
this.ToHQChartItemData=function(item, id)
{
if (!item) return null;
var date=item.date;
switch(id)
{
case 1:
if (!item.capital) return null;
return { Date:date, Value:item.capital.total };
case 7:
case "EXCHANGE": //换手率 历史流通股本
if (!item.capital) return null;
return { Date:date, Value:item.capital.a };
default:
return null;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
//内置财务数据下载
//
function DownloadFinValueData(obj)
{
this.Url=obj.Url;
this.Job=obj.Job;
this.Symbol=obj.Symbol;
this.Args=obj.Args;
this.DataKey=obj.DataKey;
this.RecvCallback=obj.Callback;
this.ErrorCallback=obj.ErrorCallback;
this.Download=function()
{
var self=this;
var fieldList=this.GetFieldList();
if (!fieldList)
{
message=`${this.Job.FunctionName}(${this.Args[0]}) can't support.`;
this.ErrorCallback(message);
self.RecvCallback(null, self.Job, self.DataKey);
return;
}
//请求数据
wx.request({
url: this.Url,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"condition":[ {"item":["finance","doc","exists","true"]}] ,
"start": 0,
"end": 200
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.ToHQChartData(recvData.data);
if (data) //排序
data.sort(function (a, b) { return (a.Date - b.Date) });
self.RecvCallback(data, self.Job, self.DataKey);
}
});
}
this.ToHQChartData=function(recvData)
{
if (!recvData.stock || recvData.stock.length!=1) return null;
var aryData=[];
var setDate=new Set(); //有重复数据 去掉
var stock=recvData.stock[0];
for(var i in stock.stockday)
{
var item=stock.stockday[i];
if (item.announcement1)
{
var hqchartItem=this.ToHQChartItemData(item.announcement1, item.finance1, item);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
if (item.announcement2)
{
var hqchartItem=this.ToHQChartItemData(item.announcement2, item.finance2, item);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
if (item.announcement3)
{
var hqchartItem=this.ToHQChartItemData(item.announcement3, item.finance3, item);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
if (item.announcement4)
{
var hqchartItem=this.ToHQChartItemData(item.announcement4, item.finance4, item);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
}
return aryData;
}
//{ Date:日期 , Value:数值 }
this.ToHQChartItemData=function(announcement, finance, sourceItem)
{
var id=this.Args[0];
var date=announcement.year*10000;
var quarter=announcement.quarter;
switch(quarter)
{
case 1:
date+=331;
break;
case 2:
date+=630;
break;
case 3:
date+=930;
break;
case 4:
date+=1231;
break;
default:
return null;
}
var result={ Date:date, Value:0 };
switch(id)
{
case 0:
result.Value=date%1000000; //0--返回报告期(YYMMDD格式),150930表示为2015年第三季
break;
case 1:
result.Value=finance.persearning;
break;
case 3:
result.Value=finance.peruprofit;
break;
case 4:
result.Value=finance.pernetasset;
break;
case 5:
result.Value=finance.percreserve;
break;
case 6:
result.Value=finance.woewa;
break;
case 7:
result.Value=finance.perccfo;
break;
case 8:
result.Value=finance.monetaryfunds;
break;
case 11:
result.Value=finance.areceivable;
break;
}
return result;
}
this.GetFieldList=function()
{
var id=this.Args[0];
switch(id)
{
case 0:
return ["finance.date"];
case 1: //persearning 每股收益
return ["finance.persearning"];
case 3: //peruprofit 每股未分配利润
return ["finance.peruprofit"];
case 4: //pernetasset 每股净资产
return ["finance.pernetasset"];
case 5: //percreserve 每股资本公积金
return ["finance.percreserve"];
case 6: //woewa 加权平均净资产收益
return ["finance.woewa"];
case 7: //perccfo 每股经营性现金流
return ["finance.perccfo"];
case 8: //monetaryfunds 货币资金
return ["finance.monetaryfunds"];
case 11: //areceivable 应收账款
return ["finance.areceivable"];
default:
return null;
}
}
}
/////////////////////////////////////////////////////////
// 内置财务数据下载 某一期的数据
//
function DownloadFinOneData(obj)
{
this.newMethod=DownloadFinValueData; //派生
this.newMethod(obj);
delete this.newMethod;
this.Download=function()
{
var self=this;
var fieldList=this.GetFieldList();
if (!fieldList)
{
message=`${this.Job.FunctionName}(${this.Args[0]}, ${this.Args[1]}, ${this.Args[2]}) can't support.`;
this.ErrorCallback(message);
self.RecvCallback(null, self.Job, self.DataKey);
return;
}
var aryCondition=[ {"item":["finance","doc","exists","true"] } ];
var year=this.Args[1];
var month=this.Args[2];
var dataIndex=0;
var dataEnd=3;
var preYear=null;
if (year==0 && month==0) //如果Y和MMDD都为0,表示最新的财报;
{
}
else if (year==0 && month<300) //如果Y为0,MMDD为小于300的数字,表示最近一期向前推MMDD期的数据,如果是331,630,930,1231这些,表示最近一期的对应季报的数据;
{
dataIndex=month;
dataEnd=200;
}
else if (month==0 && year<1000) //如果Y为0,MMDD为小于300的数字,表示最近一期向前推MMDD期的数据,如果是331,630,930,1231这些,表示最近一期的对应季报的数据;
{
preYear=year;
}
else if (year>1909)
{
if (month==331)
{
aryCondition=
[
{"item":["announcement1.year","int32","eq",year]},
{"item":["finance1","doc","exists","true"]}
];
fieldList.push("announcement1.year");
fieldList.push("announcement1.quarter");
}
else if (month==630)
{
aryCondition=
[
{"item":["announcement2.year","int32","eq",year]},
{"item":["finance2","doc","exists","true"]}
];
fieldList.push("announcement2.year");
fieldList.push("announcement2.quarter");
}
else if (month==930)
{
aryCondition=
[
{"item":["announcement3.year","int32","eq",year]},
{"item":["finance3","doc","exists","true"]}
];
fieldList.push("announcement4.year");
fieldList.push("announcement4.quarter");
}
else
{
aryCondition=
[
{"item":["announcement4.year","int32","eq",year]},
{"item":["finance4","doc","exists","true"]}
];
fieldList.push("announcement4.year");
fieldList.push("announcement4.quarter");
}
}
//请求数据
wx.request({
url: this.Url,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"condition":aryCondition,
"start": 0,
"end": dataEnd
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.ToHQChartData(recvData.data);
var result=null;
if (data && data.length>0)
{
data.sort(function (a, b) { return (b.Date-a.Date) });
if (preYear==null)
result=data[dataIndex]; //返回一个数据
else
result=self.GetPreYearData(data, preYear);
}
self.RecvCallback(result, self.Job, self.DataKey);
}
});
}
this.GetPreYearData=function(data, preYear)
{
//331,630,930,1231这些,表示最近一期的对应季报的数据;
if (preYear==331 || preYear==630|| preYear==930 || preYear==1231)
{
for(var i in data)
{
var item=data[i];
if (item.Date%10000==preYear) return item;
}
}
else
{
//如果MMDD为0,Y为一数字,表示最近一期向前推Y年的同期数据;
var month=data[0].Date%1000;
for(var i=1, j=0; i<data.length ;++i)
{
var item=data[i];
if (item.Date%10000==month)
{
++j;
if (j==preYear) return item;
}
}
return null;
}
}
}
function DownloadGPJYValue(obj)
{
this.Url=obj.Url;
this.RealtimeUrl=obj.RealtimeUrl;
this.Job=obj.Job;
this.Symbol=obj.Symbol;
this.Args=obj.Args;
this.DataKey=obj.DataKey;
this.RecvCallback=obj.Callback;
this.ErrorCallback=obj.ErrorCallback;
this.Download=function()
{
var self=this;
var fieldList=this.GetFieldList();
if (!fieldList)
{
message=`${this.Job.FunctionName}(${this.Args[0]}, ${this.Args[1]}, ${this.Args[2]}) can't support.`;
this.ErrorCallback(message);
self.RecvCallback(null, self.Job, self.DataKey, true);
return;
}
//请求数据
wx.request({
url: this.Url,
data:
{
"field": fieldList,
"symbol": [this.Symbol],
"orderfield":"date",
"order":-1,
"start":0,
"end":5
},
method: 'POST',
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.ToHQChartData(recvData.data);
if (data && data.length>0)
{
data.sort(function (a, b) { return (a.Date-b.Date) });
}
self.RecvCallback(data, self.Job, self.DataKey);
}
});
}
this.GetFieldList=function()
{
var id=this.Args[0];
switch(id)
{
case 1: //1--股东人数 股东户数(户)
return ["shareholder", "date", "symbol"];
case 2: //2--龙虎榜 买入总计(万元) 卖出总计(万元)
return ["tradedetail.buy","tradedetail.sell", "date", "symbol"];
case 3: //3--融资融券1 融资余额(万元) 融券余量(股)
return ["margin","date", "symbol"];
case 4: //4--大宗交易 成交均价(元) 成交额(万元)
return ["blocktrading.amount","blocktrading.price","date", "symbol"]
default:
return null;
}
}
this.ToHQChartData=function(recvData)
{
if (!recvData.stock || recvData.stock.length!=1) return null;
var aryData=[];
var setDate=new Set(); //有重复数据 去掉
var stock=recvData.stock[0];
var id=this.Args[0];
var subID=this.Args[1];
for(var i in stock.stockday)
{
var item=stock.stockday[i];
var hqchartItem=this.ToHQChartItemData(item,id,subID);
if (hqchartItem && !setDate.has(hqchartItem.Date))
{
aryData.push(hqchartItem);
setDate.add(hqchartItem.Date);
}
}
return aryData;
}
this.ToHQChartItemData=function(item, id, subID)
{
if (!item) return null;
var date=item.date;
switch(id)
{
case 1:
if (!item.shareholder) return null;
return { Date:date, Value:item.shareholder.count };
case 2:
if (!item.tradedetail && item.tradedetail[0]) return null;
if (subID==0)
return { Date:date, Value:item.tradedetail[0].buy };
else
return { Date:date, Value:item.tradedetail[0].sell };
case 3:
if (!item.margin) return null;
if (subID==0)
{
if (item.margin.buy)
return { Date:date, Value:item.margin.buy.balance };
}
else
{
if (item.margin.sell)
return { Date:date, Value:item.margin.sell.balance };
}
return null;
case 4:
if (!item.blocktrading) return null;
if (subID==0)
return { Date:date, Value:item.blocktrading.price };
else
return { Date:date, Value:item.blocktrading.amount };
default:
return null;
}
}
}
function DownloadGroupData(obj)
{
this.Url=obj.Url;
this.RealtimeUrl=obj.RealtimeUrl;
this.Job=obj.Job;
this.Symbol=obj.Symbol;
this.Args=obj.Args;
this.DataKey=obj.DataKey;
this.RecvCallback=obj.Callback;
this.ErrorCallback=obj.ErrorCallback;
this.Download=function()
{
var varName=this.Args[0];
switch(varName)
{
case "HYBLOCK":
case "DYBLOCK":
case "GNBLOCK":
this.DownloadGroupName(varName);
break;
}
}
this.DownloadGroupName=function(blockType)
{
var self=this;
var field=["name","symbol"];
if (blockType=="HYBLOCK") field.push("industry");
else if (blockType=="DYBLOCK") field.push("region");
else if (blockType=="GNBLOCK") field.push("concept");
wx.request({
url: self.RealtimeUrl,
data:
{
"field": field,
"symbol": [this.Symbol]
},
method:"post",
dataType: "json",
async:true,
success: function (recvData)
{
var data=self.RecvGroupName(recvData.data);
self.RecvCallback(data, self.Job, self.DataKey, 1);
},
error: function(request)
{
self.ErrorCallback(request);
}
});
}
this.RecvGroupName=function(recvData)
{
if (!recvData.stock || recvData.stock.length!=1) return null;
var stock=recvData.stock[0];
var varName=this.Args[0];
var value=null;
if (varName=="HYBLOCK")
{
var industry=stock.industry;
if (!industry) return null;
var value;
for(var i in industry)
{
var item=industry[i];
value=item.name;
}
}
else if (varName=="DYBLOCK")
{
var region=stock.region;
if (!region) return null;
for(var i in region)
{
var item=region[i];
value=item.name;
}
}
else if (varName=="GNBLOCK")
{
var concept=stock.concept;
if (!concept) return null;
value="";
for(var i in concept)
{
var item=concept[i];
if (value.length>0) value+=' ';
value+=item.name;
}
}
return { Value:value };
}
}
/* 测试例子
var code1='VARHIGH:IF(VAR1<=REF(HH,-1),REF(H,BARSLAST(VAR1>=REF(HH,1))),DRAWNULL),COLORYELLOW;';
var code2='VAR1=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);';
var code3='mm1=1-2*-9+20;';
JSConsole.Complier.Log(code1+code2)
var tokens=JSComplier.Tokenize(code1+code2);
var ast=JSComplier.Parse(code2+code1);
JSConsole.Complier.Log(ast);
*/
module.exports =
{
JSCommonComplier:
{
JSComplier: JSComplier,
},
//单个类导出
JSCommonComplier_ErrorHandler: ErrorHandler,
JSCommonComplier_JSComplier:JSComplier,
JSCommonComplier_JSParser:JSParser,
JSCommonComplier_Syntax:Syntax,
JS_EXECUTE_JOB_ID:JS_EXECUTE_JOB_ID,
g_JSComplierResource:g_JSComplierResource,
};