├── .gitignore ├── Makefile ├── README.md ├── apps ├── dealerapi.go ├── mt5apitest.go ├── ordersmysql.go ├── reportapi.go ├── tcapi.go ├── tcdealer.go └── ticksredis.go ├── bin └── .gitignore ├── conf └── .gitignore └── tests ├── dealer_api.py ├── report_api.py ├── tc_api.py ├── test_api.py ├── test_dealer_api.py └── test_quote_receive.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO = go 2 | ENV = export GOPATH=$(CURDIR)/lib/src 3 | GOGET = export GOPATH=$(CURDIR)/lib/src; $(GO) get 4 | BUILD = $(ENV); $(GO) build 5 | 6 | tst: 7 | @echo "$(CURDIR)" 8 | 9 | dep: 10 | 11 | $(GOGET) github.com/BurntSushi/toml 12 | $(GOGET) github.com/gorilla/websocket 13 | $(GOGET) gopkg.in/doug-martin/goqu.v3 14 | $(GOGET) gopkg.in/doug-martin/goqu.v3/adapters/mysql 15 | $(GOGET) github.com/go-sql-driver/mysql 16 | $(GOGET) github.com/google/gops 17 | $(GOGET) gopkg.in/iconv.v1 18 | 19 | tc-dep: dep 20 | $(GOGET) github.com/golang/protobuf/proto 21 | $(GOGET) github.com/streadway/amqp 22 | $(GOGET) github.com/assembla/cony 23 | 24 | mtapitest: 25 | $(BUILD) -o bin/mtapitest.exe apps/apitest.go 26 | 27 | mtdealerapi: dep 28 | $(BUILD) -o bin/mtdealerapi.exe apps/dealerapi.go 29 | 30 | mtreportapi: dep 31 | $(BUILD) -o bin/mtreportapi.exe apps/reportapi.go 32 | $(BUILD) -o bin/mtreportapi apps/reportapi.go 33 | 34 | tcapi: dep 35 | $(BUILD) -o bin/tcapi apps/tcapi.go 36 | 37 | tcdealer: dep 38 | $(BUILD) -o bin/tcdealer apps/tcdealer.go 39 | 40 | gops: dep 41 | $(BUILD) -o bin/gops github.com/google/gops 42 | 43 | tc-dealer: tc-dep 44 | 45 | clean: 46 | rm -rf ./bin/* 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MT4 Manager API Golang 2 | Metatrader4 manager api golang wrapper. Allows to run api in pumping mode and save ticks to redis -------------------------------------------------------------------------------- /apps/dealerapi.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "mtdealerapi" 6 | ) 7 | 8 | func main() { 9 | flag.Parse() 10 | mtdealerapi.Start() 11 | } 12 | -------------------------------------------------------------------------------- /apps/mt5apitest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | import ( 5 | "fmt" 6 | "mt5manapi" 7 | "mtconfig" 8 | "mtenv" 9 | "mtlog" 10 | "mtutils/utils" 11 | "os" 12 | 13 | "github.com/go-redis/redis" 14 | ) 15 | 16 | type MT5Config struct { 17 | RedisAddr string `toml:"redis_addr"` 18 | ServerAddr string `toml:"server_addr"` 19 | Login uint64 `toml:"login"` 20 | Password string `toml:"password"` 21 | } 22 | 23 | var ( 24 | redisCli *redis.Client 25 | mt5factory = mt5manapi.NewCMTManagerAPIFactory() 26 | mt5factoryInited = false 27 | ) 28 | 29 | func initLogger() { 30 | c := &mtconfig.Common{} 31 | 32 | err := mtconfig.LoadCommonConfig(mtenv.CONFIG_PATH, c) 33 | if err != nil { 34 | fmt.Println(err) 35 | os.Exit(1) 36 | } 37 | 38 | log, err := mtlog.NewLogger(c.LogPath, c.LogLevel) 39 | if err != nil { 40 | fmt.Println(err) 41 | os.Exit(1) 42 | } 43 | 44 | mtlog.SetDefault(log) 45 | 46 | mtlog.Info("root: \"%s\"", mtenv.DIR) 47 | mtlog.Info("config path: \"%s\"", mtenv.CONFIG_PATH) 48 | mtlog.Info("log path: \"%s\" with level \"%s\"", c.LogPath, c.LogLevel) 49 | 50 | fmt.Println("log path: ", c.LogPath) 51 | } 52 | 53 | func CreateIManagerApi() (mt5manapi.IMTManagerAPI, error) { 54 | 55 | if !mt5factoryInited { 56 | 57 | err := mt5factory.Initialize() 58 | 59 | if err != 0 { 60 | return nil, fmt.Errorf("Failed to load MT5APIManager dll. err: ", err) 61 | } 62 | 63 | var version uint 64 | errno := mt5factory.Version(&version) 65 | if errno != 0 { 66 | return nil, fmt.Errorf("Failed to load version.") 67 | } 68 | 69 | if int(version) < mt5manapi.MTManagerAPIVersion { 70 | return nil, fmt.Errorf("Wrong Manager API version %u, version %u required\n", version, mt5manapi.MTManagerAPIVersion) 71 | } 72 | 73 | mt5factoryInited = true 74 | } 75 | 76 | mtapi := mt5factory.CreateManager(uint(mt5manapi.MTManagerAPIVersion)) 77 | 78 | if mtapi == nil { 79 | return nil, fmt.Errorf("Failed to Create manager") 80 | } 81 | 82 | return mtapi, nil 83 | } 84 | 85 | func main() { 86 | 87 | initLogger() 88 | 89 | config := &MT5Config{} 90 | err := mtconfig.LoadConfig(mtenv.CONFIG_PATH, "mt5config", config) 91 | if err != nil { 92 | fmt.Println(err) 93 | os.Exit(1) 94 | } 95 | 96 | man, err := CreateIManagerApi() 97 | if err != nil { 98 | panic(err) 99 | } 100 | 101 | errno := man.Connect(config.ServerAddr, config.Login, config.Password, "", 0, 10) 102 | if errno != 0 { 103 | panic(errno) 104 | } 105 | 106 | /*var server_ref string 107 | if man.NetworkServer(server_ref) == 0 && man.NetworkRescan(0, 10000) == 0 { 108 | 109 | man.Disconnect() 110 | 111 | errno := man.Connect(server_ref, config.ServerAddr, config.Password, "", 0xffffffff, 10) 112 | if errno != 0 { 113 | panic(errno) 114 | } 115 | } */ 116 | 117 | var ticks_manager mt5manapi.IMTTickSink 118 | man.TickSubscribe(ticks_manager) 119 | man.SelectedAddAll() 120 | 121 | fmt.Println("redis connecting to ", config.RedisAddr) 122 | redisClient := redis.NewClient(&redis.Options{ 123 | Addr: config.RedisAddr, 124 | }) 125 | 126 | pong, err := redisClient.Ping().Result() 127 | if err != nil { 128 | fmt.Println(err) 129 | os.Exit(1) 130 | } 131 | //checkError(err) 132 | fmt.Println("Redis PING - ", pong) 133 | 134 | fmt.Println("working...") 135 | utils.HandleSignal() 136 | } 137 | -------------------------------------------------------------------------------- /apps/ordersmysql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | import ( 5 | "fmt" 6 | "mtconfig" 7 | "mtenv" 8 | "mtlog" 9 | "mtmanapi" 10 | "mtutils/utils" 11 | "os" 12 | "reportdb" 13 | "time" 14 | ) 15 | 16 | type Config struct { 17 | MysqlUri string `toml:"mysql_uri"` 18 | ServerAddr string `toml:"server_addr"` 19 | Login int `toml:"login"` 20 | Password string `toml:"password"` 21 | } 22 | 23 | var ( 24 | db *reportdb.Db 25 | factory = mtmanapi.NewCManagerFactory() 26 | factoryInited = false 27 | apiVer = makelong( 28 | mtmanapi.ManAPIProgramBuild, 29 | mtmanapi.ManAPIProgramVersion, 30 | ) 31 | ) 32 | 33 | func makelong(a, b int) int { 34 | return int(uint32(a) | uint32(b)<<16) 35 | } 36 | 37 | func checkError(err error) { 38 | if err != nil { 39 | fmt.Println(err) 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func initLog() { 45 | c := &mtconfig.Common{} 46 | 47 | err := mtconfig.LoadCommonConfig(mtenv.CONFIG_PATH, c) 48 | checkError(err) 49 | 50 | log, err := mtlog.NewLogger(c.LogPath, c.LogLevel) 51 | checkError(err) 52 | 53 | mtlog.SetDefault(log) 54 | 55 | mtlog.Info("root: \"%s\"", mtenv.DIR) 56 | mtlog.Info("config path: \"%s\"", mtenv.CONFIG_PATH) 57 | mtlog.Info("log path: \"%s\" with level \"%s\"", c.LogPath, c.LogLevel) 58 | 59 | fmt.Println("log path: ", c.LogPath) 60 | } 61 | 62 | func CreateManagerApi() (mtmanapi.CManagerInterface, error) { 63 | if !factoryInited { 64 | factory.Init() 65 | 66 | if factory.IsValid() == 0 { 67 | return nil, fmt.Errorf("Failed to load mtmanapi dll.") 68 | } 69 | 70 | if factory.WinsockStartup() != mtmanapi.RET_OK { 71 | return nil, fmt.Errorf("WinsockStartup failed") 72 | } 73 | factoryInited = true 74 | } 75 | 76 | mtapi := factory.Create(apiVer) 77 | if mtapi == nil { 78 | return nil, fmt.Errorf("Failed to create manager interface.") 79 | } 80 | 81 | return mtapi, nil 82 | } 83 | 84 | type PumpReceiver struct { 85 | mtapi mtmanapi.CManagerInterface 86 | reportDb *reportdb.Db 87 | //apiHelper mtmanapi.ManApiHelper 88 | } 89 | 90 | func (r *PumpReceiver) OnPump(code int, typ int, data, param uintptr) { 91 | switch { 92 | case code == mtmanapi.PUMP_START_PUMPING: 93 | fmt.Println(time.Now(), "START PUMPING") 94 | case code == mtmanapi.PUMP_UPDATE_BIDASK: 95 | return 96 | //fmt.Println(time.Now(), "PUMP_UPDATE_BIDASK") 97 | 98 | nTotalTicksGot := 0 99 | pTicksInfo := r.mtapi.TickInfoLast("", &nTotalTicksGot) 100 | //fmt.Println("nTotalTicksGot: ", nTotalTicksGot) 101 | 102 | //tis := make([]*TickInfo, 0, nTotalTicksGot) 103 | 104 | for j := 0; j < nTotalTicksGot; j++ { 105 | ti := mtmanapi.TickInfoArray_getitem(pTicksInfo, j) 106 | symbol := ti.GetSymbol() 107 | if len(symbol) > 1 { 108 | 109 | total := 0 110 | trades := r.mtapi.TradesGetBySymbol(symbol, &total) 111 | 112 | if total < 1 { 113 | continue 114 | } 115 | 116 | for i := 0; i < total; i++ { 117 | t := mtmanapi.TradeRecordArray_getitem(trades, i) 118 | 119 | if t.GetOrder() < 1 || t.GetCmd() > 1 || len(t.GetSymbol()) < 1 { 120 | continue 121 | } 122 | 123 | fmt.Println("ticket: ", t.GetOrder()) 124 | fmt.Println("Symbol: ", t.GetSymbol()) 125 | fmt.Println("price: ", t.GetOpen_price()) 126 | fmt.Println("pl: ", t.GetOpen_price()-ti.GetBid()) 127 | //fmt.Println("bid: ", ti.GetBid()) 128 | 129 | if err := r.reportDb.UpdateTradeProfit(t.GetOrder(), t.GetProfit()); err != nil { 130 | fmt.Println("Error while updating: ", err) 131 | } 132 | 133 | } 134 | r.mtapi.MemFree(trades.Swigcptr()) 135 | 136 | } 137 | //break 138 | } 139 | 140 | r.mtapi.MemFree(pTicksInfo.Swigcptr()) 141 | 142 | case code == mtmanapi.PUMP_UPDATE_TRADES: 143 | fmt.Println(time.Now(), "PUMP_UPDATE_TRADES") 144 | //r.db.AddTrade(&Trade{Ticket}) 145 | 146 | total := 0 147 | trades := r.mtapi.TradesGet(&total) 148 | //fmt.Println("total: ", total) 149 | 150 | if total < 1 { 151 | return 152 | } 153 | 154 | t := mtmanapi.TradeRecordArray_getitem(trades, 0) 155 | 156 | //fmt.Println("ticket: ", t.GetOrder()) 157 | //fmt.Println("pl: ", t.GetProfit()) 158 | //fmt.Println("symbol: ", t.GetSymbol()) 159 | if err := r.reportDb.AddTrade(reportdb.Trade{ 160 | Ticket: t.GetOrder(), 161 | Symbol: t.GetSymbol(), 162 | Login: t.GetLogin(), 163 | Cmd: t.GetCmd(), 164 | Volume: t.GetVolume(), 165 | OpenTime: time.Unix(int64(t.GetOpen_time()), 0), 166 | OpenPrice: t.GetOpen_price(), 167 | Sl: t.GetSl(), 168 | Tp: t.GetTp(), 169 | CloseTime: time.Unix(int64(t.GetClose_time()), 0), 170 | ClosePrice: t.GetClose_price(), 171 | Profit: t.GetProfit(), 172 | Magic: t.GetMagic(), 173 | Comment: t.GetComment(), 174 | }); err != nil { 175 | fmt.Println("Error while saving: ", err) 176 | } 177 | r.mtapi.MemFree(trades.Swigcptr()) 178 | default: 179 | } 180 | } 181 | 182 | func main() { 183 | 184 | initLog() 185 | 186 | config := &Config{} 187 | err := mtconfig.LoadConfig(mtenv.CONFIG_PATH, "config", config) 188 | checkError(err) 189 | 190 | receiver := &PumpReceiver{} 191 | mtmanapi.SetGlobalPumper( 192 | mtmanapi.NewDirectorPumpReceiver(receiver), 193 | ) 194 | 195 | mtapi, err := CreateManagerApi() 196 | if err != nil { 197 | panic(err) 198 | } 199 | 200 | receiver.mtapi = mtapi 201 | //receiver.apiHelper = mtmanapi.NewManApiHelper(mtapi) 202 | fmt.Println("Connecting: ", config.ServerAddr) 203 | errno := mtapi.Connect(config.ServerAddr) 204 | if errno != mtmanapi.RET_OK { 205 | panic(fmt.Sprintf("%d %s", errno, mtapi.ErrorDescription(errno))) 206 | } 207 | 208 | fmt.Println("Connected") 209 | 210 | fmt.Println("Logining in with login/pwd: ", config.Login, "/", config.Password) 211 | errno = mtapi.Login(config.Login, config.Password) 212 | if errno != mtmanapi.RET_OK { 213 | panic(fmt.Sprintf("%d %s", errno, mtapi.ErrorDescription(errno))) 214 | } 215 | 216 | fmt.Println("Logged in") 217 | 218 | flags := mtmanapi.CLIENT_FLAGS_HIDENEWS | 219 | mtmanapi.CLIENT_FLAGS_HIDEMAIL | 220 | mtmanapi.CLIENT_FLAGS_HIDEONLINE 221 | 222 | fmt.Println("mysql connecting to ", config.MysqlUri) 223 | db, err = reportdb.NewDb(config.MysqlUri) 224 | checkError(err) 225 | defer db.Close() 226 | 227 | err = db.Ping() 228 | checkError(err) 229 | 230 | receiver.reportDb = db 231 | 232 | mtmanapi.PumpingSwitchEx(mtapi, flags, 0) 233 | 234 | fmt.Println("working...") 235 | utils.HandleSignal() 236 | } 237 | -------------------------------------------------------------------------------- /apps/reportapi.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "mtreportapi" 6 | ) 7 | 8 | func main() { 9 | flag.Parse() 10 | mtreportapi.Start() 11 | } 12 | -------------------------------------------------------------------------------- /apps/tcapi.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "tcapi" 6 | ) 7 | 8 | func main() { 9 | flag.Parse() 10 | tcapi.Start() 11 | } 12 | -------------------------------------------------------------------------------- /apps/tcdealer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "tcdealer" 6 | ) 7 | 8 | func main() { 9 | flag.Parse() 10 | tcdealer.Start() 11 | } 12 | -------------------------------------------------------------------------------- /apps/ticksredis.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "mtconfig" 8 | "mtenv" 9 | "mtlog" 10 | "mtmanapi" 11 | "mtutils/utils" 12 | "os" 13 | "time" 14 | 15 | "github.com/go-redis/redis" 16 | ) 17 | 18 | type TickInfo struct { 19 | Time int 20 | Symbol string 21 | Bid float64 22 | Ask float64 23 | } 24 | 25 | type Config struct { 26 | RedisAddr string `toml:"redis_addr"` 27 | ServerAddr string `toml:"server_addr"` 28 | Login int `toml:"login"` 29 | Password string `toml:"password"` 30 | } 31 | 32 | var ( 33 | redisClient *redis.Client 34 | factory = mtmanapi.NewCManagerFactory() 35 | factoryInited = false 36 | apiVer = makelong( 37 | mtmanapi.ManAPIProgramBuild, 38 | mtmanapi.ManAPIProgramVersion, 39 | ) 40 | ) 41 | 42 | func makelong(a, b int) int { 43 | return int(uint32(a) | uint32(b)<<16) 44 | } 45 | 46 | func checkError(err error) { 47 | if err != nil { 48 | fmt.Println(err) 49 | os.Exit(1) 50 | } 51 | } 52 | 53 | func initLog() { 54 | c := &mtconfig.Common{} 55 | 56 | err := mtconfig.LoadCommonConfig(mtenv.CONFIG_PATH, c) 57 | checkError(err) 58 | 59 | log, err := mtlog.NewLogger(c.LogPath, c.LogLevel) 60 | checkError(err) 61 | 62 | mtlog.SetDefault(log) 63 | 64 | mtlog.Info("root: \"%s\"", mtenv.DIR) 65 | mtlog.Info("config path: \"%s\"", mtenv.CONFIG_PATH) 66 | mtlog.Info("log path: \"%s\" with level \"%s\"", c.LogPath, c.LogLevel) 67 | 68 | fmt.Println("log path: ", c.LogPath) 69 | } 70 | 71 | func CreateManagerApi() (mtmanapi.CManagerInterface, error) { 72 | if !factoryInited { 73 | factory.Init() 74 | 75 | if factory.IsValid() == 0 { 76 | return nil, fmt.Errorf("Failed to load mtmanapi dll.") 77 | } 78 | 79 | if factory.WinsockStartup() != mtmanapi.RET_OK { 80 | return nil, fmt.Errorf("WinsockStartup failed") 81 | } 82 | factoryInited = true 83 | } 84 | 85 | mtapi := factory.Create(apiVer) 86 | if mtapi == nil { 87 | return nil, fmt.Errorf("Failed to create manager interface.") 88 | } 89 | 90 | return mtapi, nil 91 | } 92 | 93 | type PumpReceiver struct { 94 | mtapi mtmanapi.CManagerInterface 95 | redis *redis.Client 96 | //apiHelper mtmanapi.ManApiHelper 97 | } 98 | 99 | func (r *PumpReceiver) OnPump(code int, typ int, data, param uintptr) { 100 | // fmt.Println(time.Now(), "Pumping", code, typ) 101 | switch { 102 | case code == mtmanapi.PUMP_START_PUMPING: 103 | fmt.Println(time.Now(), "START PUMPING") 104 | // symbols := r.apiHelper.SymbolsGetAll() 105 | // for i := 0; i < int(symbols.Size()); i++ { 106 | // s := symbols.Get(i) 107 | // fmt.Println("Symbol: ", s.GetSymbol()) 108 | // } 109 | // users := r.apiHelper.UsersGet() 110 | // for i := 0; i < int(users.Size()); i++ { 111 | // user := users.Get(i) 112 | // fmt.Println("User:", user.GetLogin(), user.GetName()) 113 | // } 114 | // groups := r.apiHelper.GroupsGet() 115 | // for i := 0; i < int(groups.Size()); i++ { 116 | // group := groups.Get(i) 117 | // fmt.Println("Group:", group.GetGroup()) 118 | // } 119 | // trades := r.apiHelper.TradesGet() 120 | // for i := 0; i < int(trades.Size()); i++ { 121 | // trade := trades.Get(i) 122 | // fmt.Println("Trade:", trade.GetOrder()) 123 | // } 124 | case code == mtmanapi.PUMP_UPDATE_SYMBOLS: 125 | //fmt.Println(time.Now(), "PUMP_UPDATE_SYMBOLS") 126 | case code == mtmanapi.PUMP_UPDATE_BIDASK: 127 | //fmt.Println(time.Now(), "PUMP_UPDATE_BIDASK") 128 | 129 | nTotalTicksGot := 0 130 | pTicksInfo := r.mtapi.TickInfoLast("", &nTotalTicksGot) 131 | //fmt.Println("nTotalTicksGot: ", nTotalTicksGot) 132 | 133 | //tis := make([]*TickInfo, 0, nTotalTicksGot) 134 | 135 | for i := 0; i < nTotalTicksGot; i++ { 136 | ti := mtmanapi.TickInfoArray_getitem(pTicksInfo, i) 137 | if len(ti.GetSymbol()) > 1 { 138 | ts := &TickInfo{ 139 | Symbol: ti.GetSymbol(), 140 | Bid: ti.GetBid(), 141 | Ask: ti.GetAsk(), 142 | Time: ti.GetCtm(), 143 | } 144 | 145 | // tis = append(tis, ts) 146 | jti, _ := json.Marshal(ts) 147 | fmt.Println(string(jti)) 148 | err := r.redis.Publish("bid_ask:"+ts.Symbol, string(jti)).Err() 149 | if err != nil { 150 | fmt.Errorf("Failed to publish. ", err) 151 | } 152 | 153 | } 154 | //break 155 | } 156 | 157 | r.mtapi.MemFree(pTicksInfo.Swigcptr()) 158 | 159 | case code == mtmanapi.PUMP_UPDATE_TRADES: 160 | //fmt.Println(time.Now(), "PUMP_UPDATE_TRADES") 161 | //trades := r.apiHelper.TradesGet() 162 | //for i := 0; i < int(trades.Size()); i++ { 163 | //// trade := trades.Get(i) 164 | // fmt.Println("Update trade:", trade.GetOrder()) 165 | //} 166 | default: 167 | } 168 | } 169 | 170 | func main() { 171 | 172 | initLog() 173 | 174 | config := &Config{} 175 | err := mtconfig.LoadConfig(mtenv.CONFIG_PATH, "config", config) 176 | checkError(err) 177 | 178 | receiver := &PumpReceiver{} 179 | mtmanapi.SetGlobalPumper( 180 | mtmanapi.NewDirectorPumpReceiver(receiver), 181 | ) 182 | 183 | mtapi, err := CreateManagerApi() 184 | if err != nil { 185 | panic(err) 186 | } 187 | 188 | receiver.mtapi = mtapi 189 | //receiver.apiHelper = mtmanapi.NewManApiHelper(mtapi) 190 | fmt.Println("Connecting: ", config.ServerAddr) 191 | errno := mtapi.Connect(config.ServerAddr) 192 | if errno != mtmanapi.RET_OK { 193 | panic(fmt.Sprintf("%d %s", errno, mtapi.ErrorDescription(errno))) 194 | } 195 | 196 | fmt.Println("Connected") 197 | 198 | fmt.Println("Logining in login/pwd: ", config.Login, "/", config.Password) 199 | errno = mtapi.Login(config.Login, config.Password) 200 | if errno != mtmanapi.RET_OK { 201 | panic(fmt.Sprintf("%d %s", errno, mtapi.ErrorDescription(errno))) 202 | } 203 | 204 | fmt.Println("Loged in") 205 | 206 | flags := mtmanapi.CLIENT_FLAGS_HIDENEWS | 207 | mtmanapi.CLIENT_FLAGS_HIDEMAIL | 208 | mtmanapi.CLIENT_FLAGS_HIDEONLINE 209 | 210 | fmt.Println("redis connecting to ", config.RedisAddr) 211 | redisClient := redis.NewClient(&redis.Options{ 212 | Addr: config.RedisAddr, 213 | }) 214 | 215 | pong, err := redisClient.Ping().Result() 216 | checkError(err) 217 | fmt.Println("Redis PING - ", pong) 218 | 219 | receiver.redis = redisClient 220 | 221 | mtmanapi.PumpingSwitchEx(mtapi, flags, 0) 222 | 223 | // amqpClient = NewAmqpClient(config.AmqpUrl, config.AmqpPrefix) 224 | //time.Sleep(time.Hour) 225 | fmt.Println("working...") 226 | utils.HandleSignal() 227 | 228 | // fmt.Println("stopping...") 229 | // mtapi.Disconnect() 230 | // mtapi.Release() 231 | 232 | // fmt.Println("stopped") 233 | } 234 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /conf/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/dealer_api.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import requests 4 | 5 | API_URL = "http://localhost:8080/api" 6 | #API_URL = "http://58.64.189.209:1234/api" 7 | #API_URL = "http://10.211.55.9:8080/api" 8 | TOKEN = "8e3dac16-7d9f-48af-8266-c014d2b07dcb" 9 | 10 | # Online 11 | API_URL = "http://13.112.90.111:8080/api" 12 | TOKEN = "8e5a5278-8f57-4b30-894e-c23eaa1e2534" 13 | 14 | OP_BUY = 0 15 | OP_SELL = 1 16 | OP_BUY_LIMIT = 2 17 | OP_SELL_LIMIT = 3 18 | OP_BUY_STOP = 4 19 | OP_SELL_STOP = 5 20 | 21 | """ 22 | Api request 23 | 24 | POST API_URL 25 | AgentToken: "your token" 26 | { 27 | "func": "funcname" 28 | "args": { 29 | "argname": "argvalue",.... 30 | } 31 | } 32 | 33 | Response: 34 | { 35 | "result": true or false, 36 | "msg": "Error message", 37 | "data": ... 38 | } 39 | 40 | Object: 41 | 42 | Asset: 43 | { 44 | "login": int, 45 | "balance": float64, 46 | "credit": float64, 47 | "margin": float64, 48 | "free_margin": float64, 49 | "margin_level": float64 50 | } 51 | 52 | Trade(Unclosed Trades): 53 | { 54 | "ticket": int, 55 | "login": int, 56 | "symbol": string, 57 | "digits": int, 58 | "cmd": int, 59 | "volume": int, 60 | "open_time": int, 61 | "open_price": float64, 62 | "sl": float64, 63 | "tp": float64, 64 | "comment": string, 65 | "expiration": int 66 | } 67 | """ 68 | 69 | def request_api(func, **kwargs): 70 | start = time.time() 71 | headers = {"AgentToken": TOKEN} 72 | data = json.dumps({"func": func, "args": kwargs}) 73 | r = requests.post(API_URL, data=data, headers=headers) 74 | print "request_api:%s %s cost: %s" % (func, kwargs, (time.time() - start)) 75 | print "resp code:", r.status_code, " body:", r.text 76 | 77 | if r.status_code != 200: 78 | raise Exception("Api request status:{0} error:{1}".format( 79 | r.status_code, r.text)) 80 | 81 | ret = json.loads(r.text) 82 | if ret["result"] == False: 83 | raise Exception("Error: %s", ret["msg"]) 84 | 85 | return ret["data"] 86 | 87 | def open_trade(login, cmd, symbol, volume, **kwargs): 88 | """ Return ticket """ 89 | args = { 90 | "login": login, 91 | "cmd": cmd, 92 | "symbol": symbol, 93 | "volume": volume, 94 | "price": 0, 95 | "sl": 0, 96 | "tp": 0, 97 | "comment": "", 98 | } 99 | 100 | for k, v in kwargs.items(): 101 | if k in args: 102 | args[k] = v 103 | 104 | return request_api("open_trade", **args) 105 | 106 | 107 | def create_account(name, password, email, phone): 108 | """ Return login(int) """ 109 | return request_api("create_account", name=name, password=password, email=email, phone=phone) 110 | 111 | def check_password(login, password): 112 | """ Return bool """ 113 | return request_api("check_password", login=login, password=password) 114 | 115 | def reset_password(login, password): 116 | """ Return None """ 117 | return request_api("reset_password", login=login, password=password) 118 | 119 | def get_asset(login): 120 | """ Return Asset """ 121 | 122 | return request_api("get_asset", login=login) 123 | 124 | def close_trade(login, ticket, volume): 125 | """ Return None """ 126 | return request_api("close_trade", login=login, ticket=ticket, volume=volume) 127 | 128 | def modify_trade(login, ticket, sl, tp): 129 | """ Return None """ 130 | return request_api("modify_trade", login=login, ticket=ticket, sl=sl, tp=tp) 131 | 132 | 133 | # All belows trades means unclosed trades. 134 | def get_trades(): 135 | """ Return Trade Array """ 136 | return request_api("get_trades") 137 | 138 | def get_user_trades(login): 139 | """ Return Trade Array """ 140 | return request_api("get_user_trades", login=login) 141 | 142 | def get_trade(login, ticket): 143 | """ Return Trade or None """ 144 | return request_api("get_trade", login=login, ticket=ticket) 145 | 146 | def get_quote(symbol): 147 | """ Return Quote or None """ 148 | return request_api("get_quote", symbol=symbol) 149 | 150 | def get_quotes(): 151 | """ Return Quote or None """ 152 | return request_api("get_quotes") 153 | -------------------------------------------------------------------------------- /tests/report_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # *_* coding=utf8 *_* 3 | 4 | import time 5 | import json 6 | import requests 7 | 8 | API_URL = "http://127.0.0.1:8081/api" 9 | TOKEN = "8e3dac16-7d9f-48af-8266-c014d2b07dcb" 10 | 11 | # Staging 12 | #API_URL = "http://113.10.168.68:8081/api" 13 | #TOKEN = "f6c0f0cc-7201-42fd-a934-f3a5c39e6a98" 14 | 15 | # Online 16 | #API_URL = "http://13.112.90.111:8081/api" 17 | #TOKEN = "8e5a5278-8f57-4b30-894e-c23eaa1e2534" 18 | 19 | OP_BUY = 0 20 | OP_SELL = 1 21 | OP_BUY_LIMIT = 2 22 | OP_SELL_LIMIT = 3 23 | OP_BUY_STOP = 4 24 | OP_SELL_STOP = 5 25 | OP_BALANCE = 6 26 | OP_CREDIT = 7 27 | 28 | def request_api(func, **kwargs): 29 | start = time.time() 30 | headers = {"AgentToken": TOKEN} 31 | data = json.dumps({"func": func, "args": kwargs}) 32 | r = requests.post(API_URL, data=data, headers=headers) 33 | print "request_api:%s %s cost: %s" % (func, kwargs, (time.time() - start)) 34 | print "resp code:", r.status_code, " body:", r.text 35 | 36 | if r.status_code != 200: 37 | raise Exception("Api request status:{0} error:{1}".format( 38 | r.status_code, r.text)) 39 | 40 | ret = json.loads(r.text) 41 | if ret["result"] == False: 42 | raise Exception("Error: %s" % ret["msg"]) 43 | 44 | return ret["data"] 45 | 46 | """ 47 | { 48 | "func": "page_trade", 49 | "args": { 50 | "page_index": 0, 51 | "page_size": 20, 52 | "filter": {} 53 | } 54 | } 55 | """ 56 | 57 | def page_trade(page_index=0, page_size=20, **filter): 58 | """ 59 | Parameter: 60 | filter: { 61 | "ticket": int, // 订单号 62 | "login": int, // 用户ID 63 | "symbol": string, // 产品 64 | "cmd": int, // 订单类型 65 | "open_time": [string, string], // 开仓时间 时间格式:["2016-12-11 23:00:01", "2016-12-12 23:00:01"] 66 | "close_time": [string, string], // 平仓时间 时间格式:["2016-12-11 23:00:01", "2016-12-12 23:00:01"] 67 | } 68 | Return: 69 | { 70 | "total": int(符合条件的数据总数), 71 | "data": [{ 72 | "ticket":814468, //int 订单号 73 | "login":2003, // int 用户ID 74 | "symbol":"GOLDx", // string 产品类型 75 | "digits":2, 76 | "cmd":1, // int 指令类型 77 | "volume":1000, // int volume / 1000 = lots(手数) 78 | "open_time":"2016-12-16T11:22:57Z", // 开仓时间 79 | "open_price":1132.77, // float64 开仓价格 80 | "close_price":1133.43, // float64 平仓价格 81 | "close_time":"2016-12-16T11:23:27Z", // 平仓时间 如等于 "1970-01-01T00:00:00Z" 表示仍未平仓 82 | "sl":0, // float64 stop loss 83 | "tp":0, // float64 take profit 84 | "expiration":"1970-01-01T00:00:00Z", // 过期时间 限价单过期时间 85 | "conv_rate1":1, // 外汇的转换率(可忽略) 86 | "conv_rate2":1, // 外汇的转换率(可忽略) 87 | "swaps":0, 88 | "profit":-660, // float64 利润 89 | "taxes":0, 90 | "comment":"abcf", // float64 备注 91 | "margin_rate":1, 92 | "timestamp":1481887407, 93 | "modify_time":"2016-12-16T17:55:27Z" 94 | }...] 95 | } 96 | """ 97 | return request_api("page_trade", page_index=page_index, 98 | page_size=page_size, filter=filter) 99 | 100 | def page_user(page_index=0, page_size=20, **filter): 101 | """ 102 | Parameter: 103 | filter: { 104 | "login": int // 用户ID 105 | "name": int // 用户名称 106 | } 107 | Return: 108 | { 109 | "total": int(符合条件的数据总数), 110 | "data": [ 111 | { 112 | "login":2003, // int 用户ID 113 | "name":"TEXT-TW3", // string 用户姓名 114 | "city":"rh-city2", // string 代理编号 115 | "phone":"", 116 | "email":"", 117 | "regdate":"2016-05-05T16:11:54Z", // string 注册时间 118 | "lastdate":"2016-12-13T10:10:15Z", // string 最后登录时间 119 | "balance":-18990.26, // float64 用户余额 120 | "credit":200000 // float64 用户信用 121 | } 122 | , ...] 123 | } 124 | """ 125 | return request_api("page_user", page_index=page_index, 126 | page_size=page_size, filter=filter) 127 | 128 | def page_profit(page_index=0, page_size=20, **filter): 129 | """ 130 | Parameter: 131 | filter: { 132 | "login": int // 用户ID 133 | "name": int // 用户名称 134 | } 135 | Return: 136 | { 137 | "total": int(符合条件的数据总数), 138 | "data": [ 139 | { 140 | "login":2003, // int 用户ID 141 | "history_profit": double , 142 | "history_swaps": double , 143 | "history_commission": double , 144 | "hlistory_taxes": double , 145 | } 146 | , ...] 147 | } 148 | """ 149 | return request_api("page_profit", page_index=page_index, 150 | page_size=page_size, filter=filter) 151 | -------------------------------------------------------------------------------- /tests/tc_api.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import requests 4 | 5 | API_URL = "http://127.0.0.1:8082/api" 6 | TOKEN = "8e3dac16-7d9f-48af-8266-c014d2b07dcb" 7 | 8 | # Online 9 | #API_URL = "http://13.112.90.111:8082/api" 10 | #TOKEN = "8e5a5278-8f57-4b30-894e-c23eaa1e2534" 11 | 12 | def request_api(func, **kwargs): 13 | start = time.time() 14 | headers = {"AgentToken": TOKEN} 15 | data = json.dumps({"func": func, "args": kwargs}) 16 | r = requests.post(API_URL, data=data, headers=headers) 17 | print "request_api:%s %s cost: %s" % (func, kwargs, (time.time() - start)) 18 | print "resp code:", r.status_code, " body:", r.text 19 | 20 | if r.status_code != 200: 21 | raise Exception("Api request status:{0} error:{1}".format( 22 | r.status_code, r.text)) 23 | 24 | ret = json.loads(r.text) 25 | if ret["result"] == False: 26 | raise Exception("Error: %s" % ret["msg"]) 27 | 28 | return ret["data"] 29 | 30 | def set_trader(login): 31 | return request_api("set_trader", login=login) 32 | 33 | def cancel_trader(login): 34 | return request_api("cancel_trader", login=login) 35 | 36 | def force_cancel_trader(login): 37 | return request_api("force_cancel_trader", login=login) 38 | 39 | def page_trader(page_index, page_size, **filter): 40 | return request_api("page_trader", page_index=page_index, 41 | page_size=page_size, filter=filter) 42 | 43 | def follow(trader, client, strategy, size, direction, exit): 44 | return request_api("follow", trader=trader, client=client, 45 | strategy=strategy, size=size, direction=direction, 46 | exit=exit) 47 | 48 | def unfollow(trader, client): 49 | return request_api("unfollow", trader=trader, client=client) 50 | 51 | def page_follow(page_index, page_size, **filter): 52 | return request_api("page_follow", page_index=page_index, 53 | page_size=page_size, filter=filter) 54 | 55 | def page_follow_order(page_index, page_size, **filter): 56 | return request_api("page_follow_order", page_index=page_index, 57 | page_size=page_size, filter=filter) 58 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | import dealer_api 2 | import unittest 3 | 4 | RET_LOGIN = None 5 | RET_TICKET = None 6 | LOGIN = 2003 7 | 8 | class Dealer_ApiTestCase(unittest.TestCase): 9 | 10 | def test_001_create_user(self): 11 | global RET_LOGIN 12 | login = dealer_api.create_account("TestUser", "abcd1234", 13 | "tangwanwan@qq.com", "12322321121") 14 | RET_LOGIN = login 15 | print "RET_LOGIN: ", RET_LOGIN 16 | 17 | def test_002_check_password(self): 18 | global RET_LOGIN 19 | dealer_api.check_password(RET_LOGIN, "abcd1234") 20 | 21 | def test_003_reset_password(self): 22 | global RET_LOGIN 23 | dealer_api.reset_password(RET_LOGIN, "abcd2345") 24 | 25 | def test_004_recheck_password(self): 26 | global RET_LOGIN 27 | dealer_api.check_password(RET_LOGIN, "abcd2345") 28 | 29 | def test_005_get_asset(self): 30 | global RET_LOGIN 31 | ret = dealer_api.get_asset(RET_LOGIN) 32 | 33 | print ret 34 | 35 | def test_006_open_trade(self): 36 | global LOGIN, RET_TICKET 37 | ret = dealer_api.open_trade(LOGIN, dealer_api.OP_BUY, "GOLDx", 10) 38 | 39 | print ret 40 | 41 | RET_TICKET = ret["ticket"] 42 | print "RET_TICKET", RET_TICKET 43 | 44 | def test_007_get_trade(self): 45 | global LOGIN, RET_TICKET 46 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 47 | 48 | print ret 49 | 50 | def test_008_get_user_trades(self): 51 | global LOGIN 52 | ret = dealer_api.get_user_trades(LOGIN) 53 | 54 | def test_009_get_trades(self): 55 | global LOGIN 56 | 57 | def test_010_modify_trade(self): 58 | global LOGIN, RET_TICKET 59 | dealer_api.modify_trade(LOGIN, RET_TICKET, 0, 1350) 60 | 61 | def test_011_get_trade(self): 62 | global LOGIN, RET_TICKET 63 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 64 | print ret 65 | 66 | def test_012_close_trade(self): 67 | global LOGIN, RET_TICKET 68 | ret = dealer_api.close_trade(LOGIN, RET_TICKET, 10) 69 | 70 | def test_013_get_trade(self): 71 | global LOGIN, RET_TICKET 72 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 73 | print ret 74 | 75 | if __name__ == "__main__": 76 | unittest.main() 77 | -------------------------------------------------------------------------------- /tests/test_dealer_api.py: -------------------------------------------------------------------------------- 1 | import dealer_api 2 | import unittest 3 | 4 | RET_LOGIN = None 5 | RET_TICKET = None 6 | LOGIN = 2003 7 | 8 | class Dealer_ApiTestCase(unittest.TestCase): 9 | 10 | def test_001_create_user(self): 11 | global RET_LOGIN 12 | login = dealer_api.create_account("TestUser", "abcd1234", 13 | "tangwanwan@qq.com", "12322321121") 14 | RET_LOGIN = login 15 | print "RET_LOGIN: ", RET_LOGIN 16 | 17 | def test_002_check_password(self): 18 | global RET_LOGIN 19 | dealer_api.check_password(RET_LOGIN, "abcd1234") 20 | 21 | def test_003_reset_password(self): 22 | global RET_LOGIN 23 | dealer_api.reset_password(RET_LOGIN, "abcd2345") 24 | 25 | def test_004_recheck_password(self): 26 | global RET_LOGIN 27 | dealer_api.check_password(RET_LOGIN, "abcd2345") 28 | 29 | def test_005_get_asset(self): 30 | global RET_LOGIN 31 | ret = dealer_api.get_asset(RET_LOGIN) 32 | 33 | print ret 34 | 35 | def test_006_open_trade(self): 36 | global LOGIN, RET_TICKET 37 | ticket = dealer_api.open_trade(LOGIN, dealer_api.OP_BUY, "GOLDx", 10) 38 | 39 | RET_TICKET = ticket 40 | print "RET_TICKET", RET_TICKET 41 | 42 | def test_007_get_trade(self): 43 | global LOGIN, RET_TICKET 44 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 45 | 46 | print ret 47 | 48 | def test_008_get_user_trades(self): 49 | global LOGIN 50 | ret = dealer_api.get_user_trades(LOGIN) 51 | 52 | def test_009_get_trades(self): 53 | global LOGIN 54 | 55 | def test_010_modify_trade(self): 56 | global LOGIN, RET_TICKET 57 | dealer_api.modify_trade(LOGIN, RET_TICKET, 0, 1350) 58 | 59 | def test_011_get_trade(self): 60 | global LOGIN, RET_TICKET 61 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 62 | print ret 63 | 64 | def test_012_close_trade(self): 65 | global LOGIN, RET_TICKET 66 | ret = dealer_api.close_trade(LOGIN, RET_TICKET, 10) 67 | 68 | def test_013_get_trade(self): 69 | global LOGIN, RET_TICKET 70 | ret = dealer_api.get_trade(LOGIN, RET_TICKET) 71 | print ret 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /tests/test_quote_receive.py: -------------------------------------------------------------------------------- 1 | import websocket 2 | import thread 3 | import time 4 | 5 | TOKEN = "8e3dac16-7d9f-48af-8266-c014d2b07dcb" 6 | QUOTE_URL = "ws://localhost:8080/ws/quote" 7 | 8 | #QUOTE_URL = "ws://58.64.189.209:1234/ws/quote" 9 | 10 | 11 | # Online 12 | QUOTE_URL = "ws://13.112.90.111:8080/ws/quote" 13 | TOKEN = "8e5a5278-8f57-4b30-894e-c23eaa1e2534" 14 | 15 | 16 | def on_message(ws, message): 17 | print message 18 | 19 | def on_error(ws, error): 20 | print "### on open ###" 21 | print error 22 | 23 | def on_close(ws): 24 | print "### closed ###" 25 | 26 | def on_open(ws): 27 | print "### on open ###" 28 | 29 | if __name__ == "__main__": 30 | websocket.enableTrace(True) 31 | ws = websocket.WebSocketApp( 32 | QUOTE_URL, header = ["AgentToken: %s" % TOKEN], 33 | on_message = on_message, 34 | on_error = on_error, 35 | on_close = on_close 36 | ) 37 | ws.on_open = on_open 38 | ws.run_forever() 39 | --------------------------------------------------------------------------------