├── .DS_Store
├── .gitignore
├── .idea
├── binance-quant-robot.iml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── DoubleAverageLines_static.py
├── README.md
├── __init__.py
├── app
├── BinanceAPI.py
├── OrderManager.py
├── __init__.py
├── __pycache__
│ ├── BinanceAPI.cpython-38.pyc
│ ├── __init__.cpython-38.pyc
│ ├── authorization.cpython-38.pyc
│ └── dingding.cpython-38.pyc
├── authorization.py
└── dingding.py
├── requirements.txt
├── robot-run.py
└── strategyConfig.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | venv/
2 |
--------------------------------------------------------------------------------
/.idea/binance-quant-robot.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | 均线
126 | api_key
127 | time
128 | coin_base
129 | has_key
130 | buy_limit
131 | pytest
132 | assetOfTestAccount
133 | get_ticker_price
134 | float(
135 | time_str
136 | true
137 | test_kline
138 | sellStrategy3
139 | writeOrderInfo
140 | isDefaultToken
141 | sellStrategy
142 | df.size
143 | df数据行数2
144 | dateTime_interval
145 | iterrows
146 | 读取--
147 | gain_kline
148 | True
149 | gain_k
150 | self.coin_base
151 | judgeToBuyCommand
152 | orderInfo_path
153 | orderManager
154 | DoubleAverageLines
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 | 1619185329040
367 |
368 |
369 | 1619185329040
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 | file://$PROJECT_DIR$/app/BinanceAPI.py
483 | 197
484 |
485 |
486 |
487 | file://$PROJECT_DIR$/app/dingding.py
488 | 42
489 |
490 |
491 |
492 | file://$PROJECT_DIR$/DoubleAverageLines_static.py
493 | 153
494 |
495 |
496 |
497 | file://$PROJECT_DIR$/DoubleAverageLines_static.py
498 | 157
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
--------------------------------------------------------------------------------
/DoubleAverageLines_static.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 |
3 | import json
4 | import numpy as np
5 | import pandas as pd
6 | import time
7 | import datetime
8 |
9 | class DoubleAverageLines:
10 |
11 | def __init__(self):
12 | pass
13 |
14 | # [
15 | # 1499040000000, // 开盘时间
16 | # "0.01634790", // 开盘价
17 | # "0.80000000", // 最高价
18 | # "0.01575800", // 最低价
19 | # "0.01577100", // 收盘价(当前K线未结束的即为最新价)
20 | # "148976.11427815", // 成交量
21 | # 1499644799999, // 收盘时间
22 | # "2434.19055334", // 成交额
23 | # 308, // 成交笔数
24 | # "1756.87402397", // 主动买入成交量
25 | # "28.46694368", // 主动买入成交额
26 | # "17928899.62484339" // 请忽略该参数
27 | # ]
28 |
29 | def klinesToDataFrame(self,klines):
30 |
31 | if klines is None:
32 | print("klinesToDataFrame---error:klines is None.")
33 | return None
34 |
35 | openTimeList = []
36 | openPriceList = []
37 | maxPriceList = []
38 | minPriceList = []
39 | closePriceList = []
40 | dealVoluMeList = []
41 | closeTimeList = []
42 | dealTotalMoneyList = []
43 | dealCountList = []
44 | dealBuyVolumeList = []
45 | dealBuyTotalMoneyList = []
46 |
47 |
48 | for kline in klines:
49 | if (type(kline)).__name__ == 'list':
50 | openTimeList.append(self.stampToTime(kline[0]))
51 | openPriceList.append(kline[1])
52 | maxPriceList.append(kline[2])
53 | minPriceList.append(kline[3])
54 | closePriceList.append(kline[4])
55 | dealVoluMeList.append(kline[5])
56 | closeTimeList.append(self.stampToTime(kline[6]))
57 | dealTotalMoneyList.append(kline[7])
58 | dealCountList.append(kline[8])
59 | dealBuyVolumeList.append(kline[9])
60 | dealBuyTotalMoneyList.append(kline[10])
61 | else:
62 | print("error: kline is not list.")
63 |
64 | kLinesDict = {"openTime": openTimeList, "openPrice": openPriceList, "maxPrice": maxPriceList, "minPrice":minPriceList, "closePrice":closePriceList, "closeTime":closeTimeList,"openTime2": openTimeList}
65 |
66 | klines_df = pd.DataFrame(kLinesDict)
67 |
68 | # for index, row in klines_df.iterrows():
69 | # print(str(row["openTime"]) + "\t" +row["openPrice"] + "\t" +row["maxPrice"] + "\t"+row["minPrice"] + "\t"+row["closePrice"] + "\t"+str(row["closeTime"]) + "\t")
70 |
71 | return klines_df
72 |
73 |
74 | def readJsonFromFile(self, filePath):
75 | # Opening JSON file
76 | f = open(filePath, )
77 | data = json.load(f)
78 | f.close()
79 | # Iterating through the json
80 | # list
81 | print("readJsonFromFile =")
82 | if (type(data)).__name__ == 'list':
83 | for i in data:
84 | print(i)
85 | # Closing file
86 | return data
87 |
88 | return None
89 |
90 |
91 | def release_trade_stock(self, ma_x_line, ma_y_line, code, df):
92 |
93 | print('\n' + code + ' 均线 ' + str(ma_x_line) + ' 和 ' + str(ma_y_line) + ' :')
94 |
95 | df[["openTime"]] = df[["openTime"]].astype(str) # int类型 转换 成str类型,否则会被当做时间戳使用,造成时间错误
96 | df[["openTime2"]] = df[["openTime2"]].astype(str) # int类型 转换 成str类型,否则会被当做时间戳使用,造成时间错误
97 |
98 | # print("===========================================\n")
99 | df['openTime'] = pd.to_datetime(df['openTime'])
100 | df['openTime2'] = pd.to_datetime(df['openTime2'])
101 |
102 | df.set_index('openTime2', inplace=True)
103 | df = df.sort_index(ascending=True)
104 |
105 | # 求出均线
106 | maX = df['closePrice'].rolling(ma_x_line).mean()
107 | maY = df['closePrice'].rolling(ma_y_line).mean()
108 |
109 | df = df[ma_y_line:] # 这个切片很重要,否则会报错,因为数据不匹配
110 | # 因为 ma_x_line < ma_y_line ,所以均线 切到 ma_y_line
111 | maX = maX[ma_y_line:] # 切片,与 df 数据条数保持一致
112 | maY = maY[ma_y_line:] # 切片,与 df 数据条数保持一致
113 |
114 | # print("df数据行数=" +str(len(df)))
115 | # print(df)
116 | # 从尾部,删除1行
117 | # df.drop(df.tail(1).index, inplace=True)
118 |
119 | # print("tmp_last_df--数据切片:")
120 | # for index, row in df.iterrows():
121 | # print(str(row["openTime"]) + "\t" +row["openPrice"] + "\t" +row["maxPrice"] + "\t"+row["minPrice"] + "\t"+row["closePrice"] + "\t"+str(row["closeTime"]) + "\t")
122 |
123 | print("最后一行数据:")
124 | last_row = df.iloc[-1,:] #第1行,所有列
125 | print(str(last_row["openTime"]) + "\t" +last_row["openPrice"] + "\t" +last_row["maxPrice"] + "\t"+last_row["minPrice"] + "\t"+last_row["closePrice"] + "\t"+str(last_row["closeTime"]) + "\t")
126 |
127 | print("-------------------------------------------------------\n")
128 | s1 = maX < maY # 得到 bool 类型的 Series
129 | s2 = maX > maY
130 |
131 | death_ex = s1 & s2.shift(1) # 判定死叉的条件
132 | death_date = df.loc[death_ex].index # 死叉对应的日期
133 |
134 | golden_ex = ~(s1 | s2.shift(1)) # 判断金叉的条件
135 | golden_record = df.loc[golden_ex]
136 | golden_date = golden_record.index # 金叉的日期
137 |
138 | s1 = pd.Series(data=1, index=golden_date) # 1 作为金叉的标识
139 | s2 = pd.Series(data=0, index=death_date) # 0 作为死叉的标识
140 |
141 | s = s1.append(s2) # 合并
142 | s = s.sort_index(ascending=True) # 排序
143 |
144 | # print("金叉和死叉对应的时间:")
145 | # print(s)
146 |
147 | hold = 0 # 持有的股数
148 |
149 | trade_buy_price = 0
150 |
151 | for i in range(0, len(s)):
152 |
153 | if s[i] == 1:
154 | time = s.index[i]
155 | close_price = float(df.loc[time]['closePrice']) # 收盘价
156 |
157 | open_time = df.loc[time]['openTime'] # 开盘时间
158 | close_time = df.loc[time]['closeTime'] # 收盘时间
159 |
160 |
161 | isRightTime = self.judgeCurrentTimeWithLastRecordTime(str(open_time), str(close_time))
162 |
163 |
164 | # print(open_price)
165 | trade_buy_price = close_price # 记录买入的价格
166 | str_date = str(time)
167 | print(str_date + "\t" + "买入" + code + "\t" + str(round(close_price, 8))+"---"+str(isRightTime))
168 | if isRightTime:
169 | print("release_trade_stock---buy")
170 | return "buy,"+str(open_time)
171 |
172 | else:
173 | # 卖出股票的单价
174 | death_time = s.index[i]
175 | p_death = float(df.loc[death_time]['closePrice'])
176 | str_date = str(death_time)
177 |
178 | open_time = df.loc[death_time]['openTime'] # 开盘时间
179 | close_time = df.loc[death_time]['closeTime'] # 收盘时间
180 | isRightTime = self.judgeCurrentTimeWithLastRecordTime(str(open_time), str(close_time))
181 |
182 | print(str_date + "\t" + "卖出" + str(code) + "\t"+ str(round(p_death, 8)) +"---"+str(isRightTime))
183 | if isRightTime:
184 | print("release_trade_stock---sell")
185 | return "sell"
186 |
187 | print("release_trade_stock---None")
188 |
189 | return None
190 |
191 |
192 |
193 |
194 | # 判断当前时间,是否在k线时间范围内
195 | def judgeCurrentTimeWithLastRecordTime(self, openTime, closeTime):
196 |
197 | dateTime_interval = pd.to_datetime(closeTime) - pd.to_datetime(openTime)
198 |
199 | seconds_interval = dateTime_interval.seconds # int类型,秒数
200 | # print("seconds_interval 的类型=")
201 | # print(type(seconds_interval))
202 | # print(seconds_interval)
203 |
204 | now = int(round((time.time()-seconds_interval) * 1000))
205 |
206 | now02 = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now / 1000))
207 |
208 | if now02>=openTime and now02<=closeTime:
209 | # print("成功---"+openTime+"\t"+now02+"\t"+closeTime)
210 | return True
211 | else:
212 | # print("失败---"+openTime+"\t"+now02+"\t"+closeTime)
213 | return False
214 |
215 |
216 | def stampToTime(self, stamp):
217 |
218 | # now = int(round(time.time() * 1000))
219 | stamp_int = int(stamp)
220 |
221 | now02 = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(stamp_int / 1000))
222 |
223 | # mytime = datetime.datetime.fromtimestamp(stamp/1000)
224 | # # print(stamp)
225 | # print("mytime type is : " + type(now02).__name__)
226 | return now02
227 |
228 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # binance-quant-robot
2 | 数字货币,币安Binance, 比特币BTC 以太坊ETH 莱特币LTC 狗币DOGE 屎币SHIB 量化交易系统 火币 OKEX 交易策略 量化策略 自动交易
3 |
4 | 如果国内不能访问币安api,需要科学上网.
5 |
6 | ## 简介
7 | 这是一个数字货币量化交易系统,使用的Binance币安的交易API.
8 |
9 | 如果你还没有币安账号:[注册页面](https://accounts.binancezh.io/zh-CN/register?ref=FJO3SX0X)(通过链接注册,享受交易返现优惠政策)
10 |
11 | 这世上,没有百分百赚钱的方式,量化交易策略只是一个辅助工具。
12 |
13 | 生死有命,富贵在天!币圈有风险,入市需谨慎!!
14 |
15 | ## 双均线策略
16 | 以 ETH 为例,5分钟K线数据,均线5 和 均线60 为例:
17 |
18 | 均线5上穿均线60是金叉,执行买入;
19 | 均线5下穿均线60是死叉,执行卖出;
20 | 
21 | 这是一个比较好的情况,可以赚一点钱。
22 |
23 |
24 | 这是一个比较震荡的情况,会亏损。
25 |
26 |
27 | 使用时,必须根据自身情况,调整 K线 和 均线!!!!
28 |
29 |
30 | 如果你还没有币安账号:[注册页面](https://accounts.binancezh.io/zh-CN/register?ref=FJO3SX0X)(通过链接注册,享受交易返现优惠政策)
31 |
32 |
33 | ## 为什么选择币安交易所
34 | 交易的手续费看起来很少,但是随着交易次数逐步增多,手续费也是一笔不小的开支。
35 | 所以我选择了币安,手续费低的大平台交易所
36 | > 火币手续费 Maker 0.2% Taker 0.2%
37 |
38 | > 币安手续费 Maker 0.1% Taker 0.1% (加上BNB家持手续费低至0.075%)
39 |
40 |
41 | 如果你还没有币安账号:[注册页面](https://accounts.binancezh.io/zh-CN/register?ref=FJO3SX0X)(通过链接注册,享受交易返现优惠政策)
42 |
43 |
44 |
45 | ## 运行环境
46 | python3
47 |
48 | 由于交易所的api在大陆无法访问,需要科学上网。
49 |
50 |
51 |
52 | ## 快速使用
53 |
54 | 1、获取币安API的 api_key 和 api_secret
55 |
56 | 申请api_key地址:
57 |
58 | [币安API管理页面](https://www.binance.com/cn/usercenter/settings/api-management)
59 |
60 |
61 | 2、注册钉钉自定义机器人Webhook,用于推送交易信息到指定的钉钉群
62 |
63 | [钉钉自定义机器人注册方法](https://m.dingtalk.com/qidian/help-detail-20781541)
64 |
65 |
66 | 3、修改app目录下的authorization文件
67 |
68 | ```
69 | api_key='你的币安key'
70 | api_secret='你的币安secret'
71 | dingding_token = '申请钉钉群助手的token' # 强烈建议您使用
72 | ```
73 |
74 |
75 | 4、交易策略配置信息 strategyConfig.py
76 | 设置你的配置信息:
77 |
78 | ```
79 | # 均线, ma_x 要大于 ma_y
80 | ma_x = 5
81 | ma_y = 60
82 |
83 | # 币安
84 | binance_market = "SPOT"#现货市场
85 | kLine_type = '15m' # 15分钟k线类型,你可以设置为5分钟K线:5m;1小时为:1h;1天为:1d
86 | ```
87 | 当 kline 5 向上穿过 kline 60, 则执行买入。
88 |
89 | 当 kline 5 向下穿过 kline 60, 则执行卖出。
90 |
91 | 你可根据自己的喜好,调整 ma_x 和 ma_y 的值。
92 |
93 | 你也可以调整 kLine_type ,来选择 5分钟K线、15分钟K线、30分钟K线、1小时K线、1天K线等;
94 |
95 | 不同的K线,最终效果也是不一样的。
96 |
97 |
98 | 5、同时交易多币种
99 |
100 | robot-run.py中
101 |
102 | 创建多个订单管理对象:
103 | ```
104 | # 使用 USDT 购买 DOGE,限定最多100个USDT
105 | orderManager_doge = OrderManager("USDT", 100,"DOGE", binance_market)
106 | # 使用 USDT 购买 ETH,限定最多100个USDT
107 | orderManager_eth = OrderManager("USDT", 100,"ETH", binance_market)
108 | ```
109 |
110 | 将orderManager_doge 和 orderManager_eth 加入定时执行的方法中:
111 | ```
112 | def binance_func():
113 | orderManager_doge.binance_func()
114 | time.sleep(10)
115 | orderManager_eth.binance_func()
116 |
117 | ```
118 |
119 | 程序可同时监控 DOGE 和 ETH 的均线,并根据策略执行交易。
120 | 使用时,可根据自身需要,增加其他币种。
121 |
122 |
123 |
124 | 6、运行程序(记得先开科学上网)
125 | ```
126 | python robot-run.py
127 | ```
128 |
129 |
130 |
131 | ## 服务器部署
132 | 购买服务器,建议是海外服务器,可以访问币安API
133 |
134 | ### 我的配置:
135 | Linux, 1核CPU, 2G内存(1G也可)
136 |
137 | 我是在阿里云购买的日本东京服务器(传说币安服务器就在东京)
138 |
139 | 也可选择 新加坡、香港服务器
140 |
141 | [阿里云,新人优惠](https://www.aliyun.com/activity?userCode=zs5is7pi)
142 |
143 | [阿里云,最新活动](https://www.aliyun.com/1111/new?userCode=zs5is7pi)
144 |
145 |
146 |
147 | ## 钉钉信息截图
148 | 
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/__init__.py
--------------------------------------------------------------------------------
/app/BinanceAPI.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*
2 | import requests, time, hmac, hashlib
3 | from app.authorization import recv_window,api_secret,api_key
4 |
5 | try:
6 | from urllib import urlencode
7 | # python3
8 | except ImportError:
9 | from urllib.parse import urlencode
10 |
11 | class BinanceAPI(object):
12 | BASE_URL = "https://www.binance.com/api/v1"
13 | FUTURE_URL = "https://fapi.binance.com"
14 | BASE_URL_V3 = "https://api.binance.com/api/v3"
15 | PUBLIC_URL = "https://www.binance.com/exchange/public/product"
16 |
17 |
18 | def __init__(self, key, secret):
19 | self.key = key
20 | self.secret = secret
21 |
22 | def ping(self):
23 | path = "%s/ping" % self.BASE_URL_V3
24 | return requests.get(path, timeout=180, verify=True).json()
25 |
26 | # 服务器时间
27 | def serverTime(self):
28 | path = "%s/time" % self.BASE_URL_V3
29 | return requests.get(path, timeout=180, verify=True).json()
30 |
31 | # 获取交易规则和交易对信息, GET /api/v3/exchangeInfo
32 | def exchangeInfo(self):
33 | path = "%s/exchangeInfo" % self.BASE_URL_V3
34 | return requests.get(path, timeout=180, verify=True).json()
35 |
36 | # 获取最新价格
37 | def get_ticker_price(self,market):
38 | path = "%s/ticker/price" % self.BASE_URL_V3
39 | params = {"symbol":market}
40 | res = self._get_no_sign(path,params)
41 | print("get_ticker_price=")
42 | print(res)
43 | time.sleep(2)
44 | return float(res['price'])
45 |
46 | # 24hr 价格变动情况
47 | def get_ticker_24hour(self,market):
48 | path = "%s/ticker/24hr" % self.BASE_URL_V3
49 | params = {"symbol":market}
50 | res = self._get_no_sign(path,params)
51 | return res
52 |
53 | #获取K线 https://api.binance.com/api/v3/klines?symbol=ETHUSDT&interval=15m&startTime=1619761128000&endTime=1619764848000
54 | def get_klines(self, market, interval, limit=0, startTime=None, endTime=None):
55 | path = "%s/klines" % self.BASE_URL_V3
56 | params = None
57 | if startTime is None:
58 | params = {"symbol": market, "interval":interval}
59 | else:
60 | params = {"symbol": market, "interval":interval, "startTime":startTime, "endTime":endTime}
61 |
62 | if limit is None or limit <=0 or limit >1000:
63 | limit = 500
64 |
65 | params['limit'] = limit
66 |
67 | return self._get_no_sign(path, params)
68 |
69 |
70 | # 现货,账户信息,GET /api/v3/account
71 | def get_Spot_UserData_account(self):
72 | stamp_now = int(round(time.time() * 1000))
73 | path = "%s/account" % self.BASE_URL_V3
74 | params = {"timestamp": stamp_now,"recvWindow":recv_window}
75 | res = self._get_with_sign(path, params)
76 | return res
77 |
78 | def get_spot_asset_by_symbol(self, symbol):
79 | ud_account = self.get_Spot_UserData_account()
80 |
81 | if ud_account is not None and "balances" in ud_account.keys():
82 | balances = ud_account["balances"]
83 | if balances is not None and type(balances).__name__=='list':
84 | for balance in balances:
85 | if str(balance["asset"])==symbol:
86 | return balance
87 |
88 |
89 |
90 | # 查询每日资产快照,/sapi/v1/accountSnapshot
91 | def get_UserData_accountSnapshot(self):
92 | stamp_now = int(round(time.time() * 1000))
93 | path = "https://www.binance.com/sapi/v1/accountSnapshot"
94 | params = {"type":"SPOT","timestamp":stamp_now,"limit":5}
95 |
96 | # res = self._post(path, params)
97 | res = self._get_with_sign(path, params).json()
98 | return res
99 |
100 | def buy_limit(self, market, quantity, rate):
101 | print("购买 "+ market +"\t"+'%f 个, ' % quantity + "价格:%f"%rate)
102 | path = "%s/order" % self.BASE_URL_V3
103 | params = self._order(market, quantity, "BUY", rate)
104 | return self._post(path, params)
105 |
106 | # 测试专用,买入
107 | def buy_limit_test(self, market, quantity, rate):
108 | tStamp = int(1000 * time.time())
109 | dict = {'symbol': market, 'orderId': 924538226, 'orderListId': -1, 'clientOrderId': 'wtswxN4L8O6hZiWNiOxuaN',\
110 | 'transactTime': tStamp, 'price': str(round(rate,8)), 'origQty': str(round(quantity,8)), 'executedQty': str(round(quantity,8)),\
111 | 'status': 'FILLED', 'timeInForce': 'GTC', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
112 | return dict
113 |
114 | # 测试专用,卖出
115 | def sell_limit_test(self, market, quantity, rate):
116 | tStamp = int(1000 * time.time())
117 | dict = {'symbol': market, 'orderId': 933997128, 'orderListId': -1, 'clientOrderId': 'uepwnRSgfVioZlBhXqTr03', \
118 | 'transactTime': tStamp, 'price': str(round(rate,8)), 'origQty': str(round(quantity,8)), 'executedQty': '0.00000000', \
119 | 'cummulativeQuoteQty': '0.00000000', 'status': 'NEW', 'timeInForce': 'GTC', 'type': 'LIMIT', 'side': 'SELL', 'fills': []}
120 | return dict
121 |
122 |
123 |
124 | def sell_limit(self, market, quantity, rate):
125 | print("出售 "+ market +"\t"+'%f 个, ' % quantity + "价格:%f"%rate)
126 |
127 | path = "%s/order" % self.BASE_URL_V3
128 | params = self._order(market, quantity, "SELL", rate)
129 | return self._post(path, params)
130 |
131 | ### ----私有函数---- ###
132 | def _order(self, market, quantity, side, price=None):
133 | '''
134 | :param market:币种类型。如:BTCUSDT、ETHUSDT
135 | :param quantity: 购买量
136 | :param side: 订单方向,买还是卖
137 | :param price: 价格
138 | :return:
139 | '''
140 | params = {}
141 |
142 | if price is not None:
143 | params["type"] = "LIMIT"
144 | params["price"] = self._format(price)
145 | params["timeInForce"] = "GTC"
146 | else:
147 | params["type"] = "MARKET"
148 |
149 | params["symbol"] = market
150 | params["side"] = side
151 | params["quantity"] = '%.8f' % quantity
152 |
153 | return params
154 |
155 | def _get_no_sign(self, path, params={}):
156 | query = urlencode(params)
157 | url = "%s?%s" % (path, query)
158 | return requests.get(url, timeout=180, verify=True).json()
159 |
160 | def _get_no_sign_header(self, path, params={},header={}):
161 | query = urlencode(params)
162 | url = "%s?%s" % (path, query)
163 | return requests.get(url, headers=header,timeout=180, verify=True).json()
164 |
165 | def _get_with_sign(self, path, params={}):
166 | tmp_signature = self._signature(params)
167 | params.update({"signature": tmp_signature})
168 | query = urlencode(params)
169 | url = "%s?%s" % (path, query)
170 | header = {"Content-Type":"application/json","X-MBX-APIKEY": self.key}
171 |
172 | return requests.get(url,headers=header, timeout=180, verify=True).json()
173 |
174 | # 生成 signature
175 | def _signature(self, params={}):
176 | data = params.copy()
177 | # ts = int(1000 * time.time())
178 | # data.update({"timestamp": ts})
179 | h = urlencode(data)
180 | b = bytearray()
181 | b.extend(self.secret.encode())
182 | signature = hmac.new(b, msg=h.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
183 | return signature
184 |
185 | def _sign(self, params={}):
186 | data = params.copy()
187 |
188 | ts = int(1000 * time.time())
189 | data.update({"timestamp": ts})
190 | h = urlencode(data)
191 | b = bytearray()
192 | b.extend(self.secret.encode())
193 | signature = hmac.new(b, msg=h.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
194 | data.update({"signature": signature})
195 | return data
196 |
197 | def _post(self, path, params={}):
198 | params.update({"recvWindow": recv_window})
199 | query = urlencode(self._sign(params))
200 | url = "%s" % (path)
201 | header = {"X-MBX-APIKEY": self.key}
202 | return requests.post(url, headers=header, data=query, \
203 | timeout=180, verify=True).json()
204 |
205 | def _format(self, price):
206 | return "{:.8f}".format(price)
207 |
208 |
209 | # 合约
210 | def market_future_order(self, side, symbol, quantity,positionSide):
211 |
212 | ''' 合约市价单
213 | :param side: 做多or做空 BUY SELL
214 | :param symbol:币种类型。如:BTCUSDT、ETHUSDT
215 | :param quantity: 购买量
216 | :param positionSide: 双向持仓 BUY-LONG 开多 SELL-SHORT 开空
217 | :param price: 开仓价格
218 | '''
219 | path = "%s/fapi/v1/order" % self.FUTURE_URL
220 | params = self._order(symbol, quantity, side, positionSide)
221 | return self._post(path, params)
222 |
223 |
224 |
225 | if __name__ == "__main__":
226 | instance = BinanceAPI(api_key,api_secret)
227 | # print(instance.buy_limit("EOSUSDT",5,2))
228 | # print(instance.get_ticker_price("WINGUSDT"))
229 | print(instance.get_ticker_24hour("WINGUSDT"))
--------------------------------------------------------------------------------
/app/OrderManager.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import json, os, time, datetime, math
4 | from app.BinanceAPI import BinanceAPI
5 |
6 | from app.authorization import api_key,api_secret
7 | from app.dingding import Message
8 | from DoubleAverageLines_static import DoubleAverageLines
9 | import schedule
10 | from strategyConfig import sellStrategy1, sellStrategy2, sellStrategy3 , ma_x, ma_y, isOpenSellStrategy, kLine_type
11 |
12 |
13 | binan = BinanceAPI(api_key,api_secret)
14 | msg = Message()
15 |
16 | dALines = DoubleAverageLines()
17 |
18 |
19 | class ExchangeRule(object):
20 | def __init__(self, dict):
21 | if dict is not None and 'symbol' in dict:
22 | self.symbol = dict['symbol']
23 | self.baseAsset = dict['baseAsset']
24 | self.baseAssetPrecision = dict['baseAssetPrecision']
25 | self.quoteAsset = dict['quoteAsset']
26 | self.quoteAssetPrecision = dict['quoteAssetPrecision']
27 |
28 | filters = dict['filters']
29 | for filter in filters:
30 | if filter['filterType'] == 'PRICE_FILTER':
31 | # "minPrice": "0.00001000",
32 | # "maxPrice": "1000.00000000",
33 | # "tickSize": "0.00001000"
34 | self.minPrice = filter['minPrice']
35 | self.maxPrice = filter['maxPrice']
36 | self.tickSize = filter['tickSize']
37 |
38 | if filter['filterType'] == 'LOT_SIZE':
39 | # "minQty": "0.10000000",
40 | # "maxQty": "9000000.00000000",
41 | # "stepSize": "0.10000000"
42 | self.minQty = filter['minQty']
43 | self.maxQty = filter['maxQty']
44 | self.stepSize = filter['stepSize']
45 |
46 |
47 |
48 |
49 | class OrderManager(object):
50 |
51 | def __init__(self, coinBase, coinBaseCount , tradeCoin, market):
52 | self.coin_base = coinBase # 基础币,例如USDT
53 | self.coin_base_count = coinBaseCount # 买币时最多可用资金量
54 | self.trade_coin = tradeCoin #买卖币种,例如 DOGER
55 | self.market = market #市场,例如:现货 "SPOT"
56 | self.symbol = tradeCoin+coinBase #交易符号,例如"DOGEUSDT"
57 | self.exchangeRule = None
58 | self.orderInfoSavePath = "./"+ self.symbol +"_buyOrderInfo.json" #订单信息存储路径
59 |
60 | def gain_exchangeRule(self, theSymbol):
61 | if self.exchangeRule is None:
62 | dict = binan.exchangeInfo()
63 | if dict is not None and 'symbols' in dict:
64 | symbol_list = dict['symbols']
65 | for tmp_symbol in symbol_list:
66 | if tmp_symbol['symbol']==theSymbol:
67 | self.exchangeRule = ExchangeRule(tmp_symbol)
68 | break;
69 |
70 | # return self.exchangeRule
71 |
72 |
73 |
74 | # 执行卖出
75 | def doSellFunc(self,symbol ,quantity, cur_price):
76 | print("马上卖出 " + str(symbol)+" "+ str(quantity)+ " 个,单价:"+str(cur_price))
77 |
78 | #如果总价值小于10
79 | if (quantity*cur_price)<10:
80 | quantity = self.format_trade_quantity(11.0/cur_price)
81 | if (quantity * cur_price) < 10:
82 | quantity = self.format_trade_quantity(13.0 / cur_price)
83 | if (quantity * cur_price) < 10:
84 | quantity = self.format_trade_quantity(16.0 / cur_price)
85 | if (quantity * cur_price) < 10:
86 | quantity = self.format_trade_quantity(20.0 / cur_price)
87 |
88 | # 卖出
89 | res_order_sell = binan.sell_limit(symbol, quantity, cur_price)
90 | print("出售部分结果:")
91 | print("量:"+str(quantity)+", 价格:"+str(cur_price)+", 总价值:"+str(quantity*cur_price))
92 | print(res_order_sell)
93 | order_result_str = self.printOrderJsonInfo(res_order_sell)
94 | msgInfo = "卖出结果:\n" + str(order_result_str)
95 |
96 | return msgInfo
97 |
98 | #分批出售策略
99 | def sellStrategy(self, filePath):
100 | msgInfo = ""
101 | dictOrder = self.readOrderInfo(filePath)
102 | if dictOrder is None:
103 | return msgInfo
104 |
105 | # 读取上次买入的价格
106 | buyPrice = self.priceOfPreviousOrder(self.orderInfoSavePath)
107 | if buyPrice > 0:
108 | # 查询当前价格
109 | cur_price = binan.get_ticker_price(self.symbol)
110 | print("当前 "+str(self.symbol)+" 价格:"+str(cur_price))
111 | #查询当前资产
112 | asset_coin = binan.get_spot_asset_by_symbol(self.trade_coin)
113 | print(self.trade_coin + " 资产2:")
114 | print(asset_coin)
115 |
116 | if "sellStrategy3" in dictOrder:
117 | print("sellStrategy--sellStrategy3--1")
118 | tmpSellStrategy = dictOrder['sellStrategy3']
119 | print("买入价格:"+str(buyPrice) + " * " + str(tmpSellStrategy) + " = " + str(buyPrice * tmpSellStrategy['profit'])+" 和 当前价格:" + str(cur_price)+" 比较")
120 | if buyPrice * tmpSellStrategy['profit'] <= cur_price:
121 | print("sellStrategy--sellStrategy3--2")
122 |
123 | quantity = self.format_trade_quantity(float(asset_coin["free"])*tmpSellStrategy['sell'])
124 | # 卖出
125 | msgInfo= msgInfo + self.doSellFunc(self.symbol,quantity,cur_price)
126 | del dictOrder['sellStrategy3']
127 | self.writeOrderInfo(filePath, dictOrder)
128 | dictOrder = self.readOrderInfo(filePath)
129 | print("部分卖出--sellStrategy3")
130 |
131 | if "sellStrategy2" in dictOrder:
132 | tmpSellStrategy = dictOrder['sellStrategy2']
133 | print("sellStrategy--sellStrategy2--1")
134 | print("买入价格:"+str(buyPrice) + " * " + str(tmpSellStrategy) + " = " + str(buyPrice * tmpSellStrategy['profit'])+" 和 当前价格:" + str(cur_price)+" 比较")
135 |
136 | if buyPrice * tmpSellStrategy['profit'] <= cur_price:
137 | print("sellStrategy--sellStrategy2--2")
138 |
139 | quantity = self.format_trade_quantity(float(asset_coin["free"]) * tmpSellStrategy['sell'])
140 | # 卖出
141 | msgInfo = msgInfo + self.doSellFunc(self.symbol, quantity, cur_price)
142 | del dictOrder['sellStrategy2']
143 | self.writeOrderInfo(filePath, dictOrder)
144 | dictOrder = self.readOrderInfo(filePath)
145 | print("部分卖出--sellStrategy2")
146 |
147 | if "sellStrategy1" in dictOrder:
148 | tmpSellStrategy = dictOrder['sellStrategy1']
149 | print("sellStrategy--sellStrategy1--1")
150 | print("买入价格:" + str(buyPrice) + " * " + str(tmpSellStrategy) + " = " + str(
151 | buyPrice * tmpSellStrategy['profit']) + " 和 当前价格:" + str(cur_price) + " 比较")
152 |
153 | if buyPrice * tmpSellStrategy['profit'] <= cur_price:
154 | print("sellStrategy--sellStrategy1--2")
155 |
156 | quantity = self.format_trade_quantity(float(asset_coin["free"]) * tmpSellStrategy['sell'])
157 | # 卖出
158 | msgInfo = msgInfo + self.doSellFunc(self.symbol, quantity, cur_price)
159 | del dictOrder['sellStrategy1']
160 | self.writeOrderInfo(filePath, dictOrder)
161 | dictOrder = self.readOrderInfo(filePath)
162 | print("部分卖出--sellStrategy1")
163 |
164 | return msgInfo
165 |
166 |
167 |
168 | # 格式化交易信息结果
169 | def printOrderJsonInfo(self, orderJson):
170 | str_result = ""
171 | if type(orderJson).__name__ == 'dict':
172 | all_keys = orderJson.keys()
173 | if 'symbol' in orderJson and 'orderId' in orderJson:
174 |
175 | time_local = time.localtime(orderJson['transactTime'] / 1000)
176 | time_str = time.strftime('%Y-%m-%d %H:%M:%S', time_local)
177 |
178 | str_result = str_result + "时间:" + str(time_str) + "\n"
179 | str_result = str_result + "币种:" + str(orderJson['symbol']) + "\n"
180 | str_result = str_result + "价格:" + str(orderJson['price']) + "\n"
181 | str_result = str_result + "数量:" + str(orderJson['origQty']) + "\n"
182 | str_result = str_result + "方向:" + str(orderJson['side']) + "\n"
183 | else:
184 | str_result = str(orderJson)
185 | else:
186 | str_result = str(orderJson)
187 |
188 | return str_result
189 |
190 | # 读取本地存储的买入订单信息
191 | def readOrderInfo(self, filePath):
192 | if os.path.exists(filePath) is False:
193 | return None
194 |
195 | with open(filePath, 'r') as f:
196 | data = json.load(f)
197 | print("读取--买入信息:")
198 | print(data)
199 | if 'symbol' in data and 'orderId' in data and 'price' in data:
200 | return data
201 | else:
202 | return None
203 |
204 | # 比较本次买入提示的str是否重复
205 | def judgeToBuyCommand(self, filePath, theToBuyCommand):
206 | orderDict = self.readOrderInfo(filePath)
207 |
208 | if orderDict is None:
209 | return True # 购买
210 |
211 | if "toBuy" in orderDict:
212 | if orderDict["toBuy"] == theToBuyCommand:
213 | print('本次购买时间是 '+str(theToBuyCommand)+' ,重复,不执行购买')
214 | return False #不执行购买,因为重复
215 |
216 | return True
217 |
218 |
219 |
220 |
221 | # 获取 上次买入订单中的价格Price
222 | def priceOfPreviousOrder(self, filePath):
223 | dataDict = self.readOrderInfo(filePath)
224 | thePrice = 0.0
225 |
226 | if dataDict is not None and 'price' in dataDict:
227 | thePrice = float(dataDict['price'])
228 |
229 | return thePrice
230 |
231 | # 清理 本地存储的买入订单信息
232 | def clearOrderInfo(self, filePath):
233 | if os.path.exists(filePath) is True:
234 | os.remove(filePath)
235 | print("清理订单信息---do")
236 |
237 | # 存储 买入订单信息
238 | def writeOrderInfo(self, filePath, dictObj):
239 |
240 | self.clearOrderInfo(filePath)
241 | print("写入--买入信息:")
242 | print(dictObj)
243 | with open(filePath, 'w') as f:
244 | json.dump(dictObj, f)
245 |
246 |
247 | def writeOrderInfoWithSellStrategy(self,filePath, dictObj):
248 |
249 | if isOpenSellStrategy:
250 | dictObj["sellStrategy1"] = sellStrategy1
251 | dictObj["sellStrategy2"] = sellStrategy2
252 | dictObj["sellStrategy3"] = sellStrategy3
253 |
254 | self.writeOrderInfo(filePath, dictObj)
255 |
256 |
257 | # 获取K线列表
258 | def gain_kline(self, symbol, timeInterval='15m'):
259 | # 结束时间
260 | millis_stamp = int(round(time.time() * 1000))
261 |
262 | # 如何处理虚假买点和虚假卖点,1000条数据中,第一条可能产生虚假的买点和卖点
263 | kline_json = binan.get_klines(symbol, timeInterval, 1000, None, millis_stamp)
264 | if type(kline_json).__name__ == 'list':
265 | return kline_json
266 | else:
267 | return None
268 |
269 | # 根据交易规则,格式化交易量
270 | def format_trade_quantity(self, originalQuantity):
271 | minQty = float(self.exchangeRule.minQty)
272 | print(self.symbol + " 原始交易量= "+str(originalQuantity))
273 | print(self.symbol + " 最小交易量限制= "+str(minQty))
274 |
275 | if self.exchangeRule is not None and minQty>0:
276 | newQuantity = (originalQuantity//minQty)*minQty
277 | else:
278 | newQuantity = math.floor(originalQuantity)
279 | print(self.symbol + " 交易量格式化= "+str(newQuantity))
280 | return newQuantity
281 |
282 | def binance_func(self):
283 | print("币种= "+self.trade_coin)
284 | try:
285 | self.gain_exchangeRule(self.symbol)
286 |
287 | msgInfo = "" # 钉钉消息
288 | isDefaultToken = False
289 |
290 | # 记录执行时间
291 | now = datetime.datetime.now()
292 | ts = now.strftime('%Y-%m-%d %H:%M:%S')
293 | print('do func time:', ts)
294 | msgInfo = msgInfo + str(ts) + "\n"
295 |
296 | # 获取K线数据
297 | kline_list = self.gain_kline(self.symbol, kLine_type)
298 | # k线数据转为 DataFrame格式
299 | kline_df = dALines.klinesToDataFrame(kline_list)
300 |
301 | # 判断交易方向
302 | trade_direction = dALines.release_trade_stock(ma_x, ma_y, self.symbol, kline_df)
303 |
304 | if trade_direction is not None:
305 |
306 | if "buy," in trade_direction:
307 |
308 | isToBuy = self.judgeToBuyCommand(self.orderInfoSavePath, trade_direction)
309 |
310 | if isToBuy is False:
311 | msgInfo = msgInfo + "服务正常3"
312 | isDefaultToken = True
313 | else:
314 | isDefaultToken = False
315 |
316 | # coin_base = "USDT"
317 | asset_coin = binan.get_spot_asset_by_symbol(self.coin_base)
318 | print(self.coin_base + " 资产:"+str(asset_coin))
319 |
320 | # 购买,所用资金量
321 | coin_base_count = float(asset_coin["free"])
322 | if self.coin_base_count <= coin_base_count:
323 | coin_base_count = self.coin_base_count
324 |
325 | print("binance_func--可用资金量coin_base_count= "+str(coin_base_count))
326 | # 查询当前价格
327 | cur_price = binan.get_ticker_price(self.symbol)
328 | # 购买量
329 | quantity = self.format_trade_quantity(coin_base_count / float(cur_price))
330 | # 购买
331 | res_order_buy = binan.buy_limit(self.symbol, quantity, cur_price)
332 | print("购买结果:")
333 | print(res_order_buy)
334 |
335 |
336 | # 存储买入订单信息
337 | if res_order_buy is not None and "symbol" in res_order_buy:
338 | res_order_buy["toBuy"] = trade_direction
339 | self.writeOrderInfoWithSellStrategy(self.orderInfoSavePath, res_order_buy)
340 |
341 | order_result_str = self.printOrderJsonInfo(res_order_buy)
342 | msgInfo = "购买结果:\n" + order_result_str
343 |
344 | elif trade_direction == "sell":
345 | dictOrder = self.readOrderInfo(self.orderInfoSavePath)
346 |
347 |
348 | if dictOrder is None:
349 | msgInfo = msgInfo + "服务正常4--已无可售"
350 | isDefaultToken = True
351 | else:
352 |
353 | asset_coin = binan.get_spot_asset_by_symbol(self.trade_coin)
354 | print(self.trade_coin + " 资产:")
355 | print(asset_coin)
356 |
357 | quantity = self.format_trade_quantity(float(asset_coin["free"]))
358 |
359 | # 查询当前价格
360 | cur_price = binan.get_ticker_price(self.symbol)
361 |
362 | if quantity<=0:
363 | msgInfo = msgInfo + "服务正常5--已无可售"
364 | isDefaultToken = True
365 | else:
366 | isDefaultToken = False
367 | # 卖出
368 | res_order_sell = binan.sell_limit(self.symbol, quantity, cur_price)
369 | # 清理本地订单信息
370 | self.clearOrderInfo(self.orderInfoSavePath)
371 | print("出售结果:")
372 | print(res_order_sell)
373 | order_result_str = self.printOrderJsonInfo(res_order_sell)
374 | msgInfo = "卖出结果:\n" + str(order_result_str)
375 |
376 | else:
377 | if isOpenSellStrategy:
378 | print("开启卖出策略---1")
379 | msgInfo = self.sellStrategy(self.orderInfoSavePath)
380 |
381 | if msgInfo == "":
382 | msgInfo = msgInfo + str(ts) + "\n"
383 | print("暂不执行任何交易2")
384 | msgInfo = msgInfo + "服务正常2"
385 | isDefaultToken = True
386 |
387 | print("-----------------------------------------------\n")
388 | except Exception as ex:
389 | err_str = "出现如下异常:%s" % ex
390 | print(err_str)
391 | msgInfo = msgInfo + str(err_str) + "\n"
392 |
393 | finally:
394 | if "服务正常" in msgInfo:
395 | pass
396 | else:
397 | msg.dingding_warn(msgInfo, isDefaultToken)
398 |
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/app/__init__.py
--------------------------------------------------------------------------------
/app/__pycache__/BinanceAPI.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/app/__pycache__/BinanceAPI.cpython-38.pyc
--------------------------------------------------------------------------------
/app/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/app/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/app/__pycache__/authorization.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/app/__pycache__/authorization.cpython-38.pyc
--------------------------------------------------------------------------------
/app/__pycache__/dingding.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luoyanbei/binance-quant-robot/993ef0e199435db74320e4dac2d9ac2fc990757a/app/__pycache__/dingding.cpython-38.pyc
--------------------------------------------------------------------------------
/app/authorization.py:
--------------------------------------------------------------------------------
1 | # Get an Key and Secret
2 | # https://www.binance.com/restapipub.html
3 |
4 | # 测试账号
5 | api_key = ''
6 | api_secret = ''
7 |
8 | recv_window = 5000
9 |
10 | dingding_token = "" # 钉钉群token
11 | dingding_token2 = "" # 钉钉群2的token
12 |
--------------------------------------------------------------------------------
/app/dingding.py:
--------------------------------------------------------------------------------
1 | import requests,json
2 |
3 | # windows
4 | from app.authorization import dingding_token, dingding_token2, recv_window,api_secret,api_key
5 | from app.BinanceAPI import BinanceAPI
6 | # linux
7 | # from app.authorization import dingding_token
8 |
9 | class Message:
10 |
11 | def buy_limit_msg(self,market, quantity, rate):
12 | try:
13 | res = BinanceAPI(api_key,api_secret).buy_limit(market, quantity, rate)
14 | if res['orderId']:
15 | buy_info = "报警:币种为:{cointype}。买单价为:{price}。买单量为:{num}".format(cointype=market,price=rate,num=quantity)
16 | self.dingding_warn(buy_info)
17 | return res
18 | except BaseException as e:
19 | error_info = "报警:币种为:{cointype},买单失败.api返回内容为:{reject}".format(cointype=market,reject=res['msg'])
20 | self.dingding_warn(error_info)
21 |
22 |
23 | def sell_limit_msg(self,market, quantity, rate):
24 | '''
25 | :param market:
26 | :param quantity: 数量
27 | :param rate: 价格
28 | :return:
29 | '''
30 | try:
31 | res = BinanceAPI(api_key,api_secret).sell_limit(market, quantity, rate)
32 | if res['orderId']:
33 | buy_info = "报警:币种为:{cointype}。卖单价为:{price}。卖单量为:{num}".format(cointype=market,price=rate,num=quantity)
34 | self.dingding_warn(buy_info)
35 | return res
36 | except BaseException as e:
37 | error_info = "报警:币种为:{cointype},卖单失败.api返回内容为:{reject}".format(cointype=market,reject=res['msg'])
38 | self.dingding_warn(error_info+str(res))
39 | return res
40 |
41 | def dingding_warn(self,text, isDefaultToken=True):
42 | tmpToken = dingding_token if isDefaultToken else dingding_token2
43 | headers = {'Content-Type': 'application/json;charset=utf-8'}
44 | api_url = "https://oapi.dingtalk.com/robot/send?access_token=%s" % tmpToken
45 | print("api_url=")
46 | print(api_url)
47 | json_text = self._msg(text+"\n______")
48 | response = requests.post(api_url, json.dumps(json_text), headers=headers).content
49 | print(response)
50 |
51 |
52 |
53 | def _msg(self,text):
54 | json_text = {
55 | "msgtype": "text",
56 | "at": {
57 | "atMobiles": [
58 | "11111"
59 | ],
60 | "isAtAll": False
61 | },
62 | "text": {
63 | "content": text
64 | }
65 | }
66 | return json_text
67 |
68 | if __name__ == "__main__":
69 | msg = Message()
70 | print(msg.buy_limit_msg("EOSUSDT",4,2))
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | certifi==2020.12.5
2 | chardet==4.0.0
3 | cycler==0.10.0
4 | idna==2.10
5 | kiwisolver==1.3.1
6 | matplotlib==3.4.1
7 | numpy==1.20.2
8 | pandas==1.2.4
9 | Pillow==8.2.0
10 | pyparsing==2.4.7
11 | python-dateutil==2.8.1
12 | pytz==2021.1
13 | requests==2.25.1
14 | schedule==1.1.0
15 | six==1.15.0
16 | urllib3==1.26.4
17 |
--------------------------------------------------------------------------------
/robot-run.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from app.BinanceAPI import BinanceAPI
3 | from app.authorization import api_key,api_secret
4 | from app.dingding import Message
5 | from app.OrderManager import OrderManager
6 |
7 | import time
8 | import datetime
9 | import schedule
10 | import math
11 | import json,os
12 | from strategyConfig import binance_market,binance_coinBase,binance_tradeCoin, binance_coinBase_count
13 |
14 |
15 | orderManager = OrderManager("USDT", 100,"DOGE", binance_market)
16 |
17 | orderManager_eth = OrderManager("USDT", 100,"ETH", binance_market)
18 |
19 | msgDing = Message()
20 |
21 | # 发送消息通知
22 | def sendInfoToDingDing( message, isDefaultToken):
23 | # 记录执行时间
24 | now = datetime.datetime.now()
25 | ts = now.strftime('%Y-%m-%d %H:%M:%S')
26 | message = str(ts) + "\n" + message
27 | msgDing.dingding_warn(message, isDefaultToken)
28 |
29 |
30 | def binance_func():
31 | orderManager.binance_func()
32 | # time.sleep(5)
33 | # orderManager_eth.binance_func()
34 |
35 |
36 | def sendServiceInfo():
37 | str = "服务正常--ok"
38 | sendInfoToDingDing(str, True)
39 |
40 | # 创建循环任务
41 | def tasklist():
42 | #清空任务
43 | schedule.clear()
44 | #创建一个按秒间隔执行任务
45 | # schedule.every().hours.at("04:05").do(binance_func)
46 |
47 | #
48 | schedule.every(15).seconds.do(binance_func)
49 |
50 | schedule.every(20).minutes.do(sendServiceInfo)
51 |
52 |
53 | while True:
54 | schedule.run_pending()
55 | time.sleep(1)
56 |
57 |
58 | # 调试看报错运行下面,正式运行用上面
59 | if __name__ == "__main__":
60 |
61 | # 启动,先从币安获取交易规则, https://api.binance.com/api/v3/exchangeInfo
62 | tasklist()
63 |
64 | # binance_func()
65 |
66 |
--------------------------------------------------------------------------------
/strategyConfig.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 |
4 | # 是否分批卖出
5 | isOpenSellStrategy = True
6 | #分批卖出,盈利百分比
7 | sellStrategy1 = {"profit":1.05, "sell":0.1}#盈利5%时,卖出10%的仓位
8 | sellStrategy2 = {"profit":1.10, "sell":0.2}#盈利10%时,卖出20%的仓位
9 | sellStrategy3 = {"profit":1.20, "sell":0.2}#盈利20%时,卖出20%的仓位
10 |
11 |
12 | # 均线, ma_x 要大于 ma_y
13 | ma_x = 5
14 | ma_y = 60
15 |
16 | # 币安
17 | binance_market = "SPOT"#现货市场
18 | binance_coinBase = "USDT"#使用USDT作为基础币种,用于购买其他货币;
19 | # 允许使用账户中的 binance_coinBase 对应个数,每次买入 最多使用 binance_coinBase_count 个 binance_coinBase
20 | binance_coinBase_count = 100 #代表100个USDT
21 | binance_tradeCoin = "DOGE"#交易目标是 DOGE 币,
22 | kLine_type = '1h' # 15分钟k线类型,你可以设置为5分钟K线:5m;1小时为:1h;1天为:1d
--------------------------------------------------------------------------------