├── .DS_Store ├── bin ├── .DS_Store ├── bin │ ├── .DS_Store │ └── robot ├── conf │ ├── config.json │ ├── option.json │ ├── simulate.json │ └── trade.json └── robot.exe ├── indictor ├── indictors.go ├── kdj.go ├── macd.go └── trend.go ├── main ├── main.go └── monitor.go ├── provider ├── account.go ├── dbsource.go ├── kline.go └── kline_test.go ├── statistical └── statistical.go ├── strategy ├── .DS_Store ├── hedge │ ├── .DS_Store │ ├── hedge.go │ ├── option.go │ ├── syncAccount.go │ ├── ticker.go │ └── tradeOrder.go ├── kdj │ ├── kdj.go │ ├── option.go │ └── tradeOrder.go ├── macd │ ├── macd-k1-quota.go │ └── macd-k1-standard.go ├── quantify │ ├── option.go │ └── quantify.go └── strategy.go └── trader ├── cache.go ├── cache_test.go └── trader.go /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/.DS_Store -------------------------------------------------------------------------------- /bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/bin/.DS_Store -------------------------------------------------------------------------------- /bin/bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/bin/bin/.DS_Store -------------------------------------------------------------------------------- /bin/bin/robot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/bin/bin/robot -------------------------------------------------------------------------------- /bin/conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "127.0.0.1:9090", 3 | "drive_name": "sqlite3", 4 | "dsn": "btcrobot.s3db", 5 | "cookie_secret": "btcrobot", 6 | "pid": "pid/btcrobot.pid", 7 | 8 | "from_email": "xxxx@gmail.com", 9 | 10 | "bitvc_base_url" : "https://www.bitvc.com/", 11 | "bitvc_login_url" : "https://www.bitvc.com/user/login", 12 | 13 | "peatio_base_url": "https://peatio.com", 14 | "peatio_api_url": "https://peatio.com/api/v2/markets", 15 | "peatio_kline_url": "https://peatio.com/api/v2/k.json?market=btccny&limit=100&period=%d", 16 | "peatio_trade_detail": "http://peatio.com/staticmarket/detail.html?jsoncallback=jQuery%s_%d&_=%d", 17 | "peatio_depth_url":"https://peatio.com/api/v2/depth.json?market=btccny&limit=10", 18 | 19 | 20 | "hb_base_url": "https://www.huobi.com/", 21 | "hb_api_url": "https://api.huobi.com/apiv3", 22 | "hb_kline_url": "http://api.huobi.com/staticmarket/btc_kline_%03d_json.js", 23 | "hb_trade_detail": "http://api.huobi.com/staticmarket/detail_btc_json.js", 24 | 25 | "ok_base_url":"https://www.okcoin.cn/", 26 | "ok_api_userinfo":"https://www.okcoin.cn/api/userinfo.do", 27 | "ok_api_getorder":"https://www.okcoin.cn/api/getorder.do", 28 | "ok_api_cancelorder": "https://www.okcoin.cn/api/cancelorder.do", 29 | "ok_api_trade":"https://www.okcoin.cn/api/trade.do", 30 | "ok_kline_url":"https://www.okcoin.cn/kline/period.do?step=%d&symbol=%s&nonce=%d", 31 | "ok_depth_url":"https://www.okcoin.cn/api/depth.do?symbol=%s", 32 | 33 | "debug": "0", 34 | "errorconsole": "0", 35 | "infoconsole": "1", 36 | "fatalconsole": "1", 37 | "debugconsole": "0", 38 | 39 | "env": "product" 40 | } 41 | -------------------------------------------------------------------------------- /bin/conf/option.json: -------------------------------------------------------------------------------- 1 | { 2 | "simulation":"1", 3 | 4 | "resttime":"5", 5 | "profit":"0.55", 6 | "peramount":"0.01", 7 | "colddowntime":"10", 8 | "tofast":"1", 9 | 10 | "tradecenter": "okcoin", 11 | "strategy": "MACD-K1-Q", 12 | "totalHour": "20000", 13 | "klines":"1,5,15,30,60", 14 | "tick_interval": "001", 15 | "timeout":"300", 16 | "tradeAmount":"0.05", 17 | "stoploss":"0.005", 18 | "stoploss_resttime":"3", 19 | "minprofit":"0.5", 20 | "stepprice":"0.02", 21 | "splitordercount":"5", 22 | 23 | "kdjperiod":"9", 24 | "jlow":"20", 25 | "jhight":"70", 26 | 27 | "signalPeriod": "9", 28 | "shortEMA": "12", 29 | "longEMA": "26", 30 | "MACDbuyThreshold": "0.1", 31 | "MACDsellThreshold": "0.1", 32 | 33 | "enable_trading": "1", 34 | "basePrice": "3827", 35 | "buyThreshold": "0.0001", 36 | "datacenter": "huobi", 37 | "disable_backtesting": "0", 38 | "discipleMode": "0", 39 | "discipleValue": "0.0", 40 | "enable_email": "0", 41 | "fluctuation": "1", 42 | "sellThreshold": "0.0082", 43 | "slippage": "0.001", 44 | "symbol": "btc_cny", 45 | "to_email": "xxxxx@qq.com" 46 | } 47 | -------------------------------------------------------------------------------- /bin/conf/simulate.json: -------------------------------------------------------------------------------- 1 | { 2 | "BTC": "0", 3 | "CNY": "24833.741095158035", 4 | "LTC": "0" 5 | } 6 | -------------------------------------------------------------------------------- /bin/conf/trade.json: -------------------------------------------------------------------------------- 1 | { 2 | "buyamount": "0.1", 3 | "buyinterval": "11", 4 | "buyprice": "100", 5 | "buytimes": "11", 6 | "maxbuyamountratio": "11", 7 | "maxsellamountratio": "10", 8 | "sellamount": "0.1", 9 | "sellinterval": "10", 10 | "sellprice": "75", 11 | "selltimes": "10" 12 | } 13 | -------------------------------------------------------------------------------- /bin/robot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/bin/robot.exe -------------------------------------------------------------------------------- /indictor/indictors.go: -------------------------------------------------------------------------------- 1 | package indictor 2 | 3 | import ( 4 | // "math" 5 | ) 6 | 7 | func getEMAdifAt(emaShort, emaLong []float64, idx int) float64 { 8 | var cel = emaLong[idx] 9 | var ces = emaShort[idx] 10 | if cel == 0 { 11 | return 0 12 | } else { 13 | return 100 * (ces - cel) / ((ces + cel) / 2) 14 | } 15 | } 16 | 17 | func getEMAdif(emaShort, emaLong []float64) []float64 { 18 | // loop through data 19 | var EMAdifs []float64 20 | length := len(emaShort) 21 | for i := 0; i < length; i++ { 22 | EMAdifAt := getEMAdifAt(emaShort, emaLong, i) 23 | EMAdifs = append(EMAdifs, EMAdifAt) 24 | } 25 | 26 | return EMAdifs 27 | } 28 | 29 | /* Function based on the idea of an exponential moving average. 30 | * 31 | * Formula: EMA = Price(t) * k + EMA(y) * (1 - k) 32 | * t = today y = yesterday N = number of days in EMA k = 2/(2N+1) 33 | * 34 | * @param Price : array of y variables. 35 | * @param periods : The amount of "days" to average from. 36 | * @return an array containing the EMA. 37 | **/ 38 | func EMA(Price []float64, periods int) []float64 { 39 | var t float64 40 | y := 0.0 41 | n := float64(periods) 42 | var k float64 43 | k = 2 / (n + 1) 44 | var ema float64 // exponential moving average. 45 | 46 | var periodArr []float64 47 | var startpos int 48 | length := len(Price) 49 | var emaLine []float64 = make([]float64, length) 50 | 51 | // loop through data 52 | for i := 0; i < length; i++ { 53 | if Price[i] != 0 { 54 | startpos = i + 1 55 | break 56 | } else { 57 | emaLine[i] = 0 58 | } 59 | } 60 | 61 | for i := startpos; i < length; i++ { 62 | periodArr = append(periodArr, Price[i]) 63 | 64 | // 0: runs if the periodArr has enough points. 65 | // 1: set currentvalue (today). 66 | // 2: set last value. either by past avg or yesterdays ema. 67 | // 3: calculate todays ema. 68 | if periods == len(periodArr) { 69 | 70 | t = Price[i] 71 | 72 | if y == 0 { 73 | y = arrayAvg(periodArr) 74 | } else { 75 | ema = (t * k) + (y * (1 - k)) 76 | y = ema 77 | } 78 | //四舍五入保留4位小数 79 | //value := math.Trunc(y*1e4+0.5) * 1e-4 80 | emaLine[i] = y 81 | 82 | // remove first value in array. 83 | periodArr = periodArr[1:] 84 | 85 | } else { 86 | 87 | emaLine[i] = 0 88 | } 89 | 90 | } 91 | 92 | return emaLine 93 | } 94 | 95 | /* Function that returns average of an array's values. 96 | * 97 | **/ 98 | func arrayAvg(arr []float64) float64 { 99 | sum := 0.0 100 | 101 | for i := 0; i < len(arr); i++ { 102 | sum = sum + arr[i] 103 | } 104 | 105 | return (sum / (float64)(len(arr))) 106 | } 107 | 108 | func Highest(Price []float64, periods int) []float64 { 109 | var periodArr []float64 110 | length := len(Price) 111 | var HighestLine []float64 = make([]float64, length) 112 | 113 | // Loop through the entire array. 114 | for i := 0; i < length; i++ { 115 | // add points to the array. 116 | periodArr = append(periodArr, Price[i]) 117 | // 1: Check if array is "filled" else create null point in line. 118 | // 2: Calculate average. 119 | // 3: Remove first value. 120 | if periods == len(periodArr) { 121 | HighestLine[i] = arrayHighest(periodArr) 122 | 123 | // remove first value in array. 124 | periodArr = periodArr[1:] 125 | } else { 126 | HighestLine[i] = 0 127 | } 128 | } 129 | 130 | return HighestLine 131 | } 132 | 133 | func Lowest(Price []float64, periods int) []float64 { 134 | var periodArr []float64 135 | length := len(Price) 136 | var LowestLine []float64 = make([]float64, length) 137 | 138 | // Loop through the entire array. 139 | for i := 0; i < length; i++ { 140 | // add points to the array. 141 | periodArr = append(periodArr, Price[i]) 142 | // 1: Check if array is "filled" else create null point in line. 143 | // 2: Calculate average. 144 | // 3: Remove first value. 145 | if periods == len(periodArr) { 146 | LowestLine[i] = arrayLowest(periodArr) 147 | 148 | // remove first value in array. 149 | periodArr = periodArr[1:] 150 | } else { 151 | LowestLine[i] = 0 152 | } 153 | } 154 | 155 | return LowestLine 156 | } 157 | 158 | /* Function based on the idea of a simple moving average. 159 | * @param Price : array of y variables. 160 | * @param periods : The amount of "days" to average from. 161 | * @return an array containing the SMA. 162 | **/ 163 | func SMA(Price []float64, periods int) []float64 { 164 | var periodArr []float64 165 | length := len(Price) 166 | var smLine []float64 = make([]float64, length) 167 | 168 | // Loop through the entire array. 169 | for i := 0; i < length; i++ { 170 | // add points to the array. 171 | periodArr = append(periodArr, Price[i]) 172 | 173 | // 1: Check if array is "filled" else create null point in line. 174 | // 2: Calculate average. 175 | // 3: Remove first value. 176 | if periods == len(periodArr) { 177 | smLine[i] = arrayAvg(periodArr) 178 | 179 | // remove first value in array. 180 | periodArr = periodArr[1:] 181 | } else { 182 | smLine[i] = 0 183 | } 184 | } 185 | 186 | return smLine 187 | } 188 | 189 | func arrayLowest(Price []float64) float64 { 190 | length := len(Price) 191 | var lowest = Price[0] 192 | 193 | // Loop through the entire array. 194 | for i := 1; i < length; i++ { 195 | if Price[i] < lowest { 196 | lowest = Price[i] 197 | } 198 | } 199 | 200 | return lowest 201 | } 202 | 203 | func arrayHighest(Price []float64) float64 { 204 | length := len(Price) 205 | var highest = Price[0] 206 | 207 | // Loop through the entire array. 208 | for i := 1; i < length; i++ { 209 | if Price[i] > highest { 210 | highest = Price[i] 211 | } 212 | } 213 | 214 | return highest 215 | } 216 | 217 | func getMACDdifAt(emaShort, emaLong []float64, idx int) float64 { 218 | var ces = emaShort[idx] 219 | var cel = emaLong[idx] 220 | if cel == 0 { 221 | return 0 222 | } else { 223 | return (ces - cel) 224 | } 225 | } 226 | 227 | func getMACDdif(emaShort, emaLong []float64) []float64 { 228 | // loop through data 229 | var MACDdif []float64 230 | length := len(emaShort) 231 | for i := 0; i < length; i++ { 232 | MACDdifAt := getMACDdifAt(emaShort, emaLong, i) 233 | MACDdif = append(MACDdif, MACDdifAt) 234 | } 235 | 236 | return MACDdif 237 | } 238 | 239 | func getMACDSignal(MACDdif []float64, signalPeriod int) []float64 { 240 | signal := EMA(MACDdif, signalPeriod) 241 | return signal 242 | } 243 | 244 | func getMACDHistogramAt(MACDdif, MACDSignal []float64, idx int) float64 { 245 | var dif = MACDdif[idx] 246 | var signal = MACDSignal[idx] 247 | if signal == 0 { 248 | return 0 249 | } else { 250 | return dif - signal 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /indictor/kdj.go: -------------------------------------------------------------------------------- 1 | package indictor 2 | 3 | import ( 4 | . "github.com/qshuai162/qian/common/model" 5 | ) 6 | 7 | type kdj struct { 8 | k float64 9 | d float64 10 | j float64 11 | } 12 | 13 | var kdjcaches map[int64]kdj = make(map[int64]kdj) 14 | 15 | func GetKDJ(records []Record, period int) (arrk []float64, arrd []float64, arrj []float64) { 16 | length := len(records) 17 | for i := 0; i < length; i++ { 18 | k, d, j := float64(0), float64(0), float64(0) 19 | val, ok := kdjcaches[records[i].Time] 20 | if ok { 21 | k = val.k 22 | d = val.d 23 | j = val.j 24 | } else { 25 | var periodLowArr, periodHighArr []float64 26 | rsv := 0.0 27 | lowest := 0.0 28 | highest := 0.0 29 | if i >= period-1 { 30 | for j := 0; j < period; j++ { 31 | periodLowArr = append(periodLowArr, records[i-j].Low) 32 | periodHighArr = append(periodHighArr, records[i-j].High) 33 | } 34 | lowest = arrayLowest(periodLowArr) 35 | highest = arrayHighest(periodHighArr) 36 | rsv = float64(100) * (records[i].Close - lowest) / (highest - lowest) 37 | } 38 | if i == 0 { 39 | k = (2.0/3)*0 + 1.0/3*rsv 40 | d = (2.0/3)*0 + 1.0/3*k 41 | } else { 42 | k = (2.0/3)*arrk[i-1] + 1.0/3*rsv 43 | d = (2.0/3)*arrd[i-1] + 1.0/3*k 44 | } 45 | j = 3*k - 2*d 46 | 47 | if k < 0 { 48 | k = 0 49 | } 50 | if k > 100 { 51 | k = 100 52 | } 53 | if d < 0 { 54 | d = 0 55 | } 56 | if d > 100 { 57 | d = 100 58 | } 59 | if j < 0 { 60 | j = 0 61 | } 62 | if j > 100 { 63 | j = 100 64 | } 65 | 66 | if i != length-1 { 67 | kdj := new(kdj) 68 | kdj.k = k 69 | kdj.d = d 70 | kdj.j = j 71 | kdjcaches[records[i].Time] = *kdj 72 | } 73 | } 74 | arrk = append(arrk, k) 75 | arrd = append(arrd, d) 76 | arrj = append(arrj, j) 77 | } 78 | return 79 | } 80 | -------------------------------------------------------------------------------- /indictor/macd.go: -------------------------------------------------------------------------------- 1 | package indictor 2 | 3 | import ( 4 | . "github.com/qshuai162/qian/common/model" 5 | // "math" 6 | ) 7 | 8 | type MACD struct { 9 | EMAShort float64 10 | EMALong float64 11 | DIF float64 12 | DEA float64 13 | BAR float64 14 | } 15 | 16 | func GetMACD(records []Record, periodShort int, periodLong int, periodDIF int) (macd []MACD) { 17 | 18 | if len(records) == 0 { 19 | return nil 20 | } 21 | var price []float64 22 | for _, v := range records { 23 | price = append(price, v.Close) 24 | } 25 | 26 | shortEma := EMA(price, periodShort) 27 | longEma := EMA(price, periodLong) 28 | macd = make([]MACD, len(price)) 29 | 30 | macd[0].EMAShort = price[0] 31 | macd[0].EMALong = price[0] 32 | macd[0].DIF = 0 33 | macd[0].DEA = 0 34 | macd[0].BAR = 0 35 | 36 | for i := 1; i < len(price); i++ { 37 | macd[i].EMAShort = shortEma[i] 38 | macd[i].EMALong = longEma[i] 39 | macd[i].DIF = macd[i].EMAShort - macd[i].EMALong 40 | //DEA(MACD)= 前一日DEA×8/10+今日DIF×2/10 41 | //DIFF平均值9日(DEA)=9日DIFF之和/9 42 | if i >= periodDIF { 43 | sum := 0.0 44 | for j := 0; j < periodDIF; j++ { 45 | sum += macd[i-j].DIF 46 | } 47 | avg := sum / float64(periodDIF) 48 | //value := math.Trunc(avg*1e4+0.5) * 1e-4 49 | macd[i].DEA = avg 50 | } else { 51 | macd[i].DEA = 0 52 | } 53 | macd[i].BAR = 2 * (macd[i].DIF - macd[i].DEA) 54 | } 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /indictor/trend.go: -------------------------------------------------------------------------------- 1 | package indictor 2 | 3 | import ( 4 | // "fmt" 5 | // . "github.com/qshuai162/qian/qrobot/trader" 6 | // "strconv" 7 | // "strings" 8 | ) 9 | 10 | type TrendOp struct { 11 | Up5 float64 12 | Up15 float64 13 | Up30 float64 14 | Up60 float64 15 | Down5 float64 16 | Down15 float64 17 | Down30 float64 18 | Down60 float64 19 | Offset float64 20 | } 21 | 22 | func GetTrend(macdmap map[int][]MACD, op TrendOp) float64 { 23 | factor := 0.0 24 | periods := []int{5, 15, 30, 60} 25 | for _, period := range periods { 26 | macd := macdmap[period] 27 | length := len(macd) 28 | if macd[length-1].BAR > 0 { 29 | //上升趋势 30 | switch period { 31 | case 5: 32 | factor += op.Up5 33 | case 15: 34 | factor += op.Up15 35 | case 30: 36 | factor += op.Up30 37 | case 60: 38 | factor += op.Up60 39 | default: 40 | factor += 0 41 | } 42 | } else { 43 | switch period { 44 | case 5: 45 | factor += op.Down5 46 | case 15: 47 | factor += op.Down15 48 | case 30: 49 | factor += op.Down30 50 | case 60: 51 | factor += op.Down60 52 | default: 53 | factor += 0 54 | } 55 | } 56 | } 57 | return factor + op.Offset 58 | } 59 | -------------------------------------------------------------------------------- /main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | // "github.com/qshuai162/btcrobot/src/config" 6 | // "io/ioutil" 7 | "math/rand" 8 | // "os" 9 | // "path/filepath" 10 | "runtime" 11 | // "strconv" 12 | "time" 13 | ) 14 | 15 | func init() { 16 | runtime.GOMAXPROCS(runtime.NumCPU()) 17 | // 设置随机数种子 18 | rand.Seed(time.Now().Unix()) 19 | } 20 | 21 | func main() { 22 | printBanner() 23 | 24 | //go webui.RunServer() 25 | 26 | RunRobot() 27 | } 28 | 29 | func printBanner() { 30 | version := "V0.1" 31 | fmt.Println("[ ---------------------------------------------------------->>> ") 32 | fmt.Println(" BTC/LTC自动化算法交易引擎", version) 33 | fmt.Println(" *@警告:API key和密码存放在conf/secret.json文件内,共享给他人前请务必删除,注意账号安全!!") 34 | fmt.Println(" <<<----------------------------------------------------------] ") 35 | } 36 | -------------------------------------------------------------------------------- /main/monitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | // . "github.com/qshuai162/qian/common/model" 6 | "github.com/qshuai162/qian/qrobot/provider" 7 | "github.com/qshuai162/qian/qrobot/strategy/hedge" 8 | // "github.com/qshuai162/qian/qrobot/strategy/kdj" 9 | "github.com/qshuai162/qian/qrobot/trader" 10 | // . "github.com/qshuai162/qian/tool/config" 11 | "github.com/qshuai162/qian/common/logger" 12 | // "strconv" 13 | "time" 14 | ) 15 | 16 | func RobotWorker() { 17 | ticker := time.NewTicker(2 * time.Second) 18 | defer ticker.Stop() 19 | fmt.Println("trade robot start working...") 20 | 21 | Centers := []string{"okcoin", "chbtc", "huobi","btcc"} 22 | p := new(provider.Provider) 23 | t := new(trader.Trader) 24 | 25 | op := hedge.GetOpion() 26 | op.Centers = Centers 27 | op.HdConfig = [][]float64{ 28 | []float64{1, 0, 0.55, 0.05}, 29 | []float64{2, 0, 0.55, 0.05}, 30 | []float64{1, 2, 0.55, 0.05}, 31 | []float64{3, 0, 0.55, 0.05}, 32 | []float64{1, 3, 0.55, 0.05}, 33 | []float64{3, 2, 0.55, 0.05}} 34 | // op.HdConfig=[][]float64{ 35 | // []float64{0,1,0.4,0.05,500} } 36 | 37 | // kdjop:=kdj.GetOpion() 38 | 39 | now:=time.Now().Unix(); 40 | hd := hedge.NewHedge(now, op, p, t) 41 | // kdjs:=kdj.NewKdjStrategy(now,kdjop,hd) 42 | 43 | go func() { 44 | for _ = range ticker.C { 45 | curt := time.Now().Unix() 46 | logger.Infoln("") 47 | logger.Infof("%s\n", time.Unix(curt, 0).Format("2006-01-02 15:04:05")) 48 | //准备数据 49 | cc := make(chan int) 50 | // var records []Record 51 | go func(){ 52 | hd.HandleTicker(curt) 53 | cc<-1 54 | }() 55 | go func(){ 56 | // _, records = p.GetKLine(kdjop.Center, kdjop.KPeriod, curt) 57 | cc<-1 58 | }() 59 | count := 0 60 | for { 61 | count += <-cc 62 | if count >= 2 { 63 | break 64 | } 65 | } 66 | go hd.Tick(curt) 67 | // go func(){ 68 | // if len(records)>0 && hd.Centers[0].CurTicker.Last>0.1{ 69 | // kdjs.Tick(curt,records,hd.Centers[0].CurTicker) 70 | // } 71 | // }() 72 | } 73 | }() 74 | 75 | time.Sleep(24 * 365 * 100 * time.Hour) 76 | } 77 | 78 | const worker_number = 1 79 | 80 | type message struct { 81 | normal bool // true means exit normal, otherwise 82 | state map[string]interface{} // goroutine state 83 | } 84 | 85 | func worker(mess chan message) { 86 | defer func() { 87 | exit_message := message{state: make(map[string]interface{})} 88 | i := recover() 89 | if i != nil { 90 | exit_message.normal = false 91 | } else { 92 | exit_message.normal = true 93 | } 94 | mess <- exit_message 95 | }() 96 | 97 | RobotWorker() 98 | } 99 | 100 | func supervisor(mess chan message) { 101 | for i := 0; i < worker_number; i++ { 102 | m := <-mess 103 | switch m.normal { 104 | case true: 105 | logger.Infoln("exit normal, nothing serious!") 106 | case false: 107 | logger.Infoln("exit abnormal, something went wrong") 108 | } 109 | } 110 | } 111 | 112 | func RunRobot() { 113 | mess := make(chan message, 10) 114 | for i := 0; i < worker_number; i++ { 115 | go worker(mess) 116 | } 117 | 118 | supervisor(mess) 119 | } 120 | -------------------------------------------------------------------------------- /provider/account.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | . "github.com/qshuai162/qian/common/model" 5 | 6 | ) 7 | 8 | func (p *Provider) GetAccount(center string) (code int, result Account) { 9 | result, success := getMarketAPI(center).GetAccount() 10 | if success { 11 | code = 0 12 | } else { 13 | code = 1 14 | } 15 | return 16 | } 17 | 18 | 19 | 20 | func (p *Provider) GetOrders(center string) (code int, orders []Order) { 21 | _, orders = getMarketAPI(center).GetOrders() 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /provider/dbsource.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | // "fmt" 5 | "github.com/qshuai162/qian/api/chbtc" 6 | "github.com/qshuai162/qian/api/huobi" 7 | "github.com/qshuai162/qian/api/okcoin" 8 | "github.com/qshuai162/qian/api/btcc" 9 | "github.com/qshuai162/qian/common/logger" 10 | . "github.com/qshuai162/qian/common/model" 11 | "gopkg.in/mgo.v2" 12 | "gopkg.in/mgo.v2/bson" 13 | ) 14 | 15 | type MarketAPI interface { 16 | GetKLine(peroid int, size int) (ret bool, records []Record) 17 | GetTicker() (ret bool, m Ticker) 18 | GetOrders() (ret bool, orders []Order) 19 | GetAccount() (account Account, ret bool) 20 | } 21 | 22 | type Provider struct { 23 | } 24 | 25 | var dbstr string = "121.41.46.25:27017" 26 | 27 | func getMarketAPI(center string) (marketAPI MarketAPI) { 28 | if center == "huobi" { 29 | marketAPI = huobi.NewHuobi() 30 | } else if center == "okcoin" { 31 | marketAPI = okcoin.NewOkcoin() 32 | } else if center == "chbtc" { 33 | marketAPI = chbtc.NewCHBTC() 34 | } else if center == "btcc" { 35 | marketAPI = btcc.NewBTCC() 36 | } else { 37 | logger.Fatalln("Please config the market center...") 38 | panic(-1) 39 | } 40 | return 41 | } 42 | 43 | func (p *Provider) GetTicker(center string, t int64) (code int, result Ticker) { 44 | _, result = getMarketAPI(center).GetTicker() 45 | return 46 | } 47 | 48 | //差值平均值 49 | func (p *Provider) GetDifAvg(center1, center2 string, start, long int64) (int, float64) { 50 | session, err := mgo.Dial(dbstr) 51 | if err != nil { 52 | panic(err) 53 | } 54 | session.SetMode(mgo.Monotonic, true) 55 | defer session.Close() 56 | 57 | ok := session.DB(center1).C("ticker") 58 | hb := session.DB(center2).C("ticker") 59 | var hb_results []Ticker 60 | hb.Find(bson.M{"time": bson.M{"$gt": start - long}}).All(&hb_results) 61 | var ok_results []Ticker 62 | ok.Find(bson.M{"time": bson.M{"$gt": start - long}}).All(&ok_results) 63 | return 0, calcAvg(&ok_results, &hb_results, start-long+1, long) 64 | } 65 | 66 | func calcAvg(ok_results, hb_results *[]Ticker, start int64, long int64) float64 { 67 | 68 | m1 := make(map[int64]float64) 69 | for i := 0; i < len(*ok_results); i++ { 70 | m1[(*ok_results)[i].Time] = (*ok_results)[i].Last 71 | } 72 | m2 := make(map[int64]float64) 73 | for i := 0; i < len(*hb_results); i++ { 74 | m2[(*hb_results)[i].Time] = (*hb_results)[i].Last 75 | } 76 | sum := 0.0 77 | count := 0 78 | for i := start; i < start+long; i++ { 79 | v1, ok1 := m1[i] 80 | v2, ok2 := m2[i] 81 | if ok1 && ok2 && v1 != 0 && v2 != 0 { 82 | offset := v1 - v2 83 | sum += offset 84 | count++ 85 | } 86 | } 87 | avg := sum / (float64(count)) 88 | logger.Infof("Offset AVG: %.2f\n", avg) 89 | return avg 90 | } 91 | -------------------------------------------------------------------------------- /provider/kline.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | // "fmt" 5 | . "github.com/qshuai162/qian/common/model" 6 | // "gopkg.in/mgo.v2" 7 | // "gopkg.in/mgo.v2/bson" 8 | "math" 9 | "time" 10 | ) 11 | 12 | type KCache struct { 13 | Time int64 14 | Records []Record 15 | } 16 | 17 | var k1Cache KCache = *new(KCache) 18 | 19 | func (p *Provider) GetKLine(center string, period int, end int64) (int, []Record) { 20 | var k1datas []Record 21 | if k1Cache.Time == end { 22 | k1datas = k1Cache.Records 23 | } else { 24 | _, k1datas = getMarketAPI(center).GetKLine(1, 60*48) 25 | k1Cache.Time = end 26 | k1Cache.Records = k1datas 27 | } 28 | records := createKByK1(k1datas, period) 29 | return 0, records 30 | } 31 | 32 | func createKByK1(k1data []Record, period int) (records []Record) { 33 | if period == 1 { 34 | return k1data 35 | } 36 | start := int64(math.Ceil(float64(k1data[0].Time)/float64(60*period)) * float64(60*period)) 37 | length := len(k1data) 38 | for i := 0; i < length; i++ { 39 | if k1data[i].Time < start { 40 | continue 41 | } else { 42 | record := new(Record) 43 | record.TimeStr = time.Unix(k1data[i].Time, 0).Format("2006-01-02 15:04:05") 44 | record.Time = k1data[i].Time 45 | record.Open = k1data[i].Open 46 | record.Volumn = k1data[i].Volumn 47 | record.Close = k1data[i].Close 48 | record.High = k1data[i].High 49 | record.Low = k1data[i].Low 50 | for j := 1; j < period && i+j < length; j++ { 51 | k1 := k1data[i+j] 52 | if k1.Open != 0 { 53 | record.Volumn += k1.Volumn 54 | record.Close = k1.Close 55 | if k1.High > record.High { 56 | record.High = k1.High 57 | } 58 | if k1.Low < record.Low { 59 | record.Low = k1.Low 60 | } 61 | } 62 | } 63 | i += period - 1 64 | records = append(records, *record) 65 | } 66 | } 67 | return 68 | } 69 | -------------------------------------------------------------------------------- /provider/kline_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | // "fmt" 5 | // idc "github.com/qshuai162/qian/qrobot/indictor" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // func Test_Kline(t *testing.T) { 11 | // p := new(Provider) 12 | // // start := time.Now().Unix() - 10*60 13 | // // end := time.Now().Unix() 14 | // // fmt.Println(sart, end) 15 | // // for i := 0; i < 3; i++ { 16 | // cur := time.Now().Unix() 17 | // t1 := time.Now().UnixNano() 18 | // _, records := p.GetKLine("okcoin", 1, cur) 19 | // fmt.Println(records[len(records)-1]) 20 | // t2 := time.Now().UnixNano() 21 | // _, records = p.GetKLine("okcoin", 5, cur) 22 | // macd5 := idc.GetMACD(records, 12, 26, 9) 23 | // fmt.Println(records[len(records)-1]) 24 | // t3 := time.Now().UnixNano() 25 | // _, records = p.GetKLine("okcoin", 15, cur) 26 | // macd15 := idc.GetMACD(records, 12, 26, 9) 27 | // fmt.Println(records[len(records)-1]) 28 | // t4 := time.Now().UnixNano() 29 | // _, records = p.GetKLine("okcoin", 30, cur) 30 | // macd30 := idc.GetMACD(records, 12, 26, 9) 31 | // fmt.Println(records[len(records)-1]) 32 | // t5 := time.Now().UnixNano() 33 | // _, records = p.GetKLine("okcoin", 60, cur) 34 | // macd60 := idc.GetMACD(records, 12, 26, 9) 35 | // fmt.Println(records[len(records)-1]) 36 | // t6 := time.Now().UnixNano() 37 | // fmt.Println(t2-t1, t3-t2, t4-t3, t5-t4, t6-t5, len(records)) 38 | // macdmap := make(map[int][]idc.MACD) 39 | // macdmap[5] = macd5 40 | // macdmap[15] = macd15 41 | // macdmap[30] = macd30 42 | // macdmap[60] = macd60 43 | // fmt.Println(macd5[len(macd5)-1]) 44 | // fmt.Println(macd15[len(macd15)-1]) 45 | // fmt.Println(macd30[len(macd30)-1]) 46 | // fmt.Println(macd60[len(macd60)-1]) 47 | // // trend := idc.GetTrend(macdmap) 48 | // // fmt.Println(trend) 49 | // // time.Sleep((30 * time.Second)) 50 | // // } 51 | // // fmt.Println(k[length-2], j[length-2], d[length-2]) 52 | // // fmt.Println(k[length-1], j[length-1], d[length-1]) 53 | // } 54 | 55 | func Test_Ticker(t *testing.T) { 56 | p := new(Provider) 57 | p.GetDifAvg("okcoin", "chbtc", time.Now().Unix(), 4800) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /statistical/statistical.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import ( 4 | "github.com/qshuai162/qian/common/logger" 5 | "time" 6 | ) 7 | 8 | func statistical(hd *Hedge) { 9 | for { 10 | if hd.Centers[0].CurTicker.Last != 0 { 11 | t := time.Now().Unix() 12 | syncAccountHandle(hd) 13 | logger.Profitf(",%s", time.Unix(t, 0).Format("2006-01-02 15:04:05")) 14 | total := newCData("total") 15 | realas := 0.0 16 | calas := 0.0 17 | for i := 0; i < len(hd.Centers); i++ { 18 | c := hd.Centers[i] 19 | realAsset := (c.BTC+c.FBTC)*c.CurTicker.Last + c.CNY + c.FCNY 20 | calAsset := (c.BTC+c.FBTC)*3000 + c.CNY + c.FCNY 21 | 22 | total.BTC += c.BTC 23 | total.FBTC += c.FBTC 24 | total.CNY += c.CNY 25 | total.FCNY += c.FCNY 26 | realas += realAsset 27 | calas += calAsset 28 | logger.Profitf(",%s,%.4f,%.4f,%.4f,%.4f,,%.4f,%.4f,,%.2f,,%.4f,%.4f", c.Center, c.BTC, c.CNY, c.FBTC, c.FCNY, c.BTC+c.FBTC, c.CNY+c.FCNY, c.CurTicker.Last, realAsset, calAsset) 29 | } 30 | c := total 31 | logger.Profitf(",%s,%.4f,%.4f,%.4f,%.4f,,%.4f,%.4f,,,,%.4f,%.4f", c.Center, c.BTC, c.CNY, c.FBTC, c.FCNY, c.BTC+c.FBTC, c.CNY+c.FCNY, realas, calas) 32 | logger.Profitf("") 33 | time.Sleep(time.Hour) 34 | } else { 35 | time.Sleep(10 * time.Second) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /strategy/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/strategy/.DS_Store -------------------------------------------------------------------------------- /strategy/hedge/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiano/qrobot/396191d2b4760f687995111384a57385b141e4af/strategy/hedge/.DS_Store -------------------------------------------------------------------------------- /strategy/hedge/hedge.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import ( 4 | "github.com/qshuai162/qian/common/logger" 5 | . "github.com/qshuai162/qian/common/model" 6 | "math" 7 | // . "github.com/qshuai162/qian/qrobot/trader" 8 | ) 9 | 10 | //CData 交易中心数据 11 | type CData struct { 12 | Center string 13 | PreLast float64 14 | CurTicker Ticker 15 | Btc float64 16 | Cny float64 17 | } 18 | 19 | func newCData(center string) *CData { 20 | c := new(CData) 21 | c.Center = center 22 | c.CurTicker = *new(Ticker) 23 | return c 24 | } 25 | 26 | type hedgeCouple struct { 27 | Center1 *CData 28 | Center2 *CData 29 | PreAvg float64 30 | PrevBuyTime int64 31 | ThawingTime int64 32 | MinProfit float64 //单边最小利润 33 | PerAmount float64 //单次交易数量 34 | } 35 | 36 | func newCouple(center1 *CData, center2 *CData, minprofit, peramount float64) *hedgeCouple { 37 | c := new(hedgeCouple) 38 | c.Center1 = center1 39 | c.Center2 = center2 40 | c.MinProfit = minprofit 41 | c.PerAmount = peramount 42 | return c 43 | } 44 | 45 | //Hedge 对冲结构 46 | type Hedge struct { 47 | Option HedgeOp 48 | Centers []CData 49 | Couples []hedgeCouple 50 | Trader *ITrader 51 | Provider *IProvider 52 | FailedOrders chan FailedOrder 53 | CurTime int64 54 | Tickers map[string][]Ticker 55 | PreCalcAvgTime int64 56 | FirstAvg bool 57 | } 58 | 59 | //NewHedge 新建 60 | func NewHedge(t int64, Option HedgeOp, Provider IProvider, Trader ITrader) *Hedge { 61 | hd := new(Hedge) 62 | hd.Option = Option 63 | hd.Trader = &Trader 64 | hd.Provider = &Provider 65 | hd.FailedOrders = make(chan FailedOrder, 999) 66 | hd.Tickers=make(map[string][]Ticker) 67 | hd.CurTime=t 68 | hd.PreCalcAvgTime=t 69 | hd.FirstAvg=false 70 | for i := 0; i < len(Option.Centers); i++ { 71 | hd.Centers = append(hd.Centers, *newCData(Option.Centers[i])) 72 | } 73 | for i := 0; i < len(Option.HdConfig); i++ { 74 | cf := hd.Option.HdConfig[i] 75 | hd.Couples = append(hd.Couples, *newCouple(&hd.Centers[int(cf[0])], &hd.Centers[int(cf[1])], cf[2], cf[3])) 76 | } 77 | 78 | hd.calDifAvg(t) 79 | go handlefailedOrders(hd) 80 | if !hd.Option.Simulation{ 81 | go hd.startSyncAccount() 82 | } 83 | return hd 84 | } 85 | 86 | 87 | func (hd *Hedge) log(formate string, args ...interface{}) { 88 | if hd.Option.LogEnabled { 89 | logger.Infof(formate, args...) 90 | } 91 | } 92 | 93 | //Tick 周期响应 94 | func (hd *Hedge) Tick(t int64) bool { 95 | hd.CurTime=t 96 | if hd.FirstAvg{ 97 | cc := make(chan int) 98 | for i := 0; i < len(hd.Couples); i++ { 99 | go hd.hedgeCore(i, &hd.Couples[i], t, cc) 100 | } 101 | count := 0 102 | for { 103 | count += <-cc 104 | if count >= len(hd.Option.HdConfig) { 105 | break 106 | } 107 | } 108 | } 109 | return true 110 | } 111 | 112 | func (hd *Hedge) hedgeCore(flag int, couple *hedgeCouple, t int64, cc chan int) { 113 | center1 := couple.Center1 114 | center2 := couple.Center2 115 | if center1.Center == "" || center2.Center == "" { 116 | cc <- 1 117 | return 118 | } 119 | oticker := center1.CurTicker 120 | hticker := center2.CurTicker 121 | 122 | check := checkData(&oticker, &hticker) 123 | if !check { 124 | hd.log("%s %s Ticker 数据异常\n", center1.Center, center2.Center) 125 | cc <- 1 126 | return 127 | } 128 | 129 | offset := oticker.Last - hticker.Last 130 | hd.log("%s - %s = %.2f\t上值:%.2f\t下值:%.2f\n", center1.Center, center2.Center, offset, couple.PreAvg+couple.MinProfit, couple.PreAvg-couple.MinProfit) 131 | 132 | if center1.PreLast>1 && center2.PreLast>1{ 133 | if (math.Abs(oticker.Last-center1.PreLast) >= hd.Option.TooFast ||math.Abs(hticker.Last-center2.PreLast) >= hd.Option.TooFast) { 134 | hd.log("%s %s价格变化过快,暂停%d秒\n", center1.Center, center2.Center, hd.Option.ColdDownTime) 135 | couple.ThawingTime = t+int64(hd.Option.ColdDownTime) 136 | } 137 | if (math.Abs(oticker.Last-center1.PreLast) >=hd.Option.Fast ||math.Abs(hticker.Last-center2.PreLast) >= hd.Option.Fast) { 138 | if couple.ThawingTime-t0 { 147 | hd.log("%s %s市场冷却中,等待%d秒\n", center1.Center, center2.Center,couple.ThawingTime-t) 148 | cc <- 1 149 | return 150 | } 151 | 152 | if t-couple.PrevBuyTime > int64(hd.Option.RestTime) { 153 | a:=0.03 154 | // at:=couple.MinProfit/2-a 155 | if offset > couple.PreAvg+couple.MinProfit { 156 | buyPrice := hticker.Last + a 157 | sellPrice := oticker.Last - a 158 | hd.log("%s -> %s 数量:%.2f\n", center1.Center, center2.Center, couple.PerAmount) 159 | if center1.Btc >= couple.PerAmount && center2.Cny >= couple.PerAmount*buyPrice { 160 | // hd.asynTrade(flag, center2, center1, buyPrice, sellPrice, couple.PerAmount) 161 | hd.syncTrade(flag,center1.Center,center2,center1,buyPrice,sellPrice,couple.PerAmount) 162 | // hd.syncSafeTrade(flag, center1.Center, center2, center1, buyPrice, sellPrice, couple.PerAmount,couple.DoneTime) 163 | couple.PrevBuyTime = t 164 | } else { 165 | hd.log("%s %s资产不足!!!\n", center1.Center, center2.Center) 166 | } 167 | } 168 | 169 | if offset < couple.PreAvg-couple.MinProfit { 170 | buyPrice := oticker.Last + a 171 | sellPrice := hticker.Last - a 172 | hd.log("%s -> %s 数量:%.2f\n", center2.Center, center1.Center, couple.PerAmount) 173 | if center2.Btc >= couple.PerAmount && center1.Cny >= couple.PerAmount*buyPrice { 174 | // hd.syncSafeTrade(flag, center1.Center, center1, center2, buyPrice, sellPrice, couple.PerAmount,couple.DoneTime) 175 | hd.syncTrade(flag,center1.Center,center1,center2,buyPrice,sellPrice,couple.PerAmount) 176 | // hd.asynTrade(flag, center1, center2, buyPrice, sellPrice, couple.PerAmount) 177 | couple.PrevBuyTime = t 178 | } else { 179 | hd.log("%s %s资产不足!!!\n", center1.Center, center2.Center) 180 | } 181 | } 182 | } else { 183 | hd.log("%s %s休息%d秒\n", center1.Center, center2.Center, hd.Option.RestTime) 184 | } 185 | cc <- 1 186 | } -------------------------------------------------------------------------------- /strategy/hedge/option.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import ( 4 | // . "github.com/qshuai162/qian/common/config" 5 | // "strconv" 6 | ) 7 | 8 | type HedgeOp struct { 9 | Centers []string 10 | HdConfig [][]float64 11 | RestTime int //休息时间,单位秒 12 | Fast float64 13 | ColdTime int 14 | TooFast float64 //价格变化过快判定值 15 | ColdDownTime int //价格变化过快等待时间 ,单位秒 16 | Refreshtime int //中值刷新时间,单位秒 17 | LogEnabled bool //是否开启日志 18 | Simulation bool //是否是模拟 19 | } 20 | 21 | func GetOpion() HedgeOp { 22 | op := new(HedgeOp) 23 | op.RestTime = 3 24 | op.Fast=0.7 25 | op.ColdTime=20 26 | op.TooFast = 1.8 27 | op.ColdDownTime = 360 28 | op.Refreshtime = 300 29 | op.LogEnabled = true 30 | op.Simulation = false 31 | return *op 32 | } 33 | -------------------------------------------------------------------------------- /strategy/hedge/syncAccount.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import( 4 | "time" 5 | "strconv" 6 | ) 7 | 8 | func (hd *Hedge) SyncAccount(){ 9 | for i := 0; i < len(hd.Centers); i++ { 10 | if hd.Centers[i].Center != "" { 11 | code2, ok2 := (*hd.Provider).GetAccount(hd.Centers[i].Center) 12 | if code2 == 0 { 13 | hd.Centers[i].Btc,_=strconv.ParseFloat(ok2.Available_btc,64) 14 | hd.Centers[i].Cny,_=strconv.ParseFloat(ok2.Available_cny,64) 15 | } 16 | } 17 | } 18 | } 19 | 20 | func (hd *Hedge) startSyncAccount() { 21 | for { 22 | time.Sleep(5 * time.Second) 23 | hd.SyncAccount() 24 | } 25 | } -------------------------------------------------------------------------------- /strategy/hedge/ticker.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import( 4 | . "github.com/qshuai162/qian/common/model" 5 | "math" 6 | // "github.com/qshuai162/qian/common/logger" 7 | ) 8 | 9 | func (hd *Hedge) calDifAvg(t int64) { 10 | // logger.Infoln(t,hd.PreCalcAvgTime,hd.Option.Refreshtime,len(hd.Tickers[hd.Centers[0].Center])) 11 | if t-hd.PreCalcAvgTime > int64(hd.Option.Refreshtime) { 12 | datas:=hd.Tickers 13 | for i := 0; i < len(hd.Couples); i++ { 14 | couple := &hd.Couples[i] 15 | if couple.Center1.Center != "" && couple.Center2.Center != "" { 16 | avg := hd.cal(datas[couple.Center1.Center], datas[couple.Center2.Center]) 17 | couple.PreAvg = avg 18 | hd.log("%s %s Offset AVG: %.2f\n", couple.Center1.Center,couple.Center2.Center,avg) 19 | } 20 | } 21 | hd.PreCalcAvgTime = t 22 | length:=len(hd.Tickers[hd.Centers[0].Center]) 23 | start:=int(math.Ceil(float64(length)*0.3)) 24 | for i:=0;i= len(hd.Centers) { 64 | break 65 | } 66 | } 67 | } 68 | hd.calDifAvg(t) 69 | } 70 | 71 | func (hd *Hedge) getTicker(center *CData, t int64, cc chan int) { 72 | if center.Center != "" { 73 | _, (*center).CurTicker = (*hd.Provider).GetTicker((*center).Center, t) 74 | hd.Tickers[(*center).Center]=append(hd.Tickers[(*center).Center],(*center).CurTicker) 75 | hd.log("%s buy:%.2f\tlast:%.2f\tsell:%.2f\t变化:%.2f\n", (*center).Center, (*center).CurTicker.Buy, (*center).CurTicker.Last, (*center).CurTicker.Sell, (*center).CurTicker.Last-(*center).PreLast) 76 | } 77 | if cc!=nil{ 78 | cc <- 1 79 | } 80 | } 81 | 82 | //检查tick数据 83 | func checkData(oticker, hticker *Ticker) bool { 84 | if oticker.Last < 1 || hticker.Last < 1 { 85 | return false 86 | } 87 | if oticker.Buy-oticker.Last > 0.3 || hticker.Buy-hticker.Last > 0.3 { 88 | return false 89 | } 90 | if oticker.Last-oticker.Sell > 0.3 || hticker.Last-hticker.Sell > 0.3 { 91 | return false 92 | } 93 | return true 94 | } 95 | 96 | -------------------------------------------------------------------------------- /strategy/hedge/tradeOrder.go: -------------------------------------------------------------------------------- 1 | package hedge 2 | 3 | import ( 4 | "github.com/qshuai162/qian/common/logger" 5 | "github.com/qshuai162/qian/common/model" 6 | "strconv" 7 | "time" 8 | // "math" 9 | ) 10 | 11 | func (hd *Hedge) Trade(flag string,center *CData, method string, price, amount float64) int { 12 | valid := false 13 | hd.log("%s btc:%.4f cny:%.4f \n",center.Center,center.Btc,center.Cny) 14 | if method == "buy" && center.Cny >= amount*price { 15 | center.Cny -= amount * price 16 | // center.Btc+=amount 17 | valid = true 18 | } 19 | if method == "sell" && center.Btc >= amount { 20 | center.Btc -= amount 21 | // center.Cny+=amount*price 22 | valid = true 23 | } 24 | 25 | if valid { 26 | var code, id int 27 | if method == "buy" { 28 | code, id = (*hd.Trader).Buy(center.Center, price, amount) 29 | } else { 30 | code, id = (*hd.Trader).Sell(center.Center, price, amount) 31 | } 32 | if code == 0 && id != 0 { 33 | if method == "buy" { 34 | logger.TradeSingle(flag, ",%s,%d,%d,%.2f,%.2f,%.2f", center.Center, -1, id, amount, price, -1*amount*price) 35 | } else { 36 | logger.TradeSingle(flag, ",%s,%d,%d,%.2f,%.2f,%.2f", center.Center, 1, id, amount, price, amount*price) 37 | } 38 | return id 39 | } 40 | } 41 | return 0 42 | } 43 | 44 | func (hd *Hedge) CancelOrder(flag string,oid int,center *CData,method string,price,amount float64) bool{ 45 | _, ret := (*hd.Trader).Cancel(center.Center, oid) 46 | if ret { 47 | if method=="buy"{ 48 | center.Cny+=amount*price 49 | logger.TradeSingle(flag, ",%s,%d,%d,%.2f,%.2f,%.2f,cancel", center.Center, 1, oid,amount,price, amount*price) 50 | }else{ 51 | center.Btc+=amount 52 | logger.TradeSingle(flag, ",%s,%d,%d,%.2f,%.2f,%.2f,cancel", center.Center, -1, oid,amount,price, -1*amount*price) 53 | } 54 | return true 55 | } 56 | logger.TradeSingle(flag, ",%s,,%d,%.2f,%.2f,,cancel faild", center.Center, oid, amount,price) 57 | return false 58 | } 59 | //GetOrder 获取委托信息 60 | func (hd *Hedge) GetOrder(center *CData,oid int) model.Order{ 61 | _,or:=(*hd.Trader).GetOrder(center.Center,oid) 62 | return or 63 | } 64 | 65 | 66 | //同步下单 67 | func (hd *Hedge) syncTrade(flag int, first string, buyCenter, sellCenter *CData, buyPrice, sellPrice, amount float64) { 68 | if buyCenter.Center == first { 69 | bid := hd.Trade(strconv.Itoa(flag), buyCenter, "buy", buyPrice, amount) 70 | if bid != 0 { 71 | sid := hd.Trade(strconv.Itoa(flag), sellCenter, "sell", sellPrice, amount) 72 | if sid == 0 { 73 | if !hd.CancelOrder(strconv.Itoa(flag),bid,buyCenter,"buy",buyPrice,amount){ 74 | hd.FailedOrders <- FailedOrder{flag, sellCenter, "sell", sellPrice, amount} 75 | } 76 | } 77 | } 78 | } 79 | if sellCenter.Center == first { 80 | sid := hd.Trade(strconv.Itoa(flag), sellCenter, "sell", sellPrice, amount) 81 | if sid != 0{ 82 | bid := hd.Trade(strconv.Itoa(flag), buyCenter, "buy", buyPrice, amount) 83 | if bid == 0 { 84 | if !hd.CancelOrder(strconv.Itoa(flag),sid,sellCenter,"sell",sellPrice,amount){ 85 | hd.FailedOrders <- FailedOrder{flag, buyCenter, "buy", buyPrice, amount} 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | //同步安全下单 93 | func (hd *Hedge) syncSafeTrade(flag int, first string, buyCenter, sellCenter *CData, buyPrice, sellPrice, amount float64,donetime int) { 94 | if buyCenter.Center == first { 95 | bid := hd.Trade(strconv.Itoa(flag), buyCenter, "buy", buyPrice, amount) 96 | if bid != 0 { 97 | time.Sleep(time.Duration(donetime)*time.Millisecond) 98 | or:=hd.GetOrder(buyCenter,bid) 99 | sellamount:=or.Amount 100 | if or.Status!=2 { 101 | logamount:=or.Amount-or.Deal_amount 102 | if or.Deal_amount<0.01{ 103 | logamount=or.Amount 104 | } 105 | result:=hd.CancelOrder(strconv.Itoa(flag),bid,buyCenter,"buy",or.Price,logamount) 106 | if result { 107 | sellamount=or.Deal_amount 108 | } 109 | } 110 | if sellamount>=0.01{ 111 | sid := hd.Trade(strconv.Itoa(flag), sellCenter, "sell", sellPrice, sellamount) 112 | if sid == 0 { 113 | hd.FailedOrders <- FailedOrder{flag, sellCenter, "sell", sellPrice, sellamount} 114 | } 115 | } 116 | } 117 | } 118 | if sellCenter.Center == first { 119 | sid := hd.Trade(strconv.Itoa(flag), sellCenter, "sell", sellPrice, amount) 120 | if sid != 0{ 121 | time.Sleep(time.Duration(donetime)*time.Millisecond) 122 | or:=hd.GetOrder(sellCenter,sid) 123 | buyamount:=or.Amount 124 | if or.Status!=2 { 125 | logamount:=or.Amount-or.Deal_amount 126 | if or.Deal_amount<0.01{ 127 | logamount=or.Amount 128 | } 129 | result:=hd.CancelOrder(strconv.Itoa(flag),sid,sellCenter,"sell",or.Price,logamount) 130 | if result { 131 | buyamount=or.Deal_amount 132 | } 133 | } 134 | if buyamount>=0.01{ 135 | bid := hd.Trade(strconv.Itoa(flag), buyCenter, "buy", buyPrice, buyamount) 136 | if bid == 0 { 137 | hd.FailedOrders <- FailedOrder{flag, buyCenter, "buy", buyPrice, buyamount} 138 | } 139 | } 140 | } 141 | } 142 | } 143 | //异步下单 144 | func (hd *Hedge) asynTrade(flag int, buyCenter, sellCenter *CData, buyPrice, sellPrice, amount float64) { 145 | go func() { 146 | id := hd.Trade(strconv.Itoa(flag), buyCenter, "buy", buyPrice, amount) 147 | if id == 0 { 148 | hd.FailedOrders <- FailedOrder{flag, buyCenter, "buy", buyPrice, amount} 149 | } 150 | }() 151 | go func() { 152 | id := hd.Trade(strconv.Itoa(flag), sellCenter, "sell", sellPrice, amount) 153 | if id == 0 { 154 | hd.FailedOrders <- FailedOrder{flag, sellCenter, "sell", sellPrice, amount} 155 | } 156 | }() 157 | } 158 | 159 | //FailedOrder 失败的委托 160 | type FailedOrder struct { 161 | Flag int 162 | Center *CData 163 | Type string 164 | Price float64 165 | Amount float64 166 | } 167 | 168 | func handlefailedOrders(hd *Hedge) { 169 | for { 170 | bo := <-hd.FailedOrders 171 | time.Sleep(3 * time.Second) 172 | logger.Infof("开始重试失败的委托: %s %s %.2f %.2f\n", bo.Center.Center, bo.Type, bo.Price, bo.Amount) 173 | price := bo.Price 174 | amount := bo.Amount 175 | if (bo.Type == "buy" && bo.Center.Cny >= price*amount) || 176 | (bo.Type == "sell" && bo.Center.Btc >= amount) { 177 | go func(){ 178 | curPrice:=price 179 | if curPrice!=0 && 180 | ((bo.Type=="buy" && bo.Center.CurTicker.LastcurPrice+0.1)) { 182 | curPrice=bo.Center.CurTicker.Last 183 | } 184 | id := hd.Trade(strconv.Itoa(bo.Flag), bo.Center, bo.Type, curPrice, amount) 185 | if id == 0 { 186 | hd.FailedOrders <- FailedOrder{bo.Flag, bo.Center, bo.Type, price, amount} 187 | } 188 | }() 189 | } else { 190 | hd.log("%s 资产不足!!!\n", bo.Center.Center) 191 | hd.FailedOrders <- FailedOrder{bo.Flag, bo.Center, bo.Type, price, amount} 192 | } 193 | } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /strategy/kdj/kdj.go: -------------------------------------------------------------------------------- 1 | package kdj 2 | 3 | import ( 4 | "github.com/qshuai162/qian/common/logger" 5 | idc "github.com/qshuai162/qian/qrobot/indictor" 6 | . "github.com/qshuai162/qian/qrobot/trader" 7 | "github.com/qshuai162/qian/qrobot/strategy/hedge" 8 | // "strconv" 9 | . "github.com/qshuai162/qian/common/model" 10 | 11 | // "fmt" 12 | // "time" 13 | ) 14 | 15 | type KdjStrategy struct { 16 | Option KdjOp 17 | PrevBuyTime map[string]int64 18 | PrevStoplossTime int64 19 | Orders map[string]*OCaches 20 | HD *hedge.Hedge 21 | 22 | } 23 | 24 | func (kdj *KdjStrategy) getOrders(str string) *OCaches{ 25 | temp:=kdj.Orders[str] 26 | return temp 27 | } 28 | 29 | func NewKdjStrategy(t int64, Option KdjOp,hd *hedge.Hedge) *KdjStrategy { 30 | kdj := new(KdjStrategy) 31 | kdj.Option = Option 32 | kdj.PrevStoplossTime = t - 3600*24*365 33 | kdj.HD=hd 34 | 35 | kdj.Orders=make(map[string]*OCaches) 36 | kdj.Orders["KDJ1"]=new(OCaches) 37 | kdj.Orders["MACD1"]=new(OCaches) 38 | kdj.getOrders("KDJ1").SaveHistory=true 39 | kdj.getOrders("MACD1").SaveHistory=true 40 | 41 | kdj.PrevBuyTime=make(map[string]int64) 42 | kdj.PrevBuyTime["KDJ1"]=t-3600*24*365 43 | kdj.PrevBuyTime["MACD1"]=t-3600*24*365 44 | return kdj 45 | } 46 | 47 | func (kdj *KdjStrategy) Tick(t int64,records []Record,ticker Ticker) bool { 48 | length:=len(records) 49 | if kdj.Option.Simulation { 50 | //kdj.Orders.SimDone(kdj.Option.Center,ticker.Last) 51 | } else { 52 | _, orders := (*kdj.HD.Provider).GetOrders(kdj.Option.Center) 53 | kdj.getOrders("KDJ1").Sync(orders) 54 | kdj.getOrders("MACD1").Sync(orders) 55 | } 56 | 57 | go kdj.kdjCore(t,records,length,ticker) 58 | go kdj.macdCore(t,records,length,ticker) 59 | return true 60 | } 61 | 62 | func (kdj *KdjStrategy) kdjCore(t int64,records []Record,length int,ticker Ticker){ 63 | // K线为白,D线为黄,J线为红,K in middle 64 | k, d, j := idc.GetKDJ(records, 9) 65 | kdj.log("LAST: d(黄线)%0.2f\tk(白线)%0.2f\tj(红线)%0.2f\n", d[length-2], k[length-2], j[length-2]) 66 | kdj.log("CURR: d(黄线)%0.2f\tk(白线)%0.2f\tj(红线)%0.2f\n", d[length-1], k[length-1], j[length-1]) 67 | //买 68 | if j[length-4]>j[length-3] && j[length-3]KDJ buy\n") 70 | kdj.Buy("KDJ1",t,ticker) 71 | } 72 | //卖 73 | if j[length-1]macd[length-3].BAR && macd[length-2].BAR > macd[length-3].BAR && macd[length-1].BAR > macd[length-2].BAR { 88 | kdj.log("--->MACD buy\n") 89 | kdj.Buy("MACD1",t,ticker) 90 | } 91 | //卖 92 | if macd[length-1].BAR= 60*5 { 11 | if len(kdj.getOrders(str).Buy_NotSell()) < kdj.Option.MaxCount && t-kdj.PrevBuyTime[str] >= 15 { 12 | amount := kdj.Option.PerAmount 13 | price:=ticker.Sell+0.03 14 | bid:=kdj.HD.Trade(str,&kdj.HD.Centers[0],"buy",price,amount) 15 | if bid!=0{ 16 | kdj.getOrders(str).Buy(t,kdj.Option.Center,bid,price,amount) 17 | // kdj.getOrders(str).PrintAll() 18 | kdj.PrevBuyTime[str] = t 19 | return true 20 | } 21 | } 22 | } else { 23 | kdj.log("止损保护中,不允许交易\n") 24 | } 25 | return false 26 | } 27 | 28 | func (kdj *KdjStrategy) Sell(str string,t int64,ticker Ticker) bool { 29 | sellPrice := ticker.Buy - 0.03 30 | forsell := kdj.getOrders(str).WaitForSell(sellPrice - kdj.Option.MinProfit) 31 | for i := 0; i < len(forsell); i++ { 32 | buyorder := kdj.getOrders(str).Find(forsell[i]) 33 | sid:=kdj.HD.Trade(str,&kdj.HD.Centers[0],"sell",sellPrice,buyorder.Order_Amount) 34 | if sid!=0{ 35 | kdj.getOrders(str).Sell(t,kdj.Option.Center,buyorder.Id,sid,sellPrice,buyorder.Order_Amount) 36 | } 37 | } 38 | return true 39 | } 40 | 41 | func (kdj *KdjStrategy) TryCancel(str string,t int64,ticker Ticker) { 42 | forcancel := kdj.getOrders(str).WaitForCancel(t - int64(kdj.Option.TimeOut)) 43 | for i := 0; i < len(forcancel); i++ { 44 | cancelorder := kdj.getOrders(str).Find(forcancel[i]) 45 | ret:=kdj.HD.CancelOrder(str,cancelorder.Id,&kdj.HD.Centers[0],cancelorder.Type,cancelorder.Price,cancelorder.Order_Amount) 46 | if ret{ 47 | kdj.getOrders(str).Cancel(cancelorder.Id) 48 | } 49 | } 50 | } 51 | 52 | func (kdj *KdjStrategy) TryStoploss(str string,t int64,ticker Ticker){ 53 | p := ticker.Buy - 0.1 54 | forstop := kdj.getOrders(str).WaitForStopLoss(p, kdj.Option.StopLoss) 55 | if len(forstop) != 0 { 56 | kdj.log("stop loss") 57 | logger.TradeSingle(str, ",%s,,,,,,%s", kdj.HD.Centers[0].Center,"stoploss") 58 | for i := 0; i < len(forstop); i++ { 59 | stoporder := kdj.getOrders(str).Find(forstop[i]) 60 | if stoporder.RefId == 0 { 61 | sid:=kdj.HD.Trade("KDJ1",&kdj.HD.Centers[0],"sell",p,stoporder.Order_Amount) 62 | if sid!=0{ 63 | kdj.getOrders(str).Sell(t,kdj.Option.Center,stoporder.Id,sid,p,stoporder.Order_Amount) 64 | } 65 | } 66 | } 67 | kdj.PrevStoplossTime = t 68 | } 69 | } -------------------------------------------------------------------------------- /strategy/macd/macd-k1-quota.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | . "github.com/qshuai162/qian/common/config" 5 | . "github.com/qshuai162/qian/common/model" 6 | // "github.com/qshuai162/btcrobot/src/huobi" 7 | "fmt" 8 | "github.com/qshuai162/qian/common/logger" 9 | idc "github.com/qshuai162/qian/qrobot/indictor" 10 | . "github.com/qshuai162/qian/qrobot/trader" 11 | "strconv" 12 | "time" 13 | ) 14 | 15 | type MACDStrategyQK1 struct { 16 | PrevBuyTime time.Time 17 | Orders OCaches 18 | } 19 | 20 | func init() { 21 | macd := new(MACDStrategyQK1) 22 | macd.PrevBuyTime = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 23 | Register("MACD-K1-Q", macd) 24 | } 25 | 26 | // KDJ-EX strategy 27 | func (sty *MACDStrategyQK1) Tick() bool { 28 | center := Option["tradecenter"] 29 | market := GetMarket(center) 30 | trader := NewTrader(center, &sty.Orders) 31 | trend := idc.CalcByMACD(&market) 32 | if trend <= 0 { 33 | return false 34 | } 35 | kperoid, _ := strconv.Atoi(Option["tick_interval"]) 36 | records := market.GetKLine(kperoid) 37 | orders := market.GetOrders(false) 38 | length := len(records) 39 | if length < 3 { 40 | return false 41 | } 42 | 43 | numTradeAmount, _ := strconv.ParseFloat(Option["tradeAmount"], 64) 44 | // timeout, _ := strconv.Atoi(Option["timeout"]) 45 | // stoploss, _ := strconv.ParseFloat(Option["stoploss"], 64) 46 | // stoploss_resttime, _ := strconv.Atoi(Option["stoploss_resttime"]) 47 | shortEMA, _ := strconv.Atoi(Option["shortEMA"]) 48 | longEMA, _ := strconv.Atoi(Option["longEMA"]) 49 | signalPeriod, _ := strconv.Atoi(Option["signalPeriod"]) 50 | // MACDbuyThreshold, _ := strconv.ParseFloat(Option["MACDbuyThreshold"], 64) 51 | // MACDsellThreshold, _ := strconv.ParseFloat(Option["MACDsellThreshold"], 64) 52 | 53 | logger.Infoln("策略: MACD-K1-Q MACD参数:", shortEMA, longEMA, signalPeriod, "K线周期:", kperoid) 54 | sty.Orders.Sync(orders) 55 | 56 | macd := idc.GetMACD(records, shortEMA, longEMA, signalPeriod) 57 | logger.Infoln("当前价", records[length-1].Close) 58 | logger.Infof("Prev:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-3].Close, macd[length-3].DIF, macd[length-3].DEA, macd[length-3].BAR) 59 | logger.Infof("LAST:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-2].Close, macd[length-2].DIF, macd[length-2].DEA, macd[length-2].BAR) 60 | logger.Infof("CURR:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-1].Close, macd[length-1].DIF, macd[length-1].DEA, macd[length-1].BAR) 61 | 62 | //卖单超时处理,撤销后再次按实价挂卖单 63 | buys, sells := sty.Orders.Buy_Selling() 64 | if len(sells) != 0 { 65 | sellprice := trader.GetSellPrice() 66 | for i := 0; i < len(sells); i++ { 67 | s := sty.Orders.Find(sells[i]) 68 | if time.Now().Sub(s.Time) >= 300*time.Second { 69 | logger.Infoln("resell") 70 | if trader.Cancel(s.Id) { 71 | buyorder := sty.Orders.Find(s.RefId) 72 | tradePrice := fmt.Sprintf("%.2f", sellprice) 73 | amount := fmt.Sprintf("%.2f", buyorder.Order_Amount) 74 | trader.Sell(buyorder.Id, tradePrice, amount) 75 | } 76 | } 77 | } 78 | } 79 | 80 | //新卖单 81 | if orders != nil { 82 | sty.addSell(&market, &trader) 83 | } 84 | 85 | //买 86 | amount := numTradeAmount * trend 87 | if amount > 0 { 88 | c, r := DownRate(records) 89 | if macd[length-1].BAR > macd[length-2].BAR || c >= 3 { 90 | logger.Infoln("--->KDJ up cross,准备买入, macd:", macd[length-1].BAR) 91 | if len(buys) < 20 && time.Now().Sub(sty.PrevBuyTime) >= 60*time.Second { 92 | if (r < -0.0008 || c >= 3) && records[length-1].Close > records[length-2].Close { 93 | 94 | if trader.BuyGroup(amount) { 95 | sty.PrevBuyTime = time.Now() 96 | time.Sleep(1 * time.Second) 97 | sty.Orders.Sync(market.GetOrders(true)) 98 | sty.addSell(&market, &trader) 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | trader.CancelGroup(300) 106 | return true 107 | } 108 | 109 | //下跌百分比 110 | func DownRate(records []Record) (count int, rate float64) { 111 | count = 0 112 | length := len(records) 113 | logger.Infof("records[%d]\topen=%.2f\tclose=%.2f\t\n", length-2, records[length-2].Open, records[length-2].Close) 114 | for i := length - 2; i >= 0; i-- { 115 | if records[i-1].Open < records[i].Close { 116 | logger.Infof("records[%d]\topen=%.2f\tclose=%.2f\t\n", i-1, records[i-1].Open, records[i-1].Close) 117 | open := records[i].Open 118 | rate = (records[length-1].Close - open) / open 119 | logger.Infoln("跌幅:", fmt.Sprintf("%.5f", rate)) 120 | return 121 | } 122 | count++ 123 | logger.Infof("records[%d]\topen=%.2f\tclose=%.2f\t\n", i-1, records[i-1].Open, records[i-1].Close) 124 | } 125 | return 126 | } 127 | 128 | func (sty *MACDStrategyQK1) addSell(market *Market, trader *Trader) { 129 | forsell := sty.Orders.Buy_NotSell() 130 | if len(forsell) > 0 { 131 | sellprice := trader.GetSellPrice() - 0.1 132 | for i := 0; i < len(forsell)-1; i++ { 133 | buyorder := sty.Orders.Find(forsell[i]) 134 | var sprice float64 135 | if buyorder.Price+0.6 < sellprice { 136 | sprice = sellprice 137 | } else { 138 | sprice = buyorder.Price + 1.5 139 | } 140 | tradePrice := fmt.Sprintf("%.2f", sprice) 141 | amount := fmt.Sprintf("%.2f", buyorder.Order_Amount) 142 | trader.Sell(buyorder.Id, tradePrice, amount) 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /strategy/macd/macd-k1-standard.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | // . "github.com/qshuai162/qian/common" 5 | . "github.com/qshuai162/qian/common/config" 6 | // "github.com/qshuai162/qian/huobi" 7 | // "fmt" 8 | "github.com/qshuai162/qian/common/logger" 9 | idc "github.com/qshuai162/qian/qrobot/indictor" 10 | . "github.com/qshuai162/qian/qrobot/trader" 11 | "strconv" 12 | "time" 13 | ) 14 | 15 | type MACDStrategy struct { 16 | PrevBuyTime time.Time 17 | PrevStoplossTime time.Time 18 | Orders OCaches 19 | } 20 | 21 | func init() { 22 | macd := new(MACDStrategy) 23 | macd.PrevBuyTime = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 24 | macd.PrevStoplossTime = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 25 | Register("MACD", macd) 26 | } 27 | 28 | // KDJ-EX strategy 29 | func (sty *MACDStrategy) Tick() bool { 30 | center := Option["tradecenter"] 31 | market := GetMarket(center) 32 | trader := NewTrader(center, &sty.Orders) 33 | trend := idc.CalcByMACD(&market) 34 | if trend <= 0 { 35 | return false 36 | } 37 | kperoid, _ := strconv.Atoi(Option["tick_interval"]) 38 | records := market.GetKLine(kperoid) 39 | orders := market.GetOrders(false) 40 | length := len(records) 41 | if length < 3 { 42 | return false 43 | } 44 | 45 | numTradeAmount, _ := strconv.ParseFloat(Option["tradeAmount"], 64) 46 | timeout, _ := strconv.Atoi(Option["timeout"]) 47 | stoploss, _ := strconv.ParseFloat(Option["stoploss"], 64) 48 | stoploss_resttime, _ := strconv.Atoi(Option["stoploss_resttime"]) 49 | shortEMA, _ := strconv.Atoi(Option["shortEMA"]) 50 | longEMA, _ := strconv.Atoi(Option["longEMA"]) 51 | signalPeriod, _ := strconv.Atoi(Option["signalPeriod"]) 52 | MACDbuyThreshold, _ := strconv.ParseFloat(Option["MACDbuyThreshold"], 64) 53 | // MACDsellThreshold, _ := strconv.ParseFloat(Option["MACDsellThreshold"], 64) 54 | 55 | logger.Infoln("策略: MACD MACD参数:", shortEMA, longEMA, signalPeriod, "K线周期:", kperoid) 56 | sty.Orders.Sync(orders) 57 | 58 | macd := idc.GetMACD(records, shortEMA, longEMA, signalPeriod) 59 | logger.Infof("Prev:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-3].Close, macd[length-3].DIF, macd[length-3].DEA, macd[length-3].BAR) 60 | logger.Infof("LAST:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-2].Close, macd[length-2].DIF, macd[length-2].DEA, macd[length-2].BAR) 61 | logger.Infof("CURR:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-1].Close, macd[length-1].DIF, macd[length-1].DEA, macd[length-1].BAR) 62 | //买 63 | if (macd[length-1].BAR > MACDbuyThreshold && macd[length-2].BAR > 0 && macd[length-1].BAR > macd[length-2].BAR && macd[length-1].DEA > 0 && macd[length-1].DIF > 0) && 64 | (records[length-2].Close > records[length-3].Close && records[length-1].Close > records[length-2].Close) { 65 | maxbar_index := length - 1 66 | for i := length - 2; i >= length-4; i-- { 67 | if macd[i].BAR < 0 { 68 | break 69 | } else { 70 | if macd[i].BAR > macd[maxbar_index].BAR { 71 | maxbar_index = i 72 | } 73 | } 74 | } 75 | if maxbar_index == length-1 { 76 | logger.Infoln("--->KDJ up cross,准备买入, macd:", macd[length-1].BAR) 77 | if time.Now().Sub(sty.PrevStoplossTime) >= time.Duration(stoploss_resttime)*time.Minute { 78 | if len(sty.Orders.Buy_NotSell()) < 20 && time.Now().Sub(sty.PrevBuyTime) >= time.Duration(kperoid*8)*time.Minute { 79 | amount := numTradeAmount * trend 80 | if amount > 0 { 81 | if trader.BuyGroup(amount) { 82 | sty.PrevBuyTime = time.Now() 83 | } 84 | } 85 | } 86 | } else { 87 | logger.Infoln("止损保护中,不允许交易") 88 | } 89 | } 90 | } 91 | 92 | //卖 93 | if macd[length-2].BAR > 0 && macd[length-2].BAR > macd[length-1].BAR { 94 | logger.Infoln("<---KDJ down cross,准备卖出, macd:", macd[length-1].BAR) 95 | trader.SellGroup() 96 | } 97 | 98 | trader.CancelGroup(int32(timeout)) 99 | if trend < 8 { 100 | if trader.StopLoss(records[length-1].Close, float64(stoploss)) { 101 | sty.PrevStoplossTime = time.Now() 102 | } 103 | } 104 | return true 105 | } 106 | -------------------------------------------------------------------------------- /strategy/quantify/option.go: -------------------------------------------------------------------------------- 1 | package quantify 2 | 3 | import ( 4 | . "github.com/qshuai162/qian/common/config" 5 | idc "github.com/qshuai162/qian/qrobot/indictor" 6 | "strconv" 7 | ) 8 | 9 | type QuantifyOp struct { 10 | Center string 11 | KPeriod int 12 | PerAmount float64 13 | MinProfit float64 //最低利润 14 | MaxCount int 15 | StopLoss float64 //止损百分比 16 | TimeOut int //订单超时时间,单位秒 17 | LogEnabled bool //是否开启日志 18 | Simulation bool //是否是模拟 19 | } 20 | 21 | func GetOpion() QuantifyOp { 22 | op := new(QuantifyOp) 23 | op.Center = "okcoin" 24 | op.LogEnabled = true 25 | op.Simulation = false 26 | op.KPeriod = 1 27 | op.TimeOut = 600 28 | op.MaxCount = 10 29 | op.MinProfit = 0.2 30 | op.PerAmount = 0.01 31 | op.StopLoss = 0.005 32 | return *op 33 | 34 | } 35 | -------------------------------------------------------------------------------- /strategy/quantify/quantify.go: -------------------------------------------------------------------------------- 1 | package quantify 2 | 3 | import ( 4 | "github.com/qshuai162/qian/common/logger" 5 | . "github.com/qshuai162/qian/common/model" 6 | idc "github.com/qshuai162/qian/qrobot/indictor" 7 | . "github.com/qshuai162/qian/qrobot/trader" 8 | "github.com/qshuai162/qian/qrobot/strategy/hedge" 9 | // "strconv" 10 | // "fmt" 11 | "time" 12 | ) 13 | 14 | type QuantifyStrategy struct { 15 | Option QuantifyOp 16 | PrevBuyTime int64 17 | PrevStoplossTime int64 18 | Orders OCaches 19 | HD *hedge.Hedge 20 | } 21 | 22 | 23 | func NewQuantifyStrategy(t int64, Option QuantifyOp,hd *hedge.Hedge) *QuantifyStrategy { 24 | qu := new(QuantifyStrategy) 25 | qu.Option = Option 26 | qu.PrevBuyTime = t - 3600*24*365 27 | qu.PrevStoplossTime = t - 3600*24*365 28 | qu.HD=hd 29 | qu.Orders.SaveHistory=true 30 | return hd 31 | } 32 | 33 | func (kdj *QuantifyStrategy) Tick(t int64,records []Record,ticker Ticker) bool { 34 | 35 | if kdj.Option.Simulation { 36 | kdj.Orders.SimDone(kdj.Option.Center,ticker.Last) 37 | } else { 38 | _, orders := (*kdj.Provider).GetOrders(kdj.Option.Center) 39 | kdj.Orders.Sync(orders) 40 | } 41 | 42 | length:=len(records) 43 | macd := idc.GetMACD(records, 12, 26, 9) 44 | kdj.log("Prev:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-3].Close, macd[length-3].DIF, macd[length-3].DEA, macd[length-3].BAR) 45 | kdj.log("LAST:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-2].Close, macd[length-2].DIF, macd[length-2].DEA, macd[length-2].BAR) 46 | kdj.log("CURR:price=%.2f\tdif=%0.2f\tdea=%0.2f\tmacd=%0.2f\n", records[length-1].Close, macd[length-1].DIF, macd[length-1].DEA, macd[length-1].BAR) 47 | 48 | //买 49 | if macd[length-4].BAR>macd[length-3].BAR && macd[length-2].BAR > macd[length-3].BAR && macd[length-1].BAR > macd[length-2].BAR { 50 | kdj.log("--->MACD up cross,准备买入\n") 51 | if t-kdj.PrevStoplossTime >= 60*5 { 52 | if len(kdj.Orders.Buy_NotSell()) < kdj.Option.MaxCount && t-kdj.PrevBuyTime >= 15 { 53 | amount := kdj.Option.PerAmount 54 | price:=ticker.Sell+0.03 55 | if amount > 0 { 56 | bid:=kdj.HD.Trade("MACD1",&kdj.HD.Centers[0],"buy",price,amount) 57 | if bid!=0{ 58 | kdj.Orders.Buy(t,kdj.Option.Center,bid,price,amount) 59 | } 60 | kdj.PrevBuyTime = t 61 | } 62 | } 63 | } else { 64 | kdj.log("止损保护中,不允许交易\n") 65 | } 66 | } 67 | 68 | //卖 69 | if macd[length-1].BARmacd[length-2].BAR {//&& j[length-2]>j[length-1]{ 70 | kdj.log("<---MACD down cross,尝试卖出\n") 71 | sellPrice := ticker.Buy - 0.03 72 | forsell := kdj.Orders.WaitForSell(sellPrice - kdj.Option.MinProfit) 73 | for i := 0; i < len(forsell); i++ { 74 | buyorder := kdj.Orders.Find(forsell[i]) 75 | sid:=kdj.HD.Trade("MACD1",&kdj.HD.Centers[0],"sell",sellPrice,buyorder.Order_Amount) 76 | if sid!=0{ 77 | kdj.Orders.Sell(t,kdj.Option.Center,buyorder.Id,sid,sellPrice,buyorder.Order_Amount) 78 | } 79 | } 80 | return true 81 | } 82 | 83 | forcancel := kdj.Orders.WaitForCancel(t - int64(kdj.Option.TimeOut)) 84 | for i := 0; i < len(forcancel); i++ { 85 | cancelorder := kdj.Orders.Find(forcancel[i]) 86 | kdj.cancel(t, cancelorder.Id) 87 | if cancelorder.Type == "sell" { 88 | kdj.sell(t, cancelorder.Id, ticker.Buy-0.02) 89 | } 90 | } 91 | 92 | p := ticker.Buy - 0.1 93 | forstop := kdj.Orders.WaitForStopLoss(p, kdj.Option.StopLoss) 94 | if len(forstop) != 0 { 95 | kdj.log("stop loss") 96 | for i := 0; i < len(forstop); i++ { 97 | stoporder := kdj.Orders.Find(forstop[i]) 98 | if stoporder.RefId != 0 { 99 | sod := kdj.Orders.Find(stoporder.RefId) 100 | kdj.cancel(t, sod.Id) 101 | } 102 | kdj.sell(t, stoporder.Id, p) 103 | kdj.StoplossCount++ 104 | } 105 | kdj.PrevStoplossTime = t 106 | } 107 | // kdj.Orders.PrintAll() 108 | return true 109 | } 110 | 111 | //下跌百分比 112 | func DownRate(records []Record) (rate float64) { 113 | length := len(records) 114 | for i := length - 2; i >= 0; i-- { 115 | if records[i-1].Open < records[i].Close { 116 | open := records[i].Open 117 | rate = (records[length-1].Close - open) / open 118 | return 119 | } 120 | } 121 | return 122 | } 123 | 124 | func (hd *QuantifyStrategy) buy(t int64, price float64, amount float64) { 125 | _, id := (*hd.Trader).Buy(hd.Option.Center, price, amount) 126 | if id != 0 { 127 | // hd.Orders.Buy(t, id, price, hd.Option.PerAmount) 128 | logger.Tradef(",%s,%s,%d,%d,%.2f,%.2f,%.2f", time.Unix(t, 0).Format("2006-01-02 15:04:05"), hd.Option.Center, -1, id, amount, price, -1*amount*price) 129 | } 130 | } 131 | 132 | func (hd *QuantifyStrategy) sell(t int64, buyid int, price float64) { 133 | _, id := (*hd.Trader).Sell(hd.Option.Center, price, hd.Option.PerAmount) 134 | if id != 0 { 135 | bo := hd.Orders.Find(buyid) 136 | // hd.Orders.Sell(t, buyid, id, price, hd.Option.PerAmount) 137 | logger.Tradef(",%s,%s,%d,%d,%.2f,%.2f,%.2f,%.2f", time.Unix(t, 0).Format("2006-01-02 15:04:05"), hd.Option.Center, 1, id, hd.Option.PerAmount, price, hd.Option.PerAmount*price, hd.Option.PerAmount*(price-bo.Price)) 138 | } 139 | } 140 | 141 | func (hd *QuantifyStrategy) cancel(t int64, oid int) { 142 | _, suc := (*hd.Trader).Cancel(hd.Option.Center, oid) 143 | if suc { 144 | hd.Orders.Cancel(oid) 145 | logger.Tradef(",%s,%s,%d,%d,,,", time.Unix(t, 0).Format("2006-01-02 15:04:05"), hd.Option.Center, 0, oid) 146 | } 147 | } 148 | 149 | func (hd *QuantifyStrategy) log(formate string, args ...interface{}) { 150 | if hd.Option.LogEnabled { 151 | logger.Infof(formate, args...) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "fmt" 5 | // . "github.com/qshuai162/qian/common" 6 | // . "github.com/qshuai162/qian/config" 7 | // "github.com/qshuai162/qian/db" 8 | // "github.com/qshuai162/qian/email" 9 | // "github.com/qshuai162/qian/huobi" 10 | "github.com/qshuai162/qian/common/logger" 11 | "strconv" 12 | // "time" 13 | ) 14 | 15 | // Strategy is the interface that must be implemented by a strategy driver. 16 | type Strategy interface { 17 | Tick() bool 18 | } 19 | 20 | var strategyMaps = make(map[string]Strategy) 21 | 22 | // Register makes a strategy available by the provided name. 23 | // If Register is called twice with the same name or if driver is nil, 24 | // it panics. 25 | func Register(strategyName string, strategy Strategy) { 26 | 27 | if strategy == nil { 28 | panic("sql: Register strategy is nil") 29 | } 30 | if _, dup := strategyMaps[strategyName]; dup { 31 | panic("sql: Register called twice for strategy " + strategyName) 32 | } 33 | strategyMaps[strategyName] = strategy 34 | logger.Infoln("注册策略:" + strategyName) 35 | } 36 | 37 | func getStrategy(name string) Strategy { 38 | strategy, ok := strategyMaps[name] 39 | if !ok { 40 | logger.Errorf("sql: 未知的策略名 %q (forgotten import? private strategy?)", name) 41 | return nil 42 | } else { 43 | return strategy 44 | } 45 | } 46 | 47 | func Tick(strategyName string) bool { 48 | return getStrategy(strategyName).Tick() 49 | } 50 | 51 | func toString(s interface{}) string { 52 | if v, ok := s.(string); ok { 53 | return v 54 | } 55 | return fmt.Sprintf("%v", s) 56 | } 57 | 58 | func toFloat(s interface{}) float64 { 59 | var ret float64 60 | switch v := s.(type) { 61 | case float64: 62 | ret = v 63 | case int64: 64 | ret = float64(v) 65 | case string: 66 | ret, err := strconv.ParseFloat(v, 64) 67 | if err != nil { 68 | logger.Errorln("convert ", s, " to float failed") 69 | return ret 70 | } 71 | } 72 | return ret 73 | } 74 | 75 | func float2str(i float64) string { 76 | return strconv.FormatFloat(i, 'f', -1, 64) 77 | } 78 | -------------------------------------------------------------------------------- /trader/cache.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import ( 4 | "fmt" 5 | "github.com/qshuai162/qian/common/logger" 6 | . "github.com/qshuai162/qian/common/model" 7 | // "strconv" 8 | "time" 9 | ) 10 | 11 | type OCache struct { 12 | Time int64 13 | Center string 14 | Id int 15 | RefId int //关联订单ID,买单与卖单的对应关系 16 | Type string // sell or buy 17 | Price float64 18 | Order_Amount float64 //订单量 19 | // Process_Amount float64 //成交量 20 | Status int64 //0-撤销 1-委托 2-全部成交 3-部分成交 21 | } 22 | 23 | type OCaches struct { 24 | List []OCache //委托中、未卖出的买单 25 | History []OCache //历史委托(撤销、已成交的卖单、已卖出的买单) 26 | SaveHistory bool //是否保存历史 27 | } 28 | 29 | //新增买单委托 30 | func (os *OCaches) Buy(t int64, center string,id int, price, amount float64) { 31 | if os.Find(id) == nil { 32 | o := new(OCache) 33 | o.Center=center 34 | o.Time = t 35 | o.Type = "buy" 36 | o.Id = id 37 | o.Price = price 38 | o.Order_Amount = amount 39 | o.Status = 1 40 | os.List = append(os.List, *o) 41 | } else { 42 | 43 | } 44 | } 45 | 46 | //没有卖出的买单 47 | func (os *OCaches) Buy_NotSell() []int { 48 | rets := make([]int, 0, 0) 49 | for i := 0; i < len(os.List); i++ { 50 | temp := os.List[i] 51 | if temp.Type == "buy" { 52 | rets = append(rets, temp.Id) 53 | } 54 | } 55 | for i:=0;i= nowprice { 291 | rets = append(rets, temp.Id) 292 | } 293 | } 294 | return rets 295 | } 296 | 297 | func (os *OCaches) SimDone(center string,curPrice float64)(dones []*OCache){ 298 | if curPrice<1{ 299 | return 300 | } 301 | for i := 0; i < len(os.List); i++ { 302 | o := os.List[i] 303 | if o.Center==center{ 304 | if (o.Type == "buy" && o.Price > curPrice) || 305 | (o.Type == "sell" && o.Price < curPrice){ 306 | d:=os.Done(o.Id) 307 | if d!=nil{ 308 | dones=append(dones,d) 309 | } 310 | } 311 | } 312 | } 313 | return dones 314 | } 315 | 316 | func (os *OCaches) Summary() (int,int) { 317 | // profit := 0.0 318 | canceled := 0 319 | donecount := 0 320 | for i := 0; i < len(os.History); i++ { 321 | donecount++ 322 | o := os.History[i] 323 | if o.Status == 0 { 324 | canceled++ 325 | } 326 | // if o.Type == "buy" && o.Status == 2 { 327 | // if o.RefId!=0{ 328 | // s := os.Find(o.RefId) 329 | // } 330 | // // profit += s.Order_Amount*s.Price - o.Order_Amount*o.Price 331 | // } 332 | } 333 | return len(os.List),donecount 334 | } 335 | -------------------------------------------------------------------------------- /trader/cache_test.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import ( 4 | "fmt" 5 | // "strconv" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func Test_Cache(t *testing.T) { 11 | os := new(OCaches) 12 | os.SaveHistory=true 13 | tt:=time.Now().Unix() 14 | fmt.Println("buy 1") 15 | os.Buy(tt,"test",1, 1456.12, 0.002) 16 | os._PrintAll() 17 | time.Sleep(500 * time.Millisecond) 18 | fmt.Println("cancel 1") 19 | os.Cancel(1) 20 | os._PrintAll() 21 | time.Sleep(500 * time.Millisecond) 22 | 23 | fmt.Println("buy 2") 24 | os.Buy(tt,"test",2, 321.12, 22.2) 25 | os._PrintAll() 26 | time.Sleep(500 * time.Millisecond) 27 | 28 | fmt.Println("done 2") 29 | os.Done(2) 30 | os._PrintAll() 31 | time.Sleep(500 * time.Millisecond) 32 | 33 | fmt.Println("sell 2 3") 34 | os.Sell(tt,"test",2, 3, 12, 32) 35 | os._PrintAll() 36 | time.Sleep(500 * time.Millisecond) 37 | 38 | fmt.Println("cancel 3") 39 | os.Cancel(3) 40 | os._PrintAll() 41 | time.Sleep(500 * time.Millisecond) 42 | 43 | fmt.Println("buy 4") 44 | os.Buy(tt,"test",4, 11, 1) 45 | os._PrintAll() 46 | time.Sleep(500 * time.Millisecond) 47 | 48 | fmt.Println("done 4") 49 | os.Done(4) 50 | os._PrintAll() 51 | time.Sleep(500 * time.Millisecond) 52 | 53 | fmt.Println("sell 40") 54 | os.Sell(tt,"test",4, 40, 123, 1) 55 | os._PrintAll() 56 | time.Sleep(500 * time.Millisecond) 57 | 58 | fmt.Println("cancel 40") 59 | os.Cancel(40) 60 | os._PrintAll() 61 | time.Sleep(500 * time.Millisecond) 62 | 63 | fmt.Println("buy not sell") 64 | t2 := os.Buy_NotSell() 65 | for i := 0; i < len(t2); i++ { 66 | t := os.Find(t2[i]) 67 | _print(t) 68 | } 69 | os._PrintAll() 70 | time.Sleep(500 * time.Millisecond) 71 | 72 | fmt.Println("wait for sell and sell") 73 | temp := os.WaitForSell(9999) 74 | for i := 0; i < len(temp); i++ { 75 | t := os.Find(temp[i]) 76 | _print(t) 77 | os.Sell(tt,"test",t.Id, t.Id+1000, 9999, os.Find(temp[i]).Order_Amount) 78 | } 79 | os._PrintAll() 80 | time.Sleep(500 * time.Millisecond) 81 | 82 | fmt.Println("wait for cancel and cancel") 83 | temp1 := os.WaitForCancel(100000000000) 84 | for i := 0; i < len(temp1); i++ { 85 | t := os.Find(temp1[i]) 86 | _print(t) 87 | os.Cancel(t.Id) 88 | } 89 | os._PrintAll() 90 | time.Sleep(500 * time.Millisecond) 91 | 92 | } 93 | -------------------------------------------------------------------------------- /trader/trader.go: -------------------------------------------------------------------------------- 1 | package trader 2 | 3 | import ( 4 | "fmt" 5 | "github.com/qshuai162/qian/api/chbtc" 6 | "github.com/qshuai162/qian/api/huobi" 7 | "github.com/qshuai162/qian/api/okcoin" 8 | "github.com/qshuai162/qian/api/btcc" 9 | . "github.com/qshuai162/qian/common/model" 10 | // . "github.com/qshuai162/qian/tool/config" 11 | "github.com/qshuai162/qian/common/logger" 12 | "strconv" 13 | // "time" 14 | ) 15 | type TradeAPI interface { 16 | Buy(price, amount string) string 17 | Sell(price, amount string) string 18 | GetOrder(order_id string) (ret bool, order Order) 19 | CancelOrder(order_id string) bool 20 | GetAccount() (Account, bool) 21 | GetOrderBook() (ret bool, orderBook OrderBook) 22 | } 23 | 24 | type Trader struct { 25 | } 26 | 27 | func getTradeAPI(name string) (tradeAPI TradeAPI) { 28 | if name == "huobi" { 29 | tradeAPI = huobi.NewHuobi() 30 | } else if name == "okcoin" { 31 | tradeAPI = okcoin.NewOkcoin() 32 | } else if name == "chbtc" { 33 | tradeAPI = chbtc.NewCHBTC() 34 | } else if name == "btcc" { 35 | tradeAPI = btcc.NewBTCC() 36 | } else { 37 | logger.Fatalln("Please config the exchange center...") 38 | panic(0) 39 | } 40 | // else if name == "bitvc" { 41 | // tradeAPI = bitvc.NewBitvc() 42 | // } else if name == "peatio" { 43 | // tradeAPI = peatio.NewPeatio() 44 | // } else if name == "bittrex" { 45 | // tradeAPI = Bittrex.Manager() 46 | // } else if name == "simulate" { 47 | // tradeAPI = simulate.NewSimulate() 48 | // } 49 | 50 | return 51 | } 52 | 53 | func (tr *Trader) Buy(center string, price, amount float64) (int, int) { 54 | api := getTradeAPI(center) 55 | buyID := api.Buy(fmt.Sprintf("%.2f", price), fmt.Sprintf("%.2f", amount)) 56 | bid, _ := strconv.Atoi(buyID) 57 | if bid != 0 { 58 | logger.Infof("[委托成功] %s 买入 <--限价单 数量:%.2f 价格:%.2f 单号:%d", center, amount, price, bid) 59 | return 0, bid 60 | } else { 61 | logger.Infoln("[委托失败]") 62 | return 1, bid 63 | } 64 | } 65 | 66 | func (tr *Trader) Sell(center string, price, amount float64) (int, int) { 67 | api := getTradeAPI(center) 68 | sellID := api.Sell(fmt.Sprintf("%.2f", price), fmt.Sprintf("%.2f", amount)) 69 | sid, _ := strconv.Atoi(sellID) 70 | if sid != 0 { 71 | logger.Infof("[委托成功] %s 卖出 <--限价单 数量:%.2f 价格:%.2f 单号:%d", center, amount, price, sid) 72 | return 0, sid 73 | } else { 74 | logger.Infoln("[委托失败]") 75 | return 1, sid 76 | } 77 | } 78 | 79 | func (tr *Trader) Cancel(center string, id int) (code int, ret bool) { 80 | api := getTradeAPI(center) 81 | 82 | if api.CancelOrder(strconv.Itoa(id)) { 83 | logger.Infof("[Cancel委托成功] %s <-----撤单------> %d\n", center, id) 84 | ret = true 85 | } else { 86 | logger.Infoln("[Cancel委托失败]") 87 | ret = false 88 | } 89 | return 90 | } 91 | 92 | func (tr *Trader) GetOrder(center string,orderId int) (bool , Order){ 93 | api := getTradeAPI(center) 94 | ret,order:=api.GetOrder(strconv.Itoa(orderId)) 95 | return ret,order 96 | } 97 | 98 | // //根据实时买卖盘添加一组买单 99 | // func (tr *Trader) BuyGroup(amount float64) bool { 100 | // ordercount, _ := strconv.Atoi(Option["splitordercount"]) 101 | // stepprice, _ := strconv.ParseFloat(Option["stepprice"], 64) 102 | // nSplitTradeAmount := amount / float64(ordercount) 103 | // splitTradeAmount := fmt.Sprintf("%.2f", nSplitTradeAmount) 104 | 105 | // buyPrice := tr.GetBuyPrice() 106 | // if buyPrice != 0 { 107 | // for i := 1; i <= ordercount; i++ { 108 | // tradePrice := fmt.Sprintf("%.2f", buyPrice+stepprice*float64(i)) 109 | // tr.Buy(tradePrice, splitTradeAmount) 110 | // } 111 | // return true 112 | // } 113 | // return false 114 | // } 115 | 116 | // //撤消所有超时订单 117 | // func (tr *Trader) CancelGroup(timeout int32) { 118 | // forcancel := tr.OCaches.WaitForCancel(timeout) 119 | // for i := 0; i < len(forcancel); i++ { 120 | // cancelorder := tr.OCaches.Find(forcancel[i]) 121 | // tr.Cancel(cancelorder.Id) 122 | // } 123 | 124 | // } 125 | 126 | // //卖出所有可卖单 127 | // func (tr *Trader) SellGroup() bool { 128 | // minprofit, _ := strconv.ParseFloat(Option["minprofit"], 64) 129 | // stepprice, _ := strconv.ParseFloat(Option["stepprice"], 64) 130 | // sellprice := tr.GetSellPrice() 131 | // if sellprice != 0 { 132 | // forsell := tr.OCaches.WaitForSell(sellprice - minprofit) 133 | // for i := 0; i < len(forsell); i++ { 134 | // buyorder := tr.OCaches.Find(forsell[i]) 135 | // tradePrice := fmt.Sprintf("%.2f", sellprice-stepprice*float64(i)) 136 | // amount := fmt.Sprintf("%.2f", buyorder.Order_Amount) 137 | // tr.Sell(buyorder.Id, tradePrice, amount) 138 | // } 139 | // return true 140 | // } 141 | // return false 142 | // } 143 | 144 | // //止损操作 145 | // func (tr *Trader) StopLoss(currentPrice, stopLossRate float64) bool { 146 | // forstop := tr.OCaches.WaitForStopLoss(currentPrice, stopLossRate) 147 | // if len(forstop) != 0 { 148 | // logger.Infoln("stop loss") 149 | // price := fmt.Sprintf("%.2f", currentPrice) 150 | // for i := 0; i < len(forstop); i++ { 151 | // stoporder := tr.OCaches.Find(forstop[i]) 152 | // amount := fmt.Sprintf("%.2f", stoporder.Order_Amount) 153 | // if stoporder.RefId == 0 { 154 | // tr.Sell(stoporder.Id, price, amount) 155 | // } 156 | // } 157 | // return true 158 | // } 159 | // return false 160 | // } 161 | 162 | // func (tr *Trader) GetOrderBook() *OrderBook { 163 | // ret, orderbook := tr.API.GetOrderBook() 164 | // if !ret { 165 | // ret, orderbook = tr.API.GetOrderBook() // try again 166 | // if !ret { 167 | // logger.Infoln("get orderbook failed 2") 168 | // return nil 169 | // } 170 | // } 171 | // logger.Infoln("卖一", (orderbook.Asks[len(orderbook.Asks)-1])) 172 | // logger.Infoln("买一", orderbook.Bids[0]) 173 | // return &orderbook 174 | // } 175 | 176 | // //获取买入价 177 | // func (tr *Trader) GetBuyPrice() float64 { 178 | // orderbook := tr.GetOrderBook() 179 | // if orderbook != nil { 180 | // return orderbook.Bids[0].Price 181 | // } else { 182 | // return 0 183 | // } 184 | // } 185 | 186 | // //获取卖出价 187 | // func (tr *Trader) GetSellPrice() float64 { 188 | // orderbook := tr.GetOrderBook() 189 | // if orderbook != nil { 190 | // return orderbook.Asks[len(orderbook.Asks)-1].Price 191 | // } else { 192 | // return 0 193 | // } 194 | // } 195 | 196 | // func (tr *Trader) GetAccount() (Account, bool) { 197 | // return tr.API.GetAccount() 198 | // } 199 | --------------------------------------------------------------------------------