网格策略的逻辑很简洁,由于交易频繁,它可能是一个有利于刷返佣的策略,最近翻阅资料尝试将基于币安交易所的合约网格实盘代码敲了出来,供大家参考
6 ?4 j, U! k3 c- |网格策略的逻辑
& i3 K* o2 F( j
9 I0 @3 G& i1 d网格策略的逻辑很简单:低买高卖。# ~6 n7 z& M7 c; h& \
假设某个标的目前1000元,那么我在999元处挂个买单,1001元处挂个卖单,价格波动一下:从1000元-->999元-->1001元,那么我就赚到了2块钱。6 u$ O7 [1 {8 S
如果我挂单频率够高,价格也如我所愿地来回波动,那么我就不断地能赚到这2块钱。% z7 z5 @- s4 } ?* W' N- ^$ I+ N; C
当然,任何策略都有风险,网格策略的风险也很明显:如果遇到单边大行情,比如价格一路下跌到900元,我999元挂的买单成交后,价格再也没有涨上来过,我就被套了。
* f1 o5 _2 B5 ?; e- J3 y/ J基于币安合约api实现的网格实盘代码$ R. T& @9 [! p
/ d' [4 T S0 |每个人可能对网格策略的理解不一样,我描述一下我的实现逻辑:3 \ O5 i# G. b6 L9 d8 Z; n! V
1. 获取某个标的当前的价格,并在当前价格下方x%处,挂一个买单,在当前价格上方x%处,挂一个卖单(价差是2*x%);1 [: R) X. i: |; l0 h$ E
2. 等待某个挂单成交后,同上一步一样,在成交价上下方x%处挂卖单和买单;
: q7 t5 K; `9 k7 s* f+ s3. 价格不断波动,程序不断收割这个价差(2*x%)。
/ ~( I- D: D, P' P$ i# H* J! \2 H为实现以上逻辑,有几点需要注意:) R, M# M6 V$ f+ ]7 a; `
· 币安合约挂单成交的手续费为0.02%,所以理论上x应该>0.02%,否则没有利润空间;
" ^+ @% T: s* A& j% f+ L( q1 c· 价格不断波动,程序会不断挂单,很可能出现同一个价位,有多个挂单,我的做法是撤销同价位的挂单,保证每个价位只有一个挂单;# w+ I2 m o N0 h
· 为避免亏损,单个方向的挂单量不得超过max_orders(我个人设为3);
5 d5 O5 Z8 t; J* c4 ]7 R6 C) {, h- C· 即使设定了最大挂单量,还是有较大风险,因为虽然挂单量有限制,但是不断下跌就会不断买(只是买完后再挂买单的数量不会太多),所以我设了网格上下限,比如当前价格为20u,我设定网格范围为[18,22],一旦价格波动太大,不在这个[18,22]的范围内,程序就撤单不再交易了,等待价格回到网内再继续执行网格逻辑(注意,只是撤单,现有仓位不平,等待解套)。3 [ E2 Z% x8 H( A1 s$ R$ w
· 我是用上一次的成交价来计算新的挂单价格的:最新挂单价 = 上一次成交价 +- x%,如果网格执行周期比较长(程序默认为30s执行一次),价格波动又较大时,我的挂单可能会变成吃单,手续费会增加,所以在计算下单价格的时候,我会判断一下上一次的成交价与当前盘口的价格,避免maker变taker。3 X- d0 H4 W- ]7 {; {' q
程序的配置文件config.ini:) J4 N2 V p, K2 U* f
! _" S% _. ~) O3 F) w( A / y# p4 z5 h1 B% V# S4 c) ]7 ^
代码主要逻辑:
w; c$ I+ ]4 Ndef grid(exchange, symbol, gap_percent, quantity, pricePrecision, max_orders, grid_up, grid_down, buy_orders, sell_orders):5 i% N: i3 @; L2 b8 X. q
. W% P" {/ J& s! _ # 用于存放已在交易所挂单的订单信息
# t y; o; @$ I buy_orders = buy_orders0 D8 t$ H# w: E& `# L. b
sell_orders = sell_orders
$ ^5 m% c* c* T( S" P& q' n7 Y& S7 H% d7 {' c0 d
# 获取当前买一卖一价
3 U% l; G# s) B0 d9 j; U! b, z# F bid_price, ask_price = get_bid_ask_price(exchange, symbol)
# h6 Q4 W- ~2 g3 n print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 当前ask_price: {ask_price} bid_price: {bid_price}')" r- {0 f& p, C! b
! G8 t9 M6 n X8 }2 |$ [9 \* R # 判断是否还在网内,如果破网则撤单,不执行网格逻辑,直到价格回到网内 j8 I# ^# I4 R/ `# ]0 Z6 v
if bid_price > grid_up or ask_price < grid_down:
2 Z" F/ s$ Q! a; G. I+ w$ | c / O& a" } t" C4 T, A) D( g
# 撤掉目前所有挂单" ?0 O' y9 u2 \4 w3 L- R
cancel_open_orders(exchange, symbol)( w" K$ O2 M3 i
buy_orders = []& h& S! P* u h$ H7 k- g( I
sell_orders = []
6 P# U( T" \9 X print('破网!已撤销所有挂单,等待价格回归至网内继续交易。')
- L+ {! l- D* s/ l* L% K& N4 Y" L/ u# d print(f'grid_up: {grid_up} grid_down: {grid_down}')' k- y- B1 h* i
print(f'ask_price: {ask_price} bid_price: {bid_price}')1 r' r" [: r/ F* m4 L& @, C% Q+ J
: |4 M) C, k1 O
return buy_orders, sell_orders0 J( z7 p1 M r
& b7 F9 J+ B1 T1 {* b4 K
buy_orders.sort(key=lambda x: float(x['price']), reverse=True) # 按价格高到底排序(高价更容易成交)
0 |" l: D2 ]& [+ P2 J" X/ ? sell_orders.sort(key=lambda x: float(x['price']), reverse=False) # 按价格低到高排序(低价更容易成交)
, R% U) ^& v5 L* a
! H1 V: P3 Y- J+ G2 N. @8 F # 若buy_orders已记录有买单,逐个检查是否有最新成交: f) I% V* A/ J% p( X
for buy_order_info in buy_orders:
: `6 ~0 X: M' M7 Y+ G! A
$ z# l( ]/ w6 |/ A8 H5 J9 N& Z7 } # 获取该订单当前最新状态 [+ d# `/ V7 `& _" e
order_info = get_order_info(exchange, symbol, buy_order_info.get('orderId'))1 B! y8 w9 R( A1 c. ?/ m
@8 u' z. \4 S5 I1 } # 若订单当下已成交
; f7 [' |) n6 U if order_info.get('status') == 'FILLED':7 B( ?# J) d2 s ?" |8 A9 G
final_price = float(order_info.get('price'))
; i* o6 D) |6 h2 S; k print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} {symbol}买单成交了,买单价为{final_price}')
' ~, F4 G3 K; y9 E) k T$ G% k% {
" D0 u$ ~6 |. d/ N* B4 G9 e# W& Y # 成交后,从buy_orders中删除! O; n3 u" R9 L5 M
# 注意remove的是buy_order_info而不是order_info
5 ?% v; J% [ M # order_info是成交后的buy_order_info
- a, f$ s% p9 L buy_orders.remove(buy_order_info)
! `' ]- R0 Q( n+ H o2 {, E' Q0 g2 A- U. ~; e& j6 a
# 买单成交,挂上一格的卖单& S3 {, Z9 d8 g9 y2 Q1 Z
sell_price = final_price * (1 + gap_percent)
* B3 K& b k6 E) t& y& F/ W sell_price = round(sell_price, pricePrecision) # 处理价格精度
5 l% g& U- n, ?6 m; r8 [* L' d" a$ Q, R6 p0 E6 Z6 {
# 防止新计算的sell_price比当前ask低
: s5 t! ~0 n) R/ @ k4 K m # (如果网格运行时间间隔较长会出现该情况,因为新挂单价是由上一个成交价决定的,从成交到现在价格可能已经大幅波动了)
% d: K" @9 S# _5 H: y if sell_price < ask_price:
$ i1 w) t0 Q/ e w+ G1 } # 如果新计算的sell_price比当前ask低,以ask挂卖单,防止挂单变吃单
1 a) ]. _; Z1 ?9 K1 ~7 r sell_price = round(ask_price, pricePrecision)
q0 o j5 y0 |. `2 ?; q9 Z( R
* _3 l( |. K. `6 Q+ }& j # 挂新卖单& z a; j4 }$ o% {+ I" |. R
new_sell_order_info = place_order(exchange, symbol, 'SELL', quantity, sell_price)
3 S( K0 Q, v9 `# \ # 挂单成功后3 R7 X) |2 H o, `
if new_sell_order_info.get('orderId'):
1 }% M5 s+ i* ^- p& V8 u6 l # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 买单成交,根据成交价挂上一格卖单: {new_sell_order_info.get("symbol")} | price: {new_sell_order_info.get("price")} | qty: {new_sell_order_info.get("origQty")} | orderId: {new_sell_order_info.get("orderId")}')
) M+ ?% H$ E$ R# F7 L print_order(new_sell_order_info, '买单成交,根据成交价挂上一格卖单')2 H* u7 A# q( ^& _& C. F' `& T. l+ I
# 新的卖单加入到sell_orders中, Y. r! y" X6 M% q% Q" N$ H
sell_orders.append(new_sell_order_info)7 p/ Z. Z! H2 }! h, O
' @0 k# h; K# Q6 p$ R2 M$ _4 v. J9 |/ `8 P
# 买单成交,挂下一格的买单(挂更便宜的买单)
4 t1 V1 P; y) |( d8 l6 f buy_price = final_price * (1 - gap_percent)% e; n5 d; J& T; {5 y/ ^. d& h
buy_price = round(buy_price, pricePrecision) # 处理价格精度1 e9 s \4 k$ e- _, f l# |
! `& Y9 { g2 v7 n V
# 防止新计算的buy_price比当前bid高
" t* ~5 D) A! t# l j # (如果网格运行时间间隔较长会出现该情况,因为新挂单价是由上一个成交价决定的,从成交到现在价格可能已经大幅波动了)
& K6 d/ m! W1 U T+ I if buy_price < bid_price:4 R" X' X, ]" _ {
# 如果新计算的buy_price比当前bid高,以bid挂买单,防止挂单变吃单
0 Z) b, e8 O. s& V sell_price = round(ask_price, pricePrecision)
9 J' P/ b7 m. d) O: o" V0 k4 C$ {. K4 Y9 G3 G* {: j1 t7 x' D
# 挂新买单2 h; c" {: Y: e/ u, J
new_buy_order_info = place_order(exchange, symbol, 'BUY', quantity, buy_price)
$ a/ J6 B u- w # 挂单成功后
; c+ L1 |# x8 l4 H1 N if new_buy_order_info.get('orderId'):7 t9 m/ G5 h, A9 J1 F
# print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 买单成交,根据成交价挂下一格买单: {new_buy_order_info.get("symbol")} | price: {new_buy_order_info.get("price")} | qty: {new_buy_order_info.get("origQty")} | orderId: {new_buy_order_info.get("orderId")}')
4 a0 ~0 F! d5 s! k1 \' N print_order(new_buy_order_info, '买单成交,根据成交价挂下一格买单')/ x$ [0 `! Q* r/ d' Q* E
# 新的卖单加入到sell_orders中
8 |4 G, t* @4 q5 u buy_orders.append(new_buy_order_info)7 ~1 n8 H! |# u! g6 f
) N1 z0 ]& u7 A' [! ^
elif order_info.get('status') == 'CANCELED':: j" f4 a4 T5 U9 f; v9 I, P
# 订单被取消,从buy_orders中删除
+ M8 x r, q! a3 d buy_orders.remove(buy_order_info), o( H0 G1 ?: M6 T% x6 a; b; H
4 s5 l( {. J' d z( A( m elif order_info.get('status') == 'NEW':4 G1 @+ ~" S0 p" ~5 P9 o$ Z
print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 买单尚未成交,挂单价{order_info.get("price")}')
; A. Q H. O! S, a3 e- T7 u( D" f
6 a2 S' u. k. E else:
) Y# n1 `- P6 g1 l1 O% ] print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 买单当前状态为{order_info.get("status")}')1 P1 R& b; J9 T/ S9 M6 @
( j! y+ F0 _" l& O0 Q% g( l& ^
. O2 l4 y9 o2 E/ ?. r4 ~" l
N- @5 R1 O; r. R; a& g% |, Z" \0 z( I# ]. |0 X
# 若sell_orders已记录有卖单,逐个检查是否有最新成交' H& Q% E! q$ }/ D/ `7 k0 I$ ?
for sell_order_info in sell_orders:
. O6 ?3 M# [+ ^- `7 A - n& Y8 Y2 m/ Y2 b2 S9 G
# 获取该订单当前最新状态
$ Y0 C4 l `' g+ o& }* H order_info = get_order_info(exchange, symbol, sell_order_info.get('orderId'))
% `% a6 k+ Y7 o; a7 a6 q ' X( ?, s# F; w6 W3 B3 M* n3 ]
# 若订单当下已成交 a! c0 ~, X4 ~8 r, Y3 M) b
if order_info.get('status') == 'FILLED':
% q) B1 F$ \, y: g) ]1 X final_price = float(order_info.get('price'))0 A) k" ]" {3 u1 k1 r
print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} {symbol}卖单成交了,卖单价为{final_price}')4 _8 B+ I) G- v0 {) N
E8 L" b4 i- `& ?- }' y
# 成交后,从buy_orders中删除9 b# C; C7 R/ y9 \% O
# 注意remove的是sell_order_info不是order_info9 |/ B1 ^$ d9 L. u; g
# order_info是成交后的sell_order_info- O! ?: c% [( o# F
sell_orders.remove(sell_order_info)
9 b: Q# o8 f5 k, I K) a
* H! r' S; l6 G6 x* d q # 卖单成交,挂下一格的买单
. [* ?4 ~* `$ C8 V6 Y buy_price = final_price * (1 - gap_percent)2 F1 w$ Q; t, ~0 }
buy_price = round(buy_price, pricePrecision) # 处理价格精度0 D0 R5 S i2 ?9 K' q" s3 b5 H" w
: x3 R( x: c5 H1 @
# 防止新计算的buy_price比当前bid高
. `, t, C2 r' p6 V k #(如果网格运行时间间隔较长会出现该情况,因为新挂单价是由上一个成交价决定的,从成交到现在价格可能已经大幅波动了); e. L) Q F7 s! a2 I0 ^/ m5 y) Z
if buy_price < bid_price:
% {/ O; R9 {6 @. F # 如果新计算的buy_price比当前bid高,以bid挂买单,防止挂单变吃单 t: `7 Q _/ f n9 f- o% Z
sell_price = round(ask_price, pricePrecision)
' M' Y0 k- T( ~5 @2 m8 V1 c
0 [7 x# h( C, {5 i4 A z3 R o # 挂新买单) f; m: I- L/ Z7 l0 E& |8 d$ b
new_buy_order_info = place_order(exchange, symbol, 'BUY', quantity, buy_price)
. d* [; z8 f/ h6 A) a: r! Q O: U # 挂单成功后6 s. j2 [) [" U0 l* y5 l4 ?
if new_buy_order_info.get('orderId'):
) J8 Q, u* x1 S/ _; ? G6 u& F O # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 卖单成交,根据成交价挂下一格买单: {new_buy_order_info.get("symbol")} | price: {new_buy_order_info.get("price")} | qty: {new_buy_order_info.get("origQty")} | orderId: {new_buy_order_info.get("orderId")}')
f) k# y$ L+ D1 I0 ~ print_order(new_buy_order_info, '卖单成交,根据成交价挂下一格买单')' [1 h+ I+ C% Q {
# 新的买单加入到buy_orders中7 {3 p9 n1 v0 q: B/ _/ l: K* f2 s: n
buy_orders.append(new_buy_order_info)
% g8 [. {# V! E5 a7 X2 w$ c4 P: R
/ V% ^% W2 J4 J7 {+ N4 p! M. u) k: v# U" a" l' t8 p. p" U2 U
# 卖单成交,挂上一格的卖单(挂更高价的卖单)& Y3 s7 [! M" J
sell_price = final_price * (1 + gap_percent)
2 s6 J+ ]" v0 ^( y sell_price = round(sell_price, pricePrecision) # 处理价格精度8 F. R% ^8 w9 @/ q. u. d
+ e1 b2 e. L& d7 {% i
# 防止新计算的sell_price比当前ask低
7 c }2 f% R5 E: Y # (如果网格运行时间间隔较长会出现该情况,因为新挂单价是由上一个成交价决定的,从成交到现在价格可能已经大幅波动了)
; p: B" y' N# k7 ]( U( Z if sell_price < ask_price:
2 }# f* g. \4 _" s/ V( t # 如果新计算的sell_price比当前ask低,以ask挂卖单,防止挂单变吃单1 Z) s5 i3 x# O) f, a0 k
sell_price = round(ask_price, pricePrecision), V: l1 |% A% Z2 t$ p! w
7 g- d5 c0 D+ d, z) G/ C8 L6 M& U
# 挂新买单( c8 S: `+ l2 [- a. J
new_sell_order_info = place_order(exchange, symbol, 'SELL', quantity, sell_price)+ r" m. A+ V3 f% C9 X2 C% ?
# 挂单成功后) v. m$ j3 _+ W r6 ^2 r, W% ^( N
if new_sell_order_info.get('orderId'):
) g R' O& E3 D* p # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 卖单成交,根据成交价挂上一格卖单: {new_sell_order_info.get("symbol")} | price: {new_sell_order_info.get("price")} | qty: {new_sell_order_info.get("origQty")} | orderId: {new_sell_order_info.get("orderId")}')
! c3 a! m1 ~2 s" W: ~. T- t8 C3 [, m print_order(new_sell_order_info, '卖单成交,根据成交价挂上一格卖单')+ ]" ^* ?9 Q. g: o9 L; l' r
# 新的卖单加入到sell_orders中9 A% r$ s# }. ^# n9 z5 C
sell_orders.append(new_sell_order_info)
3 F7 M: s0 j: f% K) a' m: x+ ]
. i/ n9 q& z; X elif order_info.get('status') == 'CANCELED':
3 Z1 Q- v& p, w # 订单被取消,从sell_orders中删除
6 ?. k, m6 W. O# W7 \& j( [2 A sell_orders.remove(sell_order_info)
1 W" C; A# a( ^6 ^. W3 ] + X1 s. \+ W9 `' j- g6 _0 Q
elif order_info.get('status') == 'NEW':
) F; v2 h; p/ x/ f+ d0 q0 K+ v' i print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 卖单尚未成交,挂单价{order_info.get("price")}')( u7 M* x2 }% N
& C0 ^% _! x' X( D, N4 X
else:
2 f: G# D% S6 a9 o9 | print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 卖单当前状态为{order_info.get("status")}')
2 Z" |9 J5 X6 s8 e. h9 P9 D( D3 _/ Q( |* z
################################################################! d% g- A7 v, I. K9 ~6 S, H
- S* s+ h) l# o% I5 O # 若当下没有挂买单 - 挂个买单' U {5 V% a0 H( U: I. B. y+ {
if not buy_orders:3 G' u: X! V& b! p
6 M0 T( n6 y8 m+ ~0 v s' a0 _
# 计算买单价格( Y% b9 H2 f; m7 {
price = bid_price * (1 - gap_percent) # 按当前买一价的下一格,挂买单
. ]% `8 ^, Q7 G price = round(price, pricePrecision) # 处理价格精度1 ?, [: t* N. V3 H* M* F1 x) D
! x2 H% ~# R) I& t7 ^; `
# 下单
) \& V: n' d- C! I8 x# h6 m buy_order_info = place_order(exchange, symbol, 'BUY', quantity, price)- g+ E! a# P8 g
& I o+ C9 z; r4 i7 ~! C$ X' Y% F! } # 下单成功后; i' E0 s* O2 s* j' @. S
if buy_order_info.get('orderId'):6 V7 C3 W& x1 p: E+ I
# print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 当下没有买单,根据买一价挂单: {buy_order_info}')
& g/ |% s6 o% g' T! y ^% t # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 当下没有买单,根据买一价挂单: {buy_order_info.get("symbol")} | price: {buy_order_info.get("price")} | qty: {buy_order_info.get("origQty")} | orderId: {buy_order_info.get("orderId")}')
3 ~( o8 _1 h0 a/ X6 e+ Y$ l2 I print_order(buy_order_info, '当下没有买单,根据买一价挂单')
6 N- E: s& j) ~1 h: G# M: o buy_orders.append(buy_order_info)9 O! q0 D" `" E; S1 H
5 B& L' l' K- ?
1 A0 f0 S; X8 a' \: h# |. q
# 若当下没有挂卖单 - 挂个卖单
# D3 a/ ~" q3 ~! K- T) T- f if not sell_orders:7 c: |3 L4 ] r( P+ i7 z, y5 D' Y
- b) s7 n4 O' e) m( F
# 计算卖单价格
) _9 x' k* \+ I price = ask_price * (1 + gap_percent) # 按当前卖一价的上一格,挂卖单1 P' C; B- G8 f( N
price = round(price, pricePrecision) # 处理价格精度
( o6 p5 { Z3 P+ ^/ G' u+ M$ p* \% w! c/ Z$ v7 A7 j( Z/ Q
# 下单- ^% C; v. ]0 I, i" B7 _' K9 Q
sell_order_info = place_order(exchange, symbol, 'SELL', quantity, price)9 S0 x4 Z0 ?. N. C1 q. ~3 e L- ~
- V8 o4 ]* k* m# W9 Y7 u4 A5 y
# 下单成功后
+ ^$ Y' s- C' m1 M8 n2 O7 N4 W if sell_order_info.get('orderId'):" o3 L. G2 }9 W5 _0 ?/ |4 E
# print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 当下没有卖单,根据卖一价挂单: {sell_order_info}')
$ N0 m, |3 C/ a # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 当下没有卖单,根据卖一价挂单: {sell_order_info.get("symbol")} | price: {sell_order_info.get("price")} | qty: {sell_order_info.get("origQty")} | orderId: {sell_order_info.get("orderId")}')" D. x* K, {* T& X5 S. ~. c2 }) v
print_order(sell_order_info, '当下没有卖单,根据卖一价挂单')5 g* R4 f; x/ b% g! Y
sell_orders.append(sell_order_info)6 R& Q3 R* r: V! ]
( a% v; G6 \- w8 R: x ################################################################* j# Y+ v3 Q2 v8 l# `5 b+ W
) z$ M! x$ O; K- S # 确保某个价位下,仅有一个买单! K& t! v5 V( ]3 G. E4 \
if buy_orders:+ O2 r }: {3 A
buy_orders.sort(key=lambda x: float(x['price']), reverse=False) # 最低价到最高价# S7 q! M+ h) t. P% y+ z
delete_orders = [], m/ C4 o0 T2 z& r$ S
for i in range(len(buy_orders)-1):
+ R: x; ]9 w5 [+ F order = buy_orders! { u2 h0 B/ n5 {5 j- Q3 O7 C
next_order = buy_orders[i+1]
" z% ^' j K1 a7 _: C # 价差过小,即为重复订单. @) J6 s- ^- ?7 U1 h1 _
if abs(float(next_order['price'])/float(order['price']) - 1) < 0.0001:
" F9 N! Z- ~' S h# ?9 f) n # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 有重复买单,撤销订单:{next_order.get("symbol")} | price: {next_order.get("price")} | qty: {next_order.get("origQty")} | orderId: {next_order.get("orderId")}')
4 v' r& q. a1 ^ print_order(next_order, '有重复买单,撤销订单')
, |1 l( P" I; t' k( h; J d # 撤单8 ~3 C( Z; c6 o z- |
cancel_order(exchange, symbol, next_order.get('orderId'))5 p: s9 k4 u2 F. o' G# X
# 将next_order放入delete_orders,循环结束后再remove,否则会影响buy_orders的下标i
7 @; F. E# X$ ^- E delete_orders.append(next_order)
5 J! L$ [# @, I( [
4 M. L. Z# Z8 M) L" d # 将buy_orders中,价格重复的订单remove, X9 Q$ j4 e2 B0 \; ^+ ?, D
for order in delete_orders:
" c# t& h8 X3 r buy_orders.remove(order), k, Y6 O& |& g: ]8 D
2 E3 `) p, }+ P, g- C2 g# q
|( {7 ^. F6 h5 M # 确保某个价位下,仅有一个卖单
+ S" N [1 |6 U. r if sell_orders:
; N) y2 h, }% \ sell_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最低价到最高价$ \+ ^+ h- O9 `3 p1 v2 z- {$ f
delete_orders = []
: ?4 N3 C: E& \. N) Q! Z for i in range(len(sell_orders)-1):
2 P: V0 P. @+ u' i# @ order = sell_orders. R2 y+ Q- \4 \( H) ]
next_order = sell_orders[i+1]
, K' `* L# v% { Z# B3 k # 价差过小,即为重复订单# H0 e+ `3 `, o( y+ Z
if abs(float(next_order['price'])/float(order['price']) - 1) < 0.0001:! ]) @' s X9 H2 f1 N
# print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 有重复卖单,撤销订单:{next_order.get("symbol")} | price: {next_order.get("price")} | qty: {next_order.get("origQty")} | orderId: {next_order.get("orderId")}')
9 f- \4 |, G' N. }, p% L0 } print_order(next_order, '有重复卖单,撤销订单')- w3 |% v# O" i9 s+ ^! Q
# 撤单- m! Z7 C: }/ _- e1 P
cancel_order(exchange, symbol, next_order.get('orderId'))5 C) \& G1 P) ]0 F* |% v% F
# 将next_order放入delete_orders,循环结束后再remove,否则会影响buy_orders的下标i) C5 h, @& E H/ }. P7 i# s% F
delete_orders.append(next_order)
! f$ u( l( N: R0 ?, j& H8 [; w: _9 W1 q" H
# 将buy_orders中,价格重复的订单remove
b0 { [. g8 A, t. v for order in delete_orders:
1 ^! \4 ^0 Y8 z6 M) S sell_orders.remove(order)
; S& O& w9 }* l2 C5 ~0 p9 r3 h: G5 _0 {6 E( l1 `( B
################################################################9 b6 L, K% m1 a9 f, @0 U8 l% Y( L% D, ^
( L4 [1 ?' k2 P5 n+ Q* j8 ]6 q& G$ Q3 f: x, ]6 ?4 }6 b' e5 P
# 如果当前买单挂单数大于设置量,则撤掉部分买单: H; b# E: B+ l5 e# {" A
if len(buy_orders) > max_orders:
; ?3 \5 j2 E# E! Y
) E/ ^! e; a1 b: A2 r& ^5 D # 最低价到最高价
$ R+ r/ @! N9 M buy_orders.sort(key=lambda x: float(x['price']), reverse=False)
6 |' n! b2 O+ M9 t. u( Q # 取出最低价的买单1 [+ k, X5 C7 V8 {
delete_order = buy_orders[0]
) M1 Q! W% S* m # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 买单超过设置量{max_orders},撤销最低价的买单:{delete_order}')* J; E5 \2 U$ W/ w4 z
print_order(delete_order, f'买单超过设置量{max_orders},撤销最低价的买单')6 K' r$ ^4 L) b+ k2 x
# 撤单
+ h# M& \0 U% g) V% g/ Z cancel_order(exchange, symbol, delete_order.get('orderId'))# z; v, ]0 [9 q1 H3 M
# 从buy_orders移除
) O4 V+ P3 ~; D buy_orders.remove(delete_order)# ?) F* S) H" u" r! I3 Z
) C5 m. [$ ^" q" A$ F2 ?
# 如果当前买单挂单数大于设置量,则撤掉部分买单& T3 H- V' e( E& P+ y$ x8 q
if len(sell_orders) > max_orders:9 Y: r% T5 w. d8 S6 N
2 y, e6 X5 U4 g8 d! Y& m4 T' _ # 最高价到最低价6 [, G. i: B) U- R2 m w/ O5 ^
sell_orders.sort(key=lambda x: float(x['price']), reverse=True)4 R) z& n: V3 w# @3 y
# 取出最高价的卖单1 Q/ B$ {' n0 ^- B
delete_order = sell_orders[0]
/ b' I: T) F. X* _# p; \ # print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 卖单超过设置量{max_orders},撤销最高价的卖单:{delete_order}')1 Q9 Y; u/ ~" U5 }) q
print_order(delete_order, f'卖单超过设置量{max_orders},撤销最高价的卖单')
" m H7 l) _1 D9 m) ? # 撤单" @3 X/ s. b* T
cancel_order(exchange, symbol, delete_order.get('orderId'))
% x- J7 h% C# y* m4 l: o5 G" p$ F # 从sell_orders移除) \2 w" _- o+ r. }2 }$ }1 v$ |
sell_orders.remove(delete_order)0 k" y: E5 u6 x3 @& u' h3 h6 ^
: m' q" N0 t Q `. s* `+ |6 d
# return最新的buy_orders, sell_orders! L( D; r3 C* b/ {' L# _
sell_orders.sort(key=lambda x: float(x['price']), reverse=True)
$ @) _3 f% |( S) F buy_orders.sort(key=lambda x: float(x['price']), reverse=True)- R8 d' L/ z# L7 ~& F; d
print_orders(sell_orders)" r* r( k0 _' _: F8 [
print_orders(buy_orders)8 b5 f3 Z% `$ E' V2 K& H4 O
0 g* X2 {9 J: A
/ E: B+ w+ i$ g3 G4 P
9 [* w+ _+ F7 H2 r; P0 g return buy_orders, sell_orders所有代码可参考附件,往config.ini填入api后即可使用。
4 Q+ J; c ]% f7 b7 u程序我100u小资金在LINKUSDT上测试了两天,没碰到什么大的单边行情,赚了1u…程序逻辑上貌似没有什么大问题~- m) m( D6 f1 l3 j! a5 C
8 g+ F, P; D0 `+ _' l) T + r# m4 q5 u l" ~. p. b t
注意事项+ j$ C, y. q4 z. H1 H
. g; S3 w, d" Z% X· 码力微薄,可能有我没发现的bug,建议阅读代码理清逻辑后谨慎使用,我尽量每句代码都有注释;$ k/ T8 u; c& a7 p! v, L
· 程序有钉钉和微信两种报错方式,我暂时都注释掉了,大家可自行打开;
3 P$ |% B8 R/ n |7 S! y! ?/ y5 x2 f· 网格可能是一个赚小钱亏大钱的策略,大家注意风险,网格范围设小一些。
! U" C2 p4 Q3 W% V7 I代码附件:, r% w* [% S+ }8 R2 Y( e
% q W. C2 `8 d! C
 |