├── .gitignore ├── README.md ├── cmd ├── compile │ ├── TEST.d │ ├── build.sh │ └── main.go └── el2go │ ├── TEST.d │ ├── build.sh │ ├── main.go │ └── test.sh ├── datalibrary └── datalibrary.go ├── easylang ├── Makefile ├── compiler.go ├── context.go ├── description.go ├── easylang.go ├── easylang.y ├── error.go ├── expression.go ├── generator.go ├── gogenerator.go ├── luagenerator.go ├── tokenizer.go └── tokenizer.l ├── formulalibrary ├── base │ ├── factory │ │ └── factory.go │ └── formula │ │ ├── drawactions.go │ │ ├── formula.go │ │ ├── manager.go │ │ └── meta.go ├── easylang │ └── easylangfactory │ │ ├── creator.go │ │ └── creatorfactory.go ├── formulalibrary.go ├── lua │ ├── luafactory │ │ ├── creator.go │ │ └── creatorfactory.go │ └── luaformula │ │ ├── luaformula.go │ │ ├── luafunc.go │ │ └── meta.go └── native │ ├── creator.go │ ├── nativefactory │ ├── creator.go │ └── creatorfactory.go │ └── nativeformulas │ ├── baseformula.go │ └── nativeformulas.go ├── function ├── base_func.go ├── const.go ├── draw.go ├── function_test.go ├── ma_func.go ├── op_func.go ├── stat_func.go ├── util.go └── value.go ├── go.mod ├── go.sum ├── stockfunc └── function │ ├── crossvalue.go │ ├── data.go │ ├── field.go │ ├── indexmap.go │ └── periods.go └── test ├── 300666.SZ.json ├── CROSS.d ├── DRAW.d ├── DRAWLINE.d ├── MA.d ├── MACD.d ├── MACDBUY.d ├── PLOYLINE.d ├── VOL.d ├── account.lua ├── data.json ├── data └── vipdoc │ ├── infoex.dat │ └── sz │ └── lday │ └── sz000001.day ├── easylang_test.go ├── el ├── MACD.d └── MACDBUY.d ├── formula_test.go ├── function_test.go ├── luaformula_test.go ├── luar_test.go ├── luas ├── MACD.d.lua └── MACDBUY.d.lua ├── native ├── cross.go ├── draw.go ├── drawline.go ├── ma.go ├── macd.go ├── macdbuy.go ├── ployline.go └── vol.go └── suite_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | TdxProtocol 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goformula 2 | A formula system with lua support. 3 | -------------------------------------------------------------------------------- /cmd/compile/TEST.d: -------------------------------------------------------------------------------- 1 | A: DRAWTEXT(CLOSE/OPEN>1.08,LOW,'abc'); 2 | DRAWLINE(HIGH>=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1); 3 | PLOYLINE(HIGH>=HHV(HIGH,20),HIGH),COLORRED,LINETHICK9; 4 | STICKLINE(CLOSE>OPEN,CLOSE,OPEN,0.8,1); 5 | DRAWICON(CLOSE>OPEN,LOW,1); 6 | DRAWKLINE(HIGH,OPEN,LOW,CLOSE), NODRAW; -------------------------------------------------------------------------------- /cmd/compile/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go build -------------------------------------------------------------------------------- /cmd/compile/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "github.com/z-ray/log" 6 | "github.com/stephenlyu/goformula/easylang" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func Usage() { 12 | log.Println(`Usage: compile input-files`) 13 | os.Exit(1) 14 | } 15 | 16 | func compile(inputFile string) { 17 | dir := filepath.Dir(inputFile) 18 | parts := strings.Split(filepath.Base(inputFile), ".") 19 | 20 | if len(parts) > 1 { 21 | parts = parts[:len(parts) - 1] 22 | } 23 | parts = append(parts, "lua") 24 | 25 | outputFile := filepath.Join(dir, strings.ToLower(strings.Join(parts, "."))) 26 | 27 | err := easylang.Compile(inputFile, outputFile, nil, true, true) 28 | if err != nil { 29 | log.Errorf("compile %s fail, error: %v", inputFile, err) 30 | } 31 | } 32 | 33 | func main() { 34 | if len(os.Args) < 2 { 35 | Usage() 36 | } 37 | 38 | for _, file := range os.Args[1:] { 39 | compile(file) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cmd/el2go/TEST.d: -------------------------------------------------------------------------------- 1 | LONG=>(9, 2, 100); 2 | A: DRAWTEXT(CLOSE/OPEN>1.08,LOW,'abc'); 3 | DRAWLINE(HIGH>=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1); 4 | PLOYLINE(HIGH>=HHV(HIGH,20),HIGH),COLORRED,LINETHICK9; 5 | STICKLINE(CLOSE>OPEN,CLOSE,OPEN,0.8,1); 6 | DRAWICON(CLOSE>OPEN,LOW,1); 7 | DRAWKLINE(HIGH,OPEN,LOW,CLOSE), NODRAW; 8 | D:"000001$MACD.MACD"; -------------------------------------------------------------------------------- /cmd/el2go/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go build -------------------------------------------------------------------------------- /cmd/el2go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "github.com/z-ray/log" 6 | "github.com/stephenlyu/goformula/easylang" 7 | "path/filepath" 8 | "strings" 9 | "flag" 10 | ) 11 | 12 | func Usage() { 13 | log.Println(`Usage: compile [-package package-path] input-files`) 14 | os.Exit(1) 15 | } 16 | 17 | func compile(inputFile string, packagePath string) { 18 | parts := strings.Split(filepath.Base(inputFile), ".") 19 | 20 | if len(parts) > 1 { 21 | parts = parts[:len(parts) - 1] 22 | } 23 | if packagePath == "" { 24 | packagePath = strings.ToLower(parts[len(parts) - 1]) 25 | } 26 | parts = append(parts, "go") 27 | outputFile := filepath.Join(strings.ToLower(strings.Join(parts, "."))) 28 | 29 | err := easylang.Compile2Go(inputFile, outputFile, nil, true, packagePath) 30 | if err != nil { 31 | log.Errorf("compile %s fail, error: %v", inputFile, err) 32 | } 33 | } 34 | 35 | func main() { 36 | packagePathPtr := flag.String("package", "", "Package full path") 37 | flag.Parse() 38 | 39 | if len(flag.Args()) <= 0 { 40 | Usage() 41 | } 42 | 43 | for _, file := range flag.Args() { 44 | compile(file, *packagePathPtr) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/el2go/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for d in * 4 | do 5 | if [ -d $d ]; then 6 | cd $d 7 | go build 8 | fi 9 | done 10 | -------------------------------------------------------------------------------- /datalibrary/datalibrary.go: -------------------------------------------------------------------------------- 1 | package datalibrary 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "unicode" 7 | 8 | "github.com/stephenlyu/goformula/stockfunc/function" 9 | "github.com/stephenlyu/tds/datasource" 10 | tdxdatasource "github.com/stephenlyu/tds/datasource/tdx" 11 | "github.com/stephenlyu/tds/entity" 12 | . "github.com/stephenlyu/tds/period" 13 | "github.com/stephenlyu/tds/util" 14 | "github.com/z-ray/log" 15 | ) 16 | 17 | const ( 18 | DATA_TYPE_ORIGINAL = iota 19 | DATA_TYPE_FORWARD_ADJUST 20 | ) 21 | 22 | type DataLibrary interface { 23 | // Set data type, original or forward adjust? 24 | SetDataType(dataType int) 25 | 26 | // Get data with specific code & period 27 | GetData(code string, period string) *function.RVector 28 | 29 | // Release data 30 | ReleaseData(data *function.RVector) 31 | } 32 | 33 | type dataLibrary struct { 34 | dataType int 35 | 36 | dataDir string 37 | ds datasource.DataSource 38 | } 39 | 40 | func NewDataLibrary(dataDir string) DataLibrary { 41 | ds := tdxdatasource.NewDataSource(dataDir, true) 42 | return &dataLibrary{dataDir: dataDir, ds: ds} 43 | } 44 | 45 | func (this *dataLibrary) SetDataType(dataType int) { 46 | util.Assert(dataType == DATA_TYPE_ORIGINAL || dataType == DATA_TYPE_FORWARD_ADJUST, "") 47 | this.dataType = dataType 48 | } 49 | 50 | func (this *dataLibrary) translateCode(code string) string { 51 | if strings.Index(code, ".") != -1 { 52 | return code 53 | } 54 | 55 | code = strings.ToUpper(code) 56 | if code[0] == '0' || code[0] == '3' { 57 | return code + ".SZ" 58 | } else if unicode.IsNumber(rune(code[0])) { 59 | return code + ".SH" 60 | } 61 | if strings.HasPrefix(code, "SZ") { 62 | return code[2:] + ".SZ" 63 | } 64 | if strings.HasPrefix(code, "SH") { 65 | return code[2:] + ".SH" 66 | } 67 | 68 | return code + ".SH" 69 | } 70 | 71 | func (this *dataLibrary) GetData(code string, periodString string) *function.RVector { 72 | code = this.translateCode(code) 73 | fmt.Printf("load data: %s %s\n", code, periodString) 74 | 75 | security, err := entity.ParseSecurity(code) 76 | util.Assert(err == nil, "bad security code") 77 | 78 | err, period := PeriodFromString(periodString) 79 | util.Assert(err == nil, "bad period") 80 | 81 | var data []entity.Record 82 | switch this.dataType { 83 | case DATA_TYPE_ORIGINAL: 84 | err, data = this.ds.GetData(security, period) 85 | case DATA_TYPE_FORWARD_ADJUST: 86 | err, data = this.ds.GetForwardAdjustedData(security, period) 87 | } 88 | 89 | ret := make([]function.Record, len(data)) 90 | if err != nil { 91 | log.Errorf("DataLibrary.GetData fail, error: %v", err) 92 | return function.RecordVectorEx(code, period, ret) 93 | } 94 | 95 | for i := range data { 96 | ret[i] = &data[i] 97 | } 98 | 99 | fmt.Println(data[0].GetDate(), data[len(data)-1].GetDate()) 100 | 101 | return function.RecordVectorEx(code, period, ret) 102 | } 103 | 104 | func (this *dataLibrary) ReleaseData(data *function.RVector) { 105 | } 106 | 107 | var GlobalDataLibrary DataLibrary 108 | 109 | func SetDataLibrary(dl DataLibrary) { 110 | GlobalDataLibrary = dl 111 | } 112 | -------------------------------------------------------------------------------- /easylang/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # blame: jnml, labs.nic.cz 6 | 7 | all: easylang.go tokenizer.go context.go expression.go error.go 8 | gofmt -l -s -w *.go 9 | go build $^ 10 | 11 | run: all 12 | ./easylang 13 | 14 | tokenizer.go: tokenizer.l 15 | golex -o $@ $< 16 | 17 | easylang.go: easylang.y 18 | goyacc -o $@ $< 19 | 20 | clean: 21 | go clean 22 | rm -f tokenizer.go easylang.go lex.yy.go y.go y.output *~ 23 | 24 | nuke: clean 25 | rm -f easylang 26 | -------------------------------------------------------------------------------- /easylang/compiler.go: -------------------------------------------------------------------------------- 1 | package easylang 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | func CompileFile(sourceFile string, formulaManager formula.FormulaManager, numberingVar bool, debug bool) (error, string) { 15 | file, err := os.Open(sourceFile) 16 | if err != nil { 17 | return err, "" 18 | } 19 | defer file.Close() 20 | 21 | _context = newContext() 22 | _context.SetFormulaManager(formulaManager) 23 | _context.SetNumberingVar(numberingVar) 24 | ret := yyParse(newLexer(bufio.NewReader(file))) 25 | if ret == 1 { 26 | return errors.New("compile failure"), "" 27 | } 28 | 29 | if _context.outputErrors() { 30 | return errors.New("compile failure"), "" 31 | } 32 | 33 | _context.Epilog() 34 | 35 | baseName := filepath.Base(sourceFile) 36 | mainName := strings.Split(baseName, ".")[0] 37 | 38 | DEBUG = debug 39 | 40 | generator := NewLuaGenerator(_context) 41 | return nil, generator.GenerateCode(mainName) 42 | } 43 | 44 | func Compile(sourceFile string, destFile string, formulaManager formula.FormulaManager, numberingVar bool, debug bool) error { 45 | err, code := CompileFile(sourceFile, formulaManager, numberingVar, debug) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | if debug { 51 | dumpVarMapping(sourceFile + ".sym") 52 | } 53 | 54 | return ioutil.WriteFile(destFile, []byte(code), 0666) 55 | } 56 | 57 | func Tokenizer(sourceFile string) error { 58 | file, err := os.Open(sourceFile) 59 | if err != nil { 60 | return err 61 | } 62 | defer file.Close() 63 | 64 | lexer := newLexer(bufio.NewReader(file)) 65 | lval := &yySymType{} 66 | for { 67 | char := lexer.Lex(lval) 68 | if char <= 0 { 69 | break 70 | } 71 | 72 | if char == NUM { 73 | fmt.Println(lval.value) 74 | } else { 75 | fmt.Println(lval.str) 76 | } 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func Compile2GoCode(sourceFile string, formulaManager formula.FormulaManager, numberingVar bool, packagePath string) (error, string) { 83 | file, err := os.Open(sourceFile) 84 | if err != nil { 85 | return err, "" 86 | } 87 | defer file.Close() 88 | 89 | _context = newContext() 90 | _context.SetFormulaManager(formulaManager) 91 | _context.SetNumberingVar(numberingVar) 92 | ret := yyParse(newLexer(bufio.NewReader(file))) 93 | if ret == 1 { 94 | return errors.New("compile failure"), "" 95 | } 96 | 97 | if _context.outputErrors() { 98 | return errors.New("compile failure"), "" 99 | } 100 | 101 | _context.Epilog() 102 | 103 | baseName := filepath.Base(sourceFile) 104 | mainName := strings.Split(baseName, ".")[0] 105 | 106 | generator := NewGoGenerator(_context, packagePath) 107 | return nil, generator.GenerateCode(mainName) 108 | } 109 | 110 | func Compile2Go(sourceFile string, destFile string, formulaManager formula.FormulaManager, numberingVar bool, packagePath string) error { 111 | err, code := Compile2GoCode(sourceFile, formulaManager, numberingVar, packagePath) 112 | if err != nil { 113 | return err 114 | } 115 | os.MkdirAll(packagePath, 0777) 116 | 117 | return ioutil.WriteFile(filepath.Join(packagePath, destFile), []byte(code), 0666) 118 | } 119 | -------------------------------------------------------------------------------- /easylang/context.go: -------------------------------------------------------------------------------- 1 | package easylang 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | PERIOD_MAP = map[string]string{ 11 | "DAY": "D1", 12 | "WEEK": "W1", 13 | "MIN1": "M1", 14 | "MIN5": "M5", 15 | "MIN15": "M15", 16 | "MIN30": "M30", 17 | "MIN60": "M60", 18 | } 19 | 20 | CODE_MAP = map[string]int{ 21 | "": 0, 22 | } 23 | DEBUG = false 24 | ) 25 | 26 | func translatePeriod(period string) string { 27 | period1, ok := PERIOD_MAP[period] 28 | if ok { 29 | return period1 30 | } 31 | return "" 32 | } 33 | 34 | type context interface { 35 | newAnonymousVarName() string 36 | define(varName string, expr expression) 37 | defineParam(varName string, expr expression) 38 | refFormula(formulaName string, code string, period string) 39 | refData(code string, period string) 40 | addDrawFunction(expr *functionexpr) 41 | isDefined(varName string) bool 42 | isParamDefined(varName string) bool 43 | isNumberingVar() bool 44 | } 45 | 46 | func getCodeId(code string) int { 47 | ret, ok := CODE_MAP[code] 48 | if ok { 49 | return ret 50 | } 51 | CODE_MAP[code] = len(CODE_MAP) 52 | return CODE_MAP[code] 53 | } 54 | 55 | func formatCode(code string) string { 56 | return fmt.Sprintf("code%d", getCodeId(code)) 57 | } 58 | 59 | func getRefFormulaVarName(name string, code string, period string) string { 60 | return fmt.Sprintf("formula_%s_%s_%s", formatCode(code), strings.ToLower(period), strings.ToLower(name)) 61 | } 62 | 63 | func getRefDataVarName(code string, period string) string { 64 | return fmt.Sprintf("__data_code%d_%s__", getCodeId(code), strings.ToLower(period)) 65 | } 66 | 67 | func getIndexMapVarName(code string, period string) string { 68 | return fmt.Sprintf("__index_map_code%d_%s__", getCodeId(code), strings.ToLower(period)) 69 | } 70 | 71 | type RefFormula struct { 72 | name string 73 | code string 74 | period string 75 | } 76 | 77 | func (this *RefFormula) String() string { 78 | return getRefFormulaVarName(this.name, this.code, this.period) 79 | } 80 | 81 | type RefData struct { 82 | code string 83 | period string 84 | } 85 | 86 | type Context struct { 87 | sequence int 88 | 89 | formulaManager formula.FormulaManager 90 | numberingVar bool 91 | 92 | params []string 93 | paramMap map[string]expression 94 | 95 | definedVars []string 96 | definedVarMap map[string]expression 97 | 98 | refFormulas []RefFormula 99 | refDataList []RefData 100 | 101 | outputVars []string 102 | outputDescriptions map[string][]string 103 | 104 | notOutputVars []string 105 | notOutputDescriptions map[string][]string 106 | 107 | drawFunctions []*functionexpr 108 | 109 | // TODO: Handle errors 110 | errors []SyncError 111 | } 112 | 113 | func newContext() *Context { 114 | CODE_MAP = map[string]int{ 115 | "": 0, 116 | } 117 | 118 | resetAll() 119 | return &Context{ 120 | paramMap: map[string]expression{}, 121 | definedVarMap: map[string]expression{}, 122 | outputDescriptions: map[string][]string{}, 123 | notOutputDescriptions: map[string][]string{}, 124 | } 125 | } 126 | 127 | func (this *Context) SetFormulaManager(formulaManager formula.FormulaManager) { 128 | this.formulaManager = formulaManager 129 | } 130 | 131 | func (this *Context) SetNumberingVar(v bool) { 132 | this.numberingVar = v 133 | } 134 | 135 | func (this *Context) isNumberingVar() bool { 136 | return this.numberingVar 137 | } 138 | 139 | func (this *Context) addError(err SyncError) { 140 | this.errors = append(this.errors, err) 141 | } 142 | 143 | func (this *Context) outputErrors() bool { 144 | for _, err := range this.errors { 145 | fmt.Println(err.String()) 146 | } 147 | return len(this.errors) > 0 148 | } 149 | 150 | func (this *Context) newAnonymousVarName() string { 151 | ret := fmt.Sprintf("__anonymous_%d", this.sequence) 152 | this.sequence++ 153 | return ret 154 | } 155 | 156 | func (this *Context) isReferenceSupport(formulaName string, refVarName string) bool { 157 | if this.formulaManager == nil { 158 | return true 159 | } 160 | formulaName = strings.ToUpper(formulaName) 161 | refVarName = strings.ToUpper(refVarName) 162 | return this.formulaManager.CanSupportVar(formulaName, refVarName) 163 | } 164 | 165 | func (this *Context) isPeriodSupport(period string) bool { 166 | if this.formulaManager == nil { 167 | return true 168 | } 169 | return this.formulaManager.CanSupportPeriod(period) 170 | } 171 | 172 | func (this *Context) isSecuritySupport(code string) bool { 173 | if this.formulaManager == nil { 174 | return true 175 | } 176 | return this.formulaManager.CanSupportSecurity(code) 177 | } 178 | 179 | func (this *Context) refFormula(formulaName string, code string, period string) { 180 | for _, r := range this.refFormulas { 181 | if r.name == formulaName && r.code == code && r.period == period { 182 | return 183 | } 184 | } 185 | this.refFormulas = append(this.refFormulas, RefFormula{formulaName, code, period}) 186 | } 187 | 188 | func (this *Context) refData(code string, period string) { 189 | for _, r := range this.refDataList { 190 | if r.code == code && r.period == period { 191 | return 192 | } 193 | } 194 | this.refDataList = append(this.refDataList, RefData{code, period}) 195 | } 196 | 197 | func (this *Context) define(varName string, expr expression) { 198 | varName = strings.ToLower(varName) 199 | if expr, ok := this.definedVarMap[varName]; ok { 200 | expr.IncrementRefCount() 201 | return 202 | } 203 | this.definedVarMap[varName] = expr 204 | this.definedVars = append(this.definedVars, varName) 205 | expr.IncrementRefCount() 206 | } 207 | 208 | func (this *Context) defineParam(varName string, expr expression) { 209 | varName = strings.ToLower(varName) 210 | if _, ok := this.paramMap[varName]; ok { 211 | return 212 | } 213 | this.paramMap[varName] = expr 214 | this.params = append(this.params, varName) 215 | 216 | this.definedVarMap[varName] = expr 217 | this.definedVars = append(this.definedVars, varName) 218 | expr.IncrementRefCount() 219 | } 220 | 221 | func (this Context) isDefined(varName string) bool { 222 | _, ok := this.definedVarMap[varName] 223 | return ok 224 | } 225 | 226 | func (this Context) isParamDefined(varName string) bool { 227 | _, ok := this.paramMap[varName] 228 | return ok 229 | } 230 | 231 | func (this *Context) addOutput(varName string, descriptions []string, line, column int) { 232 | varName = strings.ToLower(varName) 233 | for _, v := range this.outputVars { 234 | if v == varName { 235 | panic("duplicate output variable") 236 | } 237 | } 238 | this.outputVars = append(this.outputVars, varName) 239 | this.outputDescriptions[varName] = descriptions 240 | } 241 | 242 | func (this *Context) addNotOutputVar(varName string, descriptions []string, line, column int) { 243 | varName = strings.ToLower(varName) 244 | for _, v := range this.notOutputVars { 245 | if v == varName { 246 | panic("duplicate output variable") 247 | } 248 | } 249 | this.notOutputVars = append(this.notOutputVars, varName) 250 | this.notOutputDescriptions[varName] = descriptions 251 | } 252 | 253 | func (this *Context) defined(varName string) expression { 254 | expr, _ := this.definedVarMap[strings.ToLower(varName)] 255 | return expr 256 | } 257 | 258 | func (this *Context) definedParam(varName string) expression { 259 | expr, _ := this.paramMap[strings.ToLower(varName)] 260 | return expr 261 | } 262 | 263 | func (this *Context) addDrawFunction(expr *functionexpr) { 264 | this.drawFunctions = append(this.drawFunctions, expr) 265 | } 266 | 267 | func (this *Context) Epilog() { 268 | var outputVars []string 269 | for _, varName := range this.outputVars { 270 | exp := this.definedVarMap[varName] 271 | if !exp.IsValid() { 272 | continue 273 | } 274 | outputVars = append(outputVars, varName) 275 | } 276 | this.outputVars = outputVars 277 | } 278 | -------------------------------------------------------------------------------- /easylang/description.go: -------------------------------------------------------------------------------- 1 | package easylang 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | var ColorLiterals = []string{ 11 | "COLORBLACK", 12 | "COLORBLUE", 13 | "COLORGREEN", 14 | "COLORCYAN", 15 | "COLORRED", 16 | "COLORMAGENTA", 17 | "COLORBROWN", 18 | "COLORLIGRAY", 19 | "COLORLIBLUE", 20 | "COLORLIGREEN", 21 | "COLORLICYAN", 22 | "COLORLIRED", 23 | "COLORLIMAGENTA", 24 | "COLORYELLOW", 25 | "COLORWHITE", 26 | } 27 | 28 | var colorDefinitions = map[string]*formula.Color{ 29 | "COLORBLACK": {0, 0, 0}, 30 | "COLORBLUE": {0, 0, 255}, 31 | "COLORGREEN": {0, 255, 0}, 32 | "COLORCYAN": {0, 255, 255}, 33 | "COLORRED": {255, 0, 0}, 34 | "COLORMAGENTA": {255, 0, 255}, 35 | "COLORBROWN": {165, 42, 42}, 36 | "COLORLIGRAY": {211, 211, 211}, 37 | "COLORLIBLUE": {173, 216, 230}, 38 | "COLORLIGREEN": {144, 238, 144}, 39 | "COLORLICYAN": {224, 255, 255}, 40 | "COLORLIRED": {255, 0, 128}, 41 | "COLORLIMAGENTA": {255, 128, 128}, 42 | "COLORYELLOW": {255, 255, 0}, 43 | "COLORWHITE": {255, 255, 255}, 44 | } 45 | 46 | var colorRegexp, _ = regexp.Compile("^COLOR([0-F]{2})([0-F]{2})([0-F]{2})$") 47 | var lineThickRegexp, _ = regexp.Compile("^LINETHICK[1-9]$") 48 | 49 | func IsValidColorLiteral(color string) bool { 50 | if colorRegexp.Match([]byte(color)) { 51 | return true 52 | } 53 | 54 | for i := range ColorLiterals { 55 | if ColorLiterals[i] == color { 56 | return true 57 | } 58 | } 59 | return false 60 | } 61 | 62 | func ParseColorLiteral(s string) *formula.Color { 63 | subMatch := colorRegexp.FindSubmatch([]byte(s)) 64 | if len(subMatch) > 0 { 65 | b, _ := strconv.Atoi(string(subMatch[1])) 66 | g, _ := strconv.Atoi(string(subMatch[2])) 67 | r, _ := strconv.Atoi(string(subMatch[3])) 68 | return &formula.Color{Red: r, Green: g, Blue: b} 69 | } 70 | 71 | color := colorDefinitions[s] 72 | if color == nil { 73 | panic("Invalid color literal") 74 | } 75 | 76 | return color 77 | } 78 | 79 | func IsValidDescription(desc string) bool { 80 | switch { 81 | case desc == "DRAWABOVE": 82 | return true 83 | case desc == "NOFRAME": 84 | return true 85 | case desc == "NODRAW": 86 | return true 87 | case desc == "NOTEXT": 88 | return true 89 | case desc == "COLORSTICK": 90 | return true 91 | case desc == "STICK": 92 | return true 93 | case desc == "LINESTICK": 94 | return true 95 | case desc == "VOLSTICK": 96 | return true 97 | case desc == "DOTLINE": 98 | return true 99 | case desc == "CROSSDOT": 100 | return true 101 | case desc == "CIRCLEDOT": 102 | return true 103 | case desc == "POINTDOT": 104 | return true 105 | case strings.HasPrefix(desc, "COLOR"): 106 | return IsValidColorLiteral(desc) 107 | case strings.HasPrefix(desc, "LINETHICK"): 108 | return lineThickRegexp.Match([]byte(desc)) 109 | } 110 | return false 111 | } 112 | -------------------------------------------------------------------------------- /easylang/easylang.y: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file is a modified excerpt from the GNU Bison Manual examples originally found here: 4 | http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.htmlInfix-Calc 5 | 6 | The Copyright License for the GNU Bison Manual can be found in the "fdl-1.3" file. 7 | 8 | */ 9 | 10 | /* Infix notation calculator. */ 11 | 12 | /* TODO: 13 | 1. 支持字符串 [DONE] 14 | 2. 支持绘制函数 [DONE] 15 | 3. 支持跨公式引用 KDJ.K [DONE] 16 | 4. 支持跨周期引用 17 | a. CLOSE#WEEK [DONE] 18 | b. KDJ.K#WEEK [DONE] 19 | c. MIN1, MIN5, MIN15, MIN30, MIN60, WEEK, SEASON, DAY [DONE] 20 | 5. 支持跨品种数据引用 "000001$CLOSE" [DONE] 21 | 6. 支持更多函数 22 | */ 23 | 24 | %{ 25 | 26 | package easylang 27 | 28 | import "strings" 29 | 30 | var _context = newContext() 31 | 32 | %} 33 | 34 | %union{ 35 | value float64 36 | str string 37 | expr expression 38 | descriptions []string 39 | arguments []expression 40 | } 41 | 42 | %token ID 43 | %token NUM 44 | %token STRING 45 | %token STRING_EXPR 46 | %token EQUALS 47 | %token PARAMEQUAL 48 | %token COLONEQUAL 49 | %token LPAREN 50 | %token RPAREN 51 | %token COMMA 52 | %token SEMI 53 | %token DOT 54 | %token POUND 55 | %token DOLLAR 56 | %token NOT 57 | 58 | %left OR 59 | %left AND 60 | %left EQ NE 61 | %left GT GE LT LE 62 | %left MINUS PLUS 63 | %left TIMES DIVIDE 64 | %left UNARY 65 | 66 | %type NUM 67 | %type STRING 68 | %type STRING_EXPR 69 | %type ID graph_description OR AND EQ NE GT GE LT LE MINUS PLUS TIMES DIVIDE NOT 70 | %type statement expression primary_expression postfix_expression unary_expression multiplicative_expression additive_expression relational_expression equality_expression logical_and_expression 71 | %type statement_suffix graph_description_list 72 | %type argument_expression_list 73 | 74 | 75 | %% /* The grammar follows. */ 76 | 77 | formula : statement_list 78 | ; 79 | 80 | statement_list: statement 81 | | statement_list statement 82 | ; 83 | 84 | statement: ID EQUALS expression statement_suffix { 85 | if _, ok := $3.(*stringexpr); ok { 86 | lexer, _ := yylex.(*yylexer) 87 | _context.addError(GeneralError(lexer.lineno, lexer.column, "string can't be right value")) 88 | } 89 | $$ = AssignmentExpression(_context, $1, $3, false) 90 | _context.addOutput($$.VarName(), $4, 0, 0) 91 | } 92 | | ID COLONEQUAL expression statement_suffix { 93 | if _, ok := $3.(*stringexpr); ok { 94 | lexer, _ := yylex.(*yylexer) 95 | _context.addError(GeneralError(lexer.lineno, lexer.column, "string can't be right value")) 96 | } 97 | $$ = AssignmentExpression(_context, $1, $3, false) 98 | _context.addNotOutputVar($$.VarName(), $4, 0, 0) 99 | } 100 | | ID PARAMEQUAL LPAREN NUM COMMA NUM COMMA NUM RPAREN SEMI { 101 | $$ = ParamExpression(_context, $1, $4, $6, $8) 102 | } 103 | | expression statement_suffix { 104 | if _, ok := $1.(*stringexpr); ok { 105 | lexer, _ := yylex.(*yylexer) 106 | _context.addError(GeneralError(lexer.lineno, lexer.column, "string can't be right value")) 107 | } 108 | varName := _context.newAnonymousVarName() 109 | $$ = AssignmentExpression(_context, varName, $1, true) 110 | _context.addOutput(varName, $2, 0, 0) 111 | } 112 | ; 113 | 114 | statement_suffix: SEMI {} 115 | | graph_description_list SEMI { $$ = $1 } 116 | 117 | graph_description_list: COMMA graph_description { 118 | if !IsValidDescription($2) { 119 | lexer, _ := yylex.(*yylexer) 120 | _context.addError(BadGraphDescError(lexer.lineno, lexer.column, $2)) 121 | } 122 | $$ = append($$, $2) 123 | } 124 | | graph_description_list COMMA graph_description { 125 | if !IsValidDescription($3) { 126 | lexer, _ := yylex.(*yylexer) 127 | _context.addError(BadGraphDescError(lexer.lineno, lexer.column, $3)) 128 | } 129 | $$ = append($1, $3) 130 | } 131 | 132 | graph_description : ID { $$ = $1 } 133 | 134 | primary_expression: ID { 135 | expr := _context.defined($1) 136 | if expr == nil { 137 | expr = _context.definedParam($1) 138 | } 139 | if expr != nil { 140 | } else if funcName, ok := noArgFuncMap[$1]; ok { 141 | expr = FunctionExpression(_context, funcName, nil) 142 | } else { 143 | lexer, _ := yylex.(*yylexer) 144 | _context.addError(UndefinedVarError(lexer.lineno, lexer.column, $1)) 145 | expr = ErrorExpression(_context, $1) 146 | } 147 | $$ = expr 148 | } 149 | | NUM { $$ = ConstantExpression(_context, $1) } 150 | | STRING { $$ = StringExpression(_context, $1[1:len($1) - 1]) } 151 | | LPAREN expression RPAREN { $$ = $2 } 152 | 153 | postfix_expression: primary_expression { $$ = $1 } 154 | | ID LPAREN argument_expression_list RPAREN { 155 | if _, ok := funcMap[$1]; !ok { 156 | lexer, _ := yylex.(*yylexer) 157 | _context.addError(UndefinedFunctionError(lexer.lineno, lexer.column, $1)) 158 | $$ = ErrorExpression(_context, $1) 159 | } else { 160 | $$ = FunctionExpression(_context, $1, $3) 161 | } 162 | } 163 | | ID DOT ID { 164 | if !_context.isReferenceSupport($1, $3) { 165 | lexer, _ := yylex.(*yylexer) 166 | _context.addError(GeneralError(lexer.lineno, lexer.column, __yyfmt__.Sprintf("%s.%s not supported", $1, $3))) 167 | $$ = ErrorExpression(_context, $3) 168 | } else { 169 | $$ = ReferenceExpression(_context, $1, $3) 170 | } 171 | } 172 | | ID POUND ID { 173 | period:=translatePeriod($3) 174 | if funcName, ok := noArgFuncMap[$1]; ok && _context.isPeriodSupport(period) { 175 | $$ = CrossFunctionExpression(_context, funcName, "", period) 176 | } else { 177 | lexer, _ := yylex.(*yylexer) 178 | _context.addError(GeneralError(lexer.lineno, lexer.column, __yyfmt__.Sprintf("%s#%s not supported", $1, $3))) 179 | $$ = ErrorExpression(_context, $3) 180 | } 181 | } 182 | | ID DOT ID POUND ID { 183 | period:=translatePeriod($5) 184 | if !_context.isReferenceSupport($1, $3) || !_context.isPeriodSupport(period) { 185 | lexer, _ := yylex.(*yylexer) 186 | _context.addError(GeneralError(lexer.lineno, lexer.column, __yyfmt__.Sprintf("%s.%s#%s not supported", $1, $3, $5))) 187 | $$ = ErrorExpression(_context, $3) 188 | } else { 189 | $$ = CrossReferenceExpression(_context, $1, $3, "", period) 190 | } 191 | } 192 | | STRING_EXPR { 193 | parts := strings.Split($1[1:len($1)-1], "$") 194 | lexer, _ := yylex.(*yylexer) 195 | 196 | var reportError = func(msg string) { 197 | if msg == "" { 198 | msg = __yyfmt__.Sprintf("\"%s\" not supported", $1) 199 | } 200 | _context.addError(GeneralError(lexer.lineno, lexer.column, msg)) 201 | $$ = ErrorExpression(_context, $1) 202 | } 203 | 204 | if len(parts) != 2 { 205 | reportError("") 206 | break 207 | } 208 | 209 | code := parts[0] 210 | if !_context.isSecuritySupport(code) { 211 | reportError(__yyfmt__.Sprintf("code %s not supported", code)) 212 | break 213 | } 214 | 215 | expr := parts[1] 216 | 217 | var period string 218 | 219 | parts = strings.Split(expr, "#") 220 | if len(parts) > 2 { 221 | reportError("") 222 | break 223 | } else if len(parts) == 2 { 224 | period = translatePeriod(parts[1]) 225 | if !_context.isPeriodSupport(period) { 226 | reportError(__yyfmt__.Sprintf("period %s not supported", parts[1])) 227 | break 228 | } 229 | } 230 | 231 | parts = strings.Split(parts[0], ".") 232 | switch len(parts) { 233 | case 1: 234 | if funcName, ok := noArgFuncMap[parts[0]]; !ok { 235 | reportError(__yyfmt__.Sprintf("function %s not supported", parts[0])) 236 | } else { 237 | $$ = CrossFunctionExpression(_context, funcName, code, period) 238 | } 239 | case 2: 240 | if !_context.isReferenceSupport(parts[0], parts[1]) { 241 | reportError("") 242 | } else { 243 | $$ = CrossReferenceExpression(_context, parts[0], parts[1], code, period) 244 | } 245 | default: 246 | reportError("") 247 | } 248 | } 249 | 250 | argument_expression_list: expression { $$ = []expression{$1} } 251 | | argument_expression_list COMMA expression { $$ = append($$, $3) } 252 | 253 | unary_expression 254 | : postfix_expression { $$ = $1 } 255 | | NOT unary_expression %prec UNARY { $$ = UnaryExpression(_context, $1, $2) } 256 | | MINUS unary_expression %prec UNARY { $$ = UnaryExpression(_context, $1, $2) } 257 | 258 | multiplicative_expression 259 | : unary_expression { $$ = $1 } 260 | | multiplicative_expression TIMES unary_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 261 | | multiplicative_expression DIVIDE unary_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 262 | 263 | additive_expression 264 | : multiplicative_expression { $$ = $1 } 265 | | additive_expression PLUS multiplicative_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 266 | | additive_expression MINUS multiplicative_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 267 | 268 | relational_expression 269 | : additive_expression { $$ = $1 } 270 | | relational_expression LT additive_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 271 | | relational_expression GT additive_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 272 | | relational_expression LE additive_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 273 | | relational_expression GE additive_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 274 | 275 | equality_expression 276 | : relational_expression { $$ = $1 } 277 | | equality_expression EQ relational_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 278 | | equality_expression NE relational_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 279 | 280 | logical_and_expression 281 | : equality_expression { $$ = $1 } 282 | | logical_and_expression AND equality_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 283 | 284 | expression 285 | : logical_and_expression { $$ = $1 } 286 | | expression OR logical_and_expression { $$ = BinaryExpression(_context, $2, $1, $3) } 287 | 288 | %% 289 | -------------------------------------------------------------------------------- /easylang/error.go: -------------------------------------------------------------------------------- 1 | package easylang 2 | 3 | import "fmt" 4 | 5 | type SyncError interface { 6 | String() string 7 | } 8 | 9 | type synxerror struct { 10 | line int 11 | column int 12 | } 13 | 14 | type undefinedVarError struct { 15 | synxerror 16 | varName string 17 | } 18 | 19 | func UndefinedVarError(line, column int, varName string) *undefinedVarError { 20 | return &undefinedVarError{ 21 | synxerror: synxerror{ 22 | line: line, column: column, 23 | }, 24 | varName: varName, 25 | } 26 | } 27 | 28 | func (this undefinedVarError) String() string { 29 | return fmt.Sprintf("undefined variable '%s' at line %d column %d", this.varName, this.line, this.column) 30 | } 31 | 32 | type undefinedFunctionError struct { 33 | synxerror 34 | varName string 35 | } 36 | 37 | func UndefinedFunctionError(line, column int, varName string) *undefinedFunctionError { 38 | return &undefinedFunctionError{ 39 | synxerror: synxerror{ 40 | line: line, column: column, 41 | }, 42 | varName: varName, 43 | } 44 | } 45 | 46 | func (this undefinedFunctionError) String() string { 47 | return fmt.Sprintf("undefined function '%s' at line %d column %d", this.varName, this.line, this.column) 48 | } 49 | 50 | type badGraphDescError struct { 51 | synxerror 52 | desc string 53 | } 54 | 55 | func BadGraphDescError(line, column int, desc string) *badGraphDescError { 56 | return &badGraphDescError{ 57 | synxerror: synxerror{ 58 | line: line, column: column, 59 | }, 60 | desc: desc, 61 | } 62 | } 63 | 64 | func (this badGraphDescError) String() string { 65 | return fmt.Sprintf("bad graph description '%s' at line %d column %d", this.desc, this.line, this.column) 66 | } 67 | 68 | type generalError struct { 69 | synxerror 70 | desc string 71 | } 72 | 73 | func GeneralError(line, column int, desc string) *generalError { 74 | return &generalError{ 75 | synxerror: synxerror{ 76 | line: line, column: column, 77 | }, 78 | desc: desc, 79 | } 80 | } 81 | 82 | func (this generalError) String() string { 83 | return fmt.Sprintf("'%s' at line %d column %d", this.desc, this.line, this.column) 84 | } 85 | -------------------------------------------------------------------------------- /easylang/generator.go: -------------------------------------------------------------------------------- 1 | package easylang 2 | 3 | type Generator interface { 4 | GenerateCode(name string) string 5 | } -------------------------------------------------------------------------------- /easylang/tokenizer.go: -------------------------------------------------------------------------------- 1 | // CAUTION: Generated file - DO NOT EDIT. 2 | 3 | package easylang 4 | 5 | import ( 6 | "bufio" 7 | "bytes" 8 | "fmt" 9 | "log" 10 | "strconv" 11 | "unicode/utf8" 12 | ) 13 | 14 | var keywords map[string]int = map[string]int{ 15 | "AND": AND, 16 | "OR": OR, 17 | "NOT": NOT, 18 | } 19 | 20 | type yylexer struct { 21 | src *bufio.Reader 22 | buf *bytes.Buffer 23 | empty bool 24 | current rune 25 | 26 | lineno int 27 | column int 28 | } 29 | 30 | func addRune(b *bytes.Buffer, c rune) { 31 | if _, err := b.WriteRune(c); err != nil { 32 | log.Fatalf("WriteRune: %s", err) 33 | } 34 | } 35 | 36 | func newLexer(src *bufio.Reader) (y *yylexer) { 37 | y = &yylexer{src: src, buf: &bytes.Buffer{}, lineno: 1, column: 0} 38 | if r, _, err := src.ReadRune(); err == nil { 39 | y.current = r 40 | } 41 | return 42 | } 43 | 44 | func (y *yylexer) getc() rune { 45 | if y.current != 0 { 46 | addRune(y.buf, y.current) 47 | } 48 | y.current = 0 49 | if r, _, err := y.src.ReadRune(); err == nil { 50 | y.current = r 51 | } 52 | return y.current 53 | } 54 | 55 | func (y yylexer) Error(e string) { 56 | fmt.Printf("Error: %s at %d:%d\n", e, y.lineno, y.column) 57 | } 58 | 59 | func (y *yylexer) Lex(lval *yySymType) int { 60 | var err error 61 | c := y.current 62 | if y.empty { 63 | c, y.empty = y.getc(), false 64 | } 65 | 66 | yystate0: 67 | 68 | y.column += utf8.RuneCount(y.buf.Bytes()) 69 | y.buf.Reset() 70 | 71 | goto yystart1 72 | 73 | goto yystate0 // silence unused label error 74 | goto yystate1 // silence unused label error 75 | yystate1: 76 | c = y.getc() 77 | yystart1: 78 | switch { 79 | default: 80 | goto yyabort 81 | case c == '!': 82 | goto yystate6 83 | case c == '"': 84 | goto yystate8 85 | case c == '#': 86 | goto yystate10 87 | case c == '$': 88 | goto yystate11 89 | case c == '&': 90 | goto yystate12 91 | case c == '(': 92 | goto yystate16 93 | case c == ')': 94 | goto yystate17 95 | case c == '*': 96 | goto yystate18 97 | case c == '+': 98 | goto yystate19 99 | case c == ',': 100 | goto yystate20 101 | case c == '-': 102 | goto yystate21 103 | case c == '.': 104 | goto yystate22 105 | case c == '/': 106 | goto yystate23 107 | case c == ':': 108 | goto yystate27 109 | case c == ';': 110 | goto yystate29 111 | case c == '<': 112 | goto yystate30 113 | case c == '=': 114 | goto yystate32 115 | case c == '>': 116 | goto yystate34 117 | case c == '\'': 118 | goto yystate14 119 | case c == '\n': 120 | goto yystate3 121 | case c == '\r': 122 | goto yystate4 123 | case c == '\t' || c == ' ': 124 | goto yystate2 125 | case c == '{': 126 | goto yystate37 127 | case c == '|': 128 | goto yystate39 129 | case c >= '0' && c <= '9': 130 | goto yystate24 131 | case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z' || c >= 0x4e00 && c <= 0x9fcb: 132 | goto yystate36 133 | } 134 | 135 | yystate2: 136 | c = y.getc() 137 | switch { 138 | default: 139 | goto yyrule3 140 | case c == '\t' || c == ' ': 141 | goto yystate2 142 | } 143 | 144 | yystate3: 145 | c = y.getc() 146 | goto yyrule2 147 | 148 | yystate4: 149 | c = y.getc() 150 | switch { 151 | default: 152 | goto yyrule2 153 | case c == '\n': 154 | goto yystate5 155 | } 156 | 157 | yystate5: 158 | c = y.getc() 159 | goto yyrule1 160 | 161 | yystate6: 162 | c = y.getc() 163 | switch { 164 | default: 165 | goto yyabort 166 | case c == '=': 167 | goto yystate7 168 | } 169 | 170 | yystate7: 171 | c = y.getc() 172 | goto yyrule20 173 | 174 | yystate8: 175 | c = y.getc() 176 | switch { 177 | default: 178 | goto yyabort 179 | case c == '"': 180 | goto yystate9 181 | case c >= '\x01' && c <= '!' || c >= '#' && c <= 'ÿ': 182 | goto yystate8 183 | } 184 | 185 | yystate9: 186 | c = y.getc() 187 | goto yyrule8 188 | 189 | yystate10: 190 | c = y.getc() 191 | goto yyrule29 192 | 193 | yystate11: 194 | c = y.getc() 195 | goto yyrule30 196 | 197 | yystate12: 198 | c = y.getc() 199 | switch { 200 | default: 201 | goto yyabort 202 | case c == '&': 203 | goto yystate13 204 | } 205 | 206 | yystate13: 207 | c = y.getc() 208 | goto yyrule21 209 | 210 | yystate14: 211 | c = y.getc() 212 | switch { 213 | default: 214 | goto yyabort 215 | case c == '\'': 216 | goto yystate15 217 | case c >= '\x01' && c <= '&' || c >= '(' && c <= 'ÿ' || c >= 0x4e00 && c <= 0x9fcb: 218 | goto yystate14 219 | } 220 | 221 | yystate15: 222 | c = y.getc() 223 | goto yyrule7 224 | 225 | yystate16: 226 | c = y.getc() 227 | goto yyrule25 228 | 229 | yystate17: 230 | c = y.getc() 231 | goto yyrule26 232 | 233 | yystate18: 234 | c = y.getc() 235 | goto yyrule12 236 | 237 | yystate19: 238 | c = y.getc() 239 | goto yyrule10 240 | 241 | yystate20: 242 | c = y.getc() 243 | goto yyrule27 244 | 245 | yystate21: 246 | c = y.getc() 247 | goto yyrule11 248 | 249 | yystate22: 250 | c = y.getc() 251 | goto yyrule31 252 | 253 | yystate23: 254 | c = y.getc() 255 | goto yyrule13 256 | 257 | yystate24: 258 | c = y.getc() 259 | switch { 260 | default: 261 | goto yyrule6 262 | case c == '.': 263 | goto yystate25 264 | case c >= '0' && c <= '9': 265 | goto yystate24 266 | } 267 | 268 | yystate25: 269 | c = y.getc() 270 | switch { 271 | default: 272 | goto yyabort 273 | case c >= '0' && c <= '9': 274 | goto yystate26 275 | } 276 | 277 | yystate26: 278 | c = y.getc() 279 | switch { 280 | default: 281 | goto yyrule5 282 | case c >= '0' && c <= '9': 283 | goto yystate26 284 | } 285 | 286 | yystate27: 287 | c = y.getc() 288 | switch { 289 | default: 290 | goto yyrule23 291 | case c == '=': 292 | goto yystate28 293 | } 294 | 295 | yystate28: 296 | c = y.getc() 297 | goto yyrule24 298 | 299 | yystate29: 300 | c = y.getc() 301 | goto yyrule28 302 | 303 | yystate30: 304 | c = y.getc() 305 | switch { 306 | default: 307 | goto yyrule15 308 | case c == '=': 309 | goto yystate31 310 | } 311 | 312 | yystate31: 313 | c = y.getc() 314 | goto yyrule14 315 | 316 | yystate32: 317 | c = y.getc() 318 | switch { 319 | default: 320 | goto yyrule19 321 | case c == '>': 322 | goto yystate33 323 | } 324 | 325 | yystate33: 326 | c = y.getc() 327 | goto yyrule18 328 | 329 | yystate34: 330 | c = y.getc() 331 | switch { 332 | default: 333 | goto yyrule17 334 | case c == '=': 335 | goto yystate35 336 | } 337 | 338 | yystate35: 339 | c = y.getc() 340 | goto yyrule16 341 | 342 | yystate36: 343 | c = y.getc() 344 | switch { 345 | default: 346 | goto yyrule4 347 | case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z' || c >= 0x4e00 && c <= 0x9fcb: 348 | goto yystate36 349 | } 350 | 351 | yystate37: 352 | c = y.getc() 353 | switch { 354 | default: 355 | goto yyabort 356 | case c == '}': 357 | goto yystate38 358 | case c >= '\x01' && c <= '!' || c >= '#' && c <= '|' || c >= '~' && c <= 'ÿ' || c >= 0x4e00 && c <= 0x9fcb: 359 | goto yystate37 360 | } 361 | 362 | yystate38: 363 | c = y.getc() 364 | goto yyrule9 365 | 366 | yystate39: 367 | c = y.getc() 368 | switch { 369 | default: 370 | goto yyabort 371 | case c == '|': 372 | goto yystate40 373 | } 374 | 375 | yystate40: 376 | c = y.getc() 377 | goto yyrule22 378 | 379 | yyrule1: // \r\n 380 | { 381 | 382 | y.lineno++ 383 | y.column = 0 384 | goto yystate0 385 | } 386 | yyrule2: // [\r\n] 387 | { 388 | 389 | y.lineno++ 390 | y.column = 0 391 | goto yystate0 392 | } 393 | yyrule3: // [ \t]+ 394 | 395 | goto yystate0 396 | yyrule4: // {IDENTIFIER} 397 | { 398 | 399 | s := string(y.buf.Bytes()) 400 | lval.str = s 401 | if token, ok := keywords[s]; ok { 402 | return token 403 | } 404 | return ID 405 | } 406 | yyrule5: // {FLOAT} 407 | { 408 | 409 | if lval.value, err = strconv.ParseFloat(string(y.buf.Bytes()), 64); err != nil { 410 | y.Error(err.Error()) 411 | } 412 | return NUM 413 | } 414 | yyrule6: // {INTEGER} 415 | { 416 | 417 | if lval.value, err = strconv.ParseFloat(string(y.buf.Bytes()), 64); err != nil { 418 | y.Error(err.Error()) 419 | } 420 | return NUM 421 | } 422 | yyrule7: // {STRING_LITERAL} 423 | { 424 | 425 | lval.str = string(y.buf.Bytes()) 426 | return STRING 427 | } 428 | yyrule8: // {STRING_EXPR} 429 | { 430 | 431 | lval.str = string(y.buf.Bytes()) 432 | return STRING_EXPR 433 | } 434 | yyrule9: // {COMMENT} 435 | 436 | goto yystate0 437 | yyrule10: // {PLUS} 438 | { 439 | 440 | lval.str = string(y.buf.Bytes()) 441 | return PLUS 442 | } 443 | yyrule11: // {MINUS} 444 | { 445 | 446 | lval.str = string(y.buf.Bytes()) 447 | return MINUS 448 | } 449 | yyrule12: // {TIMES} 450 | { 451 | 452 | lval.str = string(y.buf.Bytes()) 453 | return TIMES 454 | } 455 | yyrule13: // {DIVIDE} 456 | { 457 | 458 | lval.str = string(y.buf.Bytes()) 459 | return DIVIDE 460 | } 461 | yyrule14: // {LE} 462 | { 463 | 464 | lval.str = string(y.buf.Bytes()) 465 | return LE 466 | } 467 | yyrule15: // {LT} 468 | { 469 | 470 | lval.str = string(y.buf.Bytes()) 471 | return LT 472 | } 473 | yyrule16: // {GE} 474 | { 475 | 476 | lval.str = string(y.buf.Bytes()) 477 | return GE 478 | } 479 | yyrule17: // {GT} 480 | { 481 | 482 | lval.str = string(y.buf.Bytes()) 483 | return GT 484 | } 485 | yyrule18: // {PARAMEQUAL} 486 | { 487 | 488 | lval.str = string(y.buf.Bytes()) 489 | return PARAMEQUAL 490 | } 491 | yyrule19: // {EQ} 492 | { 493 | 494 | lval.str = string(y.buf.Bytes()) 495 | return EQ 496 | } 497 | yyrule20: // {NE} 498 | { 499 | 500 | lval.str = string(y.buf.Bytes()) 501 | return NE 502 | } 503 | yyrule21: // {AND} 504 | { 505 | 506 | lval.str = string(y.buf.Bytes()) 507 | return AND 508 | } 509 | yyrule22: // {OR} 510 | { 511 | 512 | lval.str = string(y.buf.Bytes()) 513 | return OR 514 | } 515 | yyrule23: // {EQUALS} 516 | { 517 | 518 | lval.str = string(y.buf.Bytes()) 519 | return EQUALS 520 | } 521 | yyrule24: // {COLONEQUAL} 522 | { 523 | 524 | lval.str = string(y.buf.Bytes()) 525 | return COLONEQUAL 526 | } 527 | yyrule25: // {LPAREN} 528 | { 529 | 530 | lval.str = string(y.buf.Bytes()) 531 | return LPAREN 532 | } 533 | yyrule26: // {RPAREN} 534 | { 535 | 536 | lval.str = string(y.buf.Bytes()) 537 | return RPAREN 538 | } 539 | yyrule27: // {COMMA} 540 | { 541 | 542 | lval.str = string(y.buf.Bytes()) 543 | return COMMA 544 | } 545 | yyrule28: // {SEMI} 546 | { 547 | 548 | lval.str = string(y.buf.Bytes()) 549 | return SEMI 550 | } 551 | yyrule29: // {POUND} 552 | { 553 | 554 | lval.str = string(y.buf.Bytes()) 555 | return POUND 556 | } 557 | yyrule30: // {DOLLAR} 558 | { 559 | 560 | lval.str = string(y.buf.Bytes()) 561 | return DOLLAR 562 | } 563 | yyrule31: // {DOT} 564 | { 565 | 566 | lval.str = string(y.buf.Bytes()) 567 | return DOT 568 | } 569 | panic("unreachable") 570 | 571 | goto yyabort // silence unused label error 572 | 573 | yyabort: // no lexem recognized 574 | y.empty = true 575 | return int(c) 576 | } 577 | -------------------------------------------------------------------------------- /easylang/tokenizer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | package easylang 4 | 5 | import ( 6 | "bufio" 7 | "log" 8 | "strconv" 9 | "bytes" 10 | "unicode/utf8" 11 | "fmt" 12 | ) 13 | 14 | var keywords map[string]int = map[string]int{ 15 | "AND": AND, 16 | "OR": OR, 17 | "NOT": NOT, 18 | } 19 | 20 | type yylexer struct{ 21 | src *bufio.Reader 22 | buf *bytes.Buffer 23 | empty bool 24 | current rune 25 | 26 | lineno int 27 | column int 28 | } 29 | 30 | func addRune(b *bytes.Buffer, c rune) { 31 | if _, err := b.WriteRune(c); err != nil { 32 | log.Fatalf("WriteRune: %s", err) 33 | } 34 | } 35 | 36 | func newLexer(src *bufio.Reader) (y *yylexer) { 37 | y = &yylexer{src: src, buf: &bytes.Buffer{}, lineno: 1, column: 0} 38 | if r, _, err := src.ReadRune(); err == nil { 39 | y.current = r 40 | } 41 | return 42 | } 43 | 44 | func (y *yylexer) getc() rune { 45 | if y.current != 0 { 46 | addRune(y.buf, y.current) 47 | } 48 | y.current = 0 49 | if r, _, err := y.src.ReadRune(); err == nil { 50 | y.current = r 51 | } 52 | return y.current 53 | } 54 | 55 | func (y yylexer) Error(e string) { 56 | fmt.Printf("Error: %s at %d:%d\n", e, y.lineno, y.column) 57 | } 58 | 59 | func (y *yylexer) Lex(lval *yySymType) int { 60 | var err error 61 | c := y.current 62 | if y.empty { 63 | c, y.empty = y.getc(), false 64 | } 65 | %} 66 | 67 | %yyc c 68 | %yyn c = y.getc() 69 | 70 | 71 | IDENTIFIER [a-zA-Z_][a-zA-Z0-9_]* 72 | INTEGER [0-9]+ 73 | FLOAT [0-9]+"."[0-9]+ 74 | STRING_LITERAL '[^\']*' 75 | STRING_EXPR "\""[^"\""]*"\"" 76 | COMMENT "{"[^"}"]*"}" 77 | PLUS "+" 78 | MINUS "-" 79 | TIMES "*" 80 | DIVIDE "/" 81 | LT < 82 | GT > 83 | LE <= 84 | GE >= 85 | EQ = 86 | NE != 87 | AND && 88 | OR "||" 89 | EQUALS : 90 | COLONEQUAL := 91 | PARAMEQUAL => 92 | 93 | LPAREN "(" 94 | RPAREN ")" 95 | COMMA , 96 | SEMI ; 97 | DOT "." 98 | POUND "#" 99 | DOLLAR "$" 100 | 101 | %% 102 | y.column += utf8.RuneCount(y.buf.Bytes()) 103 | y.buf.Reset() 104 | 105 | \r\n 106 | y.lineno++ 107 | y.column = 0 108 | 109 | [\r\n] 110 | y.lineno++ 111 | y.column = 0 112 | 113 | [ \t]+ 114 | 115 | {IDENTIFIER} 116 | s := string(y.buf.Bytes()) 117 | lval.str = s 118 | if token, ok := keywords[s]; ok { 119 | return token 120 | } 121 | return ID 122 | 123 | {FLOAT} 124 | if lval.value, err = strconv.ParseFloat(string(y.buf.Bytes()), 64); err != nil { 125 | y.Error(err.Error()) 126 | } 127 | return NUM 128 | 129 | {INTEGER} 130 | if lval.value, err = strconv.ParseFloat(string(y.buf.Bytes()), 64); err != nil { 131 | y.Error(err.Error()) 132 | } 133 | return NUM 134 | 135 | {STRING_LITERAL} 136 | lval.str = string(y.buf.Bytes()) 137 | return STRING 138 | 139 | {STRING_EXPR} 140 | lval.str = string(y.buf.Bytes()) 141 | return STRING_EXPR 142 | 143 | {COMMENT} 144 | 145 | {PLUS} 146 | lval.str = string(y.buf.Bytes()) 147 | return PLUS 148 | 149 | {MINUS} 150 | lval.str = string(y.buf.Bytes()) 151 | return MINUS 152 | 153 | {TIMES} 154 | lval.str = string(y.buf.Bytes()) 155 | return TIMES 156 | 157 | {DIVIDE} 158 | lval.str = string(y.buf.Bytes()) 159 | return DIVIDE 160 | 161 | {LE} 162 | lval.str = string(y.buf.Bytes()) 163 | return LE 164 | 165 | {LT} 166 | lval.str = string(y.buf.Bytes()) 167 | return LT 168 | 169 | {GE} 170 | lval.str = string(y.buf.Bytes()) 171 | return GE 172 | 173 | {GT} 174 | lval.str = string(y.buf.Bytes()) 175 | return GT 176 | 177 | {PARAMEQUAL} 178 | lval.str = string(y.buf.Bytes()) 179 | return PARAMEQUAL 180 | 181 | {EQ} 182 | lval.str = string(y.buf.Bytes()) 183 | return EQ 184 | 185 | {NE} 186 | lval.str = string(y.buf.Bytes()) 187 | return NE 188 | 189 | {AND} 190 | lval.str = string(y.buf.Bytes()) 191 | return AND 192 | 193 | {OR} 194 | lval.str = string(y.buf.Bytes()) 195 | return OR 196 | 197 | {EQUALS} 198 | lval.str = string(y.buf.Bytes()) 199 | return EQUALS 200 | 201 | {COLONEQUAL} 202 | lval.str = string(y.buf.Bytes()) 203 | return COLONEQUAL 204 | 205 | {LPAREN} 206 | lval.str = string(y.buf.Bytes()) 207 | return LPAREN 208 | 209 | {RPAREN} 210 | lval.str = string(y.buf.Bytes()) 211 | return RPAREN 212 | 213 | {COMMA} 214 | lval.str = string(y.buf.Bytes()) 215 | return COMMA 216 | 217 | {SEMI} 218 | lval.str = string(y.buf.Bytes()) 219 | return SEMI 220 | 221 | {POUND} 222 | lval.str = string(y.buf.Bytes()) 223 | return POUND 224 | 225 | {DOLLAR} 226 | lval.str = string(y.buf.Bytes()) 227 | return DOLLAR 228 | 229 | {DOT} 230 | lval.str = string(y.buf.Bytes()) 231 | return DOT 232 | 233 | %% 234 | y.empty = true 235 | return int(c) 236 | } 237 | -------------------------------------------------------------------------------- /formulalibrary/base/factory/factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/stockfunc/function" 5 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 6 | ) 7 | 8 | type FormulaCreator interface { 9 | CreateFormula(data *function.RVector) (error, formula.Formula) 10 | } 11 | 12 | type FormulaCreatorFactory interface { 13 | CreateFormulaCreator(args []float64) FormulaCreator 14 | GetMeta() formula.FormulaMeta 15 | } 16 | -------------------------------------------------------------------------------- /formulalibrary/base/formula/drawactions.go: -------------------------------------------------------------------------------- 1 | package formula 2 | 3 | import "github.com/stephenlyu/goformula/function" 4 | 5 | // Draw Text Action 6 | type DrawTextAction struct { 7 | ActionType int 8 | NoDraw int 9 | Color *Color 10 | LineThick int 11 | 12 | Cond function.Value 13 | Price function.Value 14 | Text string 15 | } 16 | 17 | func (this *DrawTextAction) GetActionType() int { 18 | return this.ActionType 19 | } 20 | 21 | func (this *DrawTextAction) GetColor() *Color { 22 | return this.Color 23 | } 24 | 25 | func (this *DrawTextAction) GetLineThick() int { 26 | return this.LineThick 27 | } 28 | 29 | func (this *DrawTextAction) IsNoDraw() bool { 30 | return this.NoDraw != 0 31 | } 32 | 33 | func (this *DrawTextAction) GetVarIndex() int { 34 | return -1 35 | } 36 | 37 | func (this *DrawTextAction) GetCond(index int) float64 { 38 | return this.Cond.Get(index) 39 | } 40 | 41 | func (this *DrawTextAction) GetPrice(index int) float64 { 42 | return this.Price.Get(index) 43 | } 44 | 45 | func (this *DrawTextAction) GetText() string { 46 | return this.Text 47 | } 48 | 49 | // Draw Icon Action 50 | type DrawIconAction struct { 51 | ActionType int 52 | NoDraw int 53 | Color *Color 54 | LineThick int 55 | 56 | Cond function.Value 57 | Price function.Value 58 | Type int 59 | } 60 | 61 | func (this *DrawIconAction) GetActionType() int { 62 | return this.ActionType 63 | } 64 | 65 | func (this *DrawIconAction) GetColor() *Color { 66 | return this.Color 67 | } 68 | 69 | func (this *DrawIconAction) GetLineThick() int { 70 | return this.LineThick 71 | } 72 | 73 | func (this *DrawIconAction) IsNoDraw() bool { 74 | return this.NoDraw != 0 75 | } 76 | 77 | func (this *DrawIconAction) GetVarIndex() int { 78 | return -1 79 | } 80 | 81 | func (this *DrawIconAction) GetCond(index int) float64 { 82 | return this.Cond.Get(index) 83 | } 84 | 85 | func (this *DrawIconAction) GetPrice(index int) float64 { 86 | return this.Price.Get(index) 87 | } 88 | 89 | func (this *DrawIconAction) GetType() int { 90 | return this.Type 91 | } 92 | 93 | // Draw Line Action 94 | 95 | type DrawLineAction struct { 96 | ActionType int 97 | NoDraw int 98 | Color *Color 99 | LineThick int 100 | VarIndex int 101 | 102 | Cond1 function.Value 103 | Price1 function.Value 104 | Cond2 function.Value 105 | Price2 function.Value 106 | Expand int 107 | } 108 | 109 | func (this *DrawLineAction) GetActionType() int { 110 | return this.ActionType 111 | } 112 | 113 | func (this *DrawLineAction) GetColor() *Color { 114 | return this.Color 115 | } 116 | 117 | func (this *DrawLineAction) GetLineThick() int { 118 | return this.LineThick 119 | } 120 | 121 | func (this *DrawLineAction) IsNoDraw() bool { 122 | return this.NoDraw != 0 123 | } 124 | 125 | func (this *DrawLineAction) GetVarIndex() int { 126 | return this.VarIndex 127 | } 128 | 129 | func (this *DrawLineAction) GetCond1(index int) float64 { 130 | return this.Cond1.Get(index) 131 | } 132 | 133 | func (this *DrawLineAction) GetPrice1(index int) float64 { 134 | return this.Price1.Get(index) 135 | } 136 | 137 | func (this *DrawLineAction) GetCond2(index int) float64 { 138 | return this.Cond2.Get(index) 139 | } 140 | 141 | func (this *DrawLineAction) GetPrice2(index int) float64 { 142 | if index < 0 || index >= this.Price2.Len() { 143 | return function.NaN 144 | } 145 | return this.Price2.Get(index) 146 | } 147 | 148 | func (this *DrawLineAction) GetExpand() int { 149 | return this.Expand 150 | } 151 | 152 | // Stick Line Action 153 | 154 | type StickLineAction struct { 155 | ActionType int 156 | NoDraw int 157 | Color *Color 158 | LineThick int 159 | 160 | Cond function.Value 161 | Price1 function.Value 162 | Price2 function.Value 163 | Width float64 164 | Empty float64 165 | } 166 | 167 | func (this *StickLineAction) GetActionType() int { 168 | return this.ActionType 169 | } 170 | 171 | func (this *StickLineAction) GetColor() *Color { 172 | return this.Color 173 | } 174 | 175 | func (this *StickLineAction) GetLineThick() int { 176 | return this.LineThick 177 | } 178 | 179 | func (this *StickLineAction) IsNoDraw() bool { 180 | return this.NoDraw != 0 181 | } 182 | 183 | func (this *StickLineAction) GetVarIndex() int { 184 | return -1 185 | } 186 | 187 | func (this *StickLineAction) GetCond(index int) float64 { 188 | return this.Cond.Get(index) 189 | } 190 | 191 | func (this *StickLineAction) GetPrice1(index int) float64 { 192 | return this.Price1.Get(index) 193 | } 194 | 195 | func (this *StickLineAction) GetPrice2(index int) float64 { 196 | return this.Price2.Get(index) 197 | } 198 | 199 | func (this *StickLineAction) GetWidth() float64 { 200 | return this.Width 201 | } 202 | 203 | func (this *StickLineAction) GetEmpty() float64 { 204 | return this.Empty 205 | } 206 | 207 | // Ploy Line Action 208 | 209 | type PloyLineAction struct { 210 | ActionType int 211 | NoDraw int 212 | Color *Color 213 | LineThick int 214 | VarIndex int 215 | 216 | Cond function.Value 217 | Price function.Value 218 | } 219 | 220 | func (this *PloyLineAction) GetActionType() int { 221 | return this.ActionType 222 | } 223 | 224 | func (this *PloyLineAction) GetColor() *Color { 225 | return this.Color 226 | } 227 | 228 | func (this *PloyLineAction) GetLineThick() int { 229 | return this.LineThick 230 | } 231 | 232 | func (this *PloyLineAction) IsNoDraw() bool { 233 | return this.NoDraw != 0 234 | } 235 | 236 | func (this *PloyLineAction) GetVarIndex() int { 237 | return this.VarIndex 238 | } 239 | 240 | func (this *PloyLineAction) GetCond(index int) float64 { 241 | return this.Cond.Get(index) 242 | } 243 | 244 | func (this *PloyLineAction) GetPrice(index int) float64 { 245 | return this.Price.Get(index) 246 | } 247 | 248 | // Draw KLine Action 249 | 250 | type DrawKLineAction struct { 251 | ActionType int 252 | NoDraw int 253 | Color *Color 254 | LineThick int 255 | 256 | High function.Value 257 | Open function.Value 258 | Low function.Value 259 | Close function.Value 260 | } 261 | 262 | func (this *DrawKLineAction) GetActionType() int { 263 | return this.ActionType 264 | } 265 | 266 | func (this *DrawKLineAction) GetColor() *Color { 267 | return this.Color 268 | } 269 | 270 | func (this *DrawKLineAction) GetLineThick() int { 271 | return this.LineThick 272 | } 273 | 274 | func (this *DrawKLineAction) IsNoDraw() bool { 275 | return this.NoDraw != 0 276 | } 277 | 278 | func (this *DrawKLineAction) GetVarIndex() int { 279 | return -1 280 | } 281 | 282 | func (this *DrawKLineAction) GetHigh(index int) float64 { 283 | return this.High.Get(index) 284 | } 285 | 286 | func (this *DrawKLineAction) GetOpen(index int) float64 { 287 | return this.Open.Get(index) 288 | } 289 | 290 | func (this *DrawKLineAction) GetLow(index int) float64 { 291 | return this.Low.Get(index) 292 | } 293 | 294 | func (this *DrawKLineAction) GetClose(index int) float64 { 295 | return this.Close.Get(index) 296 | } 297 | -------------------------------------------------------------------------------- /formulalibrary/base/formula/formula.go: -------------------------------------------------------------------------------- 1 | package formula 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/function" 5 | "github.com/stephenlyu/tds/period" 6 | ) 7 | 8 | const ( 9 | FORMULA_VAR_FLAG_NO_DRAW = 1 10 | FORMULA_VAR_FLAG_NO_TEXT = 2 11 | FORMULA_VAR_FLAG_DRAW_ABOVE = 4 12 | FORMULA_VAR_FLAG_NO_FRAME = 8 13 | ) 14 | 15 | const ( 16 | FORMULA_GRAPH_NONE = iota 17 | FORMULA_GRAPH_LINE 18 | FORMULA_GRAPH_COLOR_STICK 19 | FORMULA_GRAPH_STICK 20 | FORMULA_GRAPH_VOL_STICK 21 | FORMULA_GRAPH_LINE_STICK 22 | ) 23 | 24 | const ( 25 | FORMULA_LINE_STYLE_SOLID = iota 26 | FORMULA_LINE_STYLE_DOT 27 | FORMULA_LINE_STYLE_CROSS_DOT 28 | FORMULA_LINE_STYLE_CIRCLE_DOT 29 | FORMULA_LINE_STYLE_POINT_DOT 30 | ) 31 | 32 | const ( 33 | FORMULA_DRAW_ACTION_PLOYLINE = iota 34 | FORMULA_DRAW_ACTION_DRAWLINE 35 | FORMULA_DRAW_ACTION_DRAWKLINE 36 | FORMULA_DRAW_ACTION_STICKLINE 37 | FORMULA_DRAW_ACTION_DRAWICON 38 | FORMULA_DRAW_ACTION_DRAWTEXT 39 | ) 40 | 41 | type Color struct { 42 | Red int 43 | Green int 44 | Blue int 45 | } 46 | 47 | type DrawAction interface { 48 | GetActionType () int 49 | GetVarIndex() int 50 | GetColor() *Color 51 | GetLineThick() int 52 | IsNoDraw() bool 53 | } 54 | 55 | type PloyLine interface { 56 | DrawAction 57 | 58 | GetCond(index int) float64 59 | GetPrice(index int) float64 60 | } 61 | 62 | type DrawLine interface { 63 | DrawAction 64 | 65 | GetCond1(index int) float64 66 | GetPrice1(index int) float64 67 | GetCond2(index int) float64 68 | GetPrice2(index int) float64 69 | GetExpand() int 70 | } 71 | 72 | type DrawKLine interface { 73 | DrawAction 74 | 75 | GetHigh(index int) float64 76 | GetOpen(index int) float64 77 | GetLow(index int) float64 78 | GetClose(index int) float64 79 | } 80 | 81 | type StickLine interface { 82 | DrawAction 83 | 84 | GetCond(index int) float64 85 | GetPrice1(index int) float64 86 | GetPrice2(index int) float64 87 | GetWidth() float64 88 | GetEmpty() float64 89 | } 90 | 91 | type DrawIcon interface { 92 | DrawAction 93 | 94 | GetCond(index int) float64 95 | GetPrice(index int) float64 96 | GetType() int 97 | } 98 | 99 | type DrawText interface { 100 | DrawAction 101 | 102 | GetCond(index int) float64 103 | GetPrice(index int) float64 104 | GetText() string 105 | } 106 | 107 | type Formula interface { 108 | FormulaMeta 109 | 110 | Period() period.Period 111 | Len() int 112 | UpdateLastValue() 113 | Get(index int) []float64 114 | Ref(offset int) []float64 115 | GetVarValue(varName string) function.Value 116 | 117 | // 绘制图形 118 | DrawActions() []DrawAction 119 | 120 | Destroy() 121 | } 122 | -------------------------------------------------------------------------------- /formulalibrary/base/formula/manager.go: -------------------------------------------------------------------------------- 1 | package formula 2 | 3 | import "github.com/stephenlyu/goformula/stockfunc/function" 4 | 5 | type FormulaManager interface { 6 | // 是否支持名为name的公式 7 | CanSupportVar(name string, varName string) bool 8 | 9 | // 是否支持周期 10 | CanSupportPeriod(period string) bool 11 | 12 | // 是否支持证券品种 13 | CanSupportSecurity(code string) bool 14 | 15 | // 使用默认参数创建公式 16 | NewFormula(name string, data *function.RVector) Formula 17 | 18 | // 使用指定参数创建公式 19 | NewFormulaWithArgs(name string, data *function.RVector, args []float64) Formula 20 | } 21 | -------------------------------------------------------------------------------- /formulalibrary/base/formula/meta.go: -------------------------------------------------------------------------------- 1 | package formula 2 | 3 | type FormulaMeta interface { 4 | GetName() string // 公式名 5 | 6 | // 输出变量 7 | VarCount() int 8 | VarName(index int) string // Ref变量名列表 9 | HasVar(name string) bool 10 | NoDraw(index int) bool // 是否绘制图形 11 | NoText(index int) bool // 是否绘制文本 12 | DrawAbove(index int) bool 13 | NoFrame(index int) bool 14 | Color(index int) *Color // 变量颜色 15 | LineThick(index int) int // 线宽,1-9 16 | LineStyle(index int) int // 线宽,1-9 17 | GraphType(index int) int 18 | 19 | // 公式参数 20 | ArgCount() int 21 | ArgRange(index int) (float64, float64) // 参数范围 22 | ArgDefault(index int) float64 // 参数默认值 23 | DefaultArgs() []float64 24 | } 25 | 26 | // Formula Meta Implementations 27 | 28 | type Arg struct { 29 | Min float64 30 | Max float64 31 | Default float64 32 | } 33 | 34 | type FormulaMetaImpl struct { 35 | Name string 36 | ArgNames []string 37 | ArgMeta []Arg 38 | Flags []int 39 | Colors []*Color 40 | LineThicks []int 41 | LineStyles []int 42 | GraphTypes []int 43 | Vars []string 44 | } 45 | 46 | func (this *FormulaMetaImpl) GetName() string { 47 | return this.Name 48 | } 49 | 50 | // 输出变量 51 | 52 | func (this *FormulaMetaImpl) VarCount() int { 53 | return len(this.Vars) 54 | } 55 | 56 | func (this *FormulaMetaImpl) VarName(index int) string { 57 | if index < 0 || index >= len(this.Vars) { 58 | return "" 59 | } 60 | return this.Vars[index] 61 | } 62 | 63 | func (this *FormulaMetaImpl) HasVar(varName string) bool { 64 | for _, v := range this.Vars { 65 | if v == varName { 66 | return true 67 | } 68 | } 69 | return false 70 | } 71 | 72 | func (this *FormulaMetaImpl) NoDraw(index int) bool { 73 | if index < 0 || index >= len(this.Flags) { 74 | return false 75 | } 76 | return (this.Flags[index] & FORMULA_VAR_FLAG_NO_DRAW) != 0 77 | } 78 | 79 | func (this *FormulaMetaImpl) NoText(index int) bool { 80 | if index < 0 || index >= len(this.Flags) { 81 | return false 82 | } 83 | return (this.Flags[index] & FORMULA_VAR_FLAG_NO_TEXT) != 0 84 | } 85 | 86 | func (this *FormulaMetaImpl) DrawAbove(index int) bool { 87 | if index < 0 || index >= len(this.Flags) { 88 | return false 89 | } 90 | return this.Flags[index] & FORMULA_VAR_FLAG_DRAW_ABOVE != 0 91 | } 92 | 93 | func (this *FormulaMetaImpl) NoFrame(index int) bool { 94 | if index < 0 || index >= len(this.Flags) { 95 | return false 96 | } 97 | return this.Flags[index] & FORMULA_VAR_FLAG_NO_FRAME != 0 98 | } 99 | 100 | func (this *FormulaMetaImpl) Color(index int) *Color { 101 | if index < 0 || index >= len(this.Colors) { 102 | return nil 103 | } 104 | return this.Colors[index] 105 | } 106 | 107 | func (this *FormulaMetaImpl) LineThick(index int) int { 108 | if index < 0 || index >= len(this.LineThicks) { 109 | return 1 110 | } 111 | return this.LineThicks[index] 112 | } 113 | 114 | func (this *FormulaMetaImpl) LineStyle(index int) int { 115 | if index < 0 || index >= len(this.LineStyles) { 116 | return 1 117 | } 118 | return this.LineStyles[index] 119 | } 120 | 121 | func (this *FormulaMetaImpl) GraphType(index int) int { 122 | if index < 0 || index >= len(this.GraphTypes) { 123 | return 1 124 | } 125 | return this.GraphTypes[index] 126 | } 127 | 128 | // 公式参数 129 | 130 | func (this *FormulaMetaImpl) ArgCount() int { 131 | return len(this.ArgNames) 132 | } 133 | 134 | func (this *FormulaMetaImpl) ArgRange(index int) (float64, float64) { 135 | if index < 0 || index >= len(this.ArgMeta) { 136 | return 0, 0 137 | } 138 | return this.ArgMeta[index].Min, this.ArgMeta[index].Max 139 | } 140 | 141 | func (this *FormulaMetaImpl) ArgDefault(index int) float64 { 142 | if index < 0 || index >= len(this.ArgMeta) { 143 | return 0 144 | } 145 | return this.ArgMeta[index].Default 146 | } 147 | 148 | func (this *FormulaMetaImpl) DefaultArgs() []float64 { 149 | ret := make([]float64, this.ArgCount()) 150 | for i := range this.ArgMeta { 151 | ret[i] = this.ArgMeta[i].Default 152 | } 153 | return ret 154 | } 155 | -------------------------------------------------------------------------------- /formulalibrary/easylang/easylangfactory/creator.go: -------------------------------------------------------------------------------- 1 | package easylangfactory 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 5 | "github.com/stephenlyu/goformula/stockfunc/function" 6 | "github.com/stephenlyu/goformula/formulalibrary/lua/luaformula" 7 | ) 8 | 9 | type easyLangFormulaCreator struct { 10 | factory *easylangFormulaCreatorFactory 11 | args []float64 12 | } 13 | 14 | func (this *easyLangFormulaCreator) CreateFormula(data *function.RVector) (error, formula.Formula) { 15 | return luaformula.NewFormulaFromState(this.factory.L, this.factory.Meta, data, this.args) 16 | } 17 | -------------------------------------------------------------------------------- /formulalibrary/easylang/easylangfactory/creatorfactory.go: -------------------------------------------------------------------------------- 1 | package easylangfactory 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/aarzilli/golua/lua" 7 | "github.com/stephenlyu/goformula/easylang" 8 | "github.com/stephenlyu/goformula/formulalibrary/base/factory" 9 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 10 | "github.com/stephenlyu/goformula/formulalibrary/lua/luaformula" 11 | "github.com/stevedonovan/luar" 12 | ) 13 | 14 | type easylangFormulaCreatorFactory struct { 15 | easyLangFile string 16 | 17 | L *lua.State 18 | Meta *formula.FormulaMetaImpl 19 | } 20 | 21 | func NewEasyLangFormulaCreatorFactory(easyLangFile string, formulaManager formula.FormulaManager, debug bool) (error, factory.FormulaCreatorFactory) { 22 | // Compile Easy Lang File 23 | err, code := easylang.CompileFile(easyLangFile, formulaManager, true, debug) 24 | if err != nil { 25 | return err, nil 26 | } 27 | if debug { 28 | ioutil.WriteFile(easyLangFile+".lua", []byte(code), 0666) 29 | } 30 | 31 | // Init Lua Engine 32 | L := luar.Init() 33 | luar.Register(L, "", luaformula.GetFunctionMap(luar.Map{})) 34 | 35 | err = L.DoString(code) 36 | if err != nil { 37 | return err, nil 38 | } 39 | 40 | L.GetGlobal("FormulaClass") 41 | meta := &formula.FormulaMetaImpl{} 42 | luaformula.GetMetaFromLuaState(L, meta) 43 | L.Pop(1) 44 | 45 | return nil, &easylangFormulaCreatorFactory{easyLangFile: easyLangFile, L: L, Meta: meta} 46 | } 47 | 48 | func (this *easylangFormulaCreatorFactory) GetMeta() formula.FormulaMeta { 49 | return this.Meta 50 | } 51 | 52 | func (this *easylangFormulaCreatorFactory) CreateFormulaCreator(args []float64) factory.FormulaCreator { 53 | if args == nil { 54 | args = this.Meta.DefaultArgs() 55 | } 56 | 57 | return &easyLangFormulaCreator{ 58 | factory: this, 59 | args: args, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /formulalibrary/formulalibrary.go: -------------------------------------------------------------------------------- 1 | package formulalibrary 2 | 3 | import ( 4 | . "github.com/stephenlyu/goformula/formulalibrary/base/factory" 5 | "github.com/stephenlyu/goformula/formulalibrary/easylang/easylangfactory" 6 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 7 | "github.com/stephenlyu/goformula/stockfunc/function" 8 | "sync" 9 | "errors" 10 | "github.com/stephenlyu/goformula/formulalibrary/lua/luaformula" 11 | "github.com/stephenlyu/goformula/formulalibrary/lua/luafactory" 12 | "github.com/z-ray/log" 13 | "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 14 | "github.com/stephenlyu/goformula/formulalibrary/native/nativefactory" 15 | "strings" 16 | "path/filepath" 17 | "fmt" 18 | "github.com/stephenlyu/goformula/datalibrary" 19 | ) 20 | 21 | type FormulaChangeListener interface { 22 | OnFormulaChanged(name string) 23 | } 24 | 25 | type FormulaLibrary struct { 26 | formulaFactories map[string]FormulaCreatorFactory 27 | lock sync.Mutex 28 | 29 | debug bool 30 | 31 | loadNative bool // 是否加载Native公式 32 | loadLua bool // 是否加载Lua公式 33 | loadEasyLang bool // 是否加载EasyLang公式 34 | 35 | formulaChangedListeners []FormulaChangeListener 36 | 37 | dataLibrary datalibrary.DataLibrary 38 | } 39 | 40 | func newFormulaLibrary() *FormulaLibrary { 41 | this := &FormulaLibrary {} 42 | this.Reset() 43 | 44 | luaformula.SetFormulaManager(this) 45 | return this 46 | } 47 | 48 | func (this *FormulaLibrary) SetDebug(v bool) { 49 | this.debug = v 50 | } 51 | 52 | func (this *FormulaLibrary) SetLoadNative(v bool) { 53 | this.loadNative = v 54 | } 55 | 56 | func (this *FormulaLibrary) SetLoadLua(v bool) { 57 | this.loadLua = v 58 | } 59 | 60 | func (this *FormulaLibrary) SetLoadEasyLang(v bool) { 61 | this.loadEasyLang = v 62 | } 63 | 64 | func (this *FormulaLibrary) SetDataLibrary(dl datalibrary.DataLibrary) { 65 | this.dataLibrary = dl 66 | luaformula.SetDataLibrary(dl) 67 | datalibrary.SetDataLibrary(dl) 68 | } 69 | 70 | func (this *FormulaLibrary) Reset() { 71 | this.formulaFactories = make(map[string]FormulaCreatorFactory) 72 | this.formulaChangedListeners = []FormulaChangeListener{} 73 | this.debug = false 74 | this.loadNative = true 75 | this.loadLua = true 76 | this.loadEasyLang = true 77 | this.dataLibrary = nil 78 | } 79 | 80 | func (this *FormulaLibrary) CanSupport(name string) bool { 81 | this.lock.Lock() 82 | defer this.lock.Unlock() 83 | _, ok := this.formulaFactories[name] 84 | return ok 85 | } 86 | 87 | func (this *FormulaLibrary) LoadNativeFormulas() { 88 | if !this.loadNative { 89 | log.Warnf("Please turn loadNative on first!") 90 | return 91 | } 92 | 93 | for _, nf := range nativeformulas.NativeFormulas { 94 | name := strings.ToUpper(nf.Meta.Name) 95 | _, factory := nativefactory.NewNativeFormulaCreatorFactory(name) 96 | this.Register(name, factory) 97 | } 98 | } 99 | 100 | func (this *FormulaLibrary) LoadLuaFormulas(dir string) { 101 | if !this.loadLua { 102 | log.Warnf("Please turn loadLua on first!") 103 | return 104 | } 105 | 106 | files, err := filepath.Glob(filepath.Join(dir, "*.lua")) 107 | if err != nil { 108 | log.Errorf("Load lua formulas fail, error: %v", err) 109 | return 110 | } 111 | 112 | for _, filePath := range files { 113 | baseName := filepath.Base(filePath) 114 | parts := strings.Split(baseName, ".") 115 | name := strings.ToUpper(parts[0]) 116 | if this.IsFormulaExisted(name) { 117 | log.Errorf("Formula %s exist, lua version ignored.", name) 118 | continue 119 | } 120 | 121 | err = this.RegisterLuaFile(name, filePath) 122 | if err != nil { 123 | log.Errorf("Load lua formula %s fail, error: %v", filePath, err) 124 | } else { 125 | log.Infof("Load lua formula %s success", filePath) 126 | } 127 | } 128 | } 129 | 130 | func (this *FormulaLibrary) LoadEasyLangFormulas(dir string) { 131 | if !this.loadLua { 132 | log.Warnf("Please turn loadEasyLang on first!") 133 | return 134 | } 135 | 136 | files, err := filepath.Glob(filepath.Join(dir, "*.d")) 137 | if err != nil { 138 | log.Errorf("Load lua formulas fail, error: %v", err) 139 | return 140 | } 141 | 142 | var errorFiles []string 143 | 144 | for { 145 | changed := false 146 | errorFiles = []string{} 147 | for _, filePath := range files { 148 | baseName := filepath.Base(filePath) 149 | parts := strings.Split(baseName, ".") 150 | name := strings.ToUpper(parts[0]) 151 | if this.IsFormulaExisted(name) { 152 | log.Errorf("Formula %s exist, easylang version ignored.", name) 153 | continue 154 | } 155 | err = this.RegisterEasyLangFile(name, filePath, this.debug) 156 | if err != nil { 157 | log.Errorf("Load easy lang formula %s fail, error: %+v", filePath, err) 158 | errorFiles = append(errorFiles, filePath) 159 | } else { 160 | changed = true 161 | } 162 | } 163 | if changed { 164 | files = errorFiles 165 | } else { 166 | break 167 | } 168 | } 169 | } 170 | 171 | func (this *FormulaLibrary) LoadAllFormulas(luaDir string, easyLangDir string) { 172 | if this.loadNative { 173 | this.LoadNativeFormulas() 174 | } 175 | 176 | if this.loadLua { 177 | this.LoadLuaFormulas(luaDir) 178 | } 179 | 180 | if this.loadEasyLang { 181 | this.LoadEasyLangFormulas(easyLangDir) 182 | } 183 | } 184 | 185 | func (this *FormulaLibrary) RegisterEasyLangFile(name, easyLangFile string, debug bool) (err error) { 186 | defer func() { 187 | if err1 := recover(); err1 != nil { 188 | err = errors.New(fmt.Sprintf("%v", err1)) 189 | } 190 | }() 191 | 192 | err, factory := easylangfactory.NewEasyLangFormulaCreatorFactory(easyLangFile, this, debug) 193 | if err != nil { 194 | return err 195 | } 196 | this.Register(name, factory) 197 | return nil 198 | } 199 | 200 | func (this *FormulaLibrary) RegisterLuaFile(name, luaFile string) (err error) { 201 | defer func() { 202 | if err1 := recover(); err1 != nil { 203 | err = errors.New(fmt.Sprintf("%v", err1)) 204 | } 205 | }() 206 | 207 | err, factory := luafactory.NewLuaFormulaCreatorFactory(luaFile) 208 | if err != nil { 209 | return err 210 | } 211 | this.Register(name, factory) 212 | return nil 213 | } 214 | 215 | func (this *FormulaLibrary) IsFormulaExisted(name string) bool { 216 | this.lock.Lock() 217 | defer this.lock.Unlock() 218 | _, ok := this.formulaFactories[name] 219 | return ok 220 | } 221 | 222 | func (this *FormulaLibrary) Register(name string, creatorFactory FormulaCreatorFactory) { 223 | this.lock.Lock() 224 | defer this.lock.Unlock() 225 | this.formulaFactories[name] = creatorFactory 226 | } 227 | 228 | func (this *FormulaLibrary) Unregister(name string, creatorFactory FormulaCreatorFactory) { 229 | this.lock.Lock() 230 | defer this.lock.Unlock() 231 | delete(this.formulaFactories, name) 232 | } 233 | 234 | func (this *FormulaLibrary) GetCreatorFactory(name string) FormulaCreatorFactory { 235 | this.lock.Lock() 236 | defer this.lock.Unlock() 237 | return this.formulaFactories[name] 238 | } 239 | 240 | // FormulaChangedListener相关Routine 241 | 242 | func (this *FormulaLibrary) AddFormulaChangeListener(listener FormulaChangeListener) { 243 | if listener == nil { 244 | return 245 | } 246 | this.lock.Lock() 247 | defer this.lock.Unlock() 248 | for _, l := range this.formulaChangedListeners { 249 | if l == listener { 250 | return 251 | } 252 | } 253 | this.formulaChangedListeners = append(this.formulaChangedListeners, listener) 254 | } 255 | 256 | func (this *FormulaLibrary) RemoveFormulaChangeListener(listener FormulaChangeListener) { 257 | if listener == nil { 258 | return 259 | } 260 | this.lock.Lock() 261 | defer this.lock.Unlock() 262 | for i, l := range this.formulaChangedListeners { 263 | if l == listener { 264 | this.formulaChangedListeners = append(this.formulaChangedListeners[:i], this.formulaChangedListeners[i+1:]...) 265 | return 266 | } 267 | } 268 | } 269 | 270 | func (this *FormulaLibrary) NotifyFormulaChanged(name string) { 271 | this.lock.Lock() 272 | defer this.lock.Unlock() 273 | for _, listener := range this.formulaChangedListeners { 274 | listener.OnFormulaChanged(name) 275 | } 276 | } 277 | 278 | // 实现FormulaManager接口 279 | 280 | 281 | func (this *FormulaLibrary) CanSupportVar(name string, varName string) bool { 282 | this.lock.Lock() 283 | defer this.lock.Unlock() 284 | factory, ok := this.formulaFactories[name] 285 | if !ok { 286 | return false 287 | } 288 | 289 | return factory.GetMeta().HasVar(varName) 290 | } 291 | 292 | func (this *FormulaLibrary) CanSupportPeriod(period string) bool { 293 | return true 294 | } 295 | 296 | func (this *FormulaLibrary) CanSupportSecurity(code string) bool { 297 | return true 298 | } 299 | 300 | func (this *FormulaLibrary) NewFormula(name string, data *function.RVector) formula.Formula { 301 | factory, ok := this.formulaFactories[name] 302 | if !ok { 303 | panic(errors.New("公式不存在")) 304 | } 305 | 306 | args := factory.GetMeta().DefaultArgs() 307 | return this.NewFormulaWithArgs(name, data, args) 308 | } 309 | 310 | // TODO: 1. 如何缓存公式 2. 如何管理公式间的依赖 3. 公式改变后,如何更新依赖 311 | func (this *FormulaLibrary) NewFormulaWithArgs(name string, data *function.RVector, args []float64) formula.Formula { 312 | factory, ok := this.formulaFactories[name] 313 | if !ok { 314 | panic(errors.New("公式不存在")) 315 | } 316 | 317 | creator := factory.CreateFormulaCreator(args) 318 | err, formula := creator.CreateFormula(data) 319 | if err != nil { 320 | panic(err) 321 | } 322 | return formula 323 | } 324 | 325 | var GlobalLibrary = newFormulaLibrary() 326 | -------------------------------------------------------------------------------- /formulalibrary/lua/luafactory/creator.go: -------------------------------------------------------------------------------- 1 | package luafactory 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 5 | "github.com/stephenlyu/goformula/stockfunc/function" 6 | "github.com/stephenlyu/goformula/formulalibrary/lua/luaformula" 7 | ) 8 | 9 | type luaFormulaCreator struct { 10 | factory *luaFormulaCreatorFactory 11 | args []float64 12 | } 13 | 14 | func (this *luaFormulaCreator) CreateFormula(data *function.RVector) (error, formula.Formula) { 15 | return luaformula.NewFormulaFromState(this.factory.L, this.factory.Meta, data, this.args) 16 | } 17 | -------------------------------------------------------------------------------- /formulalibrary/lua/luafactory/creatorfactory.go: -------------------------------------------------------------------------------- 1 | package luafactory 2 | 3 | import ( 4 | "github.com/aarzilli/golua/lua" 5 | "github.com/stephenlyu/goformula/formulalibrary/base/factory" 6 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 7 | "github.com/stephenlyu/goformula/formulalibrary/lua/luaformula" 8 | "github.com/stevedonovan/luar" 9 | ) 10 | 11 | type luaFormulaCreatorFactory struct { 12 | luaFile string 13 | 14 | L *lua.State 15 | Meta *formula.FormulaMetaImpl 16 | } 17 | 18 | func NewLuaFormulaCreatorFactory(luaFile string) (error, factory.FormulaCreatorFactory) { 19 | L := luar.Init() 20 | 21 | luar.Register(L, "", luaformula.GetFunctionMap(luar.Map{})) 22 | 23 | err := L.DoFile(luaFile) 24 | if err != nil { 25 | return err, nil 26 | } 27 | 28 | L.GetGlobal("FormulaClass") 29 | meta := &formula.FormulaMetaImpl{} 30 | luaformula.GetMetaFromLuaState(L, meta) 31 | L.Pop(1) 32 | 33 | return nil, &luaFormulaCreatorFactory{luaFile: luaFile, L: L, Meta: meta} 34 | } 35 | func (this *luaFormulaCreatorFactory) GetMeta() formula.FormulaMeta { 36 | return this.Meta 37 | } 38 | 39 | func (this *luaFormulaCreatorFactory) CreateFormulaCreator(args []float64) factory.FormulaCreator { 40 | if args == nil { 41 | args = this.Meta.DefaultArgs() 42 | } 43 | 44 | return &luaFormulaCreator{ 45 | factory: this, 46 | args: args, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /formulalibrary/lua/luaformula/luaformula.go: -------------------------------------------------------------------------------- 1 | package luaformula 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math" 7 | "strings" 8 | 9 | "github.com/aarzilli/golua/lua" 10 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 11 | "github.com/stephenlyu/goformula/function" 12 | stockfunc "github.com/stephenlyu/goformula/stockfunc/function" 13 | "github.com/stephenlyu/tds/period" 14 | "github.com/stephenlyu/tds/util" 15 | "github.com/stevedonovan/luar" 16 | ) 17 | 18 | type LuaFormula struct { 19 | *FormulaMetaImpl 20 | L *lua.State 21 | 22 | period period.Period 23 | code string 24 | data *stockfunc.RVector 25 | 26 | args []float64 27 | refValues []function.Value 28 | 29 | id int64 30 | 31 | // Draw Actions 32 | drawActions []DrawAction 33 | } 34 | 35 | func newFormulaByLuaState(L *lua.State, meta *FormulaMetaImpl, data *stockfunc.RVector, args []float64) (error, *LuaFormula) { 36 | L.GetGlobal("FormulaClass") 37 | if meta == nil { 38 | meta = &FormulaMetaImpl{} 39 | GetMetaFromLuaState(L, meta) 40 | } 41 | 42 | L.GetField(-1, "new") 43 | L.PushValue(-2) 44 | luar.GoToLuaProxy(L, data) 45 | for _, arg := range args { 46 | luar.GoToLua(L, arg) 47 | } 48 | L.Call(2+len(args), 1) 49 | if L.IsNil(-1) { 50 | return errors.New("Create formula fail"), nil 51 | } 52 | 53 | L.GetField(-1, "ref_values") 54 | var values []function.Value 55 | luar.LuaToGo(L, -1, &values) 56 | L.Pop(1) 57 | 58 | L.GetField(-1, "__id__") 59 | var id int64 60 | luar.LuaToGo(L, -1, &id) 61 | L.Pop(1) 62 | 63 | L.GetField(-1, "drawTextActions") 64 | var drawTexts []DrawTextAction 65 | luar.LuaToGo(L, -1, &drawTexts) 66 | L.Pop(1) 67 | 68 | L.GetField(-1, "drawIconActions") 69 | var drawIcons []DrawIconAction 70 | luar.LuaToGo(L, -1, &drawIcons) 71 | L.Pop(1) 72 | 73 | L.GetField(-1, "drawLineActions") 74 | var drawLines []DrawLineAction 75 | luar.LuaToGo(L, -1, &drawLines) 76 | L.Pop(1) 77 | 78 | L.GetField(-1, "drawKLineActions") 79 | var drawKLines []DrawKLineAction 80 | luar.LuaToGo(L, -1, &drawKLines) 81 | L.Pop(1) 82 | 83 | L.GetField(-1, "stickLineActions") 84 | var stickLines []StickLineAction 85 | luar.LuaToGo(L, -1, &stickLines) 86 | L.Pop(1) 87 | 88 | L.GetField(-1, "ployLineActions") 89 | var ployLines []PloyLineAction 90 | luar.LuaToGo(L, -1, &ployLines) 91 | L.Pop(1) 92 | 93 | drawActions := make([]DrawAction, len(drawTexts)+len(drawIcons)+len(drawLines)+len(drawKLines)+len(stickLines)+len(ployLines)) 94 | i := 0 95 | for j := range drawTexts { 96 | action := &drawTexts[j] 97 | if action.Color != nil && action.Color.Red == -1 { 98 | action.Color = nil 99 | } 100 | drawActions[i] = action 101 | i++ 102 | } 103 | for j := range drawIcons { 104 | action := &drawIcons[j] 105 | drawActions[i] = action 106 | i++ 107 | } 108 | for j := range drawLines { 109 | action := &drawLines[j] 110 | if action.Color != nil && action.Color.Red == -1 { 111 | action.Color = nil 112 | } 113 | drawActions[i] = action 114 | i++ 115 | } 116 | for j := range drawKLines { 117 | action := &drawKLines[j] 118 | drawActions[i] = action 119 | i++ 120 | } 121 | for j := range stickLines { 122 | action := &stickLines[j] 123 | if action.Color != nil && action.Color.Red == -1 { 124 | action.Color = nil 125 | } 126 | drawActions[i] = action 127 | i++ 128 | } 129 | for j := range ployLines { 130 | action := &ployLines[j] 131 | if action.Color != nil && action.Color.Red == -1 { 132 | action.Color = nil 133 | } 134 | drawActions[i] = action 135 | i++ 136 | } 137 | 138 | L.Remove(1) // Remove FormulaClass from stack 139 | L.Pop(1) // Remove formula object from stack 140 | util.Assert(L.GetTop() == 0, "") 141 | 142 | formula := &LuaFormula{ 143 | FormulaMetaImpl: meta, 144 | L: L, 145 | 146 | period: data.Period(), 147 | code: data.Code(), 148 | data: data, 149 | 150 | args: args, 151 | refValues: values, 152 | 153 | id: id, 154 | 155 | drawActions: drawActions, 156 | } 157 | 158 | return nil, formula 159 | } 160 | 161 | func NewFormula(luaFile string, data *stockfunc.RVector, args []float64) (error, *LuaFormula) { 162 | L := luar.Init() 163 | 164 | luar.Register(L, "", GetFunctionMap(luar.Map{})) 165 | 166 | err := L.DoFile(luaFile) 167 | if err != nil { 168 | return err, nil 169 | } 170 | 171 | return newFormulaByLuaState(L, nil, data, args) 172 | } 173 | 174 | func NewFormulaFromState(L *lua.State, meta *FormulaMetaImpl, data *stockfunc.RVector, args []float64) (error, *LuaFormula) { 175 | return newFormulaByLuaState(L, meta, data, args) 176 | } 177 | 178 | func NewFormulaFromCode(luaCode string, data *stockfunc.RVector, args []float64) (error, *LuaFormula) { 179 | L := luar.Init() 180 | 181 | luar.Register(L, "", GetFunctionMap(luar.Map{})) 182 | 183 | err := L.DoString(luaCode) 184 | if err != nil { 185 | return err, nil 186 | } 187 | 188 | return newFormulaByLuaState(L, nil, data, args) 189 | } 190 | 191 | func (this *LuaFormula) Destroy() { 192 | this.L.GetGlobal("RemoveObject") 193 | this.L.PushInteger(this.id) 194 | this.L.Call(1, 1) 195 | } 196 | 197 | func (this *LuaFormula) Period() period.Period { 198 | return this.period 199 | } 200 | 201 | func (this *LuaFormula) Len() int { 202 | return this.data.Len() 203 | } 204 | 205 | func (this *LuaFormula) getObject() { 206 | this.L.GetGlobal("GetObject") 207 | this.L.PushInteger(this.id) 208 | this.L.Call(1, 1) 209 | } 210 | 211 | func (this *LuaFormula) UpdateLastValue() { 212 | //util.Assert(this.L.GetTop() == 0, "") 213 | this.getObject() 214 | this.L.GetField(-1, "updateLastValue") 215 | this.L.PushValue(-2) 216 | this.L.Call(1, 0) 217 | this.L.Pop(1) 218 | //util.Assert(this.L.GetTop() == 0, "") 219 | } 220 | 221 | func (this *LuaFormula) Get(index int) []float64 { 222 | ret := make([]float64, len(this.refValues)) 223 | 224 | for i, refValue := range this.refValues { 225 | ret[i] = refValue.Get(index) 226 | } 227 | 228 | return ret 229 | } 230 | 231 | func (this *LuaFormula) Ref(offset int) []float64 { 232 | index := this.Len() - 1 - offset 233 | return this.Get(index) 234 | } 235 | 236 | func (this *LuaFormula) GetVarValue(varName string) function.Value { 237 | for i, v := range this.FormulaMetaImpl.Vars { 238 | if varName == v { 239 | return this.refValues[i] 240 | } 241 | } 242 | return nil 243 | } 244 | 245 | func (this *LuaFormula) Name() string { 246 | if len(this.ArgMeta) == 0 { 247 | return this.GetName() 248 | } 249 | 250 | formatValue := func(v float64) string { 251 | if float64(int(v)) == v { 252 | return fmt.Sprintf("%d", int(v)) 253 | } 254 | 255 | n := math.Log(math.Abs(v)) 256 | switch { 257 | case n > 4: 258 | return fmt.Sprintf("%.0f", v) 259 | case n == 3: 260 | return fmt.Sprintf("%.1f", v) 261 | case n > -2: 262 | return fmt.Sprintf("%.2f", v) 263 | case n == -3: 264 | return fmt.Sprintf("%.3f", v) 265 | default: 266 | return fmt.Sprintf("%.4f", v) 267 | } 268 | } 269 | 270 | items := make([]string, len(this.ArgMeta)) 271 | for i, arg := range this.args { 272 | items[i] = fmt.Sprintf(formatValue(arg)) 273 | } 274 | 275 | return fmt.Sprintf("%s(%s)", this.GetName(), strings.Join(items, ", ")) 276 | } 277 | 278 | func (this *LuaFormula) DrawActions() []DrawAction { 279 | return this.drawActions 280 | } 281 | -------------------------------------------------------------------------------- /formulalibrary/lua/luaformula/luafunc.go: -------------------------------------------------------------------------------- 1 | package luaformula 2 | 3 | import ( 4 | "github.com/stevedonovan/luar" 5 | "github.com/stephenlyu/goformula/function" 6 | stockfunc "github.com/stephenlyu/goformula/stockfunc/function" 7 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 8 | "github.com/stephenlyu/goformula/datalibrary" 9 | ) 10 | 11 | var functionMap luar.Map = luar.Map{ 12 | // Scalar & Vector 13 | "Scalar": function.Scalar, 14 | "Vector": function.Vector, 15 | 16 | // Op functions 17 | "NOT": function.NOT, 18 | "MINUS": function.MINUS, 19 | "ADD": function.ADD, 20 | "SUB": function.SUB, 21 | "MUL": function.MUL, 22 | "DIV": function.DIV, 23 | "GT": function.GT, 24 | "GE": function.GE, 25 | "LT": function.LT, 26 | "LE": function.LE, 27 | "EQ": function.EQ, 28 | "NEQ": function.NEQ, 29 | "AND": function.AND, 30 | "OR": function.OR, 31 | 32 | // MA functions 33 | 34 | "MA": function.MA, 35 | "SMA": function.SMA, 36 | "DMA": function.DMA, 37 | "EMA": function.EMA, 38 | "EXPEMA": function.EXPMEMA, 39 | 40 | // Stat functionA 41 | 42 | "LLV": function.LLV, 43 | "LLVBARS": function.LLVBARS, 44 | "HHV": function.HHV, 45 | "HHVBARS": function.HHVBARS, 46 | "STD": function.STD, 47 | "AVEDEV": function.AVEDEV, 48 | "SUM": function.SUM, 49 | "CROSS": function.CROSS, 50 | "COUNT": function.COUNT, 51 | "IF": function.IF, 52 | "EVERY": function.EVERY, 53 | "BARSLAST": function.BARSLAST, 54 | "BARSCOUNT": function.BARSCOUNT, 55 | "ROUND2": function.ROUND2, 56 | "REF": function.REF, 57 | "MIN": function.MIN, 58 | "MAX": function.MAX, 59 | "ABS": function.ABS, 60 | "SLOPE": function.SLOPE, 61 | 62 | // Stock functions 63 | 64 | "RecordVector": stockfunc.RecordVector, 65 | "OPEN": stockfunc.OPEN, 66 | "CLOSE": stockfunc.CLOSE, 67 | "LOW": stockfunc.LOW, 68 | "HIGH": stockfunc.HIGH, 69 | "AMOUNT": stockfunc.AMOUNT, 70 | "VOLUME": stockfunc.VOLUME, 71 | "PERIOD": stockfunc.PERIOD, 72 | "ISLASTBAR": stockfunc.ISLASTBAR, 73 | 74 | // Draw Functions 75 | 76 | "DRAWLINE": function.DRAWLINE, 77 | "PLOYLINE": function.PLOYLINE, 78 | 79 | // Cross Access Routines 80 | 81 | "IndexMap": stockfunc.NewIndexMap, 82 | "CrossValue": stockfunc.CrossValue, 83 | } 84 | 85 | func SetFormulaManager(formulaManager formula.FormulaManager) { 86 | functionMap["FormulaManager"] = formulaManager 87 | } 88 | 89 | func SetDataLibrary(dataLibrary datalibrary.DataLibrary) { 90 | functionMap["DataLibrary"] = dataLibrary 91 | } 92 | 93 | func GetFunctionMap(inMap luar.Map) luar.Map { 94 | result := luar.Map{} 95 | if inMap != nil { 96 | for k, v := range inMap { 97 | result[k] = v 98 | } 99 | } 100 | 101 | for k, v := range functionMap { 102 | result[k] = v 103 | } 104 | return result 105 | } 106 | -------------------------------------------------------------------------------- /formulalibrary/lua/luaformula/meta.go: -------------------------------------------------------------------------------- 1 | package luaformula 2 | 3 | import ( 4 | "github.com/aarzilli/golua/lua" 5 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 6 | "github.com/stevedonovan/luar" 7 | ) 8 | 9 | func getFormulaDesc(L *lua.State) (name string, argNames []string, args []Arg, flags []int, colors []*Color, lineThick []int, lineStyles []int, graphTypes []int, vars []string) { 10 | L.GetField(-1, "name") 11 | luar.LuaToGo(L, -1, &name) 12 | L.Pop(1) 13 | 14 | L.GetField(-1, "argName") 15 | luar.LuaToGo(L, -1, &argNames) 16 | L.Pop(1) 17 | 18 | if len(argNames) > 0 { 19 | args = make([]Arg, len(argNames)) 20 | var values []float64 21 | for i, argName := range argNames { 22 | L.GetField(-1, argName) 23 | luar.LuaToGo(L, -1, &values) 24 | L.Pop(1) 25 | 26 | args[i].Default = values[0] 27 | args[i].Min = values[1] 28 | args[i].Max = values[2] 29 | } 30 | } 31 | 32 | L.GetField(-1, "flags") 33 | luar.LuaToGo(L, -1, &flags) 34 | L.Pop(1) 35 | 36 | L.GetField(-1, "color") 37 | luar.LuaToGo(L, -1, &colors) 38 | for i, color := range colors { 39 | if color.Red == -1 { 40 | colors[i] = nil 41 | } 42 | } 43 | L.Pop(1) 44 | 45 | L.GetField(-1, "lineThick") 46 | luar.LuaToGo(L, -1, &lineThick) 47 | L.Pop(1) 48 | 49 | L.GetField(-1, "lineStyle") 50 | luar.LuaToGo(L, -1, &lineStyles) 51 | L.Pop(1) 52 | 53 | L.GetField(-1, "graphType") 54 | luar.LuaToGo(L, -1, &graphTypes) 55 | L.Pop(1) 56 | 57 | L.GetField(-1, "vars") 58 | luar.LuaToGo(L, -1, &vars) 59 | L.Pop(1) 60 | 61 | return 62 | } 63 | 64 | func GetMetaFromLuaState(L *lua.State, meta *FormulaMetaImpl) { 65 | name, argNames, argDefs, flags, colors, lineThick, lineStyles, graphTypes, vars := getFormulaDesc(L) 66 | 67 | meta.Name = name 68 | meta.ArgNames = argNames 69 | meta.ArgMeta = argDefs 70 | meta.Flags = flags 71 | meta.Colors = colors 72 | meta.LineThicks = lineThick 73 | meta.LineStyles = lineStyles 74 | meta.GraphTypes = graphTypes 75 | meta.Vars = vars 76 | } 77 | -------------------------------------------------------------------------------- /formulalibrary/native/creator.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/stockfunc/function" 5 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 6 | ) 7 | 8 | type CreateFunc func (data *function.RVector, args []float64) formula.Formula 9 | 10 | type NativeFormula struct { 11 | Creator CreateFunc 12 | Meta *formula.FormulaMetaImpl 13 | } 14 | -------------------------------------------------------------------------------- /formulalibrary/native/nativefactory/creator.go: -------------------------------------------------------------------------------- 1 | package nativefactory 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 5 | "github.com/stephenlyu/goformula/stockfunc/function" 6 | ) 7 | 8 | type nativeFormulaCreator struct { 9 | factory *nativeFormulaCreatorFactory 10 | args []float64 11 | } 12 | 13 | func (this *nativeFormulaCreator) CreateFormula(data *function.RVector) (error, formula.Formula) { 14 | return nil, this.factory.nativeFormula.Creator(data, this.args) 15 | } 16 | -------------------------------------------------------------------------------- /formulalibrary/native/nativefactory/creatorfactory.go: -------------------------------------------------------------------------------- 1 | package nativefactory 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/formulalibrary/base/factory" 5 | "github.com/stephenlyu/goformula/formulalibrary/native" 6 | "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 7 | "errors" 8 | "fmt" 9 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 10 | ) 11 | 12 | type nativeFormulaCreatorFactory struct { 13 | nativeFormula *native.NativeFormula 14 | } 15 | 16 | func NewNativeFormulaCreatorFactory(name string) (error, factory.FormulaCreatorFactory) { 17 | var nativeFormula *native.NativeFormula 18 | for i := range nativeformulas.NativeFormulas { 19 | nf := &nativeformulas.NativeFormulas[i] 20 | if nf.Meta.Name == name { 21 | nativeFormula = nf 22 | break 23 | } 24 | } 25 | 26 | if nativeFormula == nil { 27 | return errors.New(fmt.Sprintf("No %s formula", name)), nil 28 | } 29 | 30 | return nil, &nativeFormulaCreatorFactory{nativeFormula: nativeFormula} 31 | } 32 | func (this *nativeFormulaCreatorFactory) GetMeta() formula.FormulaMeta { 33 | return this.nativeFormula.Meta 34 | } 35 | 36 | func (this *nativeFormulaCreatorFactory) CreateFormulaCreator(args []float64) factory.FormulaCreator { 37 | if args == nil { 38 | args = this.nativeFormula.Meta.DefaultArgs() 39 | } 40 | 41 | return &nativeFormulaCreator{ 42 | factory: this, 43 | args: args, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /formulalibrary/native/nativeformulas/baseformula.go: -------------------------------------------------------------------------------- 1 | package nativeformulas 2 | 3 | import ( 4 | stockfunc "github.com/stephenlyu/goformula/stockfunc/function" 5 | "github.com/stephenlyu/goformula/function" 6 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 7 | "github.com/stephenlyu/tds/period" 8 | ) 9 | 10 | type BaseNativeFormula struct { 11 | *FormulaMetaImpl 12 | RefValues__ []function.Value 13 | DrawActions__ []DrawAction 14 | Data__ *stockfunc.RVector 15 | } 16 | 17 | func (this *BaseNativeFormula) Period() period.Period { 18 | return this.Data__.Period() 19 | } 20 | 21 | func (this *BaseNativeFormula) Len() int { 22 | return this.Data__.Len() 23 | } 24 | 25 | func (this BaseNativeFormula) Get(index int) []float64 { 26 | ret := make([]float64, len(this.RefValues__)) 27 | for i, v := range this.RefValues__ { 28 | ret[i] = v.Get(index) 29 | } 30 | 31 | return ret 32 | } 33 | 34 | func (this BaseNativeFormula) Ref(offset int) []float64 { 35 | index := this.Data__.Len() - 1 - offset 36 | return this.Get(index) 37 | } 38 | 39 | func (this BaseNativeFormula) GetVarValue(varName string) function.Value { 40 | for i, v := range this.FormulaMetaImpl.Vars { 41 | if varName == v { 42 | return this.RefValues__[i] 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func (this *BaseNativeFormula) Destroy() { 49 | } 50 | 51 | func (this *BaseNativeFormula) Name() string { 52 | return this.GetName() 53 | } 54 | 55 | func (this *BaseNativeFormula) DrawActions() []DrawAction { 56 | return this.DrawActions__ 57 | } 58 | 59 | -------------------------------------------------------------------------------- /formulalibrary/native/nativeformulas/nativeformulas.go: -------------------------------------------------------------------------------- 1 | package nativeformulas 2 | 3 | import ( 4 | . "github.com/stephenlyu/goformula/formulalibrary/native" 5 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 6 | "sync" 7 | "fmt" 8 | "github.com/z-ray/log" 9 | ) 10 | 11 | var lock sync.Mutex 12 | var NativeFormulas = []NativeFormula { 13 | } 14 | 15 | func RegisterNativeFormula(create CreateFunc, meta *formula.FormulaMetaImpl) error { 16 | log.Infof("RegisterNativeFormula name: %s", meta.GetName()) 17 | lock.Lock() 18 | defer lock.Unlock() 19 | 20 | for i := range NativeFormulas { 21 | if NativeFormulas[i].Meta.GetName() == meta.GetName() { 22 | return fmt.Errorf("native formula %s already exist", meta.GetName()) 23 | } 24 | } 25 | 26 | NativeFormulas = append(NativeFormulas, NativeFormula{Creator: create, Meta: meta}) 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /function/base_func.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | 4 | type function interface { 5 | Value 6 | Data() Value 7 | ListOfData() []Value 8 | BuildValueAt(index int) float64 9 | } 10 | 11 | type funcbase struct { 12 | vector 13 | data Value 14 | } 15 | 16 | func (this funcbase) Data() Value { 17 | return this.data 18 | } 19 | 20 | func (this funcbase) ListOfData() []Value { 21 | return []Value{this.data} 22 | } 23 | 24 | func updateLastValue(this function) { 25 | var length int 26 | for _, v := range this.ListOfData() { 27 | if !v.IsScalar() { 28 | length = v.Len() 29 | break 30 | } 31 | } 32 | 33 | if length < this.Len() || length == 0 { 34 | return 35 | } 36 | 37 | if this.Len() == length { 38 | v := this.BuildValueAt(length - 1) 39 | this.Set(this.Len() - 1, v) 40 | } else { 41 | for i := this.Len(); i < length; i++ { 42 | v := this.BuildValueAt(i) 43 | this.Append(v) 44 | } 45 | } 46 | } 47 | 48 | func initValues(this function, values []float64) { 49 | for i := 0; i < len(values); i++ { 50 | v := this.BuildValueAt(i) 51 | values[i] = v 52 | } 53 | } 54 | 55 | type simplefuncbase struct { 56 | data Value 57 | } 58 | 59 | func (this *simplefuncbase) IsScalar() bool { 60 | return false 61 | } 62 | 63 | func (this *simplefuncbase) Len() int { 64 | return this.data.Len() 65 | } 66 | 67 | func (this *simplefuncbase) Get(index int) float64 { 68 | panic("Not implemented") 69 | } 70 | 71 | func (this *simplefuncbase) Set(index int, v float64) { 72 | panic("Not implemented") 73 | } 74 | 75 | func (this *simplefuncbase) UpdateLastValue() { 76 | } 77 | 78 | func (this *simplefuncbase) Append(v float64) { 79 | panic("Not implemented") 80 | } 81 | -------------------------------------------------------------------------------- /function/const.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "math" 4 | 5 | var NaN = math.NaN() 6 | 7 | func IsNaN(v float64) bool { 8 | return math.IsNaN(v) 9 | } 10 | -------------------------------------------------------------------------------- /function/draw.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "math" 4 | 5 | // TODO: 处理未来函数。所有的函数需要增加一个withFutureData参数,如果withFutureData为true, 则UpdateLastValue时,需要更新整个数据 6 | 7 | // DRAWLINE 8 | // TODO: 不支持延长线 9 | 10 | type drawline struct { 11 | funcbase 12 | Cond1 Value 13 | Price1 Value 14 | Cond2 Value 15 | Price2 Value 16 | Expand Value 17 | 18 | lastCond1Index int 19 | lastCond2Indices []int 20 | } 21 | 22 | func (this *drawline) BuildValueAt(index int) { 23 | cond1 := IsTrue(this.Cond1.Get(index)) 24 | cond2 := IsTrue(this.Cond2.Get(index)) 25 | 26 | if cond1 { 27 | this.lastCond1Index = index 28 | this.lastCond2Indices = []int{} 29 | this.Values[index] = NaN 30 | } else if cond2 { 31 | if this.lastCond1Index >= 0 { 32 | Interpolate(this.Values, this.lastCond1Index, index, this.Price1.Get(this.lastCond1Index), this.Price2.Get(index)) 33 | this.lastCond2Indices = append(this.lastCond2Indices, index) 34 | } else { 35 | this.Values[index] = NaN 36 | } 37 | } else { 38 | if this.lastCond1Index >= 0 { 39 | n := len(this.lastCond2Indices) 40 | if n > 0 { 41 | prevCond2Index := this.lastCond2Indices[n - 1] 42 | if prevCond2Index == index { 43 | // 信号出现了又消失的情况 44 | this.lastCond2Indices = this.lastCond2Indices[:n-1] 45 | n-- 46 | 47 | var from int 48 | if len(this.lastCond2Indices) > 0 { 49 | // 一个起点,多个终点时,仅将上一个终点之后的数值修改成NaN 50 | from = this.lastCond2Indices[n - 1] + 1 51 | } else { 52 | // 修改为未找到终点时的情况,即起点后的所有index修改为NaN 53 | from = this.lastCond1Index 54 | } 55 | for i := from; i < index; i++ { 56 | this.Values[i] = NaN 57 | } 58 | } else { 59 | this.Values[index] = NaN 60 | } 61 | } else { 62 | this.Values[index] = NaN 63 | } 64 | } else { 65 | this.Values[index] = NaN 66 | } 67 | } 68 | } 69 | 70 | func (this *drawline) UpdateLastValue() { 71 | if this.Data().Len() < this.Len() { 72 | return 73 | } 74 | if this.Data().Len() == this.Len() { 75 | this.BuildValueAt(this.Data().Len() - 1) 76 | } else { 77 | start := this.Len() 78 | for i := this.Len(); i < this.Data().Len(); i++ { 79 | this.Append(0) 80 | } 81 | 82 | for i := start; i < this.Len(); i++ { 83 | this.BuildValueAt(i) 84 | } 85 | } 86 | } 87 | 88 | func (this *drawline) initValues() { 89 | for i := 0; i < this.Data().Len(); i++ { 90 | this.BuildValueAt(i) 91 | } 92 | } 93 | 94 | func DRAWLINE(cond1 Value, price1 Value, cond2 Value, price2 Value, expand Value) Value { 95 | if cond1.IsScalar() && price1.IsScalar() && cond2.IsScalar() && price2.IsScalar() && expand.IsScalar() { 96 | return Scalar(math.NaN()) 97 | } 98 | 99 | if expand == nil { 100 | expand = Scalar(0) 101 | } 102 | 103 | var length int 104 | switch { 105 | case !cond1.IsScalar(): 106 | length = cond1.Len() 107 | case !price1.IsScalar(): 108 | length = price1.Len() 109 | case !cond2.IsScalar(): 110 | length = cond2.Len() 111 | case !price2.IsScalar(): 112 | length = price2.Len() 113 | default: 114 | length = expand.Len() 115 | } 116 | 117 | ret := &drawline{ 118 | funcbase: funcbase { 119 | data: cond1, 120 | }, 121 | Cond1: cond1, 122 | Price1: price1, 123 | Cond2: cond2, 124 | Price2: price2, 125 | Expand: expand, 126 | 127 | lastCond1Index: -1, 128 | } 129 | ret.Values = make([]float64, length) 130 | ret.initValues() 131 | return ret 132 | } 133 | 134 | // PLOYLINE 135 | 136 | type ployline struct { 137 | funcbase 138 | Cond Value 139 | Price Value 140 | 141 | lastSegFromIndex int // 最后一条线段的起始位置 142 | lastSegToIndex int // 最后一条线段结束位置 143 | } 144 | 145 | func (this *ployline) BuildValueAt(index int) { 146 | yes := IsTrue(this.Cond.Get(index)) 147 | 148 | if yes { 149 | this.Values[index] = this.Price.Get(index) 150 | 151 | if this.lastSegToIndex >= 0 { 152 | Assert(index >= this.lastSegToIndex, "") 153 | if index > this.lastSegToIndex { 154 | Interpolate(this.Values, this.lastSegToIndex, index, this.Price.Get(this.lastSegToIndex), this.Price.Get(index)) 155 | this.lastSegFromIndex = this.lastSegToIndex 156 | this.lastSegToIndex = index 157 | } else { 158 | Interpolate(this.Values, this.lastSegFromIndex, index, this.Price.Get(this.lastSegFromIndex), this.Price.Get(index)) 159 | } 160 | } else { 161 | this.Values[index] = this.Price.Get(index) 162 | this.lastSegFromIndex = index 163 | this.lastSegToIndex = index 164 | } 165 | } else { 166 | if this.lastSegToIndex == index { 167 | // 信号忽闪时,需要恢复前面的值 168 | if this.lastSegFromIndex >= 0 { 169 | for i := this.lastSegFromIndex + 1; i < index; i++ { 170 | this.Values[i] = NaN 171 | } 172 | } 173 | } else { 174 | this.Values[index] = NaN 175 | } 176 | } 177 | } 178 | 179 | func (this *ployline) UpdateLastValue() { 180 | if this.Data().Len() < this.Len() { 181 | return 182 | } 183 | if this.Data().Len() == this.Len() { 184 | this.BuildValueAt(this.Data().Len() - 1) 185 | } else { 186 | start := this.Len() 187 | for i := this.Len(); i < this.Data().Len(); i++ { 188 | this.Append(0) 189 | } 190 | 191 | for i := start; i < this.Len(); i++ { 192 | this.BuildValueAt(i) 193 | } 194 | } 195 | } 196 | 197 | func (this *ployline) initValues() { 198 | for i := 0; i < this.Data().Len(); i++ { 199 | this.BuildValueAt(i) 200 | } 201 | } 202 | 203 | func PLOYLINE(cond Value, price Value) Value { 204 | if cond.IsScalar() && price.IsScalar() { 205 | return Scalar(math.NaN()) 206 | } 207 | 208 | var length int 209 | if !cond.IsScalar() { 210 | length = cond.Len() 211 | } else { 212 | length = price.Len() 213 | } 214 | 215 | ret := &ployline{ 216 | funcbase: funcbase { 217 | data: cond, 218 | }, 219 | Cond: cond, 220 | Price: price, 221 | lastSegFromIndex: -1, 222 | lastSegToIndex: -1, 223 | } 224 | ret.Values = make([]float64, length) 225 | ret.initValues() 226 | return ret 227 | } 228 | -------------------------------------------------------------------------------- /function/function_test.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stephenlyu/tds/util" 11 | ) 12 | 13 | func TestBARSLAST(t *testing.T) { 14 | values := make([]float64, 10000) 15 | //rand.Seed(0) 16 | for i := range values { 17 | if rand.Int()%30 == 0 { 18 | values[i] = 1 19 | } 20 | } 21 | v := Vector(values) 22 | 23 | s := time.Now().UnixNano() 24 | b1 := BARSLASTOLD(v) 25 | t1 := time.Now().UnixNano() - s 26 | s = time.Now().UnixNano() 27 | b2 := BARSLAST(v) 28 | t2 := time.Now().UnixNano() - s 29 | 30 | fmt.Printf("%dns %dns %d\n", t1, t2, t1/t2) 31 | 32 | for i := range values { 33 | //fmt.Println(values[i], b1.Get(i), b2.Get(i)) 34 | util.Assert((math.IsNaN(b1.Get(i)) && math.IsNaN(b2.Get(i))) || b1.Get(i) == b2.Get(i), "") 35 | } 36 | } 37 | 38 | func TestBARSLASTS(t *testing.T) { 39 | values := make([]float64, 200) 40 | rand.Seed(0) 41 | for i := range values { 42 | if rand.Int()%30 == 0 { 43 | values[i] = 1 44 | } 45 | } 46 | v := Vector(values) 47 | b := BARSLASTS(v, Scalar(2)) 48 | 49 | for i := range values { 50 | fmt.Println(i, values[i], b.Get(i), i-int(b.Get(i))) 51 | } 52 | 53 | v.Append(1) 54 | b.UpdateLastValue() 55 | for i := range v.Values { 56 | fmt.Println(i, v.Get(i), b.Get(i), i-int(b.Get(i))) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /function/ma_func.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "github.com/chanxuehong/util/math" 5 | ) 6 | 7 | // MA - moving average 8 | 9 | type ma struct { 10 | funcbase 11 | N Value 12 | } 13 | 14 | func (this ma) BuildValueAt(index int) float64 { 15 | n := int(this.N.Get(index)) 16 | if index < n -1 { 17 | return NaN 18 | } 19 | 20 | end := index + 1 21 | start := int(math.Max(0, int64(end - n))) 22 | 23 | return ma_(this.data, start, end) 24 | } 25 | 26 | func (this *ma) UpdateLastValue() { 27 | updateLastValue(this) 28 | } 29 | 30 | func MA(data Value, N Value) Value { 31 | if data.IsScalar() { 32 | return Scalar(data.Get(0)) 33 | } 34 | 35 | if N == nil { 36 | N = Scalar(5) 37 | } 38 | 39 | ret := &ma{ 40 | funcbase: funcbase { 41 | data: data, 42 | }, 43 | N: N, 44 | } 45 | ret.Values = make([]float64, data.Len()) 46 | initValues(ret, ret.Values) 47 | return ret 48 | } 49 | 50 | // SMA 51 | 52 | type sma struct { 53 | funcbase 54 | N Value 55 | M Value 56 | } 57 | 58 | func (this sma) BuildValueAt(index int) float64 { 59 | if index == 0 { 60 | return this.data.Get(index) 61 | } 62 | 63 | p := this.data.Get(index) 64 | lv := this.Get(index - 1) 65 | 66 | return (p * this.M.Get(index) + lv * (this.N.Get(index) - this.M.Get(index))) / this.N.Get(index) 67 | } 68 | 69 | func (this *sma) UpdateLastValue() { 70 | updateLastValue(this) 71 | } 72 | 73 | func SMA(data Value, N, M Value) Value { 74 | if data.IsScalar() { 75 | return Scalar(data.Get(0)) 76 | } 77 | if N == nil { 78 | N = Scalar(12) 79 | } 80 | 81 | if M == nil { 82 | M = Scalar(1) 83 | } 84 | 85 | ret := &sma{ 86 | funcbase: funcbase { 87 | data: data, 88 | }, 89 | N: N, 90 | M: M, 91 | } 92 | ret.Values = make([]float64, data.Len()) 93 | initValues(ret, ret.Values) 94 | 95 | return ret 96 | } 97 | 98 | // SMA 99 | 100 | type dma struct { 101 | funcbase 102 | A Value 103 | } 104 | 105 | func (this dma) BuildValueAt(index int) float64 { 106 | if index == 0 { 107 | return this.data.Get(0) 108 | } 109 | 110 | p := this.data.Get(index) 111 | lv := this.Get(index - 1) 112 | 113 | return p * this.A.Get(index) + lv * (1 - this.A.Get(index)) 114 | } 115 | 116 | func (this *dma) UpdateLastValue() { 117 | updateLastValue(this) 118 | } 119 | 120 | func DMA(data Value, A Value) Value { 121 | if data.IsScalar() { 122 | return Scalar(data.Get(0)) 123 | } 124 | if A == nil { 125 | A = Scalar(0.5) 126 | } 127 | 128 | ret := &dma{ 129 | funcbase: funcbase { 130 | data: data, 131 | }, 132 | A: A, 133 | } 134 | ret.Values = make([]float64, data.Len()) 135 | initValues(ret, ret.Values) 136 | 137 | return ret 138 | } 139 | 140 | // EMA 141 | 142 | type ema struct { 143 | funcbase 144 | N Value 145 | } 146 | 147 | func (this ema) BuildValueAt(index int) float64 { 148 | if index == 0 { 149 | return this.data.Get(0) 150 | } 151 | 152 | p := this.data.Get(index) 153 | lv := this.Get(index - 1) 154 | alpha := 2.0 / (this.N.Get(index) + 1) 155 | return p * alpha + lv * (1 - alpha) 156 | } 157 | 158 | func (this *ema) UpdateLastValue() { 159 | updateLastValue(this) 160 | } 161 | 162 | func EMA(data Value, N Value) Value { 163 | if data.IsScalar() { 164 | return Scalar(data.Get(0)) 165 | } 166 | if N == nil { 167 | N = Scalar(5) 168 | } 169 | 170 | ret := &ema{ 171 | funcbase: funcbase { 172 | data: data, 173 | }, 174 | N: N, 175 | } 176 | ret.Values = make([]float64, data.Len()) 177 | initValues(ret, ret.Values) 178 | return ret 179 | } 180 | 181 | // EXPMEMA 182 | 183 | type expmema struct { 184 | funcbase 185 | N Value 186 | } 187 | 188 | func (this expmema) BuildValueAt(index int) float64 { 189 | N := int(this.N.Get(index)) 190 | if index < N - 1 { 191 | return 0 192 | } 193 | 194 | if index == N - 1 { 195 | return ma_(this.data, 0, N) 196 | } 197 | 198 | p := this.data.Get(index) 199 | lv := this.Get(index - 1) 200 | 201 | alpha := 2.0 / (this.N.Get(index) + 1) 202 | return p * alpha + lv * (1 - alpha) 203 | } 204 | 205 | func (this *expmema) UpdateLastValue() { 206 | updateLastValue(this) 207 | } 208 | 209 | func EXPMEMA(data Value, N Value) Value { 210 | if data.IsScalar() { 211 | return Scalar(data.Get(0)) 212 | } 213 | if N == nil { 214 | N = Scalar(0.5) 215 | } 216 | 217 | ret := &expmema{ 218 | funcbase: funcbase { 219 | data: data, 220 | }, 221 | N: N, 222 | } 223 | ret.Values = make([]float64, data.Len()) 224 | initValues(ret, ret.Values) 225 | 226 | return ret 227 | } -------------------------------------------------------------------------------- /function/op_func.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | type not struct { 8 | simplefuncbase 9 | } 10 | 11 | func BuildNotValueAt(data Value, index int) float64 { 12 | v := data.Get(index) 13 | if math.IsNaN(v) { 14 | return math.NaN() 15 | } 16 | if v > 0 || v < 0 { 17 | return 0 18 | } 19 | 20 | return 1 21 | } 22 | 23 | func (this *not) Get(index int) float64 { 24 | return BuildNotValueAt(this.data, index) 25 | } 26 | 27 | func NOT(data Value) Value { 28 | if data.IsScalar() { 29 | return Scalar(BuildNotValueAt(data, 0)) 30 | } 31 | 32 | ret := ¬{ 33 | simplefuncbase{ 34 | data: data, 35 | }, 36 | } 37 | 38 | return ret 39 | } 40 | 41 | type minus struct { 42 | simplefuncbase 43 | } 44 | 45 | func (this minus) Get(index int) float64 { 46 | return -this.data.Get(index) 47 | } 48 | 49 | func MINUS(data Value) Value { 50 | if data.IsScalar() { 51 | return Scalar(-data.Get(0)) 52 | } 53 | 54 | ret := &minus{ 55 | simplefuncbase{ 56 | data: data, 57 | }, 58 | } 59 | 60 | return ret 61 | } 62 | 63 | type add struct { 64 | simplefuncbase 65 | data1 Value 66 | } 67 | 68 | func (this add) Get(index int) float64 { 69 | return this.data.Get(index) + this.data1.Get(index) 70 | } 71 | 72 | func ADD(data Value, data1 Value) Value { 73 | if data.IsScalar() && data1.IsScalar() { 74 | return Scalar(data.Get(0) + data1.Get(0)) 75 | } 76 | 77 | ret := &add{ 78 | simplefuncbase: simplefuncbase{ 79 | data: data, 80 | }, 81 | data1: data1, 82 | } 83 | 84 | return ret 85 | } 86 | 87 | type sub struct { 88 | simplefuncbase 89 | data1 Value 90 | } 91 | 92 | func (this sub) Get(index int) float64 { 93 | return this.data.Get(index) - this.data1.Get(index) 94 | } 95 | 96 | func SUB(data Value, data1 Value) Value { 97 | if data.IsScalar() && data1.IsScalar() { 98 | return Scalar(data.Get(0) - data1.Get(0)) 99 | } 100 | 101 | ret := &sub{ 102 | simplefuncbase: simplefuncbase{ 103 | data: data, 104 | }, 105 | data1: data1, 106 | } 107 | 108 | return ret 109 | } 110 | 111 | type mul struct { 112 | simplefuncbase 113 | data1 Value 114 | } 115 | 116 | func (this mul) Get(index int) float64 { 117 | return this.data.Get(index) * this.data1.Get(index) 118 | } 119 | 120 | func MUL(data Value, data1 Value) Value { 121 | if data.IsScalar() && data1.IsScalar() { 122 | return Scalar(data.Get(0) * data1.Get(0)) 123 | } 124 | 125 | ret := &mul{ 126 | simplefuncbase: simplefuncbase{ 127 | data: data, 128 | }, 129 | data1: data1, 130 | } 131 | 132 | return ret 133 | } 134 | 135 | type div struct { 136 | simplefuncbase 137 | data1 Value 138 | } 139 | 140 | func (this div) Get(index int) float64 { 141 | if this.data1.Get(index) == 0 { 142 | return NaN 143 | } 144 | return this.data.Get(index) / this.data1.Get(index) 145 | } 146 | 147 | func DIV(data Value, data1 Value) Value { 148 | if data.IsScalar() && data1.IsScalar() { 149 | if data1.Get(0) == 0 { 150 | return Scalar(NaN) 151 | } 152 | return Scalar(data.Get(0) / data1.Get(0)) 153 | } 154 | 155 | ret := &div{ 156 | simplefuncbase: simplefuncbase{ 157 | data: data, 158 | }, 159 | data1: data1, 160 | } 161 | 162 | return ret 163 | } 164 | 165 | type and struct { 166 | simplefuncbase 167 | data1 Value 168 | } 169 | 170 | func BuildAndValueAt(data, data1 Value, index int) float64 { 171 | a := data.Get(index) 172 | b := data1.Get(index) 173 | if IsTrue(a) && IsTrue(b) { 174 | return 1 175 | } 176 | return 0 177 | } 178 | 179 | func (this and) Get(index int) float64 { 180 | return BuildAndValueAt(this.data, this.data1, index) 181 | } 182 | 183 | func AND(data Value, data1 Value) Value { 184 | if data.IsScalar() && data1.IsScalar() { 185 | return Scalar(BuildAndValueAt(data, data1, 0)) 186 | } 187 | 188 | ret := &and{ 189 | simplefuncbase: simplefuncbase{ 190 | data: data, 191 | }, 192 | data1: data1, 193 | } 194 | 195 | return ret 196 | } 197 | 198 | type or struct { 199 | simplefuncbase 200 | data1 Value 201 | } 202 | 203 | func BuildOrValueAt(data, data1 Value, index int) float64 { 204 | a := data.Get(index) 205 | b := data1.Get(index) 206 | if IsTrue(a) || IsTrue(b) { 207 | return 1 208 | } 209 | return 0 210 | } 211 | 212 | func (this *or) Get(index int) float64 { 213 | return BuildOrValueAt(this.data, this.data1, index) 214 | } 215 | 216 | func OR(data Value, data1 Value) Value { 217 | if data.IsScalar() && data1.IsScalar() { 218 | return Scalar(BuildOrValueAt(data, data1, 0)) 219 | } 220 | 221 | ret := &or{ 222 | simplefuncbase: simplefuncbase{ 223 | data: data, 224 | }, 225 | data1: data1, 226 | } 227 | 228 | return ret 229 | } 230 | 231 | type lt struct { 232 | simplefuncbase 233 | data1 Value 234 | } 235 | 236 | func BuildLtValueAt(data, data1 Value, index int) float64 { 237 | a := data.Get(index) 238 | b := data1.Get(index) 239 | if a < b { 240 | return 1 241 | } 242 | return 0 243 | } 244 | 245 | func (this lt) Get(index int) float64 { 246 | return BuildLtValueAt(this.data, this.data1, index) 247 | } 248 | 249 | func LT(data Value, data1 Value) Value { 250 | if data.IsScalar() && data1.IsScalar() { 251 | return Scalar(BuildLtValueAt(data, data1, 0)) 252 | } 253 | 254 | ret := <{ 255 | simplefuncbase: simplefuncbase{ 256 | data: data, 257 | }, 258 | data1: data1, 259 | } 260 | 261 | return ret 262 | } 263 | 264 | type le struct { 265 | simplefuncbase 266 | data1 Value 267 | } 268 | 269 | func BuildLEValueAt(data, data1 Value, index int) float64 { 270 | a := data.Get(index) 271 | b := data1.Get(index) 272 | if a <= b { 273 | return 1 274 | } 275 | return 0 276 | } 277 | 278 | func (this le) Get(index int) float64 { 279 | return BuildLEValueAt(this.data, this.data1, index) 280 | } 281 | 282 | func LE(data Value, data1 Value) Value { 283 | if data.IsScalar() && data1.IsScalar() { 284 | return Scalar(BuildLEValueAt(data, data1, 0)) 285 | } 286 | ret := &le{ 287 | simplefuncbase: simplefuncbase{ 288 | data: data, 289 | }, 290 | data1: data1, 291 | } 292 | 293 | return ret 294 | } 295 | 296 | type gt struct { 297 | simplefuncbase 298 | data1 Value 299 | } 300 | 301 | func BuildGtValueAt(data, data1 Value, index int) float64 { 302 | a := data.Get(index) 303 | b := data1.Get(index) 304 | if a > b { 305 | return 1 306 | } 307 | return 0 308 | } 309 | 310 | func (this gt) Get(index int) float64 { 311 | return BuildGtValueAt(this.data, this.data1, index) 312 | } 313 | 314 | func GT(data Value, data1 Value) Value { 315 | if data.IsScalar() && data1.IsScalar() { 316 | return Scalar(BuildGtValueAt(data, data1, 0)) 317 | } 318 | ret := >{ 319 | simplefuncbase: simplefuncbase{ 320 | data: data, 321 | }, 322 | data1: data1, 323 | } 324 | 325 | return ret 326 | } 327 | 328 | type ge struct { 329 | simplefuncbase 330 | data1 Value 331 | } 332 | 333 | func BuildGeValueAt(data, data1 Value, index int) float64 { 334 | a := data.Get(index) 335 | b := data1.Get(index) 336 | if a >= b { 337 | return 1 338 | } 339 | return 0 340 | } 341 | 342 | func (this ge) Get(index int) float64 { 343 | return BuildGeValueAt(this.data, this.data1, index) 344 | } 345 | 346 | func GE(data Value, data1 Value) Value { 347 | if data.IsScalar() && data1.IsScalar() { 348 | return Scalar(BuildGeValueAt(data, data1, 0)) 349 | } 350 | ret := &ge{ 351 | simplefuncbase: simplefuncbase{ 352 | data: data, 353 | }, 354 | data1: data1, 355 | } 356 | 357 | return ret 358 | } 359 | 360 | type eq struct { 361 | simplefuncbase 362 | data1 Value 363 | } 364 | 365 | func BuildEqValueAt(data, data1 Value, index int) float64 { 366 | a := data.Get(index) 367 | b := data1.Get(index) 368 | if a == b { 369 | return 1 370 | } 371 | return 0 372 | } 373 | 374 | func (this eq) Get(index int) float64 { 375 | return BuildEqValueAt(this.data, this.data1, index) 376 | } 377 | 378 | func EQ(data Value, data1 Value) Value { 379 | if data.IsScalar() && data1.IsScalar() { 380 | return Scalar(BuildEqValueAt(data, data1, 0)) 381 | } 382 | ret := &eq{ 383 | simplefuncbase: simplefuncbase{ 384 | data: data, 385 | }, 386 | data1: data1, 387 | } 388 | 389 | return ret 390 | } 391 | 392 | type neq struct { 393 | simplefuncbase 394 | data1 Value 395 | } 396 | 397 | func BuildNeqValueAt(data, data1 Value, index int) float64 { 398 | a := data.Get(index) 399 | b := data1.Get(index) 400 | if a != b { 401 | return 1 402 | } 403 | return 0 404 | } 405 | 406 | func (this neq) Get(index int) float64 { 407 | return BuildNeqValueAt(this.data, this.data1, index) 408 | } 409 | 410 | func NEQ(data Value, data1 Value) Value { 411 | if data.IsScalar() && data1.IsScalar() { 412 | return Scalar(BuildNeqValueAt(data, data1, 0)) 413 | } 414 | ret := &neq{ 415 | simplefuncbase: simplefuncbase{ 416 | data: data, 417 | }, 418 | data1: data1, 419 | } 420 | 421 | return ret 422 | } 423 | 424 | type between struct { 425 | simplefuncbase 426 | data1 Value 427 | data2 Value 428 | } 429 | 430 | func BuildBetweenValueAt(data, data1 Value, data2 Value, index int) float64 { 431 | a := data.Get(index) 432 | b := data1.Get(index) 433 | c := data2.Get(index) 434 | if a < b { 435 | return 0 436 | } 437 | if a > c { 438 | return 0 439 | } 440 | return 1 441 | } 442 | 443 | func (this between) Get(index int) float64 { 444 | return BuildBetweenValueAt(this.data, this.data1, this.data2, index) 445 | } 446 | 447 | func BETWEEN(data Value, data1 Value, data2 Value) Value { 448 | if data.IsScalar() && data1.IsScalar() && data2.IsScalar() { 449 | return Scalar(BuildBetweenValueAt(data, data1, data2, 0)) 450 | } 451 | ret := &between{ 452 | simplefuncbase: simplefuncbase{ 453 | data: data, 454 | }, 455 | data1: data1, 456 | data2: data2, 457 | } 458 | 459 | return ret 460 | } 461 | -------------------------------------------------------------------------------- /function/util.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "math" 5 | "errors" 6 | ) 7 | 8 | func Assert(cond bool, msg string) { 9 | if !cond { 10 | panic(msg) 11 | } 12 | } 13 | 14 | func IsTrue(v float64) bool { 15 | return v != 0 && !math.IsNaN(v) 16 | } 17 | 18 | func sum(values Value, start, end int) float64 { 19 | sum := 0.0 20 | for i := start; i < end; i++ { 21 | sum += values.Get(i) 22 | } 23 | return sum 24 | } 25 | 26 | func ma_(values Value, start, end int) float64 { 27 | if start >= end { 28 | return 0 29 | } 30 | 31 | return sum(values, start, end) / float64(end - start) 32 | } 33 | 34 | func min(values Value, start, end int) float64 { 35 | if start >= end { 36 | return 0 37 | } 38 | 39 | ret := values.Get(start) 40 | for i := start + 1; i < end; i++ { 41 | if values.Get(i) < ret { 42 | ret = values.Get(i) 43 | } 44 | } 45 | return ret 46 | } 47 | 48 | func max(values Value, start, end int) float64 { 49 | if start >= end { 50 | return 0 51 | } 52 | 53 | ret := values.Get(start) 54 | for i := start + 1; i < end; i++ { 55 | if values.Get(i) > ret { 56 | ret = values.Get(i) 57 | } 58 | } 59 | return ret 60 | } 61 | 62 | func iif(condition bool, yesValue, noValue float64) float64 { 63 | if (condition) { 64 | return yesValue 65 | } 66 | return noValue 67 | } 68 | 69 | func round(val float64, places int) float64 { 70 | var t float64 71 | f := math.Pow10(places) 72 | x := val * f 73 | if math.IsInf(x, 0) || math.IsNaN(x) { 74 | return val 75 | } 76 | if x >= 0.0 { 77 | t = math.Ceil(x) 78 | if (t - x) > 0.50000000001 { 79 | t -= 1.0 80 | } 81 | } else { 82 | t = math.Ceil(-x) 83 | if (t + x) > 0.50000000001 { 84 | t -= 1.0 85 | } 86 | t = -t 87 | } 88 | x = t / f 89 | 90 | if !math.IsInf(x, 0) { 91 | return x 92 | } 93 | 94 | return t 95 | } 96 | 97 | func LinearRegression(x, y Value) (err error, slope float64, intercept float64) { 98 | if x.Len() != y.Len() { 99 | return errors.New("Length mismatch"), 0, 0 100 | } 101 | 102 | // Placeholder for the math to be done 103 | var sum [5]float64 104 | 105 | // Loop over data keeping index in place 106 | i := 0 107 | for ; i < x.Len(); i++ { 108 | sum[0] += x.Get(i) 109 | sum[1] += y.Get(i) 110 | sum[2] += x.Get(i) * x.Get(i) 111 | sum[3] += x.Get(i) * y.Get(i) 112 | sum[4] += y.Get(i) * y.Get(i) 113 | } 114 | 115 | // Find gradient and intercept 116 | f := float64(i) 117 | slope = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0]) 118 | intercept = (sum[1] / f) - (slope * sum[0] / f) 119 | 120 | err = nil 121 | return 122 | } 123 | 124 | func Interpolate(values []float64, from int, to int, fromValue float64, toValue float64){ 125 | Assert(from >= 0, "from >= 0 required") 126 | Assert(to < len(values), "to < len(values) required") 127 | 128 | if math.IsNaN(fromValue) || math.IsNaN(toValue) { 129 | for i := from + 1; i < to; i++ { 130 | values[i] = math.NaN() 131 | } 132 | return 133 | } 134 | 135 | if from == to { 136 | values[from] = fromValue 137 | return 138 | } 139 | 140 | slope := (toValue - fromValue) / float64(to - from) 141 | values[from] = fromValue 142 | values[to] = toValue 143 | for i := from + 1; i < to; i++ { 144 | values[i] = fromValue + float64(i - from) * slope 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /function/value.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type Value interface { 9 | IsScalar() bool 10 | Len() int 11 | Get(index int) float64 12 | Set(index int, v float64) 13 | UpdateLastValue() 14 | Append(v float64) 15 | } 16 | 17 | 18 | type scalar struct { 19 | value float64 20 | } 21 | 22 | func (this scalar) IsScalar() bool { 23 | return true 24 | } 25 | 26 | func (this scalar) Len() int { 27 | return -1 28 | } 29 | 30 | func (this scalar) Get(index int) float64 { 31 | return this.value 32 | } 33 | 34 | func (this scalar) Set(index int, v float64) { 35 | this.value = v 36 | } 37 | 38 | func (this scalar) Append(v float64) { 39 | this.value = v 40 | } 41 | 42 | func (this scalar) UpdateLastValue() { 43 | } 44 | 45 | func Scalar(v float64) *scalar { 46 | return &scalar{value: v} 47 | } 48 | 49 | type vector struct { 50 | Values []float64 51 | } 52 | 53 | 54 | func (this vector) IsScalar() bool { 55 | return false 56 | } 57 | 58 | func (this vector) Len() int { 59 | return len(this.Values) 60 | } 61 | 62 | func (this vector) Get(index int) float64 { 63 | if index < 0 || index >= this.Len() { 64 | panic(errors.New("index out of range")) 65 | } 66 | return this.Values[index] 67 | } 68 | 69 | func (this vector) Set(index int, v float64) { 70 | if index < 0 || index >= this.Len() { 71 | panic(errors.New("index out of range")) 72 | } 73 | this.Values[index] = v 74 | } 75 | 76 | func (this vector) UpdateLastValue() { 77 | } 78 | 79 | func (this *vector) Append(v float64) { 80 | this.Values = append(this.Values, v) 81 | } 82 | 83 | func (this vector) String() string { 84 | return fmt.Sprintf("%v", this.Values) 85 | } 86 | 87 | func Vector(values []float64) *vector { 88 | return &vector{Values: values} 89 | } 90 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stephenlyu/goformula 2 | 3 | go 1.22.1 4 | 5 | require ( 6 | github.com/aarzilli/golua v0.0.0-20241229084300-cd31ab23902e 7 | github.com/chanxuehong/util v0.0.0-20200304121633-ca8141845b13 8 | github.com/onsi/ginkgo v1.16.5 9 | github.com/onsi/gomega v1.36.2 10 | github.com/stephenlyu/tds v0.0.1 11 | github.com/stevedonovan/luar v0.0.0-20170518170841-22d247e53660 12 | github.com/z-ray/log v0.0.0-20171010041026-e59779f9e8f6 13 | ) 14 | 15 | require ( 16 | github.com/fsnotify/fsnotify v1.4.9 // indirect 17 | github.com/golang/protobuf v1.5.4 // indirect 18 | github.com/google/go-cmp v0.6.0 // indirect 19 | github.com/nxadm/tail v1.4.8 // indirect 20 | golang.org/x/net v0.33.0 // indirect 21 | golang.org/x/sys v0.28.0 // indirect 22 | golang.org/x/text v0.21.0 // indirect 23 | google.golang.org/protobuf v1.36.1 // indirect 24 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aarzilli/golua v0.0.0-20241229084300-cd31ab23902e h1:ibMKBskN7uMCz9TJgfaIVYVdPyckXm0UjFDRSNV7XB0= 2 | github.com/aarzilli/golua v0.0.0-20241229084300-cd31ab23902e/go.mod h1:hMjfaJVSqVnxenMlsxrq3Ni+vrm9Hs64tU4M7dhUoO4= 3 | github.com/chanxuehong/util v0.0.0-20200304121633-ca8141845b13 h1:c1vUDbnwvu5d2ucfzXvMzBWzeu5IxPvtESOFPl3CieA= 4 | github.com/chanxuehong/util v0.0.0-20200304121633-ca8141845b13/go.mod h1:XEYt99iTxMqkv+gW85JX/DdUINHUe43Sbe5AtqSaDAQ= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 8 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 9 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 10 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 11 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 12 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= 13 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 14 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 15 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 16 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 18 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 19 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 20 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 21 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 22 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 23 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 24 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 25 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 26 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 27 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 28 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 29 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 30 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= 31 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 32 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 33 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 34 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 35 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 36 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 37 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 38 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 39 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 40 | github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= 41 | github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= 42 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 43 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 44 | github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= 45 | github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= 46 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 47 | github.com/stephenlyu/tds v0.0.1 h1:aWN/cYbE8oEnlKkAvSp4y7tFKS2ULuDa+sTaj+mRfXw= 48 | github.com/stephenlyu/tds v0.0.1/go.mod h1:mnnMSgjHtd/7LvwOXeS1f+QJD3ko9cSbGsdSODt+5hs= 49 | github.com/stevedonovan/luar v0.0.0-20170518170841-22d247e53660 h1:BQU7miOzJXmSUHa5oLf0b88kRgUgTeFsoc8i9uAV8zA= 50 | github.com/stevedonovan/luar v0.0.0-20170518170841-22d247e53660/go.mod h1:LtHI5GcaXL3NPbZWY1Z6FAG62hWY8syhRGtD7feyN7E= 51 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 52 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 53 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 54 | github.com/z-ray/log v0.0.0-20171010041026-e59779f9e8f6 h1:uGP/7YMtsuXTk/S/r7SrqBzd7awB0hY3NIsvjvT+hLM= 55 | github.com/z-ray/log v0.0.0-20171010041026-e59779f9e8f6/go.mod h1:znVjI7LOkE2lU9sAapslHrWjabyhcyRIEEYKjHESJRI= 56 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 57 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 58 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 59 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 60 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 61 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 62 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 63 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 64 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 65 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 66 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 67 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 68 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 69 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 70 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 71 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 72 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 73 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 74 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 76 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 78 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 79 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 80 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 81 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 82 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 83 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 84 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 85 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 86 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 87 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 88 | golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= 89 | golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 90 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 91 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 92 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 93 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 94 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 95 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 96 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 97 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 98 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 99 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 100 | google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= 101 | google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 102 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 103 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 104 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 105 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 106 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 107 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 108 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 109 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 110 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 111 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 112 | -------------------------------------------------------------------------------- /stockfunc/function/crossvalue.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "github.com/stephenlyu/goformula/function" 5 | "github.com/stephenlyu/tds/util" 6 | "errors" 7 | ) 8 | 9 | // 将周期为data.Period生成的指标映射到destData.Period所表示的周期上 10 | // 实现function.Value接口 11 | type crossValue struct { 12 | value function.Value // 跨周期的数据 13 | indexMap *IndexMap 14 | } 15 | 16 | func CrossValue(value function.Value, indexMap *IndexMap) function.Value { 17 | util.Assert(!value.IsScalar(), "!value.IsScalar()") 18 | return &crossValue{value: value, indexMap: indexMap} 19 | } 20 | 21 | func (this *crossValue) IsScalar() bool { 22 | return false 23 | } 24 | 25 | func (this *crossValue) Len() int { 26 | return this.indexMap.srcData.Len() 27 | } 28 | 29 | func (this *crossValue) Get(index int) float64 { 30 | index = this.indexMap.Get(index) 31 | if index < 0 { 32 | return function.NaN 33 | } 34 | 35 | return this.value.Get(index) 36 | } 37 | 38 | func (this *crossValue) Set(index int, v float64) { 39 | panic(errors.New("Set not supported!")) 40 | } 41 | 42 | func (this *crossValue) UpdateLastValue() { 43 | this.value.UpdateLastValue() 44 | } 45 | 46 | func (this *crossValue) Append(v float64) { 47 | panic(errors.New("Append not supported!")) 48 | } 49 | -------------------------------------------------------------------------------- /stockfunc/function/data.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "errors" 5 | . "github.com/stephenlyu/tds/period" 6 | "sync" 7 | ) 8 | 9 | 10 | type Record interface { 11 | GetUTCDate() uint64 12 | GetDate() string 13 | GetOpen() float64 14 | GetClose() float64 15 | GetHigh() float64 16 | GetLow() float64 17 | GetAmount() float64 18 | GetVolume() float64 19 | } 20 | 21 | type RVector struct { 22 | Values []Record 23 | 24 | code string 25 | period Period 26 | 27 | lock sync.RWMutex 28 | } 29 | 30 | func (this *RVector) Len() int { 31 | this.lock.RLock() 32 | defer this.lock.RUnlock() 33 | return len(this.Values) 34 | } 35 | 36 | func (this *RVector) Get(index int) Record { 37 | this.lock.RLock() 38 | defer this.lock.RUnlock() 39 | if index < 0 || index >= this.Len() { 40 | panic(errors.New("index out of range")) 41 | } 42 | return this.Values[index] 43 | } 44 | 45 | func (this *RVector) Set(index int, v Record) { 46 | this.lock.Lock() 47 | defer this.lock.Unlock() 48 | if index < 0 || index >= this.Len() { 49 | panic(errors.New("index out of range")) 50 | } 51 | this.Values[index] = v 52 | } 53 | 54 | func (this *RVector) Append(v Record) { 55 | this.lock.Lock() 56 | defer this.lock.Unlock() 57 | this.Values = append(this.Values, v) 58 | } 59 | 60 | func (this *RVector) Update(offset int, values []Record) { 61 | this.lock.Lock() 62 | defer this.lock.Unlock() 63 | 64 | for i, v := range values { 65 | if offset + i < len(this.Values) { 66 | this.Values[offset + i] = v 67 | } else { 68 | this.Values = append(this.Values, v) 69 | } 70 | } 71 | } 72 | 73 | func (this *RVector) Code() string { 74 | return this.code 75 | } 76 | 77 | func (this *RVector) Period() Period { 78 | return this.period 79 | } 80 | 81 | func RecordVector(v []Record) *RVector { 82 | return &RVector{Values: v} 83 | } 84 | 85 | func RecordVectorEx(code string, period Period, v []Record) *RVector { 86 | return &RVector{ 87 | Values: v, 88 | code: code, 89 | period: period, 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /stockfunc/function/field.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "errors" 4 | 5 | type fieldbase struct { 6 | data *RVector 7 | } 8 | 9 | func (this fieldbase) IsScalar() bool { 10 | return false 11 | } 12 | 13 | func (this fieldbase ) Len() int { 14 | return this.data.Len() 15 | } 16 | 17 | func (this fieldbase) Get(index int) float64 { 18 | panic(errors.New("Not Implemented")) 19 | } 20 | 21 | func (this fieldbase) Set(index int, v float64) { 22 | panic(errors.New("Not Implemented")) 23 | } 24 | 25 | func (this fieldbase) UpdateLastValue() { 26 | } 27 | 28 | func (this fieldbase) Append(v float64) { 29 | panic(errors.New("Not Implemented")) 30 | } 31 | 32 | // CLOSE 33 | 34 | type close struct { 35 | fieldbase 36 | } 37 | 38 | func (this close) Get(index int) float64 { 39 | return float64(this.data.Get(index).GetClose()) 40 | } 41 | 42 | func CLOSE(data *RVector) *close { 43 | ret := &close{ 44 | fieldbase { 45 | data: data, 46 | }, 47 | } 48 | return ret 49 | } 50 | 51 | // OPEN 52 | 53 | type open struct { 54 | fieldbase 55 | } 56 | 57 | func (this open) Get(index int) float64 { 58 | return float64(this.data.Get(index).GetOpen()) 59 | } 60 | 61 | func OPEN(data *RVector) *open { 62 | ret := &open{ 63 | fieldbase { 64 | data: data, 65 | }, 66 | } 67 | return ret 68 | } 69 | 70 | // LOW 71 | 72 | type low struct { 73 | fieldbase 74 | } 75 | 76 | func (this low) Get(index int) float64 { 77 | return float64(this.data.Get(index).GetLow()) 78 | } 79 | 80 | func LOW(data *RVector) *low { 81 | ret := &low{ 82 | fieldbase { 83 | data: data, 84 | }, 85 | } 86 | return ret 87 | } 88 | 89 | // HIGH 90 | 91 | type high struct { 92 | fieldbase 93 | } 94 | 95 | func (this high) Get(index int) float64 { 96 | return float64(this.data.Get(index).GetHigh()) 97 | } 98 | 99 | func HIGH(data *RVector) *high { 100 | ret := &high{ 101 | fieldbase { 102 | data: data, 103 | }, 104 | } 105 | return ret 106 | } 107 | 108 | // AMOUNT 109 | 110 | type amount struct { 111 | fieldbase 112 | } 113 | 114 | func (this amount) Get(index int) float64 { 115 | return float64(this.data.Get(index).GetAmount()) 116 | } 117 | 118 | func AMOUNT(data *RVector) *amount { 119 | ret := &amount{ 120 | fieldbase { 121 | data: data, 122 | }, 123 | } 124 | return ret 125 | } 126 | 127 | // VOLUME 128 | 129 | type volume struct { 130 | fieldbase 131 | } 132 | 133 | func (this volume) Get(index int) float64 { 134 | return float64(this.data.Get(index).GetVolume()) 135 | } 136 | 137 | func VOLUME(data *RVector) *volume { 138 | ret := &volume{ 139 | fieldbase { 140 | data: data, 141 | }, 142 | } 143 | return ret 144 | } 145 | 146 | // Period 147 | 148 | type fPeriod struct { 149 | fieldbase 150 | 151 | value float64 152 | } 153 | 154 | func (this fPeriod) Get(index int) float64 { 155 | return this.value 156 | } 157 | 158 | func PERIOD(data *RVector) *fPeriod { 159 | ret := &fPeriod{ 160 | fieldbase: fieldbase { 161 | data: data, 162 | }, 163 | value: float64(GetPeriodIndex(data.period)), 164 | } 165 | return ret 166 | } 167 | 168 | // ISLASTBAR 169 | 170 | type islastbar struct { 171 | fieldbase 172 | } 173 | 174 | func (this islastbar) Get(index int) float64 { 175 | if this.data.Len() - 1 == index { 176 | return 1 177 | } 178 | return 0 179 | } 180 | 181 | func ISLASTBAR(data *RVector) *islastbar { 182 | ret := &islastbar{ 183 | fieldbase{ 184 | data: data, 185 | }, 186 | } 187 | return ret 188 | } 189 | -------------------------------------------------------------------------------- /stockfunc/function/indexmap.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "github.com/stephenlyu/tds/util" 5 | "github.com/stephenlyu/tds/period" 6 | "fmt" 7 | ) 8 | 9 | 10 | const DAY_MILLIS = 24 * 60 * 60 * 1000 11 | 12 | // 比如在分钟线中引用日线数据时,srcData为分钟线数据,destData为日线数据。 13 | // IndexMap负责将分钟线的索引映射为日线数据的索引,然后存取日线数据 14 | type IndexMap struct { 15 | srcData *RVector 16 | destData *RVector 17 | 18 | indexMap map[int]int 19 | } 20 | 21 | func NewIndexMap(srcData *RVector, destData *RVector) *IndexMap { 22 | this := &IndexMap{srcData: srcData, destData: destData} 23 | this.buildIndexMap() 24 | return this 25 | } 26 | 27 | func (this *IndexMap) buildIndexMap() { 28 | needTrimDate := this.destData.period.Unit() != period.PERIOD_UNIT_MINUTE 29 | 30 | m := make(map[int]int) 31 | 32 | for i, j := 0, 0; i < this.srcData.Len() && j < this.destData.Len(); { 33 | srcDate := this.srcData.Get(i).GetDate() 34 | if needTrimDate { 35 | srcDate = fmt.Sprintf("%s 00:00:00", srcDate[:8]) 36 | } 37 | 38 | destDate := this.destData.Get(j).GetDate() 39 | 40 | if srcDate <= destDate { 41 | m[i] = j 42 | i++ 43 | } else { 44 | j++ 45 | } 46 | } 47 | 48 | this.indexMap = m 49 | } 50 | 51 | func (this *IndexMap) Get(index int) int { 52 | // 品种相同且周期相同时,直接从value中取值 53 | if this.srcData.code == this.destData.code && this.srcData.period.Eq(this.destData.period) { 54 | util.Assert(this.srcData.Len() == this.destData.Len(), "") 55 | return index 56 | } 57 | // 大周期引用小周期数据时,返回-1(不支持大周期引用小周期) 58 | if this.srcData.period.Gt(this.destData.period) { 59 | return -1 60 | } 61 | 62 | ret, ok := this.indexMap[index] 63 | if !ok { 64 | return -1 65 | } 66 | 67 | return ret 68 | } 69 | 70 | func (this *IndexMap) UpdateLastValue() { 71 | i := this.srcData.Len() - 1 72 | j := this.destData.Len() - 1 73 | 74 | if i < 0 || j < 0 { 75 | return 76 | } 77 | needTrimDate := this.destData.period.Unit() != period.PERIOD_UNIT_MINUTE 78 | 79 | srcDate := this.srcData.Get(i).GetUTCDate() 80 | if needTrimDate { 81 | srcDate = srcDate / DAY_MILLIS * DAY_MILLIS 82 | } 83 | 84 | for j >= 0 { 85 | destDate := this.srcData.Get(j).GetUTCDate() 86 | if destDate < srcDate { 87 | break 88 | } 89 | j-- 90 | } 91 | 92 | if j + 1 < this.destData.Len() { 93 | this.indexMap[i] = j + 1 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /stockfunc/function/periods.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "errors" 5 | 6 | . "github.com/stephenlyu/tds/period" 7 | "github.com/stephenlyu/tds/util" 8 | ) 9 | 10 | var ( 11 | _, M1 = PeriodFromString("M1") 12 | _, M5 = PeriodFromString("M5") 13 | _, M15 = PeriodFromString("M15") 14 | _, M30 = PeriodFromString("M30") 15 | _, M60 = PeriodFromString("M60") 16 | _, D1 = PeriodFromString("D1") 17 | _, W1 = PeriodFromString("W1") 18 | ) 19 | 20 | var systemPeriods = []Period{ 21 | M1, 22 | M5, 23 | M15, 24 | M30, 25 | M60, 26 | D1, 27 | W1, 28 | } 29 | 30 | var currentPeriods []Period 31 | var periodIndices map[string]int 32 | 33 | func init() { 34 | periodIndices = buildPeriodIndices(systemPeriods) 35 | } 36 | 37 | func buildPeriodIndices(periods []Period) map[string]int { 38 | currentPeriods = periods 39 | m := make(map[string]int) 40 | for i, p := range periods { 41 | m[p.ShortName()] = i 42 | } 43 | return m 44 | } 45 | 46 | func SetCustomPeriods(periods []Period) error { 47 | var ps []Period 48 | ps = append(ps, systemPeriods...) 49 | for _, p := range periods { 50 | if _, ok := periodIndices[p.ShortName()]; !ok { 51 | ps = append(ps, p) 52 | } 53 | } 54 | m := buildPeriodIndices(ps) 55 | if len(m) < len(ps) { 56 | return errors.New("Duplicate period") 57 | } 58 | 59 | periodIndices = m 60 | return nil 61 | } 62 | 63 | func AddCustomPeriods(periods []Period) error { 64 | var ps []Period 65 | ps = append(ps, currentPeriods...) 66 | for _, p := range periods { 67 | if _, ok := periodIndices[p.ShortName()]; !ok { 68 | ps = append(ps, p) 69 | } 70 | } 71 | m := buildPeriodIndices(ps) 72 | if len(m) < len(ps) { 73 | return errors.New("Duplicate period") 74 | } 75 | 76 | periodIndices = m 77 | return nil 78 | } 79 | 80 | func GetPeriodIndex(period Period) int { 81 | index, ok := periodIndices[period.ShortName()] 82 | util.Assert(ok, "Bad period") 83 | return index 84 | } 85 | -------------------------------------------------------------------------------- /test/CROSS.d: -------------------------------------------------------------------------------- 1 | A:CLOSE#DAY; 2 | B:MACD.MACD#DAY; 3 | C:"000001$CLOSE"; 4 | D:"000001$MACD.MACD"; 5 | E:"999999$MACD.DIF#DAY"; 6 | -------------------------------------------------------------------------------- /test/DRAW.d: -------------------------------------------------------------------------------- 1 | DRAWTEXT(CLOSE/OPEN>1.08,LOW,'abc'), COLORBLUE; 2 | DRAWLINE(HIGH>=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1), COLORRED; 3 | PLOYLINE(HIGH>=HHV(HIGH,20),HIGH),COLORRED,LINETHICK9; 4 | STICKLINE(CLOSE>OPEN,CLOSE,OPEN,0.8,1), COLORCYAN; 5 | DRAWICON(CLOSE>OPEN,LOW,1); 6 | DRAWKLINE(HIGH,OPEN,LOW,CLOSE), NODRAW; -------------------------------------------------------------------------------- /test/DRAWLINE.d: -------------------------------------------------------------------------------- 1 | A:HIGH; 2 | B:HHV(HIGH,5); 3 | BB: HIGH>=HHV(HIGH,5); 4 | 5 | C: LOW; 6 | D: LLV(LOW,5); 7 | DD:LOW<=LLV(LOW,5); 8 | 9 | DRAWLINE(HIGH>=HHV(HIGH,5),HIGH,LOW<=LLV(LOW,5),LOW,1), COLORGREEN,LINETHICK8,NODRAW; 10 | 11 | -------------------------------------------------------------------------------- /test/MA.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | M1=>(5, 0, 1000); 3 | M2=>(10, 0, 1000); 4 | M3=>(20, 0, 1000); 5 | M4=>(60, 0, 1000); 6 | MA1:MA(CLOSE,M1), COLORWHITE; 7 | MA2:MA(CLOSE,M2), COLORRED; 8 | MA3:MA(CLOSE,M3), COLORCYAN; 9 | MA4:MA(CLOSE,M4), COLORGREEN; 10 | -------------------------------------------------------------------------------- /test/MACD.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | SHORT=>(12, 2, 200); 3 | LONG=>(26, 2, 200); 4 | MID=>(9, 2, 200); 5 | {DRAWTEXT(1,3,'大阳线');} 6 | DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG); 7 | DEA:EMA(DIF,MID); 8 | MACD:(DIF-DEA)*2,COLORSTICK; 9 | -------------------------------------------------------------------------------- /test/MACDBUY.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | 3 | 做多:CROSS(MACD.DIF,MACD.DEA); 4 | -------------------------------------------------------------------------------- /test/PLOYLINE.d: -------------------------------------------------------------------------------- 1 | PLOYLINE(HIGH>=HHV(HIGH,5),HIGH),COLORRED,LINETHICK9; 2 | -------------------------------------------------------------------------------- /test/VOL.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | M1=>(5, 0, 1000); 3 | M2=>(10, 0, 1000); 4 | VOLUME:VOL,VOLSTICK; 5 | MAVOL1:MA(VOLUME,M1); 6 | MAVOL2:MA(VOLUME,M2); 7 | -------------------------------------------------------------------------------- /test/account.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Created by IntelliJ IDEA. 3 | -- User: admin 4 | -- Date: 2017/6/19 5 | -- Time: 下午4:02 6 | -- To change this template use File | Settings | File Templates. 7 | -- 8 | 9 | Account = {value = 0} 10 | function Account:new(o) -- 这里是冒号哦 11 | o = o or {} -- 如果用户没有提供table,则创建一个 12 | setmetatable(o, self) 13 | self.__index = self 14 | return o 15 | end 16 | 17 | function Account:display() 18 | self.value = self.value + 100 19 | self.value1 = 1000 20 | print(self.value) 21 | print(self.value1) 22 | end 23 | -- 24 | --local a = Account:new{} -- 这里使用原型Account创建了一个对象a 25 | --a:display() --(1) 26 | --a:display() --(2) 27 | -- 28 | --local b = Account.new(Account, {value=1000}) 29 | --b.display(b) 30 | -------------------------------------------------------------------------------- /test/data/vipdoc/sz/lday/sz000001.day: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenlyu/goformula/65486ddaeac8d9be80f91b21d44495cd64bd42be/test/data/vipdoc/sz/lday/sz000001.day -------------------------------------------------------------------------------- /test/easylang_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | import ( 3 | . "github.com/onsi/ginkgo" 4 | "github.com/stephenlyu/goformula/easylang" 5 | "github.com/stephenlyu/goformula/stockfunc/function" 6 | "fmt" 7 | "time" 8 | "github.com/stephenlyu/goformula/formulalibrary/easylang/easylangfactory" 9 | "github.com/stephenlyu/goformula/formulalibrary/base/formula" 10 | "github.com/stephenlyu/goformula/formulalibrary" 11 | "github.com/stephenlyu/tds/period" 12 | "github.com/stephenlyu/goformula/datalibrary" 13 | ) 14 | 15 | var _ = Describe("Compile", func() { 16 | It("test", func () { 17 | err := easylang.Compile("MACDBUY.d", "output.lua", nil, true, true) 18 | if err != nil { 19 | fmt.Println(err) 20 | } 21 | }) 22 | }) 23 | 24 | var _ = Describe("CrossValue", func() { 25 | It("test", func () { 26 | err := easylang.Compile("CROSS.d", "cross.lua", nil, true, true) 27 | if err != nil { 28 | fmt.Println(err) 29 | } 30 | }) 31 | }) 32 | 33 | var _ = Describe("Token", func() { 34 | It("test", func () { 35 | err := easylang.Tokenizer("MACD.d") 36 | if err != nil { 37 | fmt.Println(err) 38 | } 39 | }) 40 | }) 41 | 42 | var _ = Describe("EasyLangMACD", func() { 43 | It("test", func () { 44 | _, data := loadJson("300666.SZ.json") 45 | rv := function.RecordVector(data) 46 | 47 | formulas := []string {"MACD.d", "MA.d", "VOL.d"} 48 | for _, name := range formulas { 49 | fmt.Println("Test formula", name, "...") 50 | start := time.Now().UnixNano() 51 | 52 | err, factory := easylangfactory.NewEasyLangFormulaCreatorFactory(name, nil, true) 53 | if err != nil { 54 | panic(err) 55 | } 56 | creator := factory.CreateFormulaCreator(nil) 57 | 58 | err, formula := creator.CreateFormula(rv) 59 | if err != nil { 60 | panic(err) 61 | } 62 | defer formula.Destroy() 63 | 64 | formula.UpdateLastValue() 65 | 66 | for i := 0; i < formula.VarCount(); i++ { 67 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 68 | } 69 | 70 | start1 := time.Now().UnixNano() 71 | len := formula.Len() 72 | fmt.Println("formula.len:", len, "data len:", rv.Len()) 73 | for i := 0; i < len; i++ { 74 | r := formula.Get(i) 75 | fmt.Printf("%s %+v\n", rv.Get(i).GetDate(), r) 76 | } 77 | 78 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 79 | } 80 | }) 81 | }) 82 | 83 | var _ = Describe("ELMACDBuy", func() { 84 | It("test", func (){ 85 | _, data := loadJson("300666.SZ.json") 86 | _, p := period.PeriodFromString("D1") 87 | rv := function.RecordVectorEx("300666", p, data) 88 | 89 | fmt.Println("data len:", len(data)) 90 | start := time.Now().UnixNano() 91 | 92 | library := formulalibrary.GlobalLibrary 93 | library.Reset() 94 | library.LoadEasyLangFormulas(".") 95 | 96 | f := library.NewFormula("MACDBUY", rv) 97 | defer f.Destroy() 98 | 99 | fmt.Println("Name:", f.GetName()) 100 | for i := 0; i < f.ArgCount(); i++ { 101 | min, max := f.ArgRange(i) 102 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 103 | } 104 | for i := 0; i < f.VarCount(); i++ { 105 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 106 | } 107 | 108 | fmt.Println(f.Len()) 109 | for i := 0; i < f.Len(); i++ { 110 | r := f.Get(i) 111 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 112 | } 113 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 114 | }) 115 | }) 116 | 117 | var _ = Describe("ELDDGS", func() { 118 | It("test", func (){ 119 | _, data := loadJson("300666.SZ.json") 120 | _, p := period.PeriodFromString("D1") 121 | rv := function.RecordVectorEx("300666", p, data) 122 | 123 | fmt.Println("data len:", rv.Len()) 124 | start := time.Now().UnixNano() 125 | 126 | library := formulalibrary.GlobalLibrary 127 | library.Reset() 128 | library.SetDebug(true) 129 | library.LoadEasyLangFormulas(".") 130 | 131 | f := library.NewFormula("DDGS", rv) 132 | defer f.Destroy() 133 | 134 | fmt.Println("Name:", f.GetName()) 135 | for i := 0; i < f.ArgCount(); i++ { 136 | min, max := f.ArgRange(i) 137 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 138 | } 139 | for i := 0; i < f.VarCount(); i++ { 140 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 141 | } 142 | 143 | fmt.Println(f.Len()) 144 | for i := 0; i < f.Len(); i++ { 145 | r := f.Get(i) 146 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 147 | } 148 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 149 | }) 150 | }) 151 | 152 | var _ = Describe("ELDrawLine", func() { 153 | It("test", func (){ 154 | _, data := loadJson("300666.SZ.json") 155 | rv := function.RecordVector(data) 156 | 157 | fmt.Println("data len:", len(data)) 158 | start := time.Now().UnixNano() 159 | 160 | err, factory := easylangfactory.NewEasyLangFormulaCreatorFactory("DRAWLINE.d", nil, true) 161 | if err != nil { 162 | panic(err) 163 | } 164 | creator := factory.CreateFormulaCreator(nil) 165 | 166 | err, f := creator.CreateFormula(rv) 167 | if err != nil { 168 | panic(err) 169 | } 170 | defer f.Destroy() 171 | 172 | fmt.Println("Name:", f.GetName()) 173 | for i := 0; i < f.ArgCount(); i++ { 174 | min, max := f.ArgRange(i) 175 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 176 | } 177 | for i := 0; i < f.VarCount(); i++ { 178 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 179 | } 180 | 181 | for i := 0; i < len(f.DrawActions()); i++ { 182 | a := f.DrawActions()[i].(formula.DrawLine) 183 | fmt.Println(a.GetActionType(), a.GetColor(), a.GetLineThick(), a.GetVarIndex()) 184 | } 185 | 186 | fmt.Println(f.Len()) 187 | for i := 0; i < f.Len(); i++ { 188 | r := f.Get(i) 189 | fmt.Printf("%d. %s\t%.02f\t%.02f\t%.02f\t%.02f\t%.02f\t%.02f\t%.02f\n", i, rv.Get(i).GetDate(), r[0], r[1], r[2], r[3], r[4], r[5], r[6]) 190 | } 191 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 192 | }) 193 | }) 194 | 195 | var _ = Describe("ELPloyLine", func() { 196 | It("test", func (){ 197 | _, data := loadJson("300666.SZ.json") 198 | rv := function.RecordVector(data) 199 | 200 | fmt.Println("data len:", len(data)) 201 | start := time.Now().UnixNano() 202 | 203 | err, factory := easylangfactory.NewEasyLangFormulaCreatorFactory("PLOYLINE.d", nil, true) 204 | if err != nil { 205 | panic(err) 206 | } 207 | creator := factory.CreateFormulaCreator(nil) 208 | 209 | err, f := creator.CreateFormula(rv) 210 | if err != nil { 211 | panic(err) 212 | } 213 | defer f.Destroy() 214 | 215 | fmt.Println("Name:", f.GetName()) 216 | for i := 0; i < f.ArgCount(); i++ { 217 | min, max := f.ArgRange(i) 218 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 219 | } 220 | for i := 0; i < f.VarCount(); i++ { 221 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 222 | } 223 | 224 | fmt.Println(f.Len()) 225 | for i := 0; i < f.Len(); i++ { 226 | r := f.Get(i) 227 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 228 | } 229 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 230 | }) 231 | }) 232 | 233 | var _ = Describe("ELDrawActions", func() { 234 | It("test", func (){ 235 | _, data := loadJson("300666.SZ.json") 236 | rv := function.RecordVector(data) 237 | 238 | fmt.Println("data len:", len(data)) 239 | start := time.Now().UnixNano() 240 | 241 | err, factory := easylangfactory.NewEasyLangFormulaCreatorFactory("DRAW.d", nil, true) 242 | if err != nil { 243 | panic(err) 244 | } 245 | creator := factory.CreateFormulaCreator(nil) 246 | 247 | err, f := creator.CreateFormula(rv) 248 | if err != nil { 249 | panic(err) 250 | } 251 | defer f.Destroy() 252 | 253 | fmt.Println("Name:", f.GetName()) 254 | for i := 0; i < f.ArgCount(); i++ { 255 | min, max := f.ArgRange(i) 256 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 257 | } 258 | for i := 0; i < f.VarCount(); i++ { 259 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 260 | } 261 | 262 | drawActions := f.DrawActions() 263 | for _, action := range drawActions { 264 | fmt.Printf("%+v\n", action) 265 | } 266 | 267 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 268 | }) 269 | }) 270 | 271 | var _ = Describe("ELCross", func() { 272 | It("test", func (){ 273 | dl := datalibrary.NewDataLibrary("data") 274 | rv := dl.GetData("000001", "M5") 275 | 276 | fmt.Println("data len:", rv.Len()) 277 | start := time.Now().UnixNano() 278 | 279 | library := formulalibrary.GlobalLibrary 280 | library.Reset() 281 | library.SetDebug(true) 282 | library.SetDataLibrary(dl) 283 | library.LoadEasyLangFormulas(".") 284 | 285 | f := library.NewFormula("CROSS", rv) 286 | defer f.Destroy() 287 | 288 | fmt.Println("Name:", f.GetName()) 289 | for i := 0; i < f.ArgCount(); i++ { 290 | min, max := f.ArgRange(i) 291 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 292 | } 293 | for i := 0; i < f.VarCount(); i++ { 294 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 295 | } 296 | 297 | for i := 0; i < f.Len(); i++ { 298 | r := f.Get(i) 299 | fmt.Printf("%d. %s", i, rv.Get(i).GetDate()) 300 | for _, v := range r { 301 | fmt.Printf("\t%.02f", v) 302 | } 303 | fmt.Println("") 304 | } 305 | 306 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 307 | }) 308 | }) 309 | 310 | var _ = Describe("ELMACDBuyIncr", func() { 311 | It("test", func (){ 312 | _, data := loadJson("300666.SZ.json") 313 | _, p := period.PeriodFromString("D1") 314 | 315 | initData := append([]function.Record{}, data[:len(data)-10]...) 316 | 317 | rv := function.RecordVectorEx("300666", p, initData) 318 | 319 | fmt.Println("data len:", len(initData)) 320 | start := time.Now().UnixNano() 321 | 322 | library := formulalibrary.GlobalLibrary 323 | library.Reset() 324 | //library.SetDebug(true) 325 | library.LoadEasyLangFormulas("el") 326 | 327 | f := library.NewFormula("MACDBUY", rv) 328 | defer f.Destroy() 329 | 330 | for i := len(data) - 10; i < len(data); i++ { 331 | r := data[i] 332 | rv.Append(r) 333 | } 334 | f.UpdateLastValue() 335 | 336 | fmt.Println("Name:", f.GetName()) 337 | for i := 0; i < f.ArgCount(); i++ { 338 | min, max := f.ArgRange(i) 339 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 340 | } 341 | for i := 0; i < f.VarCount(); i++ { 342 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 343 | } 344 | 345 | fmt.Println(f.Len()) 346 | for i := 0; i < f.Len(); i++ { 347 | r := f.Get(i) 348 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 349 | } 350 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 351 | }) 352 | }) 353 | -------------------------------------------------------------------------------- /test/el/MACD.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | SHORT=>(12, 2, 200); 3 | LONG=>(26, 2, 200); 4 | MID=>(9, 2, 200); 5 | {DRAWTEXT(1,3,'大阳线');} 6 | DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG); 7 | DEA:EMA(DIF,MID); 8 | MACD:(DIF-DEA)*2,COLORSTICK; 9 | -------------------------------------------------------------------------------- /test/el/MACDBUY.d: -------------------------------------------------------------------------------- 1 | {参数} 2 | 3 | 做多:CROSS(MACD.DIF,MACD.DEA); 4 | -------------------------------------------------------------------------------- /test/formula_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | "io/ioutil" 6 | "encoding/json" 7 | stockfunc "github.com/stephenlyu/goformula/stockfunc/function" 8 | "fmt" 9 | "time" 10 | "github.com/stephenlyu/goformula/stockfunc/function" 11 | "github.com/stephenlyu/goformula/formulalibrary/native/nativefactory" 12 | "github.com/stephenlyu/tds/date" 13 | _ "github.com/stephenlyu/goformula/test/native" 14 | ) 15 | 16 | type Record struct { 17 | Date string `json:"date"` 18 | Open float32 `json:"open"` 19 | Close float32 `json:"close"` 20 | High float32 `json:"high"` 21 | Low float32 `json:"low"` 22 | Volume float32 `json:"volume"` 23 | Amount float32 `json:"amount"` 24 | } 25 | 26 | func (this *Record) GetUTCDate() uint64 { 27 | ret, _ := date.DayString2Timestamp(this.Date) 28 | return ret 29 | } 30 | 31 | func (this *Record) GetDate() string { 32 | return this.Date 33 | } 34 | 35 | func (this *Record) GetOpen() float32 { 36 | return this.Open 37 | } 38 | 39 | func (this *Record) GetClose() float32 { 40 | return this.Close 41 | } 42 | 43 | func (this *Record) GetHigh() float32 { 44 | return this.High 45 | } 46 | 47 | func (this *Record) GetLow() float32 { 48 | return this.Low 49 | } 50 | 51 | func (this *Record) GetAmount() float32 { 52 | return this.Amount 53 | } 54 | 55 | func (this *Record) GetVolume() float32 { 56 | return this.Volume 57 | } 58 | 59 | 60 | func loadJson(jsonFile string) (error, []function.Record) { 61 | bytes, err := ioutil.ReadFile(jsonFile) 62 | if err != nil { 63 | return err, nil 64 | } 65 | 66 | var result []Record 67 | err = json.Unmarshal(bytes, &result) 68 | if err != nil { 69 | return err, nil 70 | } 71 | 72 | ret := make([]function.Record, len(result)) 73 | 74 | for i := range result { 75 | ret[i] = &result[i] 76 | } 77 | 78 | return err, ret 79 | } 80 | 81 | var _ = Describe("NMACD", func() { 82 | It("test", func (){ 83 | _, data := loadJson("data.json") 84 | rv := stockfunc.RecordVector(data) 85 | 86 | start := time.Now().UnixNano() 87 | 88 | _, factory := nativefactory.NewNativeFormulaCreatorFactory("MACD") 89 | creator := factory.CreateFormulaCreator(nil) 90 | _, macd := creator.CreateFormula(rv) 91 | 92 | fmt.Println("Name:", macd.GetName()) 93 | for i := 0; i < macd.ArgCount(); i++ { 94 | min, max := macd.ArgRange(i) 95 | fmt.Printf("default: %f min: %f max: %f\n", macd.ArgDefault(i), min, max) 96 | } 97 | for i := 0; i < macd.VarCount(); i++ { 98 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", macd.VarName(i), macd.NoDraw(i), macd.LineThick(i), macd.Color(i)) 99 | } 100 | 101 | for i := 0; i < macd.Len(); i++ { 102 | r := macd.Get(i) 103 | fmt.Printf("%s\t%.02f\t%.02f\t%.02f\n", rv.Get(i).GetDate(), r[0], r[1], r[2]) 104 | } 105 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 106 | }) 107 | }) 108 | -------------------------------------------------------------------------------- /test/function_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | "github.com/stephenlyu/goformula/function" 6 | "fmt" 7 | ) 8 | 9 | var _ = Describe("Not", func() { 10 | It("test", func (){ 11 | data := []float64{0, 1, 2, 3} 12 | vector := function.Vector(data) 13 | result := function.NOT(vector) 14 | for i := 0; i < result.Len(); i++ { 15 | fmt.Println(result.Get(i)) 16 | } 17 | 18 | vector.Append(0) 19 | result.UpdateLastValue() 20 | for i := 0; i < result.Len(); i++ { 21 | fmt.Println(result.Get(i)) 22 | } 23 | }) 24 | }) 25 | 26 | var _ = Describe("Add", func() { 27 | It("test", func (){ 28 | data := []float64{0, 1, 2, 3} 29 | data1 := []float64{0, 1, 2, 3} 30 | a := function.Vector(data) 31 | b := function.Vector(data1) 32 | result := function.ADD(a, b) 33 | for i := 0; i < result.Len(); i++ { 34 | fmt.Println(result.Get(i)) 35 | } 36 | 37 | a.Append(10) 38 | b.Append(5) 39 | result.UpdateLastValue() 40 | for i := 0; i < result.Len(); i++ { 41 | fmt.Println(result.Get(i)) 42 | } 43 | }) 44 | }) 45 | 46 | var _ = Describe("MA", func() { 47 | It("test", func (){ 48 | data := make([]float64, 100) 49 | for i := 0; i < 100; i++ { 50 | data[i] = float64(i) 51 | } 52 | a := function.Vector(data) 53 | result := function.MA(a, nil) 54 | for i := 0; i < result.Len(); i++ { 55 | fmt.Println(result.Get(i)) 56 | } 57 | 58 | fmt.Println("============================") 59 | 60 | a.Append(10) 61 | result.UpdateLastValue() 62 | for i := 0; i < result.Len(); i++ { 63 | fmt.Println(result.Get(i)) 64 | } 65 | }) 66 | }) 67 | 68 | var _ = Describe("EMA", func() { 69 | It("test", func (){ 70 | data := make([]float64, 100) 71 | for i := 0; i < 100; i++ { 72 | data[i] = float64(i) 73 | } 74 | a := function.Vector(data) 75 | result := function.EMA(a, function.Scalar(12)) 76 | for i := 0; i < result.Len(); i++ { 77 | fmt.Println(result.Get(i)) 78 | } 79 | }) 80 | }) 81 | 82 | var _ = Describe("LLVBARS", func() { 83 | It("test", func (){ 84 | data := make([]float64, 100) 85 | for i := 0; i < 100; i++ { 86 | data[i] = float64(i) 87 | } 88 | a := function.Vector(data) 89 | result := function.LLVBARS(a, function.Scalar(10)) 90 | for i := 0; i < result.Len(); i++ { 91 | fmt.Println(result.Get(i)) 92 | } 93 | 94 | fmt.Println("============================") 95 | 96 | a.Append(10) 97 | result.UpdateLastValue() 98 | for i := 0; i < result.Len(); i++ { 99 | fmt.Println(result.Get(i)) 100 | } 101 | }) 102 | }) 103 | 104 | var _ = Describe("HHVBARS", func() { 105 | It("test", func (){ 106 | data := make([]float64, 100) 107 | for i := 0; i < 100; i++ { 108 | data[i] = float64(i) 109 | } 110 | a := function.Vector(data) 111 | result := function.HHVBARS(a, function.Scalar(10)) 112 | for i := 0; i < result.Len(); i++ { 113 | fmt.Println(result.Get(i)) 114 | } 115 | 116 | fmt.Println("============================") 117 | 118 | a.Append(10) 119 | result.UpdateLastValue() 120 | for i := 0; i < result.Len(); i++ { 121 | fmt.Println(result.Get(i)) 122 | } 123 | }) 124 | }) 125 | 126 | var _ = Describe("ROUND2", func() { 127 | It("test", func (){ 128 | data := []float64{0.4949999, 0.500001, -0.4949999, -0.50000001} 129 | a := function.Vector(data) 130 | result := function.ROUND2(a, function.Scalar(2)) 131 | for i := 0; i < result.Len(); i++ { 132 | fmt.Println(result.Get(i)) 133 | } 134 | 135 | fmt.Println("============================") 136 | 137 | a.Append(3.1415926) 138 | a.Append(3.1415926) 139 | result.UpdateLastValue() 140 | for i := 0; i < result.Len(); i++ { 141 | fmt.Println(result.Get(i)) 142 | } 143 | }) 144 | }) 145 | 146 | 147 | var _ = Describe("SLOPE", func() { 148 | It("test", func (){ 149 | data := make([]float64, 100) 150 | for i := 0; i < 100; i++ { 151 | data[i] = float64(i) / 2 152 | } 153 | a := function.Vector(data) 154 | result := function.SLOPE(a, function.Scalar(5)) 155 | for i := 0; i < result.Len(); i++ { 156 | fmt.Println(result.Get(i)) 157 | } 158 | 159 | fmt.Println("============================") 160 | 161 | a.Append(10) 162 | a.Append(100) 163 | result.UpdateLastValue() 164 | for i := 0; i < result.Len(); i++ { 165 | fmt.Println(result.Get(i)) 166 | } 167 | }) 168 | }) 169 | -------------------------------------------------------------------------------- /test/luaformula_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | import ( 3 | . "github.com/onsi/ginkgo" 4 | "github.com/stephenlyu/goformula/stockfunc/function" 5 | "fmt" 6 | "time" 7 | "github.com/stephenlyu/goformula/formulalibrary" 8 | "github.com/stephenlyu/tds/period" 9 | "github.com/stephenlyu/goformula/datalibrary" 10 | "github.com/stephenlyu/tds/util" 11 | ) 12 | 13 | var _ = Describe("LuaMACD1", func() { 14 | It("test", func () { 15 | _, data := loadJson("data.json") 16 | rv := function.RecordVector(data) 17 | 18 | start := time.Now().UnixNano() 19 | 20 | library := formulalibrary.GlobalLibrary 21 | library.Reset() 22 | library.LoadLuaFormulas("luas") 23 | 24 | formula := library.NewFormula("MACD", rv) 25 | defer formula.Destroy() 26 | 27 | formula.UpdateLastValue() 28 | 29 | fmt.Println("Name:", formula.GetName()) 30 | for i := 0; i < formula.ArgCount(); i++ { 31 | min, max := formula.ArgRange(i) 32 | fmt.Printf("default: %f min: %f max: %f\n", formula.ArgDefault(i), min, max) 33 | } 34 | for i := 0; i < formula.VarCount(); i++ { 35 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 36 | } 37 | 38 | start1 := time.Now().UnixNano() 39 | len := formula.Len() 40 | fmt.Println("macd.len:", len, "data len:", rv.Len()) 41 | for i := 0; i < len; i++ { 42 | r := formula.Get(i) 43 | fmt.Printf("%s\t%.02f\t%.02f\t%.02f\t%.02f\t%.02f\n", rv.Get(i).GetDate(), r[0], r[1], r[2], r[3], r[4]) 44 | } 45 | 46 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 47 | }) 48 | }) 49 | 50 | var _ = Describe("LuaDrawLine", func() { 51 | It("test", func () { 52 | _, data := loadJson("300666.SZ.json") 53 | rv := function.RecordVector(data) 54 | 55 | start := time.Now().UnixNano() 56 | 57 | library := formulalibrary.GlobalLibrary 58 | library.Reset() 59 | library.LoadLuaFormulas("luas") 60 | 61 | formula := library.NewFormula("DRAWLINE", rv) 62 | defer formula.Destroy() 63 | 64 | fmt.Println("Name:", formula.GetName()) 65 | for i := 0; i < formula.ArgCount(); i++ { 66 | min, max := formula.ArgRange(i) 67 | fmt.Printf("default: %f min: %f max: %f\n", formula.ArgDefault(i), min, max) 68 | } 69 | for i := 0; i < formula.VarCount(); i++ { 70 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 71 | } 72 | 73 | start1 := time.Now().UnixNano() 74 | len := formula.Len() 75 | fmt.Println("formula.len:", len, "data len:", rv.Len()) 76 | for i := 0; i < len; i++ { 77 | r := formula.Get(i) 78 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 79 | } 80 | 81 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 82 | }) 83 | }) 84 | 85 | var _ = Describe("LuaDDGS", func() { 86 | It("test", func () { 87 | _, data := loadJson("300666.SZ.json") 88 | _, p := period.PeriodFromString("D1") 89 | rv := function.RecordVectorEx("300666", p, data) 90 | 91 | start := time.Now().UnixNano() 92 | 93 | library := formulalibrary.GlobalLibrary 94 | library.Reset() 95 | library.LoadLuaFormulas("luas") 96 | 97 | formula := library.NewFormula("DDGS", rv) 98 | defer formula.Destroy() 99 | 100 | fmt.Println("Name:", formula.GetName()) 101 | for i := 0; i < formula.ArgCount(); i++ { 102 | min, max := formula.ArgRange(i) 103 | fmt.Printf("default: %f min: %f max: %f\n", formula.ArgDefault(i), min, max) 104 | } 105 | for i := 0; i < formula.VarCount(); i++ { 106 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 107 | } 108 | 109 | start1 := time.Now().UnixNano() 110 | len := formula.Len() 111 | fmt.Println("formula.len:", len, "data len:", rv.Len()) 112 | for i := 0; i < len; i++ { 113 | r := formula.Get(i) 114 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 115 | } 116 | 117 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 118 | }) 119 | }) 120 | 121 | var _ = Describe("LuaEMA513", func() { 122 | It("test", func () { 123 | _, data := loadJson("300666.SZ.json") 124 | _, p := period.PeriodFromString("D1") 125 | rv := function.RecordVectorEx("300666", p, data) 126 | 127 | start := time.Now().UnixNano() 128 | 129 | library := formulalibrary.GlobalLibrary 130 | library.Reset() 131 | library.LoadLuaFormulas("luas") 132 | 133 | formula := library.NewFormula("EMA513", rv) 134 | defer formula.Destroy() 135 | 136 | fmt.Println("Name:", formula.GetName()) 137 | for i := 0; i < formula.ArgCount(); i++ { 138 | min, max := formula.ArgRange(i) 139 | fmt.Printf("default: %f min: %f max: %f\n", formula.ArgDefault(i), min, max) 140 | } 141 | for i := 0; i < formula.VarCount(); i++ { 142 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 143 | } 144 | 145 | start1 := time.Now().UnixNano() 146 | len := formula.Len() 147 | fmt.Println("formula.len:", len, "data len:", rv.Len()) 148 | for i := 0; i < len; i++ { 149 | r := formula.Get(i) 150 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 151 | } 152 | 153 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 154 | }) 155 | }) 156 | 157 | var _ = Describe("LuaCross", func() { 158 | It("test", func () { 159 | dl := datalibrary.NewDataLibrary("data") 160 | rv := dl.GetData("000001", "M5") 161 | 162 | fmt.Println("data len:", rv.Len()) 163 | start := time.Now().UnixNano() 164 | 165 | library := formulalibrary.GlobalLibrary 166 | library.Reset() 167 | library.SetDebug(true) 168 | library.SetDataLibrary(dl) 169 | library.LoadLuaFormulas("luas") 170 | 171 | formula := library.NewFormula("CROSS", rv) 172 | defer formula.Destroy() 173 | 174 | fmt.Println("Name:", formula.GetName()) 175 | for i := 0; i < formula.ArgCount(); i++ { 176 | min, max := formula.ArgRange(i) 177 | fmt.Printf("default: %f min: %f max: %f\n", formula.ArgDefault(i), min, max) 178 | } 179 | for i := 0; i < formula.VarCount(); i++ { 180 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", formula.VarName(i), formula.NoDraw(i), formula.LineThick(i), formula.Color(i)) 181 | } 182 | 183 | start1 := time.Now().UnixNano() 184 | len := formula.Len() 185 | fmt.Println("formula.len:", len, "data len:", rv.Len()) 186 | 187 | for i := 0; i < formula.Len(); i++ { 188 | r := formula.Get(i) 189 | fmt.Printf("%d. %s", i, rv.Get(i).GetDate()) 190 | for _, v := range r { 191 | fmt.Printf("\t%.02f", v) 192 | } 193 | fmt.Println("") 194 | } 195 | 196 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, (time.Now().UnixNano() - start1) / 1000000, "ms") 197 | }) 198 | }) 199 | 200 | var _ = Describe("LuaMACDBuyIncr", func() { 201 | It("test", func (){ 202 | _, data := loadJson("300666.SZ.json") 203 | _, p := period.PeriodFromString("D1") 204 | 205 | initData := append([]function.Record{}, data[:len(data)-10]...) 206 | 207 | rv := function.RecordVectorEx("300666", p, initData) 208 | 209 | fmt.Println("data len:", len(initData)) 210 | start := time.Now().UnixNano() 211 | 212 | library := formulalibrary.GlobalLibrary 213 | library.Reset() 214 | library.LoadLuaFormulas("luas") 215 | 216 | f := library.NewFormula("MACDBUY", rv) 217 | defer f.Destroy() 218 | 219 | util.Assert(f != nil, "") 220 | 221 | for i := len(data) - 10; i < len(data); i++ { 222 | r := data[i] 223 | rv.Append(r) 224 | } 225 | f.UpdateLastValue() 226 | 227 | fmt.Println("Name:", f.GetName()) 228 | for i := 0; i < f.ArgCount(); i++ { 229 | min, max := f.ArgRange(i) 230 | fmt.Printf("default: %f min: %f max: %f\n", f.ArgDefault(i), min, max) 231 | } 232 | for i := 0; i < f.VarCount(); i++ { 233 | fmt.Printf("name: %s noDraw: %v lineThick: %d color: %+v\n", f.VarName(i), f.NoDraw(i), f.LineThick(i), f.Color(i)) 234 | } 235 | 236 | fmt.Println(f.Len()) 237 | for i := 0; i < f.Len(); i++ { 238 | r := f.Get(i) 239 | fmt.Printf("%d. %s\t%.02f\n", i, rv.Get(i).GetDate(), r[0]) 240 | } 241 | fmt.Println("time cost: ", (time.Now().UnixNano() - start) / 1000000, "ms") 242 | }) 243 | }) 244 | -------------------------------------------------------------------------------- /test/luar_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | import ( 3 | . "github.com/onsi/ginkgo" 4 | "github.com/stevedonovan/luar" 5 | "fmt" 6 | "github.com/stephenlyu/goformula/function" 7 | stockfunc "github.com/stephenlyu/goformula/stockfunc/function" 8 | ) 9 | 10 | var _ = Describe("LUAR", func() { 11 | It("test", func () { 12 | const test = ` 13 | for i = 1, 3 do 14 | print(msg, i) 15 | end 16 | print(user) 17 | print(user.Name, user.Age) 18 | ` 19 | 20 | type person struct { 21 | Name string 22 | Age int 23 | } 24 | 25 | L := luar.Init() 26 | defer L.Close() 27 | 28 | user := &person{"Dolly", 46} 29 | 30 | luar.Register(L, "", luar.Map{ 31 | // Go functions may be registered directly. 32 | "print": fmt.Println, 33 | // Constants can be registered. 34 | "msg": "foo", 35 | // And other values as well. 36 | "user": user, 37 | }) 38 | 39 | L.DoString(test) 40 | }) 41 | }) 42 | 43 | var _ = Describe("VectorLua", func() { 44 | It("test", func () { 45 | const test = ` 46 | 47 | ` 48 | L := luar.Init() 49 | defer L.Close() 50 | 51 | //v := function.Vector([]float64{1, 2, 3, 4, 5}) 52 | 53 | luar.Register(L, "", luar.Map{ 54 | // Go functions may be registered directly. 55 | "print": fmt.Println, 56 | // Constants can be registered. 57 | //"v": v, 58 | "Vector": function.Vector, 59 | "Close": stockfunc.CLOSE, 60 | "RVector": stockfunc.RecordVector, 61 | }) 62 | 63 | L.DoString(test) 64 | }) 65 | }) 66 | 67 | var _ = Describe("Account", func() { 68 | It("test", func () { 69 | L := luar.Init() 70 | defer L.Close() 71 | 72 | //v := function.Vector([]float64{1, 2, 3, 4, 5}) 73 | 74 | luar.Register(L, "", luar.Map{ 75 | // Go functions may be registered directly. 76 | "print": fmt.Println, 77 | // Constants can be registered. 78 | //"v": v, 79 | "Vector": function.Vector, 80 | "Close": stockfunc.CLOSE, 81 | "RVector": stockfunc.RecordVector, 82 | }) 83 | 84 | L.DoFile("account.lua") 85 | 86 | //b = Account.new(Account, {value=1000}) 87 | //--b.display(b) 88 | 89 | 90 | L.GetGlobal("Account") 91 | println(L.GetTop()) 92 | L.GetField(-1, "new") 93 | println(L.GetTop()) 94 | 95 | L.PushValue(-2) 96 | println(L.GetTop()) 97 | L.NewTable() 98 | println(L.GetTop()) 99 | L.Call(2, 1) 100 | println(L.GetTop()) 101 | 102 | L.Remove(1) 103 | println(L.GetTop()) 104 | 105 | L.GetField(-1, "display") 106 | L.PushValue(-2) 107 | L.Call(1, 0) 108 | println(L.GetTop()) 109 | 110 | }) 111 | }) 112 | -------------------------------------------------------------------------------- /test/luas/MACD.d.lua: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------- 2 | -- GENERATED BY EASYLANG COMPILER. 3 | -- !!!! DON'T MODIFY IT!!!!!! 4 | ----------------------------------------------------------- 5 | 6 | Sequence = 0 7 | MACDObjects = {} 8 | 9 | function SetObject(id, obj) 10 | print('SetObject', id) 11 | MACDObjects[id] = obj 12 | end 13 | 14 | function RemoveObject(id) 15 | MACDObjects[id] = nil 16 | end 17 | 18 | function GetObject(id) 19 | print('GetObject', id) 20 | return MACDObjects(id) 21 | end 22 | 23 | function GetObjectCount() 24 | local count = 0 25 | for k, v in pairs(MACDObjects) do 26 | count = count + 1 27 | end 28 | return count 29 | end 30 | 31 | MACDClass = {} 32 | 33 | MACDClass['name'] = 'MACD' 34 | MACDClass['argName'] = {'short', 'long', 'mid'} 35 | MACDClass['short'] = {12.000000, 2.000000, 200.000000} 36 | MACDClass['long'] = {26.000000, 2.000000, 200.000000} 37 | MACDClass['mid'] = {9.000000, 2.000000, 200.000000} 38 | MACDClass['vars'] = {'DIF', 'DEA', 'MACD', '做多', '做空'} 39 | MACDClass['flags'] = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000} 40 | MACDClass['color'] = {{Red=-1, Green=-1, Blue=-1}, {Red=-1, Green=-1, Blue=-1}, {Red=-1, Green=-1, Blue=-1}, {Red=-1, Green=-1, Blue=-1}, {Red=-1, Green=-1, Blue=-1}} 41 | MACDClass['lineThick'] = {1, 1, 1, 1, 1} 42 | MACDClass['lineStyle'] = {0, 0, 0, 0, 0} 43 | MACDClass['graphType'] = {1, 1, 2, 1, 1} 44 | 45 | function MACDClass:new(data, short, long, mid) 46 | o = {} 47 | setmetatable(o, self) 48 | self.__index = self 49 | o.__data_code0___ = data 50 | 51 | 52 | o.short = Scalar(short) 53 | o.long = Scalar(long) 54 | o.mid = Scalar(mid) 55 | o.var3 = CLOSE(o.__data_code0___) 56 | o.var4 = EMA(o.var3, o.short) 57 | o.var5 = EMA(o.var3, o.long) 58 | o.var6 = SUB(o.var4, o.var5) 59 | o.dif = o.var6 60 | o.var7 = EMA(o.dif, o.mid) 61 | o.dea = o.var7 62 | o.var8 = SUB(o.dif, o.dea) 63 | o.const1 = Scalar(2.000000) 64 | o.var9 = MUL(o.var8, o.const1) 65 | o.macd = o.var9 66 | o.var10 = CROSS(o.dif, o.dea) 67 | o.var1 = o.var10 68 | o.var11 = CROSS(o.dea, o.dif) 69 | o.var2 = o.var11 70 | 71 | o.drawTextActions = { 72 | 73 | } 74 | 75 | o.drawIconActions = { 76 | 77 | } 78 | 79 | o.drawLineActions = { 80 | 81 | } 82 | 83 | o.drawKLineActions = { 84 | 85 | } 86 | 87 | o.stickLineActions = { 88 | 89 | } 90 | 91 | o.ployLineActions = { 92 | 93 | } 94 | 95 | o.ref_values = {o.dif, o.dea, o.macd, o.var1, o.var2} 96 | 97 | Sequence = Sequence + 1 98 | o.__id__ = Sequence 99 | 100 | SetObject(o.__id__, o) 101 | 102 | return o 103 | end 104 | 105 | function MACDClass:updateLastValue() 106 | o.var3.updateLastValue() 107 | o.var4.updateLastValue() 108 | o.var5.updateLastValue() 109 | o.var6.updateLastValue() 110 | o.var7.updateLastValue() 111 | o.var8.updateLastValue() 112 | o.var9.updateLastValue() 113 | o.var10.updateLastValue() 114 | o.var11.updateLastValue() 115 | end 116 | 117 | function MACDClass:Len() 118 | return self.__data_code0___.Len() 119 | end 120 | 121 | 122 | function MACDClass:Get(index) 123 | return { 124 | o.dif.Get(index), 125 | o.dea.Get(index), 126 | o.macd.Get(index), 127 | o.var1.Get(index), 128 | o.var2.Get(index), 129 | } 130 | end 131 | 132 | FormulaClass = MACDClass 133 | -------------------------------------------------------------------------------- /test/luas/MACDBUY.d.lua: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------- 2 | -- GENERATED BY EASYLANG COMPILER. 3 | -- !!!! DON'T MODIFY IT!!!!!! 4 | ----------------------------------------------------------- 5 | 6 | MACDBUYClass = {} 7 | 8 | MACDBUYClass['name'] = 'MACDBUY' 9 | MACDBUYClass['argName'] = {} 10 | 11 | MACDBUYClass['vars'] = {'做多'} 12 | MACDBUYClass['flags'] = {0x00000000} 13 | MACDBUYClass['color'] = {{Red=-1, Green=-1, Blue=-1}} 14 | MACDBUYClass['lineThick'] = {1} 15 | MACDBUYClass['lineStyle'] = {0} 16 | MACDBUYClass['graphType'] = {1} 17 | 18 | function MACDBUYClass:new(data) 19 | print('MACDBUYClass:new') 20 | o = {} 21 | setmetatable(o, self) 22 | self.__index = self 23 | o.__data_code0___ = data 24 | 25 | o.formula_code0__macd = FormulaManager.NewFormula('MACD', o.__data_code0___) 26 | o.var1 = o.formula_code0__macd.GetVarValue('DIF') 27 | o.var2 = o.formula_code0__macd.GetVarValue('DEA') 28 | o.var4 = CROSS(o.var1, o.var2) 29 | o.var3 = o.var4 30 | 31 | o.drawTextActions = { 32 | 33 | } 34 | 35 | o.drawIconActions = { 36 | 37 | } 38 | 39 | o.drawLineActions = { 40 | 41 | } 42 | 43 | o.drawKLineActions = { 44 | 45 | } 46 | 47 | o.stickLineActions = { 48 | 49 | } 50 | 51 | o.ployLineActions = { 52 | 53 | } 54 | 55 | o.ref_values = {o.var3} 56 | return o 57 | end 58 | 59 | function MACDBUYClass:updateLastValue() 60 | print("MACDBUYClass:updateLastValue") 61 | o.formula_code0__macd.UpdateLastValue() 62 | o.var4.UpdateLastValue() 63 | end 64 | 65 | function MACDBUYClass:Len() 66 | print("MACDBUYClass:Len") 67 | return self.__data_code0___.Len() 68 | end 69 | 70 | 71 | function MACDBUYClass:Get(index) 72 | return { 73 | o.var3.Get(index), 74 | } 75 | end 76 | 77 | FormulaClass = MACDBUYClass 78 | -------------------------------------------------------------------------------- /test/native/cross.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | . "github.com/stephenlyu/goformula/datalibrary" 14 | . "github.com/stephenlyu/goformula/formulalibrary" 15 | ) 16 | 17 | type cross struct { 18 | BaseNativeFormula 19 | 20 | // Data of all referenced period 21 | __data_code0_d1__ *RVector 22 | __index_map_code0_d1__ *IndexMap 23 | __data_code1___ *RVector 24 | __index_map_code1___ *IndexMap 25 | __data_code2_d1__ *RVector 26 | __index_map_code2_d1__ *IndexMap 27 | 28 | // Referenced Formulas 29 | formula_code0_d1_macd Formula 30 | formula_code1__macd Formula 31 | formula_code2_d1_macd Formula 32 | 33 | // Vectors 34 | var1 Value 35 | a Value 36 | var2 Value 37 | b Value 38 | var3 Value 39 | c Value 40 | var4 Value 41 | d Value 42 | var5 Value 43 | e Value 44 | } 45 | 46 | var ( 47 | CROSS_META = &FormulaMetaImpl{ 48 | Name: "CROSS", 49 | ArgNames: []string{}, 50 | ArgMeta: []Arg { 51 | 52 | }, 53 | Flags: []int{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, 54 | Colors: []*Color{{Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}}, 55 | LineThicks: []int{1, 1, 1, 1, 1}, 56 | LineStyles: []int{0, 0, 0, 0, 0}, 57 | GraphTypes: []int{1, 1, 1, 1, 1}, 58 | Vars: []string{"A", "B", "C", "D", "E"}, 59 | } 60 | ) 61 | 62 | func NewCROSS(data *RVector, args []float64) Formula { 63 | o := &cross{ 64 | BaseNativeFormula: BaseNativeFormula{ 65 | FormulaMetaImpl: CROSS_META, 66 | Data__: data, 67 | }, 68 | } 69 | 70 | // Data of all referenced period 71 | o.__data_code0_d1__ = GlobalDataLibrary.GetData(data.Code(), "D1") 72 | o.__index_map_code0_d1__ = NewIndexMap(o.Data__, o.__data_code0_d1__) 73 | o.__data_code1___ = GlobalDataLibrary.GetData("000001", data.Period().Name()) 74 | o.__index_map_code1___ = NewIndexMap(o.Data__, o.__data_code1___) 75 | o.__data_code2_d1__ = GlobalDataLibrary.GetData("999999", "D1") 76 | o.__index_map_code2_d1__ = NewIndexMap(o.Data__, o.__data_code2_d1__) 77 | 78 | // Referenced Formulas 79 | o.formula_code0_d1_macd = GlobalLibrary.NewFormula("MACD", o.__data_code0_d1__) 80 | o.formula_code1__macd = GlobalLibrary.NewFormula("MACD", o.__data_code1___) 81 | o.formula_code2_d1_macd = GlobalLibrary.NewFormula("MACD", o.__data_code2_d1__) 82 | 83 | // Vectors 84 | o.var1 = CrossValue(CLOSE(o.__data_code0_d1__), o.__index_map_code0_d1__) 85 | o.a = o.var1 86 | o.var2 = CrossValue(o.formula_code0_d1_macd.GetVarValue("MACD"), o.__index_map_code0_d1__) 87 | o.b = o.var2 88 | o.var3 = CrossValue(CLOSE(o.__data_code1___), o.__index_map_code1___) 89 | o.c = o.var3 90 | o.var4 = CrossValue(o.formula_code1__macd.GetVarValue("MACD"), o.__index_map_code1___) 91 | o.d = o.var4 92 | o.var5 = CrossValue(o.formula_code2_d1_macd.GetVarValue("DIF"), o.__index_map_code2_d1__) 93 | o.e = o.var5 94 | 95 | // Actions 96 | 97 | o.DrawActions__ = []DrawAction{ 98 | 99 | } 100 | 101 | o.RefValues__ = []Value {o.a, o.b, o.c, o.d, o.e} 102 | return o 103 | } 104 | 105 | func (this *cross) UpdateLastValue() { 106 | this.formula_code0_d1_macd.UpdateLastValue() 107 | this.formula_code1__macd.UpdateLastValue() 108 | this.formula_code2_d1_macd.UpdateLastValue() 109 | this.var1.UpdateLastValue() 110 | this.var2.UpdateLastValue() 111 | this.var3.UpdateLastValue() 112 | this.var4.UpdateLastValue() 113 | this.var5.UpdateLastValue() 114 | } 115 | 116 | func init() { 117 | RegisterNativeFormula(NewCROSS, CROSS_META) 118 | } 119 | 120 | -------------------------------------------------------------------------------- /test/native/draw.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type draw struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | var1 Value 26 | var2 Value 27 | var3 Value 28 | const1 Value 29 | var4 Value 30 | var5 Value 31 | string1 string 32 | var6 Value 33 | const2 Value 34 | var7 Value 35 | var8 Value 36 | var9 Value 37 | var10 Value 38 | const3 Value 39 | var11 Value 40 | __anonymous_1 Value 41 | var12 Value 42 | __anonymous_2 Value 43 | var13 Value 44 | const4 Value 45 | } 46 | 47 | var ( 48 | DRAW_META = &FormulaMetaImpl{ 49 | Name: "DRAW", 50 | ArgNames: []string{}, 51 | ArgMeta: []Arg { 52 | 53 | }, 54 | Flags: []int{0x00000000, 0x00000000}, 55 | Colors: []*Color{{Red:255, Green:0, Blue:0}, {Red:255, Green:0, Blue:0}}, 56 | LineThicks: []int{1, 9}, 57 | LineStyles: []int{0, 0}, 58 | GraphTypes: []int{0, 0}, 59 | Vars: []string{"", ""}, 60 | } 61 | ) 62 | 63 | func NewDRAW(data *RVector, args []float64) Formula { 64 | o := &draw{ 65 | BaseNativeFormula: BaseNativeFormula{ 66 | FormulaMetaImpl: DRAW_META, 67 | Data__: data, 68 | }, 69 | } 70 | 71 | // Data of all referenced period 72 | 73 | 74 | // Referenced Formulas 75 | 76 | 77 | // Vectors 78 | o.var1 = CLOSE(o.Data__) 79 | o.var2 = OPEN(o.Data__) 80 | o.var3 = DIV(o.var1, o.var2) 81 | o.const1 = Scalar(1.080000) 82 | o.var4 = GT(o.var3, o.const1) 83 | o.var5 = LOW(o.Data__) 84 | o.string1 = "abc" 85 | o.var6 = HIGH(o.Data__) 86 | o.const2 = Scalar(20.000000) 87 | o.var7 = HHV(o.var6, o.const2) 88 | o.var8 = GE(o.var6, o.var7) 89 | o.var9 = LLV(o.var5, o.const2) 90 | o.var10 = LE(o.var5, o.var9) 91 | o.const3 = Scalar(1.000000) 92 | o.var11 = DRAWLINE(o.var8, o.var6, o.var10, o.var5, o.const3) 93 | o.__anonymous_1 = o.var11 94 | o.var12 = PLOYLINE(o.var8, o.var6) 95 | o.__anonymous_2 = o.var12 96 | o.var13 = GT(o.var1, o.var2) 97 | o.const4 = Scalar(0.800000) 98 | 99 | // Actions 100 | 101 | o.DrawActions__ = []DrawAction{ 102 | &DrawTextAction{ActionType:5, Cond:o.var4, Price:o.var5, Text:o.string1, Color:&Color{Red:0, Green:0, Blue:255}, NoDraw:0}, 103 | &DrawLineAction{ActionType:1, Cond1:o.var8, Price1:o.var6, Cond2:o.var10, Price2:o.var5, Expand:1, NoDraw:0, Color:&Color{Red:255, Green:0, Blue:0}, LineThick:1, VarIndex:0}, 104 | &PloyLineAction{ActionType:0, Cond:o.var8, Price:o.var6, NoDraw:0, Color:&Color{Red:255, Green:0, Blue:0}, LineThick:9, VarIndex:1}, 105 | &StickLineAction{ActionType:3, Cond:o.var13, Price1:o.var1, Price2:o.var2, Width:0.800000, Empty:1, NoDraw:0, Color:&Color{Red:0, Green:255, Blue:255}, LineThick:1}, 106 | &DrawIconAction{ActionType:4, Cond:o.var13, Price:o.var5, Type:1, NoDraw:0}, 107 | &DrawKLineAction{ActionType:2, High:o.var6, Open:o.var2, Low:o.var5, Close:o.var1, NoDraw:1}, 108 | } 109 | 110 | o.RefValues__ = []Value {o.__anonymous_1, o.__anonymous_2} 111 | return o 112 | } 113 | 114 | func (this *draw) UpdateLastValue() { 115 | this.var1.UpdateLastValue() 116 | this.var2.UpdateLastValue() 117 | this.var3.UpdateLastValue() 118 | this.var4.UpdateLastValue() 119 | this.var5.UpdateLastValue() 120 | this.var6.UpdateLastValue() 121 | this.var7.UpdateLastValue() 122 | this.var8.UpdateLastValue() 123 | this.var9.UpdateLastValue() 124 | this.var10.UpdateLastValue() 125 | this.var11.UpdateLastValue() 126 | this.var12.UpdateLastValue() 127 | this.var13.UpdateLastValue() 128 | } 129 | 130 | func init() { 131 | RegisterNativeFormula(NewDRAW, DRAW_META) 132 | } 133 | 134 | -------------------------------------------------------------------------------- /test/native/drawline.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type drawline struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | var1 Value 26 | a Value 27 | const1 Value 28 | var2 Value 29 | b Value 30 | var3 Value 31 | bb Value 32 | var4 Value 33 | c Value 34 | var5 Value 35 | d Value 36 | var6 Value 37 | dd Value 38 | const2 Value 39 | var7 Value 40 | __anonymous_0 Value 41 | } 42 | 43 | var ( 44 | DRAWLINE_META = &FormulaMetaImpl{ 45 | Name: "DRAWLINE", 46 | ArgNames: []string{}, 47 | ArgMeta: []Arg { 48 | 49 | }, 50 | Flags: []int{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001}, 51 | Colors: []*Color{{Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:0, Green:255, Blue:0}}, 52 | LineThicks: []int{1, 1, 1, 1, 1, 1, 8}, 53 | LineStyles: []int{0, 0, 0, 0, 0, 0, 0}, 54 | GraphTypes: []int{1, 1, 1, 1, 1, 1, 0}, 55 | Vars: []string{"A", "B", "BB", "C", "D", "DD", ""}, 56 | } 57 | ) 58 | 59 | func NewDRAWLINE(data *RVector, args []float64) Formula { 60 | o := &drawline{ 61 | BaseNativeFormula: BaseNativeFormula{ 62 | FormulaMetaImpl: DRAWLINE_META, 63 | Data__: data, 64 | }, 65 | } 66 | 67 | // Data of all referenced period 68 | 69 | 70 | // Referenced Formulas 71 | 72 | 73 | // Vectors 74 | o.var1 = HIGH(o.Data__) 75 | o.a = o.var1 76 | o.const1 = Scalar(5.000000) 77 | o.var2 = HHV(o.var1, o.const1) 78 | o.b = o.var2 79 | o.var3 = GE(o.var1, o.var2) 80 | o.bb = o.var3 81 | o.var4 = LOW(o.Data__) 82 | o.c = o.var4 83 | o.var5 = LLV(o.var4, o.const1) 84 | o.d = o.var5 85 | o.var6 = LE(o.var4, o.var5) 86 | o.dd = o.var6 87 | o.const2 = Scalar(1.000000) 88 | o.var7 = DRAWLINE(o.var3, o.var1, o.var6, o.var4, o.const2) 89 | o.__anonymous_0 = o.var7 90 | 91 | // Actions 92 | 93 | o.DrawActions__ = []DrawAction{ 94 | &DrawLineAction{ActionType:1, Cond1:o.var3, Price1:o.var1, Cond2:o.var6, Price2:o.var4, Expand:1, NoDraw:1, Color:&Color{Red:0, Green:255, Blue:0}, LineThick:8, VarIndex:6}, 95 | } 96 | 97 | o.RefValues__ = []Value {o.a, o.b, o.bb, o.c, o.d, o.dd, o.__anonymous_0} 98 | return o 99 | } 100 | 101 | func (this *drawline) UpdateLastValue() { 102 | this.var1.UpdateLastValue() 103 | this.var2.UpdateLastValue() 104 | this.var3.UpdateLastValue() 105 | this.var4.UpdateLastValue() 106 | this.var5.UpdateLastValue() 107 | this.var6.UpdateLastValue() 108 | this.var7.UpdateLastValue() 109 | } 110 | 111 | func init() { 112 | RegisterNativeFormula(NewDRAWLINE, DRAWLINE_META) 113 | } 114 | 115 | -------------------------------------------------------------------------------- /test/native/ma.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type ma struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | m1 Value 26 | m2 Value 27 | m3 Value 28 | m4 Value 29 | var1 Value 30 | var2 Value 31 | ma1 Value 32 | var3 Value 33 | ma2 Value 34 | var4 Value 35 | ma3 Value 36 | var5 Value 37 | ma4 Value 38 | } 39 | 40 | var ( 41 | MA_META = &FormulaMetaImpl{ 42 | Name: "MA", 43 | ArgNames: []string{"m1", "m2", "m3", "m4"}, 44 | ArgMeta: []Arg { 45 | Arg{5.000000, 0.000000, 1000.000000}, 46 | Arg{10.000000, 0.000000, 1000.000000}, 47 | Arg{20.000000, 0.000000, 1000.000000}, 48 | Arg{60.000000, 0.000000, 1000.000000}, 49 | }, 50 | Flags: []int{0x00000000, 0x00000000, 0x00000000, 0x00000000}, 51 | Colors: []*Color{{Red:255, Green:255, Blue:255}, {Red:255, Green:0, Blue:0}, {Red:0, Green:255, Blue:255}, {Red:0, Green:255, Blue:0}}, 52 | LineThicks: []int{1, 1, 1, 1}, 53 | LineStyles: []int{0, 0, 0, 0}, 54 | GraphTypes: []int{1, 1, 1, 1}, 55 | Vars: []string{"MA1", "MA2", "MA3", "MA4"}, 56 | } 57 | ) 58 | 59 | func NewMA(data *RVector, args []float64) Formula { 60 | o := &ma{ 61 | BaseNativeFormula: BaseNativeFormula{ 62 | FormulaMetaImpl: MA_META, 63 | Data__: data, 64 | }, 65 | } 66 | 67 | // Data of all referenced period 68 | 69 | 70 | // Referenced Formulas 71 | 72 | 73 | // Vectors 74 | o.m1 = Scalar(args[0]) 75 | o.m2 = Scalar(args[1]) 76 | o.m3 = Scalar(args[2]) 77 | o.m4 = Scalar(args[3]) 78 | o.var1 = CLOSE(o.Data__) 79 | o.var2 = MA(o.var1, o.m1) 80 | o.ma1 = o.var2 81 | o.var3 = MA(o.var1, o.m2) 82 | o.ma2 = o.var3 83 | o.var4 = MA(o.var1, o.m3) 84 | o.ma3 = o.var4 85 | o.var5 = MA(o.var1, o.m4) 86 | o.ma4 = o.var5 87 | 88 | // Actions 89 | 90 | o.DrawActions__ = []DrawAction{ 91 | 92 | } 93 | 94 | o.RefValues__ = []Value {o.ma1, o.ma2, o.ma3, o.ma4} 95 | return o 96 | } 97 | 98 | func (this *ma) UpdateLastValue() { 99 | this.var1.UpdateLastValue() 100 | this.var2.UpdateLastValue() 101 | this.var3.UpdateLastValue() 102 | this.var4.UpdateLastValue() 103 | this.var5.UpdateLastValue() 104 | } 105 | 106 | func init() { 107 | RegisterNativeFormula(NewMA, MA_META) 108 | } 109 | 110 | -------------------------------------------------------------------------------- /test/native/macd.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type macd struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | short Value 26 | long Value 27 | mid Value 28 | var1 Value 29 | var2 Value 30 | var3 Value 31 | var4 Value 32 | dif Value 33 | var5 Value 34 | dea Value 35 | var6 Value 36 | const1 Value 37 | var7 Value 38 | macd Value 39 | } 40 | 41 | var ( 42 | MACD_META = &FormulaMetaImpl{ 43 | Name: "MACD", 44 | ArgNames: []string{"short", "long", "mid"}, 45 | ArgMeta: []Arg { 46 | Arg{12.000000, 2.000000, 200.000000}, 47 | Arg{26.000000, 2.000000, 200.000000}, 48 | Arg{9.000000, 2.000000, 200.000000}, 49 | }, 50 | Flags: []int{0x00000000, 0x00000000, 0x00000000}, 51 | Colors: []*Color{{Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}}, 52 | LineThicks: []int{1, 1, 1}, 53 | LineStyles: []int{0, 0, 0}, 54 | GraphTypes: []int{1, 1, 2}, 55 | Vars: []string{"DIF", "DEA", "MACD"}, 56 | } 57 | ) 58 | 59 | func NewMACD(data *RVector, args []float64) Formula { 60 | o := &macd{ 61 | BaseNativeFormula: BaseNativeFormula{ 62 | FormulaMetaImpl: MACD_META, 63 | Data__: data, 64 | }, 65 | } 66 | 67 | // Data of all referenced period 68 | 69 | 70 | // Referenced Formulas 71 | 72 | 73 | // Vectors 74 | o.short = Scalar(args[0]) 75 | o.long = Scalar(args[1]) 76 | o.mid = Scalar(args[2]) 77 | o.var1 = CLOSE(o.Data__) 78 | o.var2 = EMA(o.var1, o.short) 79 | o.var3 = EMA(o.var1, o.long) 80 | o.var4 = SUB(o.var2, o.var3) 81 | o.dif = o.var4 82 | o.var5 = EMA(o.dif, o.mid) 83 | o.dea = o.var5 84 | o.var6 = SUB(o.dif, o.dea) 85 | o.const1 = Scalar(2.000000) 86 | o.var7 = MUL(o.var6, o.const1) 87 | o.macd = o.var7 88 | 89 | // Actions 90 | 91 | o.DrawActions__ = []DrawAction{ 92 | 93 | } 94 | 95 | o.RefValues__ = []Value {o.dif, o.dea, o.macd} 96 | return o 97 | } 98 | 99 | func (this *macd) UpdateLastValue() { 100 | this.var1.UpdateLastValue() 101 | this.var2.UpdateLastValue() 102 | this.var3.UpdateLastValue() 103 | this.var4.UpdateLastValue() 104 | this.var5.UpdateLastValue() 105 | this.var6.UpdateLastValue() 106 | this.var7.UpdateLastValue() 107 | } 108 | 109 | func init() { 110 | RegisterNativeFormula(NewMACD, MACD_META) 111 | } 112 | 113 | -------------------------------------------------------------------------------- /test/native/macdbuy.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | . "github.com/stephenlyu/goformula/formulalibrary" 14 | ) 15 | 16 | type macdbuy struct { 17 | BaseNativeFormula 18 | 19 | // Data of all referenced period 20 | 21 | 22 | // Referenced Formulas 23 | formula_code0__macd Formula 24 | 25 | // Vectors 26 | var1 Value 27 | var2 Value 28 | var4 Value 29 | var3 Value 30 | } 31 | 32 | var ( 33 | MACDBUY_META = &FormulaMetaImpl{ 34 | Name: "MACDBUY", 35 | ArgNames: []string{}, 36 | ArgMeta: []Arg { 37 | 38 | }, 39 | Flags: []int{0x00000000}, 40 | Colors: []*Color{{Red:-1, Green:-1, Blue:-1}}, 41 | LineThicks: []int{1}, 42 | LineStyles: []int{0}, 43 | GraphTypes: []int{1}, 44 | Vars: []string{"做多"}, 45 | } 46 | ) 47 | 48 | func NewMACDBUY(data *RVector, args []float64) Formula { 49 | o := &macdbuy{ 50 | BaseNativeFormula: BaseNativeFormula{ 51 | FormulaMetaImpl: MACDBUY_META, 52 | Data__: data, 53 | }, 54 | } 55 | 56 | // Data of all referenced period 57 | 58 | 59 | // Referenced Formulas 60 | o.formula_code0__macd = GlobalLibrary.NewFormula("MACD", o.Data__) 61 | 62 | // Vectors 63 | o.var1 = o.formula_code0__macd.GetVarValue("DIF") 64 | o.var2 = o.formula_code0__macd.GetVarValue("DEA") 65 | o.var4 = CROSS(o.var1, o.var2) 66 | o.var3 = o.var4 67 | 68 | // Actions 69 | 70 | o.DrawActions__ = []DrawAction{ 71 | 72 | } 73 | 74 | o.RefValues__ = []Value {o.var3} 75 | return o 76 | } 77 | 78 | func (this *macdbuy) UpdateLastValue() { 79 | this.formula_code0__macd.UpdateLastValue() 80 | this.var4.UpdateLastValue() 81 | } 82 | 83 | func init() { 84 | RegisterNativeFormula(NewMACDBUY, MACDBUY_META) 85 | } 86 | 87 | -------------------------------------------------------------------------------- /test/native/ployline.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type ployline struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | var1 Value 26 | const1 Value 27 | var2 Value 28 | var3 Value 29 | var4 Value 30 | __anonymous_0 Value 31 | } 32 | 33 | var ( 34 | PLOYLINE_META = &FormulaMetaImpl{ 35 | Name: "PLOYLINE", 36 | ArgNames: []string{}, 37 | ArgMeta: []Arg { 38 | 39 | }, 40 | Flags: []int{0x00000000}, 41 | Colors: []*Color{{Red:255, Green:0, Blue:0}}, 42 | LineThicks: []int{9}, 43 | LineStyles: []int{0}, 44 | GraphTypes: []int{0}, 45 | Vars: []string{""}, 46 | } 47 | ) 48 | 49 | func NewPLOYLINE(data *RVector, args []float64) Formula { 50 | o := &ployline{ 51 | BaseNativeFormula: BaseNativeFormula{ 52 | FormulaMetaImpl: PLOYLINE_META, 53 | Data__: data, 54 | }, 55 | } 56 | 57 | // Data of all referenced period 58 | 59 | 60 | // Referenced Formulas 61 | 62 | 63 | // Vectors 64 | o.var1 = HIGH(o.Data__) 65 | o.const1 = Scalar(5.000000) 66 | o.var2 = HHV(o.var1, o.const1) 67 | o.var3 = GE(o.var1, o.var2) 68 | o.var4 = PLOYLINE(o.var3, o.var1) 69 | o.__anonymous_0 = o.var4 70 | 71 | // Actions 72 | 73 | o.DrawActions__ = []DrawAction{ 74 | &PloyLineAction{ActionType:0, Cond:o.var3, Price:o.var1, NoDraw:0, Color:&Color{Red:255, Green:0, Blue:0}, LineThick:9, VarIndex:0}, 75 | } 76 | 77 | o.RefValues__ = []Value {o.__anonymous_0} 78 | return o 79 | } 80 | 81 | func (this *ployline) UpdateLastValue() { 82 | this.var1.UpdateLastValue() 83 | this.var2.UpdateLastValue() 84 | this.var3.UpdateLastValue() 85 | this.var4.UpdateLastValue() 86 | } 87 | 88 | func init() { 89 | RegisterNativeFormula(NewPLOYLINE, PLOYLINE_META) 90 | } 91 | 92 | -------------------------------------------------------------------------------- /test/native/vol.go: -------------------------------------------------------------------------------- 1 | // 2 | // GENERATED BY EASYLANG COMPILER. 3 | // !!!! DON'T MODIFY IT!!!!!! 4 | // 5 | 6 | package native 7 | 8 | import ( 9 | . "github.com/stephenlyu/goformula/stockfunc/function" 10 | . "github.com/stephenlyu/goformula/function" 11 | . "github.com/stephenlyu/goformula/formulalibrary/base/formula" 12 | . "github.com/stephenlyu/goformula/formulalibrary/native/nativeformulas" 13 | ) 14 | 15 | type vol struct { 16 | BaseNativeFormula 17 | 18 | // Data of all referenced period 19 | 20 | 21 | // Referenced Formulas 22 | 23 | 24 | // Vectors 25 | m1 Value 26 | m2 Value 27 | var1 Value 28 | volume Value 29 | var2 Value 30 | mavol1 Value 31 | var3 Value 32 | mavol2 Value 33 | } 34 | 35 | var ( 36 | VOL_META = &FormulaMetaImpl{ 37 | Name: "VOL", 38 | ArgNames: []string{"m1", "m2"}, 39 | ArgMeta: []Arg { 40 | Arg{5.000000, 0.000000, 1000.000000}, 41 | Arg{10.000000, 0.000000, 1000.000000}, 42 | }, 43 | Flags: []int{0x00000000, 0x00000000, 0x00000000}, 44 | Colors: []*Color{{Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}, {Red:-1, Green:-1, Blue:-1}}, 45 | LineThicks: []int{1, 1, 1}, 46 | LineStyles: []int{0, 0, 0}, 47 | GraphTypes: []int{4, 1, 1}, 48 | Vars: []string{"VOLUME", "MAVOL1", "MAVOL2"}, 49 | } 50 | ) 51 | 52 | func NewVOL(data *RVector, args []float64) Formula { 53 | o := &vol{ 54 | BaseNativeFormula: BaseNativeFormula{ 55 | FormulaMetaImpl: VOL_META, 56 | Data__: data, 57 | }, 58 | } 59 | 60 | // Data of all referenced period 61 | 62 | 63 | // Referenced Formulas 64 | 65 | 66 | // Vectors 67 | o.m1 = Scalar(args[0]) 68 | o.m2 = Scalar(args[1]) 69 | o.var1 = VOLUME(o.Data__) 70 | o.volume = o.var1 71 | o.var2 = MA(o.volume, o.m1) 72 | o.mavol1 = o.var2 73 | o.var3 = MA(o.volume, o.m2) 74 | o.mavol2 = o.var3 75 | 76 | // Actions 77 | 78 | o.DrawActions__ = []DrawAction{ 79 | 80 | } 81 | 82 | o.RefValues__ = []Value {o.volume, o.mavol1, o.mavol2} 83 | return o 84 | } 85 | 86 | func (this *vol) UpdateLastValue() { 87 | this.var1.UpdateLastValue() 88 | this.var2.UpdateLastValue() 89 | this.var3.UpdateLastValue() 90 | } 91 | 92 | func init() { 93 | RegisterNativeFormula(NewVOL, VOL_META) 94 | } 95 | 96 | -------------------------------------------------------------------------------- /test/suite_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestIt(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Test Suite") 13 | } 14 | --------------------------------------------------------------------------------