├── .gitmodules ├── simulate ├── report.go └── simulate.go ├── .gitignore ├── bucket ├── source │ ├── source.go │ ├── upbit.go │ └── bittrex.go ├── util.go ├── bucket.go └── worker.go ├── strategy ├── manager.go ├── proto │ ├── strategy.proto │ └── strategy.pb.go ├── shared │ ├── interface.go │ └── grpc.go └── strategy.go ├── trader ├── poloniex_balance.go ├── poloniex_oder.go ├── trader.go └── poloniex.go ├── cmd ├── plugin.go └── config.go ├── db ├── data.go ├── qurey.go └── db.go ├── README-KR.md ├── README.md ├── main.go ├── manager.go └── LICENSE /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simulate/report.go: -------------------------------------------------------------------------------- 1 | package simulate 2 | -------------------------------------------------------------------------------- /simulate/simulate.go: -------------------------------------------------------------------------------- 1 | package simulate 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | .idea -------------------------------------------------------------------------------- /bucket/source/source.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "github.com/ironpark/coinex/db" 5 | "time" 6 | ) 7 | 8 | type DataSource interface { 9 | Status() int //server status 10 | Interval() int 11 | BackData(pair db.Pair,date time.Time) ([]db.OHLC, bool) 12 | RealTime(pair db.Pair) ([]db.OHLC) 13 | Name() string //exchange name 14 | MarketCreated(pair db.Pair) time.Time 15 | } 16 | 17 | -------------------------------------------------------------------------------- /strategy/manager.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | type StrategyManager struct { 4 | Strategys map[string]Strategy 5 | } 6 | 7 | func (sm *StrategyManager)LoadFromGithub(url string) { 8 | 9 | } 10 | 11 | func (sm *StrategyManager)LoadFromSource(path string) { 12 | 13 | } 14 | 15 | func (sm *StrategyManager)LoadFromFile(path string) { 16 | 17 | } 18 | 19 | func (sm *StrategyManager)GetList() { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /trader/poloniex_balance.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import "github.com/ironpark/go-poloniex" 4 | 5 | type poloBalance poloniex.Balance 6 | 7 | func (b poloBalance) Name()string { 8 | return b.Currency 9 | } 10 | func (b poloBalance) All()float64 { 11 | return b.Balance 12 | } 13 | 14 | func (b poloBalance) OnOder()float64 { 15 | return b.Pending 16 | } 17 | 18 | func (b poloBalance) Free()float64 { 19 | return b.Available 20 | } 21 | 22 | func (b poloBalance) BTC()float64 { 23 | return b.Value 24 | } 25 | 26 | -------------------------------------------------------------------------------- /bucket/util.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | import "time" 4 | 5 | func CompareStrings(a, b []string) bool { 6 | if len(a) != len(b) { 7 | return false 8 | } 9 | 10 | if (a == nil) != (b == nil) { 11 | return false 12 | } 13 | 14 | for i, v := range a { 15 | if v != b[i] { 16 | return false 17 | } 18 | } 19 | 20 | return true 21 | } 22 | 23 | func unixMilli() int64 { 24 | return time.Now().UnixNano() / int64(time.Millisecond) 25 | } 26 | 27 | func contains(s []string, e string) bool { 28 | for _, a := range s { 29 | if a == e { 30 | return true 31 | } 32 | } 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /trader/poloniex_oder.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import "github.com/ironpark/go-poloniex" 4 | 5 | //poloniex oder 6 | type poloOder struct { 7 | id int64 8 | price float64 9 | amount float64 10 | name string 11 | client *poloniex.Poloniex 12 | } 13 | 14 | func (oder poloOder) Cancel() error{ 15 | return nil 16 | } 17 | 18 | func (oder poloOder) Price() float64{ 19 | return oder.price 20 | } 21 | 22 | func (oder poloOder) Amount() float64{ 23 | return oder.amount 24 | } 25 | 26 | func (oder poloOder) Name() string{ 27 | return oder.name 28 | } 29 | 30 | func (oder poloOder) IsOpen() bool{ 31 | return false 32 | } 33 | 34 | -------------------------------------------------------------------------------- /cmd/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"github.com/hashicorp/go-plugin" 5 | //"os/exec" 6 | //"os" 7 | //"github.com/hashicorp/go-plugin/examples/grpc/shared" 8 | ) 9 | 10 | func LoadPlugin() { 11 | // We're a host. Start by launching the plugin process. 12 | //client := plugin.NewClient(&plugin.ClientConfig{ 13 | // HandshakeConfig: nil, 14 | // Plugins: shared.PluginMap, 15 | // Cmd: exec.Command("sh", "-c", os.Getenv("KV_PLUGIN")), 16 | // AllowedProtocols: []plugin.Protocol{ 17 | // plugin.ProtocolNetRPC, plugin.ProtocolGRPC}, 18 | //}) 19 | //defer client.Kill() 20 | 21 | //rpcClient, _ := client.Client() 22 | //raw ,_:= rpcClient.Dispense("strage") 23 | //kv := raw.(shared.KV) 24 | } -------------------------------------------------------------------------------- /trader/trader.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import ( 4 | talib "github.com/markcheno/go-talib" 5 | "github.com/ironpark/coinex/db" 6 | ) 7 | //import "fmt" 8 | 9 | type MyOders map[string]string 10 | type MyBalances []Balance 11 | 12 | type Balance interface { 13 | Currency() string 14 | Available() float64 15 | All() float64 16 | } 17 | 18 | type Oder interface { 19 | Cancel() error 20 | Price() float64 21 | Amount() float64 22 | Name() string 23 | IsOpen() bool 24 | } 25 | 26 | type CurrencyPair interface { 27 | SellOder(amount,price float64) Oder 28 | BuyOder(amount,price float64) Oder 29 | TickerData(resolution string) db.TikerData 30 | } 31 | 32 | type Trader interface { 33 | //info 34 | MyOpenOders() []Oder 35 | MyBalance() (MyBalances,error) 36 | Exchange() string 37 | //trade 38 | SetTradeCallback(func(trader Trader,pair CurrencyPair)) 39 | Pair(pair string) CurrencyPair 40 | //callback 41 | Call(Trader, db.TradeData) 42 | } 43 | 44 | //balance 45 | func (balance MyBalances) Size() int{ 46 | return len(balance) 47 | } 48 | -------------------------------------------------------------------------------- /strategy/proto/strategy.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package proto; 3 | 4 | message Property { 5 | string type = 1; 6 | int32 value_int = 2; 7 | float value_float = 3; 8 | string value_string = 4; 9 | bool value_bool = 5; 10 | } 11 | 12 | message Dictionary { 13 | map customInt = 1; 14 | } 15 | 16 | message Information { 17 | string Name = 1; 18 | string Version = 2; 19 | string Description = 3; 20 | } 21 | 22 | message Bool { 23 | bool Boolean = 1; 24 | } 25 | 26 | message Asset { 27 | string Name = 1; 28 | } 29 | 30 | message Empty {} 31 | 32 | service Strategy { 33 | rpc Init(Empty) returns (Empty); 34 | rpc Info(Empty) returns (Information); 35 | 36 | rpc GetProperty(Empty) returns (Dictionary); 37 | rpc SetProperty(Dictionary) returns (Empty); 38 | //filter for long position 39 | rpc SellConditions(Asset) returns (Bool); 40 | //filter for short position 41 | rpc BuyConditions(Asset) returns (Bool); 42 | //long position priority rank 43 | rpc RankFilter(Asset) returns (Bool); 44 | } -------------------------------------------------------------------------------- /trader/poloniex.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import ( 4 | "github.com/ironpark/go-poloniex" 5 | "github.com/ironpark/coinex/db" 6 | ) 7 | 8 | type TPoloniex struct { 9 | client *poloniex.Poloniex 10 | } 11 | 12 | func PoloniexTrader(key,secret string) *TPoloniex { 13 | ptrader := &TPoloniex{} 14 | ptrader.client = poloniex.New(key, secret) 15 | return ptrader 16 | } 17 | 18 | func (c *TPoloniex) TickerData(resolution string) db.TikerData{ 19 | //before := time.Now().Add(-time.Hour*24*30) 20 | //data,_ := c.db.TradeHistory(c.crypto,"poloniex",before,time.Now().Add(time.Minute*60*3),DefaultLimit,resolution) 21 | return nil 22 | } 23 | 24 | func (c *TPoloniex) MyOpenOders() []Oder{ 25 | return nil 26 | } 27 | 28 | func (c *TPoloniex) MyBalance() (balance MyBalances,err error){ 29 | balances,err := c.client.GetBalance() 30 | if err != nil{ 31 | return 32 | } 33 | return []Balance([]poloBalance(balances)),nil 34 | } 35 | 36 | func (c *TPoloniex) SellOder(pair string,amount,price float64) Oder{ 37 | return poloOder{} 38 | } 39 | 40 | func (c *TPoloniex) BuyOder(pair string,amount,price float64) Oder{ 41 | return poloOder{} 42 | } 43 | 44 | func (c *TPoloniex) SetTradeCallback(callback func(trader Trader,data poloniex.TradeData)){ 45 | //c.callback = callback 46 | } 47 | 48 | 49 | func (c *TPoloniex) Call(trader Trader,data TradeData){ 50 | //if c.callback != nil { 51 | // c.callback(trader,data) 52 | //} 53 | } 54 | 55 | func (c *TPoloniex) Pair() string{ 56 | //return c.crypto 57 | } 58 | 59 | func (c *TPoloniex) Exchange() string{ 60 | //return c.exchange 61 | } 62 | -------------------------------------------------------------------------------- /db/data.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "time" 5 | "github.com/markcheno/go-talib" 6 | ) 7 | 8 | type TradeData struct { 9 | ID int64 10 | Type string 11 | Amount float64 12 | Price float64 13 | Total float64 14 | Date time.Time 15 | } 16 | 17 | type TikerData map[string]interface{} 18 | 19 | 20 | //ticker 21 | type TickerValue []float64 22 | 23 | func (ticker TikerData) Low() TickerValue{ 24 | return ticker["low"].([]float64) 25 | } 26 | 27 | func (ticker TikerData) High() TickerValue{ 28 | return ticker["high"].([]float64) 29 | } 30 | 31 | func (ticker TikerData) Open() TickerValue{ 32 | return ticker["first"].([]float64) 33 | } 34 | 35 | func (ticker TikerData) Close() TickerValue{ 36 | return ticker["last"].([]float64) 37 | } 38 | 39 | func (ticker TikerData) Volume() TickerValue{ 40 | return ticker["volume"].([]float64) 41 | } 42 | 43 | func (ticker TikerData) Avg() TickerValue{ 44 | return ticker["avg"].([]float64) 45 | } 46 | 47 | func (ticker TikerData) WeightedAvg() TickerValue{ 48 | return ticker["avg-w"].([]float64) 49 | } 50 | 51 | func (ticker TikerData) Time() []int64{ 52 | return ticker["date"].([]int64) 53 | } 54 | 55 | func (value TickerValue) Last () float64 { 56 | return value[len(value)-1] 57 | } 58 | func (value TickerValue) Before (offset int ) float64 { 59 | return value[len(value)-1 -offset] 60 | } 61 | //math 62 | func (value TickerValue) Sma (period int) TickerValue { 63 | return talib.Sma(value,period) 64 | } 65 | 66 | //To-Do.. 67 | func (value TickerValue) Ema (period int) TickerValue { 68 | return talib.Ema(value,period) 69 | } 70 | 71 | func (value TickerValue) Size () int { 72 | return len(value) 73 | } -------------------------------------------------------------------------------- /README-KR.md: -------------------------------------------------------------------------------- 1 | [Korean](https://github.com/ironpark/coinex/blob/master/README-KR.md) | [English](https://github.com/ironpark/coinex/blob/master/README.md) 2 | 3 | # CoinEx 4 | **CoinEX(임시 가칭)** 는 다양한 암호화폐 거래소에서 거래 데이터를 실시간으로 받아와 로컬 데이터베이스에 저장 및 관리해주며 이를 기반으로 트레이딩 전략을 **개발**, **테스트**, **공유**, **관리**할 수 있는 오픈소스 플랫폼 입니다. 5 | 6 | Alpha 테스트 버전이 개발 완료되는 시점에서 이름 및 저장소가 변경될 예정입니다. 7 | 8 | **현재 지원 예정인 거래소**는 아래와 같습니다. 9 | 1. Bittrex / UPbit 10 | 2. Bitfinex 11 | 3. Poloniex 12 | 4. Coinone 13 | 14 | 15 | ## 의존성 16 | - https://github.com/influxdata/influxdb/client/v2 17 | - https://github.com/ironpark/go-poloniex 18 | - https://github.com/toorop/go-bittrex 19 | - https://github.com/gin-gonic/gin 20 | - https://github.com/asaskevich/EventBus 21 | - https://github.com/sirupsen/logrus 22 | 23 | ## 기부 24 | 이더리움(Ethereum) 주소 : 25 | 26 | ![ethereum address](https://chart.googleapis.com/chart?cht=qr&chl=%200x7EA84eFF0f9D3bd2EaD6Db190A4387B71ac42b44&chs=300x300&choe=UTF-8&chld=L|2') 27 | 28 | 0x7EA84eFF0f9D3bd2EaD6Db190A4387B71ac42b44 29 | 30 | **주의!** 이더리움 클래식(Ethereum Classic)을 보내지 마세요! 31 | 32 | ## 로드맵 33 | Goals for **CoinEX** 34 | 35 | - [ ] TravisCI 지원 36 | 37 | - [ ] 알고리드믹 트레이딩을 위한 시뮬레이션 지원 38 | - [x] 실시간 가격 데이터(OHCL)를 시계열 데이터베이스를 이용하여 보관/관리(influxDB) 39 | - [ ] 사용자 정의 거래 전략을 위한 플러그인 시스템 40 | - [ ] 거래 지표를 위한 플러그인 시스템 ex) SMA,EMA,... 41 | - [ ] 거래전략의 성능평가를 위한 거래 시뮬레이션과 보고서 지원 42 | - [ ] 플러그인 시스템 생태계를 위한 스크립트 언어지원 (python/js) 43 | 44 | - [ ] 웹기술을 기반으로한 시각화 지원 45 | 46 | ## References 47 | - apis 48 | - [poloniex](https://poloniex.com/support/api/) 49 | - [bittrex](https://bittrex.com/Home/Api) 50 | - [coinone](http://doc.coinone.co.kr) 51 | - codes 52 | - [go-poloniex](https://github.com/jyap808/go-poloniex) 53 | 54 | ## License 55 | [MPL 2.0 (Mozilla Public License Version 2.0)](https://www.mozilla.org/en-US/MPL/2.0/) -------------------------------------------------------------------------------- /strategy/shared/interface.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | 6 | "github.com/hashicorp/go-plugin" 7 | "github.com/ironpark/coinex/strategy/proto" 8 | "net/rpc" 9 | ) 10 | 11 | // Handshake is a common handshake that is shared by plugin and host. 12 | var Handshake = plugin.HandshakeConfig{ 13 | ProtocolVersion: 1, 14 | MagicCookieKey: "BASIC_PLUGIN", 15 | MagicCookieValue: "hello", 16 | } 17 | 18 | // PluginMap is the map of plugins we can dispense. 19 | var PluginMap = map[string]plugin.Plugin{ 20 | "strategy": &StrategyPlugin{}, 21 | } 22 | 23 | //rpc Init(Empty) returns (Empty); 24 | //rpc Info(Empty) returns (Information); 25 | // 26 | //rpc GetProperty(Empty) returns (Dictionary); 27 | //rpc SetProperty(Dictionary) returns (Empty); 28 | // 29 | //rpc Update(Empty) returns (Empty); 30 | 31 | // KV is the interface that we're exposing as a plugin. 32 | type Strategy interface { 33 | Init() 34 | Info() (proto.Information) 35 | GetProperty() (map[string]interface{}) 36 | SetProperty(map[string]interface{}) 37 | 38 | SellConditions(string) (bool) 39 | BuyConditions(string)(bool) 40 | RankFilter(string) (bool) 41 | } 42 | 43 | // This is the implementation of plugin.Plugin so we can serve/consume this. 44 | // We also implement GRPCPlugin so that this plugin can be served over 45 | // gRPC. 46 | type StrategyPlugin struct { 47 | // Concrete implementation, written in Go. This is only used for plugins 48 | // that are written in Go. 49 | Impl Strategy 50 | } 51 | 52 | 53 | func (p *StrategyPlugin) Server(*plugin.MuxBroker) (interface{}, error) { 54 | return nil, nil 55 | } 56 | 57 | func (*StrategyPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { 58 | return nil, nil 59 | } 60 | 61 | func (p *StrategyPlugin) GRPCServer(s *grpc.Server) error { 62 | proto.RegisterStrategyServer(s, &GRPCServer{Impl: p.Impl}) 63 | return nil 64 | } 65 | 66 | func (p *StrategyPlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) { 67 | return &GRPCClient{client: proto.NewStrategyClient(c)}, nil 68 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoinEx 2 | [Korean](https://github.com/ironpark/coinex/blob/master/README-KR.md) | [English](https://github.com/ironpark/coinex/blob/master/README.md) 3 | 4 | **CoinEX (temporary name)** is open source software that can **develop, test, share, execute and manage** trading strategies using various cryptographic exchange data. 5 | 6 | The names and repositories will change as alpha test versions are developed. 7 | 8 | **Current exchange** is as follows. 9 | 1. Bittrex / UPbit 10 | 2. Bitfinex 11 | 3. Poloniex 12 | 4. Coinone 13 | 14 | 15 | ## dependency 16 | - https://github.com/influxdata/influxdb/client/v2 17 | - https://github.com/ironpark/go-poloniex 18 | - https://github.com/toorop/go-bittrex 19 | - https://github.com/gin-gonic/gin 20 | - https://github.com/asaskevich/EventBus 21 | - https://github.com/sirupsen/logrus 22 | 23 | 24 | ## donate 25 | Ethereum Address: 26 | 27 | ![ethereum address](https://chart.googleapis.com/chart?cht=qr&chl=%200x7EA84eFF0f9D3bd2EaD6Db190A4387B71ac42b44&chs=300x300&choe=UTF-8&chld=L|2) 28 | 29 | 0x7EA84eFF0f9D3bd2EaD6Db190A4387B71ac42b44 30 | 31 | **Attention!** Do not send Ethereum Classic! 32 | 33 | ## Roadmap 34 | Goals for **CoinEX** 35 | 36 | - [ ] TravisCI support 37 | 38 | - [ ] Simulation support for algorithmic trading 39 | - [x] Storing and managing real-time price data (OHCL) using a time series database (influxDB) 40 | - [ ] Plug-in system for custom trading strategy 41 | - [ ] Plug-in system for trading indicators ex) SMA, EMA, ... 42 | - [ ] Trading simulation using historical data and report support for performance evaluation of trading strategy 43 | - [ ] Scripting language support for plug-in ecosystem (python / js) 44 | 45 | - Support visualization based on web technology 46 | 47 | ## References 48 | - apis 49 | - [poloniex](https://poloniex.com/support/api/) 50 | - [bittrex](https://bittrex.com/Home/Api) 51 | - [coinone](http://doc.coinone.co.kr) 52 | - codes 53 | - [go-poloniex](https://github.com/jyap808/go-poloniex) 54 | 55 | ## License 56 | [MPL 2.0 (Mozilla Public License Version 2.0)](https://www.mozilla.org/en-US/MPL/2.0/) -------------------------------------------------------------------------------- /strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "os/exec" 5 | 6 | "github.com/hashicorp/go-plugin" 7 | "github.com/ironpark/coinex/strategy/shared" 8 | "github.com/ironpark/coinex/strategy/proto" 9 | ) 10 | type Strategy struct { 11 | rpc *plugin.Client 12 | strategy shared.Strategy 13 | } 14 | 15 | func LoadStrategy(path string) (*Strategy){ 16 | //We're a host. Start by launching the plugin process. 17 | client := plugin.NewClient(&plugin.ClientConfig{ 18 | HandshakeConfig: shared.Handshake, 19 | Plugins: shared.PluginMap, 20 | Cmd: exec.Command("sh", "-c", path), //file path (binary or script) 21 | AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, 22 | }) 23 | 24 | rpcClient, _ := client.Client() 25 | raw ,_:= rpcClient.Dispense("strategy") 26 | st := raw.(shared.Strategy) 27 | return &Strategy{client,st} 28 | } 29 | 30 | func LoadStrategys(path string) (*Strategy){ 31 | //We're a host. Start by launching the plugin process. 32 | client := plugin.NewClient(&plugin.ClientConfig{ 33 | HandshakeConfig: shared.Handshake, 34 | Plugins: shared.PluginMap, 35 | Cmd: exec.Command("sh", "-c", path), //file path (binary or script) 36 | AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, 37 | }) 38 | 39 | rpcClient, _ := client.Client() 40 | raw ,_:= rpcClient.Dispense("strategy") 41 | st := raw.(shared.Strategy) 42 | return &Strategy{client,st} 43 | } 44 | 45 | func (st *Strategy) Init() { 46 | st.strategy.Init() 47 | } 48 | 49 | func (st *Strategy) Info() proto.Information { 50 | return st.strategy.Info() 51 | } 52 | 53 | func (st *Strategy) GetProperty() map[string]interface{} { 54 | return st.strategy.GetProperty() 55 | } 56 | 57 | func (st *Strategy) SetProperty(property map[string]interface{}) { 58 | st.strategy.SetProperty(property) 59 | } 60 | 61 | func (st *Strategy) SellConditions(name string) bool { 62 | return st.strategy.SellConditions(name) 63 | } 64 | 65 | func (st *Strategy) BuyConditions(name string) bool { 66 | return st.strategy.BuyConditions(name) 67 | } 68 | 69 | func (st *Strategy) RankFilter(name string) bool { 70 | return st.strategy.RankFilter(name) 71 | } 72 | func (st *Strategy) KillProcess() { 73 | st.rpc.Kill() 74 | } -------------------------------------------------------------------------------- /cmd/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "encoding/json" 6 | "io/ioutil" 7 | "time" 8 | "strings" 9 | ) 10 | 11 | type API_KEY struct { 12 | Key string 13 | Secret string 14 | Ex string 15 | } 16 | 17 | type Asset struct{ 18 | Ex string 19 | Base string 20 | Pair string 21 | Start int64 22 | } 23 | 24 | type Configuration struct { 25 | Bucket struct { 26 | Assets []Asset 27 | } 28 | 29 | Keys []API_KEY 30 | 31 | HashedKey string 32 | 33 | WebServer struct { 34 | Port int32 35 | Address string 36 | } 37 | } 38 | 39 | func Config() *Configuration{ 40 | file, _ := os.OpenFile("config.json",os.O_CREATE|os.O_RDWR,os.FileMode(0644)) 41 | configuration := Configuration{} 42 | data,_ := ioutil.ReadAll(file) 43 | if len(data) == 0 { 44 | jsonfile, _ := json.Marshal(configuration) 45 | file.Write(jsonfile) 46 | }else { 47 | json.Unmarshal(data, &configuration) 48 | } 49 | file.Close() 50 | return &configuration 51 | } 52 | 53 | func (conf *Configuration)AddTarget(ex,base,pair string,start time.Time){ 54 | if conf.Bucket.Assets == nil { 55 | conf.Bucket.Assets = []Asset{} 56 | } 57 | 58 | for _,target := range conf.Bucket.Assets { 59 | if target.Ex == ex && 60 | target.Base == base && 61 | target.Pair == pair { 62 | target.Start = start.UTC().Unix() 63 | conf.Save() 64 | return 65 | } 66 | } 67 | 68 | conf.Bucket.Assets = append(conf.Bucket.Assets, 69 | Asset{ 70 | Ex:ex, 71 | Base:base, 72 | Pair:pair, 73 | Start:start.UTC().Unix(), 74 | }) 75 | 76 | conf.Save() 77 | } 78 | 79 | func (conf *Configuration)RemoveTarget(ex,base,pair string,start time.Time){ 80 | for i,target := range conf.Bucket.Assets { 81 | if target.Ex == ex && 82 | target.Base == base && 83 | target.Pair == pair { 84 | conf.Bucket.Assets = append(conf.Bucket.Assets [:i], conf.Bucket.Assets [i+1:]...) 85 | return 86 | } 87 | } 88 | } 89 | 90 | func (conf *Configuration)Save() { 91 | file, _ := os.OpenFile("config.json",os.O_CREATE|os.O_RDWR|os.O_TRUNC,os.FileMode(0644)) 92 | jsonf, _ := json.Marshal(conf) 93 | //formatting 94 | str := strings.Replace(string(jsonf),",",",\n",-1) 95 | str = strings.Replace(str,"{","\n{\n",-1) 96 | str = strings.Replace(str,"[","\n[\n",-1) 97 | str = strings.Replace(str,"]","\n]\n",-1) 98 | str = strings.Replace(str,"}","\n}\n",-1) 99 | splits := strings.Split(str,"\n") 100 | count := 0 101 | final := "" 102 | for i,item := range splits { 103 | if item == "" || item == "," { 104 | continue 105 | } 106 | 107 | if strings.Contains(item,"}") || strings.Contains(item,"]") { 108 | count -- 109 | } 110 | for i:=0;i= '%s'",end_str,start_str) 54 | query.p_WHERE = append(query.p_WHERE ,q) 55 | return query 56 | } 57 | //GROUP BY 58 | func (query *queryBuilder)GroupByTime(filed string) *queryBuilder { 59 | query.p_GROUP_BY = "time("+filed+")" 60 | return query 61 | } 62 | //GROUP BY time(12m) 63 | 64 | //ORDER BY time ASC 65 | func (query *queryBuilder)ASC(filed string) *queryBuilder { 66 | query.p_ORDER_BY = filed+" ASC" 67 | return query 68 | } 69 | 70 | func (query *queryBuilder)DESC(filed string) *queryBuilder { 71 | query.p_ORDER_BY = filed+" DESC" 72 | return query 73 | } 74 | func (query *queryBuilder)Limit(limit int64) *queryBuilder { 75 | query.p_LIMIT = limit 76 | return query 77 | } 78 | 79 | //linear 80 | func (query *queryBuilder)Build() string{ 81 | final_query := "" 82 | //SELECT 83 | switch len(query.p_SELECT_FIELD) { 84 | case 0: 85 | final_query += "SELECT *" 86 | case 1: 87 | final_query += "SELECT "+ query.p_SELECT_FIELD[0] 88 | default: 89 | final_query += "SELECT " 90 | for i:=0;i< len(query.p_SELECT_FIELD);i++ { 91 | final_query += query.p_SELECT_FIELD[i] 92 | if i+1 != len(query.p_SELECT_FIELD) { 93 | final_query += "," 94 | } 95 | } 96 | } 97 | //FROM 98 | final_query += " FROM " + query.p_FROM +" " 99 | 100 | //WHERE 101 | switch len(query.p_WHERE) { 102 | case 0: 103 | 104 | case 1: 105 | final_query += "WHERE "+ query.p_WHERE[0] +" " 106 | default: 107 | final_query += "WHERE " 108 | for i:=0;i< len(query.p_WHERE);i++ { 109 | final_query += query.p_WHERE[i] 110 | if i+1 != len(query.p_WHERE) { 111 | final_query += " AND " 112 | }else{ 113 | final_query += " " 114 | } 115 | } 116 | } 117 | 118 | if query.p_GROUP_BY != "" { 119 | final_query += "GROUP BY " + query.p_GROUP_BY + " " 120 | } 121 | final_query += "Fill(none) " 122 | //ORDER BY 123 | if query.p_ORDER_BY != "" { 124 | final_query += "ORDER BY " + query.p_ORDER_BY + " " 125 | } 126 | 127 | //LIMIT 128 | if query.p_LIMIT != -1 { 129 | q := fmt.Sprintf("LIMIT %d", query.p_LIMIT) 130 | final_query += q 131 | } 132 | return final_query 133 | } 134 | -------------------------------------------------------------------------------- /bucket/bucket.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | import ( 4 | //log "github.com/sirupsen/logrus" 5 | cdb "github.com/ironpark/coinex/db" 6 | "github.com/asaskevich/EventBus" 7 | "sync" 8 | //"time" 9 | "github.com/ironpark/coinex/db" 10 | //"fmt" 11 | "github.com/ironpark/coinex/bucket/source" 12 | ) 13 | const ( 14 | /*support exchanges*/ 15 | EXCHANGE_POLONIEX = "poloniex" 16 | EXCHANGE_BITTREX = "bittrex" 17 | EXCHANGE_UPBIT = "upbit" 18 | EXCHANGE_BITFINEX = "bitfinex" 19 | 20 | /*subscribe topic list*/ 21 | //This topic is called when data comes in 22 | TOPIC_DATA = "bucket:data" 23 | //This topic is called when db is updated. 24 | TOPIC_UPDATE = "bucket:update" 25 | TOPIC_TICKER = "bucket:ticker" 26 | TOPIC_STATUS = "bucket:status" 27 | 28 | ) 29 | type UpdateEvent func(market string,pair cdb.Pair,data []cdb.MarketTake) 30 | type TickerUpdateEvent func() 31 | type StatusUpdateEvent func(market string,pair cdb.Pair,status AssetStatus) 32 | 33 | type AssetStatus struct { 34 | First int64 35 | Last int64 36 | IsStop bool 37 | } 38 | 39 | type bucket struct { 40 | events EventBus.Bus 41 | db *cdb.CoinDB 42 | workers map[string]*Worker 43 | } 44 | var instance *bucket 45 | 46 | //singleton pattern 47 | var once sync.Once 48 | 49 | func Instance() *bucket { 50 | once.Do(func() { 51 | bus := EventBus.New() 52 | db:= cdb.Default() 53 | 54 | instance = &bucket{ 55 | bus, 56 | db, 57 | make(map[string]*Worker), 58 | } 59 | 60 | 61 | instance.workers[EXCHANGE_UPBIT] = NewWorker(bus, source.NewUpbit()) 62 | //instance.workers = append(instance.workers,NewWorker(bus, source.NewPoloniex())) 63 | //instance.workers = append(instance.workers,NewWorker(bus, source.NewUpbit())) 64 | //instance.workers = append(instance.workers,NewWorker(bus, source.NewBitfinex())) 65 | 66 | bus.SubscribeAsync(TOPIC_DATA,instance.dataEvent,false) 67 | }) 68 | return instance 69 | } 70 | 71 | func (bk *bucket)Status() map[string]map[cdb.Pair]string{ 72 | return nil 73 | } 74 | 75 | func (bk *bucket)dataEvent(market string,pair cdb.Pair,data []cdb.MarketTake) { 76 | 77 | } 78 | 79 | func (bk *bucket)unsubscribe(topic string,event interface{}){ 80 | bk.events.Unsubscribe(topic,event) 81 | } 82 | func (bk *bucket)subscribe(topic string,event interface{}){ 83 | bk.events.SubscribeAsync(topic,event,false) 84 | } 85 | 86 | //Subscribe 87 | func (bk *bucket)SubTickerUpdate(pair db.Pair,fu TickerUpdateEvent){ 88 | bk.subscribe(TOPIC_TICKER+":"+pair.ToString(),fu) 89 | } 90 | func (bk *bucket)SubStatusUpdate(fu StatusUpdateEvent){ 91 | bk.subscribe(TOPIC_STATUS,fu) 92 | } 93 | func (bk *bucket)SubDBUpdate(fu UpdateEvent){ 94 | bk.subscribe(TOPIC_UPDATE,fu) 95 | } 96 | //UnSubscribe 97 | func (bk *bucket)UnSubTickerUpdate(pair db.Pair,fu TickerUpdateEvent){ 98 | bk.unsubscribe(TOPIC_TICKER+":"+pair.ToString(),fu) 99 | } 100 | func (bk *bucket)UnSubStatusUpdate(fu StatusUpdateEvent){ 101 | bk.unsubscribe(TOPIC_STATUS,fu) 102 | } 103 | func (bk *bucket)UnSubDBUpdate(fu UpdateEvent){ 104 | bk.unsubscribe(TOPIC_UPDATE,fu) 105 | } 106 | 107 | func (bk *bucket)AddToTrack(market string,pair cdb.Pair) { 108 | bk.workers[market].Add(pair) 109 | } 110 | 111 | func (bk *bucket)Close() { 112 | bk.events.Unsubscribe(TOPIC_DATA,instance.dataEvent) 113 | } 114 | 115 | func (bk *bucket)Run() { 116 | //start All workers 117 | for _,e := range bk.workers{ 118 | e.Run() 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /bucket/source/upbit.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "github.com/ironpark/coinex/db" 5 | "fmt" 6 | "math/rand" 7 | "net/http" 8 | "io/ioutil" 9 | "encoding/json" 10 | "time" 11 | "sync" 12 | "strings" 13 | ) 14 | 15 | type upbitOHLC struct { 16 | Code string `json:"code"` 17 | Utc string `json:"candleDateTime"` 18 | Kst string `json:"candleDateTimeKst"` 19 | O float64 `json:"openingPrice"` 20 | H float64 `json:"highPrice"` 21 | L float64 `json:"lowPrice"` 22 | C float64 `json:"tradePrice"` 23 | QV float64 `json:"candleAccTradeVolume"` 24 | BV float64 `json:"candleAccTradePrice"` 25 | } 26 | 27 | type Upbit struct { 28 | httpClient *http.Client 29 | once sync.Once 30 | marketCreated map[db.Pair]time.Time 31 | } 32 | 33 | func NewUpbit() *Bittrex { 34 | return &Bittrex{ 35 | &http.Client{}, 36 | sync.Once{}, 37 | make(map[db.Pair]time.Time), 38 | } 39 | } 40 | 41 | // https://bittrex.com/api/v1.1/public/getmarketsummaries 42 | func (wk *Upbit)MarketCreated(pair db.Pair) time.Time { 43 | wk.once.Do(func() { 44 | resp,_ := (&http.Client{}).Get("https://bittrex.com/api/v1.1/public/getmarketsummaries") 45 | body,_ := ioutil.ReadAll(resp.Body) 46 | 47 | js := make(map[string]interface{}) 48 | json.Unmarshal(body,&js) 49 | 50 | for _,e:=range js["result"].([]interface{}) { 51 | name := e.(map[string]interface{})["MarketName"].(string) 52 | created := e.(map[string]interface{})["Created"].(string) 53 | split := strings.Split(name,"-") 54 | base := split[0] 55 | quote := split[2] 56 | 57 | t,_ := time.Parse("2006-01-02T15:04:05",created) 58 | wk.marketCreated[db.Pair{quote,base}] = t 59 | } 60 | }) 61 | 62 | return wk.marketCreated[pair] 63 | } 64 | 65 | func (wk *Upbit)Status() int { 66 | return 200 67 | } 68 | 69 | //1 minute 70 | func (wk *Upbit)Interval() int { 71 | return 1 72 | } 73 | 74 | func (wk *Upbit)BackData(pair db.Pair,date time.Time) ([]db.OHLC,bool) { 75 | data := wk.getData(pair, date.UTC(),1000) 76 | if len(data) < 1000{ 77 | return data,true 78 | } 79 | return data,false 80 | } 81 | 82 | func (wk *Upbit)RealTime(pair db.Pair) []db.OHLC { 83 | return wk.getData(pair, time.Now().UTC(), 2) 84 | } 85 | 86 | func (wk *Upbit)Name() string { 87 | return "bittrex" 88 | } 89 | 90 | func (wk *Upbit)getData(pair db.Pair,date time.Time,limit int) []db.OHLC { 91 | return wk.get(pair,1,date.Format("2006-01-02T15:04:05.000Z"),limit) 92 | } 93 | 94 | func (wk *Upbit)get(pair db.Pair,minute int,date string,limit int) []db.OHLC { 95 | //"BTC-ETH" 96 | url := fmt.Sprintf("https://crix-api.upbit.com/v1/crix/candles/minutes/%d?code=CRIX.UPBIT.%s&count=%d&to=%s&ciqrandom=%d", 97 | minute, fmt.Sprintf("%s-%s",pair.Base,pair.Quote), limit, "2017-10-28T16:27:00.000Z", rand.Int()) 98 | 99 | req ,err := http.NewRequest(http.MethodGet,url,nil) 100 | 101 | if err != nil{ 102 | return nil 103 | } 104 | 105 | req.Header.Set("Host","crix-api.upbit.com") 106 | req.Header.Set("Origin","https://upbit.com") 107 | req.Header.Set("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36") 108 | 109 | resp,err := wk.httpClient.Do(req) 110 | defer resp.Body.Close() 111 | if err != nil{ 112 | return nil 113 | } 114 | 115 | body, err := ioutil.ReadAll(resp.Body) 116 | if err != nil{ 117 | return nil 118 | } 119 | 120 | var ohcls []bittrexOHLC 121 | if json.Unmarshal(body,ohcls) != nil{ 122 | return nil 123 | } 124 | 125 | ohclContainer := make([]db.OHLC, len(ohcls)) 126 | 127 | for i:=0;i 60*10 { 61 | nType = "BackFills" 62 | } 63 | 64 | listener <- sse.Event{ 65 | Id: "", 66 | Event: "message", 67 | Data: map[string]interface{}{ 68 | "Exchange": market, 69 | "Base": pair.Base, 70 | "Quote": pair.Quote, 71 | "Stop":status.IsStop, 72 | "First": status.First, 73 | "Last":status.Last, 74 | "Type":nType, 75 | }, 76 | } 77 | } 78 | buck.SubStatusUpdate(sub) 79 | c.Stream(func(w io.Writer) bool { 80 | //TODO First Data 81 | c.SSEvent("message", <-listener) 82 | return true 83 | }) 84 | buck.UnSubStatusUpdate(sub) 85 | }) 86 | //Server-Sent-Event for ticker data 87 | //v1.Handle(http.MethodGet, "/sse/ticker/:ex/:pair/:res", func(c *gin.Context) { 88 | // ex := c.Param("name") 89 | // pair := c.Param("pair") 90 | // //res := c.Param("res") 91 | // 92 | // listener := make(chan sse.Event) 93 | // id := buck.AddEventListener(ex, func(Ex, Pair, Type string, ListenerID int64) { 94 | // listener <- sse.Event{ 95 | // Id: "124", 96 | // Event: "message", 97 | // Data: map[string]interface{}{ 98 | // Ex: ex, 99 | // Type: Type, 100 | // Pair: Pair, 101 | // }, 102 | // } 103 | // },pair) 104 | // 105 | // c.Stream(func(w io.Writer) bool { 106 | // //TODO First Data 107 | // c.SSEvent("message", <-listener) 108 | // buck.RemoveEventListener(ex,id) 109 | // return true 110 | // }) 111 | //}) 112 | 113 | 114 | ////add asset tracking 115 | v1.POST("/bucket", func(c *gin.Context) { 116 | ex := c.PostForm("ex") 117 | base := c.PostForm("base") 118 | pair := c.PostForm("pair") 119 | //start := c.PostForm("start") 120 | //unix,_ := strconv.ParseInt(start,0,64) 121 | buck.AddToTrack(ex,db.Pair{Quote:base,Base:pair}) 122 | }) 123 | 124 | //get tracking assets 125 | v1.GET("/bucket", func(c *gin.Context) { 126 | c.JSON(http.StatusOK,buck.Status()) 127 | }) 128 | 129 | //update tracked assets setting 130 | v1.PUT("/bucket/assets/:id", func(c *gin.Context) { 131 | 132 | }) 133 | 134 | //remove tracked assets 135 | v1.DELETE("/bucket/assets/:id", func(c *gin.Context) { 136 | 137 | }) 138 | } 139 | router.Run() 140 | } 141 | -------------------------------------------------------------------------------- /bucket/worker.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | 4 | import ( 5 | "github.com/asaskevich/EventBus" 6 | coindb "github.com/ironpark/coinex/db" 7 | "github.com/google/go-cmp/cmp" 8 | "github.com/ironpark/coinex/bucket/source" 9 | "time" 10 | "os" 11 | "encoding/gob" 12 | "fmt" 13 | ) 14 | 15 | type Status struct{ 16 | First time.Time 17 | Last time.Time 18 | hasfirstTick bool 19 | } 20 | 21 | // Encode via Gob to file 22 | func saveGob(path string, object interface{}) error { 23 | file, err := os.Create(path) 24 | if err == nil { 25 | encoder := gob.NewEncoder(file) 26 | encoder.Encode(object) 27 | } 28 | file.Close() 29 | return err 30 | } 31 | 32 | // Decode Gob file 33 | func loadGob(path string, object interface{}) error { 34 | file, err := os.Open(path) 35 | if err == nil { 36 | decoder := gob.NewDecoder(file) 37 | err = decoder.Decode(object) 38 | } 39 | file.Close() 40 | return err 41 | } 42 | 43 | type Worker struct { 44 | pairs []coindb.Pair 45 | // fisrt & last time data in local database 46 | status map[coindb.Pair]Status 47 | // is have the last historical data? 48 | inLast map[coindb.Pair]bool 49 | 50 | bus EventBus.Bus 51 | add chan coindb.Pair 52 | stop chan struct{} 53 | src source.DataSource 54 | db *coindb.CoinDB 55 | } 56 | 57 | func NewWorker(bus EventBus.Bus,src source.DataSource) *Worker{ 58 | db := coindb.Default() 59 | 60 | worker := &Worker{ 61 | pairs:db.GetCurrencyPairs(src.Name()), 62 | status:make(map[coindb.Pair]Status), 63 | bus:bus, 64 | add:make(chan coindb.Pair), 65 | stop:make(chan struct{}), 66 | src:src, 67 | db:db, 68 | } 69 | 70 | if worker.pairs == nil{ 71 | worker.pairs = []coindb.Pair{} 72 | } 73 | 74 | for _,p := range worker.pairs { 75 | //get last/first update time 76 | worker.status[p] = Status{worker.db.GetLastDate(src.Name(), p),worker.db.GetFirstDate(worker.src.Name(), p),false} 77 | } 78 | 79 | var lastdata map[coindb.Pair]bool 80 | path := fmt.Sprintf("./%s",src.Name()) 81 | if loadGob(path,lastdata) != nil { 82 | for _,p := range worker.pairs { 83 | lastdata[p] = false 84 | } 85 | saveGob(path,lastdata) 86 | } 87 | 88 | worker.inLast = lastdata 89 | return worker 90 | } 91 | 92 | func (w *Worker)Run(){ 93 | path := fmt.Sprintf("./%s",w.src.Name()) 94 | defer func(){ 95 | close(w.add) 96 | }() 97 | for { 98 | select { 99 | default: 100 | for _,p := range w.pairs { 101 | //step : historical data 102 | status := w.status[p] 103 | if !w.inLast[p] { 104 | data,ok := w.src.BackData(p,status.First) 105 | status.First = data[len(data)-1].Time 106 | //update 107 | 108 | if ok { 109 | w.inLast[p] = true 110 | saveGob(path,w.inLast) 111 | } 112 | 113 | //insert database 114 | w.db.PutOHLCs(w.src.Name(),p,data...) 115 | w.status[p] = status 116 | }else{ 117 | //step : synchronization data 118 | if time.Now().UTC().Unix() - status.Last.Unix() >= 120 { 119 | start := time.Now().UTC() 120 | 121 | for { 122 | data,_ := w.src.BackData(p,start) 123 | lastDataTime := data[len(data)-1].Time.Unix() 124 | status.Last = data[0].Time 125 | w.db.PutOHLCs(w.src.Name(),p,data...) 126 | w.status[p] = status 127 | 128 | if lastDataTime < w.status[p].Last.Unix(){ 129 | break 130 | } 131 | } 132 | 133 | 134 | }else{ 135 | //step : realtime data 136 | //TODO check the duplicated data 137 | data := w.src.RealTime(p) 138 | status.First = data[len(data)-1].Time 139 | 140 | w.db.PutOHLCs(w.src.Name(),p,data...) 141 | w.status[p] = status 142 | //TODO check point (really work?) 143 | w.bus.Publish(TOPIC_DATA, w.src.Name(), p, data) 144 | } 145 | } 146 | 147 | } 148 | //Add objects to track 149 | case pair := <-w.add: 150 | for p:= range w.pairs { 151 | if cmp.Equal(p,pair) { 152 | continue 153 | } 154 | } 155 | w.pairs = append(w.pairs,pair) 156 | case <-w.stop: 157 | // stop 158 | return 159 | } 160 | } 161 | } 162 | 163 | func (w *Worker)Add(pair coindb.Pair){ 164 | w.add <- pair 165 | } 166 | 167 | func (w *Worker)Stop(){ 168 | close(w.stop) 169 | } -------------------------------------------------------------------------------- /strategy/shared/grpc.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "github.com/ironpark/coinex/strategy/proto" 5 | "golang.org/x/net/context" 6 | ) 7 | 8 | // GRPCClient is an implementation of Strategy that talks over RPC. 9 | type GRPCClient struct{ client proto.StrategyClient } 10 | 11 | func (m *GRPCClient) Init() { 12 | m.client.Init(context.Background(), &proto.Empty{}) 13 | } 14 | func (m *GRPCClient) Info() (proto.Information) { 15 | resp, _ := m.client.Info(context.Background(), &proto.Empty{}) 16 | return *resp 17 | } 18 | 19 | func (m *GRPCClient) GetProperty() (map[string]interface{}) { 20 | property, _ := m.client.GetProperty(context.Background(), &proto.Empty{}) 21 | property_map := make(map[string]interface{}) 22 | for k,v := range property.CustomInt{ 23 | switch v.Type { 24 | case "int": 25 | property_map[k] = v.ValueInt 26 | case "float": 27 | property_map[k] = v.ValueFloat 28 | case "string": 29 | property_map[k] = v.ValueString 30 | case "bool": 31 | property_map[k] = v.ValueBool 32 | } 33 | } 34 | return property_map 35 | } 36 | 37 | func (m *GRPCClient) SetProperty(property map[string]interface{}) { 38 | dic := proto.Dictionary{ 39 | make(map[string]*proto.Property), 40 | } 41 | for k,value := range property{ 42 | switch v := value.(type) { 43 | case int32: 44 | dic.CustomInt[k] = &proto.Property{ValueInt:int32(v)} 45 | case float32: 46 | dic.CustomInt[k] = &proto.Property{ValueFloat:float32(v)} 47 | case string: 48 | dic.CustomInt[k] = &proto.Property{ValueString:string(v)} 49 | case bool: 50 | dic.CustomInt[k] = &proto.Property{ValueBool:bool(v)} 51 | } 52 | } 53 | m.client.SetProperty(context.Background(), &dic) 54 | } 55 | 56 | func (m *GRPCClient) SellConditions(asset string) bool { 57 | resp, _ := m.client.SellConditions(context.Background(), &proto.Asset{Name:asset}) 58 | return resp.Boolean 59 | } 60 | func (m *GRPCClient) BuyConditions(asset string) bool { 61 | resp, _ := m.client.BuyConditions(context.Background(), &proto.Asset{Name:asset}) 62 | return resp.Boolean 63 | } 64 | func (m *GRPCClient) RankFilter(asset string) bool { 65 | resp, _ := m.client.RankFilter(context.Background(), &proto.Asset{Name:asset}) 66 | return resp.Boolean 67 | } 68 | 69 | 70 | // Here is the gRPC server that GRPCClient talks to. 71 | type GRPCServer struct { 72 | // This is the real implementation 73 | Impl Strategy 74 | } 75 | 76 | 77 | func (m *GRPCServer) Init(context.Context, *proto.Empty) (*proto.Empty, error) { 78 | m.Impl.Init() 79 | return &proto.Empty{},nil 80 | } 81 | 82 | func (m *GRPCServer) Info(context.Context, *proto.Empty) (*proto.Information, error){ 83 | information := m.Impl.Info() 84 | return &information,nil 85 | } 86 | 87 | func (m *GRPCServer) GetProperty(context.Context, *proto.Empty) (*proto.Dictionary, error) { 88 | property := m.Impl.GetProperty() 89 | dic := proto.Dictionary{ 90 | make(map[string]*proto.Property), 91 | } 92 | for k,value := range property{ 93 | switch v := value.(type) { 94 | case int32: 95 | dic.CustomInt[k] = &proto.Property{ValueInt:int32(v)} 96 | case float32: 97 | dic.CustomInt[k] = &proto.Property{ValueFloat:float32(v)} 98 | case string: 99 | dic.CustomInt[k] = &proto.Property{ValueString:string(v)} 100 | case bool: 101 | dic.CustomInt[k] = &proto.Property{ValueBool:bool(v)} 102 | } 103 | } 104 | return &dic,nil 105 | } 106 | 107 | func (m *GRPCServer) SetProperty(context context.Context, property *proto.Dictionary ) (*proto.Empty, error) { 108 | property_map := make(map[string]interface{}) 109 | for k,v := range property.CustomInt{ 110 | switch v.Type { 111 | case "int": 112 | property_map[k] = v.ValueInt 113 | case "float": 114 | property_map[k] = v.ValueFloat 115 | case "string": 116 | property_map[k] = v.ValueString 117 | case "bool": 118 | property_map[k] = v.ValueBool 119 | } 120 | } 121 | m.Impl.SetProperty(property_map) 122 | return &proto.Empty{},nil 123 | } 124 | 125 | func (m *GRPCServer) SellConditions(ctx context.Context, asset *proto.Asset) (*proto.Bool, error) { 126 | return &proto.Bool{Boolean:m.Impl.SellConditions(asset.Name)},nil 127 | } 128 | 129 | func (m *GRPCServer) BuyConditions(ctx context.Context,asset *proto.Asset) (*proto.Bool, error) { 130 | return &proto.Bool{Boolean:m.Impl.BuyConditions(asset.Name)},nil 131 | } 132 | 133 | func (m *GRPCServer) RankFilter(ctx context.Context,asset *proto.Asset) (*proto.Bool, error) { 134 | return &proto.Bool{Boolean:m.Impl.RankFilter(asset.Name)},nil 135 | } 136 | 137 | -------------------------------------------------------------------------------- /manager.go: -------------------------------------------------------------------------------- 1 | package main 2 | // 3 | //import ( 4 | // 5 | // "github.com/ironpark/coinex/db" 6 | // tr "github.com/ironpark/coinex/trader" 7 | // "log" 8 | // "time" 9 | // "github.com/ironpark/coinex/web" 10 | // "net/http" 11 | // "io/ioutil" 12 | // "runtime" 13 | // "path" 14 | // "fmt" 15 | // "github.com/ironpark/go-poloniex" 16 | //) 17 | // 18 | // 19 | //type Manager struct{ 20 | // sse *web.Broker 21 | // traders map[string]map[string][]tr.Trader 22 | // db *db.CoinDB 23 | //} 24 | // 25 | //func NewManager()(*Manager){ 26 | // traders := map[string]map[string][]tr.Trader{} 27 | // dbClient ,_:= db.Default() 28 | // 29 | // return &Manager{web.NewSSEServer(),traders,dbClient} 30 | //} 31 | // 32 | //func (ma *Manager) AddTrader(trader tr.Trader) { 33 | // exname := trader.Exchange() 34 | // pair := trader.Pair() 35 | // log.Println(exname,pair) 36 | // if ma.traders[exname] == nil{ 37 | // ma.traders[exname] = map[string][]tr.Trader{} 38 | // } 39 | // ma.traders[exname][pair] = append(ma.traders[exname][pair],trader) 40 | //} 41 | // 42 | //func (ma *Manager) insertTradeData(pair string,ex string,data []tr.TradeData) { 43 | // bp,_ := ma.db.NewBatchPoints() 44 | // for _,d := range data { 45 | // point ,_:= ma.db.NewPoint( 46 | // db.Tags{ 47 | // "cryptocurrency": pair, 48 | // "ex": ex, 49 | // "type": d.Type, 50 | // }, 51 | // db.Fields{ 52 | // "TradeID": d.ID, 53 | // "Amount": d.Amount, 54 | // "Rate": d.Price, 55 | // "Total": d.Total, 56 | // }, d.Date) 57 | // bp.AddPoint(point) 58 | // //fmt.Println(d.Date) 59 | // } 60 | // err := ma.db.Write(bp) 61 | // if err != nil { 62 | // log.Fatal(err) 63 | // } 64 | //} 65 | // 66 | //func (ma *Manager) Start(port int64){ 67 | // //for poloniex 68 | // go func() { 69 | // poloPairs := []string{} 70 | // p := poloniex.NewTrader("","","") 71 | // now := time.Now() 72 | // log.Println("poloniex trader lists ...") 73 | // //LastTradeHistory 74 | // for pair := range ma.traders["poloniex"] { 75 | // last,err := ma.db.LastTradeHistory(pair); 76 | // if err != nil { 77 | // fmt.Println("4day") 78 | // last = last.Add(-time.Hour*24*4) 79 | // } 80 | // poloPairs = append(poloPairs, pair) 81 | // his := p.TradeHistory(pair, last, now) 82 | // if his != nil { 83 | // ma.insertTradeData(pair, "poloniex", his) 84 | // } 85 | // } 86 | // 87 | // for { 88 | // time.Sleep(time.Second*1) 89 | // for _,pair := range poloPairs{ 90 | // now := time.Now() 91 | // bef := now.Add(-time.Minute) 92 | // hds := p.TradeHistory(pair, bef, now) 93 | // if len(hds) == 0{ 94 | // time.Sleep(time.Second*1) 95 | // continue; 96 | // } 97 | // //fmt.Println(hds[len(hds)-1].Date.Local()) 98 | // ma.insertTradeData(pair, "poloniex", hds) 99 | // //DB Data 100 | // before := time.Now().Add(-time.Hour*24) 101 | // before = time.Date(before.Year(),before.Month(),before.Day(),0,0,0,0,time.UTC) 102 | // 103 | // for _,trader := range ma.traders["poloniex"][pair] { 104 | // trader.Call(trader,hds[len(hds)-1]) 105 | // } 106 | // 107 | // hd,_ := ma.db.TradeHistory(pair,"poloniex",before,time.Now().Add(time.Hour*24),2000,"5m") 108 | // ma.sse.Notifier <- []byte(ma.historyToJson(hd)) 109 | // 110 | // } 111 | // } 112 | // 113 | // }() 114 | // log.Println("start web server (server send event)") 115 | // ma.webServerStart(port) 116 | //} 117 | // 118 | //func (ma *Manager) historyToJson(hd tr.TikerData) string { 119 | // dates := hd.Time() 120 | // closes := hd.Close() 121 | // opens := hd.Open() 122 | // highs := hd.High() 123 | // lows := hd.Low() 124 | // volumes := hd.Volume() 125 | // //fmt.Println(opens) 126 | // finaljson := "[" 127 | // for i := range dates { 128 | // finaljson += fmt.Sprintf( 129 | // "{\"date\":%d,\"open\":%.9f,\"high\":%.9f,\"low\":%.9f,\"close\":%.9f,\"volume\":%.9f}", 130 | // dates[i],opens[i],highs[i],lows[i],closes[i],volumes[i]) 131 | // if len(volumes) -1 != i { 132 | // finaljson += "," 133 | // } 134 | // } 135 | // finaljson += "]" 136 | // return finaljson 137 | //} 138 | // 139 | //func (ma *Manager) webServerStart(port int64){ 140 | // http.HandleFunc("/", func(w http.ResponseWriter,req *http.Request) { 141 | // file, err := ioutil.ReadFile(packagePath()+"/web/index.html") 142 | // w.Header().Set("Content-Type","text/html; charset=utf-8") 143 | // w.Write(file) 144 | // 145 | // if err != nil { 146 | // w.WriteHeader(http.StatusNotFound) 147 | // } 148 | // }) 149 | // 150 | // http.HandleFunc("/sse",ma.sse.ServeHTTP) 151 | // http.ListenAndServe("localhost:"+fmt.Sprintf("%d",port),nil) 152 | //} 153 | // 154 | //func packagePath() string{ 155 | // //package path 156 | // _, filename, _, ok := runtime.Caller(0) 157 | // if !ok { 158 | // panic("No caller information") 159 | // } 160 | // dir := path.Dir(filename) 161 | // return dir 162 | //} -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "time" 5 | "github.com/influxdata/influxdb/client/v2" 6 | "github.com/influxdata/influxdb/models" 7 | log "github.com/sirupsen/logrus" 8 | "fmt" 9 | "strings" 10 | "encoding/json" 11 | ) 12 | 13 | type Config struct{ 14 | Host string 15 | UserName string 16 | Password string 17 | } 18 | 19 | type CoinDB struct { 20 | config Config 21 | client client.Client 22 | status int64 23 | } 24 | 25 | type Pair struct { 26 | Quote string 27 | Base string 28 | } 29 | 30 | func (pair Pair) ToString() string { 31 | return pair.Base+"/"+pair.Quote 32 | } 33 | func (pair Pair) MarshalText() ([]byte, error) { 34 | return []byte(pair.ToString()), nil 35 | } 36 | type MarketTake struct { 37 | TradeID int 38 | Amount float64 39 | Rate float64 40 | Total float64 41 | Buy int 42 | Sell int 43 | Time time.Time 44 | } 45 | 46 | type OHLC struct { 47 | Open float64 48 | High float64 49 | Low float64 50 | Close float64 51 | Volume float64 52 | Time time.Time 53 | Minute int 54 | } 55 | //for influx db 56 | type Tags map[string]string 57 | type Fields map[string]interface{} 58 | 59 | func Default()*CoinDB { 60 | return New( Config{ 61 | Host: "http://localhost:8086", 62 | UserName: "", 63 | Password: "", 64 | }) 65 | } 66 | 67 | func New(config Config) *CoinDB{ 68 | coindb := &CoinDB{} 69 | var err error 70 | coindb.client, err = client.NewHTTPClient(client.HTTPConfig{ 71 | Addr: config.Host, 72 | Username: config.UserName, 73 | Password: config.Password, 74 | }) 75 | if err != nil { 76 | log.Fatal("Influxdb connect error: ", err) 77 | } 78 | 79 | if _, _, err := coindb.client.Ping(30 * time.Second); err != nil { 80 | log.Fatal("Influxdb connect error: ", err) 81 | } else { 82 | coindb.status = 1 83 | log.Info("Influxdb connect successfully") 84 | } 85 | //TODO check database 86 | return coindb 87 | } 88 | 89 | //influx database wrapper 90 | func (db *CoinDB)batch(dbname string)(client.BatchPoints, error){ 91 | bp, err := client.NewBatchPoints(client.BatchPointsConfig{ 92 | Database: dbname, 93 | Precision: "s", 94 | }) 95 | return bp, err 96 | } 97 | //influx database wrapper 98 | func (db *CoinDB)point(name string,tags Tags,fields Fields,date time.Time) (*client.Point,error){ 99 | pt, err := client.NewPoint( 100 | name, 101 | tags, 102 | fields, 103 | date) 104 | return pt, err 105 | } 106 | 107 | //influx database wrapper 108 | func (db *CoinDB) write(bp client.BatchPoints) (error) { 109 | if err := db.client.Write(bp); err != nil { 110 | return err 111 | } 112 | return nil 113 | } 114 | func (db *CoinDB) putMarket(market string) error { 115 | q := client.NewQuery(fmt.Sprintf(`CREATE DATABASE "market_%s"`, market), "", "") 116 | if response, err := db.client.Query(q); err != nil { 117 | log.Error(err) 118 | return err 119 | } else if err = response.Error(); err != nil { 120 | log.Error(err) 121 | return err 122 | } 123 | return nil 124 | } 125 | //Data to Point 126 | func (db *CoinDB)ohlc(pair Pair,data OHLC) *client.Point{ 127 | point, _ := db.point(pair.ToString(), 128 | Tags{ 129 | "pair": pair.Quote, 130 | "base": pair.Base, 131 | }, Fields{ 132 | "Open": data.Open, 133 | "High": data.High, 134 | "Low": data.Low, 135 | "Close": data.Close, 136 | "Volume": data.Volume, 137 | }, data.Time) 138 | return point 139 | } 140 | func (db *CoinDB)take(pair Pair,data MarketTake) *client.Point{ 141 | point, _ := db.point(pair.ToString(), 142 | Tags{ 143 | "Quote": pair.Quote, 144 | "Base": pair.Base, 145 | }, Fields{ 146 | "TradeID": data.TradeID, 147 | "Amount": data.Amount, 148 | "Rate": data.Rate, 149 | "Total": data.Total, 150 | "Buy": data.Buy, 151 | "Sell": data.Sell, 152 | }, data.Time) 153 | return point 154 | } 155 | //data insert 156 | func (db *CoinDB)PutOHLC(market string,pair Pair,data OHLC,date time.Time) { 157 | batch, err := db.batch("market_" + market) 158 | if err != nil{ 159 | log.Error(err) 160 | } 161 | batch.AddPoint(db.ohlc(pair,data)) 162 | err = db.write(batch) 163 | if err != nil{ 164 | if strings.Contains(err.Error(), "database not found") { 165 | db.putMarket(market) 166 | db.write(batch) 167 | return 168 | } 169 | } 170 | } 171 | 172 | func (db *CoinDB)PutOHLCs(market string,pair Pair,data... OHLC) { 173 | batch, err := db.batch("market_" + market) 174 | if err != nil{ 175 | log.Error(err) 176 | } 177 | for _,item := range data { 178 | batch.AddPoint(db.ohlc(pair,item)) 179 | } 180 | err = db.write(batch) 181 | if err != nil{ 182 | if strings.Contains(err.Error(), "database not found") { 183 | db.putMarket(market) 184 | db.write(batch) 185 | return 186 | } 187 | } 188 | } 189 | 190 | func (db *CoinDB)PutMarketTake(market string,pair Pair,data MarketTake) { 191 | batch, err := db.batch("market_" + market) 192 | if err != nil{ 193 | log.Error(err) 194 | } 195 | batch.AddPoint(db.take(pair,data)) 196 | err = db.write(batch) 197 | if err != nil{ 198 | if strings.Contains(err.Error(), "database not found") { 199 | db.putMarket(market) 200 | db.write(batch) 201 | return 202 | } 203 | } 204 | } 205 | 206 | func (db *CoinDB)PutMarketTakes(market string,pair Pair,data []MarketTake) { 207 | batch, err := db.batch("market_" + market) 208 | if err != nil { 209 | log.Error(err) 210 | } 211 | for _, item := range data { 212 | batch.AddPoint(db.take(pair, item)) 213 | } 214 | err = db.write(batch) 215 | if err != nil { 216 | if strings.Contains(err.Error(), "database not found") { 217 | db.putMarket(market) 218 | db.write(batch) 219 | return 220 | } 221 | } 222 | } 223 | 224 | // GetOHLC return the list of OHLC from CurrencyPair TODO : This function is not tested. 225 | func (driver *CoinDB) GetOHLC(market string, pair Pair,start,end time.Time,period string) (data []OHLC) { 226 | raw := fmt.Sprintf( 227 | `SELECT FIRST("Rate"), MAX("Rate"), MIN("Rate"), LAST("Rate"), SUM("Amount") FROM "%v" WHERE time >= %vs AND time < %vs GROUP BY time(%vs)`, 228 | pair.Base+"/"+pair.Quote, start, end, period) 229 | q := client.NewQuery(raw, "market_"+market, "s") 230 | data = []OHLC{} 231 | driver.execute(q, func(row models.Row) { 232 | for _,item := range row.Values { 233 | t, _ := time.Parse(time.RFC3339, item[0].(string)) 234 | d := OHLC{ 235 | Time: t, 236 | Volume: item[5].(float64), 237 | Open: item[1].(float64), 238 | High: item[2].(float64), 239 | Low: item[3].(float64), 240 | Close: item[4].(float64), 241 | } 242 | data = append(data, d) 243 | } 244 | }) 245 | return 246 | } 247 | 248 | // GetMarkets return the list of market name 249 | func (driver *CoinDB) GetMarkets() (data []string) { 250 | data = []string{} 251 | q := client.NewQuery("SHOW DATABASES", "", "s") 252 | if response, err := driver.client.Query(q); err == nil && response.Err == "" && len(response.Results) > 0 { 253 | result := response.Results[0] 254 | if result.Err == "" && len(result.Series) > 0 && len(result.Series[0].Values) > 0 { 255 | for _, v := range result.Series[0].Values { 256 | if len(v) > 0 { 257 | name := fmt.Sprint(v[0]) 258 | if strings.HasPrefix(name, "market_") { 259 | data = append(data, strings.TrimPrefix(name, "market_")) 260 | } 261 | } 262 | } 263 | } 264 | } 265 | return 266 | } 267 | 268 | // GetMarkets return the list of GetCurrencyPair from market 269 | func (driver *CoinDB) GetCurrencyPairs(market string) (data []Pair) { 270 | data = []Pair{} 271 | q := client.NewQuery("SHOW MEASUREMENTS", "market_"+market, "s") 272 | if response, err := driver.client.Query(q); err == nil && response.Err == "" && len(response.Results) > 0 { 273 | result := response.Results[0] 274 | if result.Err == "" && len(result.Series) > 0 && len(result.Series[0].Values) > 0 { 275 | for _, v := range result.Series[0].Values { 276 | if len(v) > 0 { 277 | name := fmt.Sprint(v[0]) 278 | spl := strings.Split(name,"/") 279 | data = append(data,Pair{ 280 | Base:spl[0], 281 | Quote:spl[1], 282 | }) 283 | } 284 | } 285 | } 286 | } 287 | return 288 | } 289 | 290 | func (driver *CoinDB) GetFirstDate(market string, pair Pair) time.Time { 291 | raw := fmt.Sprintf(`SELECT * FROM "%v" order by time asc limit 1`, pair.Base+"/"+pair.Quote) 292 | q := client.NewQuery(raw, "market_"+market, "s") 293 | var t time.Time = time.Now() 294 | driver.execute(q, func(row models.Row) { 295 | unixtime ,_ := row.Values[0][0].(json.Number).Int64() 296 | t = time.Unix(unixtime,0) 297 | }) 298 | return t 299 | } 300 | 301 | func (driver *CoinDB) GetLastDate(market string, pair Pair) time.Time { 302 | raw := fmt.Sprintf(`SELECT * FROM "%v" order by time desc limit 1`, pair.Base+"/"+pair.Quote) 303 | q := client.NewQuery(raw, "market_"+market, "s") 304 | t := time.Now().UTC() 305 | driver.execute(q, func(row models.Row) { 306 | unixtime ,_ := row.Values[0][0].(json.Number).Int64() 307 | t = time.Unix(unixtime,0) 308 | }) 309 | 310 | return t 311 | } 312 | 313 | func (driver *CoinDB) execute(q client.Query,fn func(row models.Row)) { 314 | if response, err := driver.client.Query(q); err == nil && response.Err == "" && len(response.Results) > 0 { 315 | result := response.Results[0] 316 | if len(result.Series) > 0 { 317 | fn(result.Series[0]) 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /strategy/proto/strategy.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: strategy.proto 3 | 4 | /* 5 | Package proto is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | strategy.proto 9 | 10 | It has these top-level messages: 11 | Property 12 | Dictionary 13 | Information 14 | Bool 15 | Asset 16 | Empty 17 | */ 18 | package proto 19 | 20 | import proto1 "github.com/golang/protobuf/proto" 21 | import fmt "fmt" 22 | import math "math" 23 | 24 | import ( 25 | context "golang.org/x/net/context" 26 | grpc "google.golang.org/grpc" 27 | ) 28 | 29 | // Reference imports to suppress errors if they are not otherwise used. 30 | var _ = proto1.Marshal 31 | var _ = fmt.Errorf 32 | var _ = math.Inf 33 | 34 | // This is a compile-time assertion to ensure that this generated file 35 | // is compatible with the proto package it is being compiled against. 36 | // A compilation error at this line likely means your copy of the 37 | // proto package needs to be updated. 38 | const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package 39 | 40 | type Property struct { 41 | Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"` 42 | ValueInt int32 `protobuf:"varint,2,opt,name=value_int,json=valueInt" json:"value_int,omitempty"` 43 | ValueFloat float32 `protobuf:"fixed32,3,opt,name=value_float,json=valueFloat" json:"value_float,omitempty"` 44 | ValueString string `protobuf:"bytes,4,opt,name=value_string,json=valueString" json:"value_string,omitempty"` 45 | ValueBool bool `protobuf:"varint,5,opt,name=value_bool,json=valueBool" json:"value_bool,omitempty"` 46 | } 47 | 48 | func (m *Property) Reset() { *m = Property{} } 49 | func (m *Property) String() string { return proto1.CompactTextString(m) } 50 | func (*Property) ProtoMessage() {} 51 | func (*Property) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 52 | 53 | func (m *Property) GetType() string { 54 | if m != nil { 55 | return m.Type 56 | } 57 | return "" 58 | } 59 | 60 | func (m *Property) GetValueInt() int32 { 61 | if m != nil { 62 | return m.ValueInt 63 | } 64 | return 0 65 | } 66 | 67 | func (m *Property) GetValueFloat() float32 { 68 | if m != nil { 69 | return m.ValueFloat 70 | } 71 | return 0 72 | } 73 | 74 | func (m *Property) GetValueString() string { 75 | if m != nil { 76 | return m.ValueString 77 | } 78 | return "" 79 | } 80 | 81 | func (m *Property) GetValueBool() bool { 82 | if m != nil { 83 | return m.ValueBool 84 | } 85 | return false 86 | } 87 | 88 | type Dictionary struct { 89 | CustomInt map[string]*Property `protobuf:"bytes,1,rep,name=customInt" json:"customInt,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` 90 | } 91 | 92 | func (m *Dictionary) Reset() { *m = Dictionary{} } 93 | func (m *Dictionary) String() string { return proto1.CompactTextString(m) } 94 | func (*Dictionary) ProtoMessage() {} 95 | func (*Dictionary) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 96 | 97 | func (m *Dictionary) GetCustomInt() map[string]*Property { 98 | if m != nil { 99 | return m.CustomInt 100 | } 101 | return nil 102 | } 103 | 104 | type Information struct { 105 | Name string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"` 106 | Version string `protobuf:"bytes,2,opt,name=Version" json:"Version,omitempty"` 107 | Description string `protobuf:"bytes,3,opt,name=Description" json:"Description,omitempty"` 108 | } 109 | 110 | func (m *Information) Reset() { *m = Information{} } 111 | func (m *Information) String() string { return proto1.CompactTextString(m) } 112 | func (*Information) ProtoMessage() {} 113 | func (*Information) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 114 | 115 | func (m *Information) GetName() string { 116 | if m != nil { 117 | return m.Name 118 | } 119 | return "" 120 | } 121 | 122 | func (m *Information) GetVersion() string { 123 | if m != nil { 124 | return m.Version 125 | } 126 | return "" 127 | } 128 | 129 | func (m *Information) GetDescription() string { 130 | if m != nil { 131 | return m.Description 132 | } 133 | return "" 134 | } 135 | 136 | type Bool struct { 137 | Boolean bool `protobuf:"varint,1,opt,name=Boolean" json:"Boolean,omitempty"` 138 | } 139 | 140 | func (m *Bool) Reset() { *m = Bool{} } 141 | func (m *Bool) String() string { return proto1.CompactTextString(m) } 142 | func (*Bool) ProtoMessage() {} 143 | func (*Bool) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 144 | 145 | func (m *Bool) GetBoolean() bool { 146 | if m != nil { 147 | return m.Boolean 148 | } 149 | return false 150 | } 151 | 152 | type Asset struct { 153 | Name string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"` 154 | } 155 | 156 | func (m *Asset) Reset() { *m = Asset{} } 157 | func (m *Asset) String() string { return proto1.CompactTextString(m) } 158 | func (*Asset) ProtoMessage() {} 159 | func (*Asset) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 160 | 161 | func (m *Asset) GetName() string { 162 | if m != nil { 163 | return m.Name 164 | } 165 | return "" 166 | } 167 | 168 | type Empty struct { 169 | } 170 | 171 | func (m *Empty) Reset() { *m = Empty{} } 172 | func (m *Empty) String() string { return proto1.CompactTextString(m) } 173 | func (*Empty) ProtoMessage() {} 174 | func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 175 | 176 | func init() { 177 | proto1.RegisterType((*Property)(nil), "proto.Property") 178 | proto1.RegisterType((*Dictionary)(nil), "proto.Dictionary") 179 | proto1.RegisterType((*Information)(nil), "proto.Information") 180 | proto1.RegisterType((*Bool)(nil), "proto.Bool") 181 | proto1.RegisterType((*Asset)(nil), "proto.Asset") 182 | proto1.RegisterType((*Empty)(nil), "proto.Empty") 183 | } 184 | 185 | // Reference imports to suppress errors if they are not otherwise used. 186 | var _ context.Context 187 | var _ grpc.ClientConn 188 | 189 | // This is a compile-time assertion to ensure that this generated file 190 | // is compatible with the grpc package it is being compiled against. 191 | const _ = grpc.SupportPackageIsVersion4 192 | 193 | // Client API for Strategy service 194 | 195 | type StrategyClient interface { 196 | Init(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) 197 | Info(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Information, error) 198 | GetProperty(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Dictionary, error) 199 | SetProperty(ctx context.Context, in *Dictionary, opts ...grpc.CallOption) (*Empty, error) 200 | // filter for long position 201 | SellConditions(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) 202 | // filter for short position 203 | BuyConditions(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) 204 | // long position priority rank 205 | RankFilter(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) 206 | } 207 | 208 | type strategyClient struct { 209 | cc *grpc.ClientConn 210 | } 211 | 212 | func NewStrategyClient(cc *grpc.ClientConn) StrategyClient { 213 | return &strategyClient{cc} 214 | } 215 | 216 | func (c *strategyClient) Init(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { 217 | out := new(Empty) 218 | err := grpc.Invoke(ctx, "/proto.Strategy/Init", in, out, c.cc, opts...) 219 | if err != nil { 220 | return nil, err 221 | } 222 | return out, nil 223 | } 224 | 225 | func (c *strategyClient) Info(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Information, error) { 226 | out := new(Information) 227 | err := grpc.Invoke(ctx, "/proto.Strategy/Info", in, out, c.cc, opts...) 228 | if err != nil { 229 | return nil, err 230 | } 231 | return out, nil 232 | } 233 | 234 | func (c *strategyClient) GetProperty(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Dictionary, error) { 235 | out := new(Dictionary) 236 | err := grpc.Invoke(ctx, "/proto.Strategy/GetProperty", in, out, c.cc, opts...) 237 | if err != nil { 238 | return nil, err 239 | } 240 | return out, nil 241 | } 242 | 243 | func (c *strategyClient) SetProperty(ctx context.Context, in *Dictionary, opts ...grpc.CallOption) (*Empty, error) { 244 | out := new(Empty) 245 | err := grpc.Invoke(ctx, "/proto.Strategy/SetProperty", in, out, c.cc, opts...) 246 | if err != nil { 247 | return nil, err 248 | } 249 | return out, nil 250 | } 251 | 252 | func (c *strategyClient) SellConditions(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) { 253 | out := new(Bool) 254 | err := grpc.Invoke(ctx, "/proto.Strategy/SellConditions", in, out, c.cc, opts...) 255 | if err != nil { 256 | return nil, err 257 | } 258 | return out, nil 259 | } 260 | 261 | func (c *strategyClient) BuyConditions(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) { 262 | out := new(Bool) 263 | err := grpc.Invoke(ctx, "/proto.Strategy/BuyConditions", in, out, c.cc, opts...) 264 | if err != nil { 265 | return nil, err 266 | } 267 | return out, nil 268 | } 269 | 270 | func (c *strategyClient) RankFilter(ctx context.Context, in *Asset, opts ...grpc.CallOption) (*Bool, error) { 271 | out := new(Bool) 272 | err := grpc.Invoke(ctx, "/proto.Strategy/RankFilter", in, out, c.cc, opts...) 273 | if err != nil { 274 | return nil, err 275 | } 276 | return out, nil 277 | } 278 | 279 | // Server API for Strategy service 280 | 281 | type StrategyServer interface { 282 | Init(context.Context, *Empty) (*Empty, error) 283 | Info(context.Context, *Empty) (*Information, error) 284 | GetProperty(context.Context, *Empty) (*Dictionary, error) 285 | SetProperty(context.Context, *Dictionary) (*Empty, error) 286 | // filter for long position 287 | SellConditions(context.Context, *Asset) (*Bool, error) 288 | // filter for short position 289 | BuyConditions(context.Context, *Asset) (*Bool, error) 290 | // long position priority rank 291 | RankFilter(context.Context, *Asset) (*Bool, error) 292 | } 293 | 294 | func RegisterStrategyServer(s *grpc.Server, srv StrategyServer) { 295 | s.RegisterService(&_Strategy_serviceDesc, srv) 296 | } 297 | 298 | func _Strategy_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 299 | in := new(Empty) 300 | if err := dec(in); err != nil { 301 | return nil, err 302 | } 303 | if interceptor == nil { 304 | return srv.(StrategyServer).Init(ctx, in) 305 | } 306 | info := &grpc.UnaryServerInfo{ 307 | Server: srv, 308 | FullMethod: "/proto.Strategy/Init", 309 | } 310 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 311 | return srv.(StrategyServer).Init(ctx, req.(*Empty)) 312 | } 313 | return interceptor(ctx, in, info, handler) 314 | } 315 | 316 | func _Strategy_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 317 | in := new(Empty) 318 | if err := dec(in); err != nil { 319 | return nil, err 320 | } 321 | if interceptor == nil { 322 | return srv.(StrategyServer).Info(ctx, in) 323 | } 324 | info := &grpc.UnaryServerInfo{ 325 | Server: srv, 326 | FullMethod: "/proto.Strategy/Info", 327 | } 328 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 329 | return srv.(StrategyServer).Info(ctx, req.(*Empty)) 330 | } 331 | return interceptor(ctx, in, info, handler) 332 | } 333 | 334 | func _Strategy_GetProperty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 335 | in := new(Empty) 336 | if err := dec(in); err != nil { 337 | return nil, err 338 | } 339 | if interceptor == nil { 340 | return srv.(StrategyServer).GetProperty(ctx, in) 341 | } 342 | info := &grpc.UnaryServerInfo{ 343 | Server: srv, 344 | FullMethod: "/proto.Strategy/GetProperty", 345 | } 346 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 347 | return srv.(StrategyServer).GetProperty(ctx, req.(*Empty)) 348 | } 349 | return interceptor(ctx, in, info, handler) 350 | } 351 | 352 | func _Strategy_SetProperty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 353 | in := new(Dictionary) 354 | if err := dec(in); err != nil { 355 | return nil, err 356 | } 357 | if interceptor == nil { 358 | return srv.(StrategyServer).SetProperty(ctx, in) 359 | } 360 | info := &grpc.UnaryServerInfo{ 361 | Server: srv, 362 | FullMethod: "/proto.Strategy/SetProperty", 363 | } 364 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 365 | return srv.(StrategyServer).SetProperty(ctx, req.(*Dictionary)) 366 | } 367 | return interceptor(ctx, in, info, handler) 368 | } 369 | 370 | func _Strategy_SellConditions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 371 | in := new(Asset) 372 | if err := dec(in); err != nil { 373 | return nil, err 374 | } 375 | if interceptor == nil { 376 | return srv.(StrategyServer).SellConditions(ctx, in) 377 | } 378 | info := &grpc.UnaryServerInfo{ 379 | Server: srv, 380 | FullMethod: "/proto.Strategy/SellConditions", 381 | } 382 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 383 | return srv.(StrategyServer).SellConditions(ctx, req.(*Asset)) 384 | } 385 | return interceptor(ctx, in, info, handler) 386 | } 387 | 388 | func _Strategy_BuyConditions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 389 | in := new(Asset) 390 | if err := dec(in); err != nil { 391 | return nil, err 392 | } 393 | if interceptor == nil { 394 | return srv.(StrategyServer).BuyConditions(ctx, in) 395 | } 396 | info := &grpc.UnaryServerInfo{ 397 | Server: srv, 398 | FullMethod: "/proto.Strategy/BuyConditions", 399 | } 400 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 401 | return srv.(StrategyServer).BuyConditions(ctx, req.(*Asset)) 402 | } 403 | return interceptor(ctx, in, info, handler) 404 | } 405 | 406 | func _Strategy_RankFilter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 407 | in := new(Asset) 408 | if err := dec(in); err != nil { 409 | return nil, err 410 | } 411 | if interceptor == nil { 412 | return srv.(StrategyServer).RankFilter(ctx, in) 413 | } 414 | info := &grpc.UnaryServerInfo{ 415 | Server: srv, 416 | FullMethod: "/proto.Strategy/RankFilter", 417 | } 418 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 419 | return srv.(StrategyServer).RankFilter(ctx, req.(*Asset)) 420 | } 421 | return interceptor(ctx, in, info, handler) 422 | } 423 | 424 | var _Strategy_serviceDesc = grpc.ServiceDesc{ 425 | ServiceName: "proto.Strategy", 426 | HandlerType: (*StrategyServer)(nil), 427 | Methods: []grpc.MethodDesc{ 428 | { 429 | MethodName: "Init", 430 | Handler: _Strategy_Init_Handler, 431 | }, 432 | { 433 | MethodName: "Info", 434 | Handler: _Strategy_Info_Handler, 435 | }, 436 | { 437 | MethodName: "GetProperty", 438 | Handler: _Strategy_GetProperty_Handler, 439 | }, 440 | { 441 | MethodName: "SetProperty", 442 | Handler: _Strategy_SetProperty_Handler, 443 | }, 444 | { 445 | MethodName: "SellConditions", 446 | Handler: _Strategy_SellConditions_Handler, 447 | }, 448 | { 449 | MethodName: "BuyConditions", 450 | Handler: _Strategy_BuyConditions_Handler, 451 | }, 452 | { 453 | MethodName: "RankFilter", 454 | Handler: _Strategy_RankFilter_Handler, 455 | }, 456 | }, 457 | Streams: []grpc.StreamDesc{}, 458 | Metadata: "strategy.proto", 459 | } 460 | 461 | func init() { proto1.RegisterFile("strategy.proto", fileDescriptor0) } 462 | 463 | var fileDescriptor0 = []byte{ 464 | // 439 bytes of a gzipped FileDescriptorProto 465 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8b, 0xd3, 0x40, 466 | 0x10, 0xc7, 0xd9, 0xb6, 0xf1, 0x92, 0xc9, 0x59, 0x75, 0x9e, 0x42, 0x0f, 0x31, 0x06, 0xc4, 0xa0, 467 | 0xd0, 0x87, 0xfa, 0x22, 0x3e, 0x08, 0xde, 0x2f, 0xe9, 0x83, 0x22, 0x5b, 0xf0, 0x4d, 0x8e, 0x5c, 468 | 0xdd, 0x1e, 0xcb, 0xa5, 0xbb, 0x61, 0x77, 0x2a, 0xec, 0xdf, 0x22, 0xf8, 0x87, 0xf8, 0xd7, 0xc9, 469 | 0x6e, 0x92, 0x5e, 0x7b, 0x15, 0xf1, 0x69, 0x67, 0x66, 0x3f, 0x33, 0x93, 0xef, 0x77, 0x03, 0x63, 470 | 0x4b, 0xa6, 0x22, 0x71, 0xe3, 0xa6, 0x8d, 0xd1, 0xa4, 0x31, 0x0a, 0x47, 0xf1, 0x8b, 0x41, 0xfc, 471 | 0xc5, 0xe8, 0x46, 0x18, 0x72, 0x88, 0x30, 0x22, 0xd7, 0x88, 0x8c, 0xe5, 0xac, 0x4c, 0x78, 0x88, 472 | 0xf1, 0x04, 0x92, 0x1f, 0x55, 0xbd, 0x11, 0x57, 0x52, 0x51, 0x36, 0xc8, 0x59, 0x19, 0xf1, 0x38, 473 | 0x14, 0xe6, 0x8a, 0xf0, 0x19, 0xa4, 0xed, 0xe5, 0xaa, 0xd6, 0x15, 0x65, 0xc3, 0x9c, 0x95, 0x03, 474 | 0x0e, 0xa1, 0x74, 0xe9, 0x2b, 0xf8, 0x1c, 0x8e, 0x5b, 0xc0, 0x92, 0x91, 0xea, 0x26, 0x1b, 0x85, 475 | 0xc9, 0x6d, 0xd3, 0x22, 0x94, 0xf0, 0x29, 0xb4, 0x0d, 0x57, 0xd7, 0x5a, 0xd7, 0x59, 0x94, 0xb3, 476 | 0x32, 0xe6, 0xed, 0xca, 0x53, 0xad, 0xeb, 0xe2, 0x27, 0x03, 0x38, 0x97, 0x4b, 0x92, 0x5a, 0x55, 477 | 0xc6, 0xe1, 0x7b, 0x48, 0x96, 0x1b, 0x4b, 0x7a, 0x3d, 0x57, 0x94, 0xb1, 0x7c, 0x58, 0xa6, 0xb3, 478 | 0xbc, 0x55, 0x34, 0xbd, 0xa3, 0xa6, 0x67, 0x3d, 0x72, 0xa1, 0xc8, 0x38, 0x7e, 0xd7, 0x32, 0xf9, 479 | 0x04, 0xe3, 0xfd, 0x4b, 0x7c, 0x0c, 0xc3, 0x5b, 0xe1, 0x3a, 0xcd, 0x3e, 0xc4, 0x17, 0x10, 0x85, 480 | 0xfd, 0x41, 0x6e, 0x3a, 0x7b, 0xd4, 0xcd, 0xef, 0x6d, 0xe2, 0xed, 0xed, 0xbb, 0xc1, 0x5b, 0x56, 481 | 0x7c, 0x83, 0x74, 0xae, 0x56, 0xda, 0xac, 0x2b, 0xbf, 0xda, 0x1b, 0xf8, 0xb9, 0x5a, 0x6f, 0x0d, 482 | 0xf4, 0x31, 0x66, 0x70, 0xf4, 0x55, 0x18, 0x2b, 0xb5, 0x0a, 0xf3, 0x12, 0xde, 0xa7, 0x98, 0x43, 483 | 0x7a, 0x2e, 0xec, 0xd2, 0xc8, 0xc6, 0x37, 0x07, 0xf7, 0x12, 0xbe, 0x5b, 0x2a, 0x72, 0x18, 0x79, 484 | 0x13, 0xfc, 0x0c, 0x7f, 0x8a, 0x4a, 0x85, 0xd1, 0x31, 0xef, 0xd3, 0xe2, 0x04, 0xa2, 0x0f, 0xd6, 485 | 0x0a, 0xfa, 0xdb, 0xea, 0xe2, 0x08, 0xa2, 0x8b, 0x75, 0x43, 0x6e, 0xf6, 0x7b, 0x00, 0xf1, 0xa2, 486 | 0x7b, 0x7f, 0x2c, 0x60, 0x34, 0x57, 0x92, 0xf0, 0xb8, 0xd3, 0x15, 0x90, 0xc9, 0x5e, 0x86, 0xa5, 487 | 0x67, 0x56, 0xfa, 0x1e, 0x83, 0x5d, 0xb6, 0x2b, 0x79, 0x0a, 0xe9, 0x47, 0x41, 0xdb, 0x5f, 0x68, 488 | 0xbf, 0xe1, 0xc9, 0xc1, 0xd3, 0x78, 0x7e, 0xb1, 0xc3, 0x1f, 0x12, 0xf7, 0xbe, 0xe4, 0x35, 0x8c, 489 | 0x17, 0xa2, 0xae, 0xcf, 0xb4, 0xfa, 0x2e, 0x3d, 0x61, 0xb7, 0x2b, 0x82, 0xee, 0x49, 0xda, 0x65, 490 | 0xc1, 0xa7, 0x57, 0xf0, 0xf0, 0x74, 0xe3, 0xfe, 0x8f, 0x7d, 0x09, 0xc0, 0x2b, 0x75, 0x7b, 0x29, 491 | 0x6b, 0x12, 0xe6, 0x1f, 0xe0, 0xf5, 0x83, 0x10, 0xbf, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0x28, 492 | 0xb1, 0x3d, 0x2f, 0x42, 0x03, 0x00, 0x00, 493 | } 494 | --------------------------------------------------------------------------------