这次迭代主要基于一个问题考虑,就是reblance的问题,短线在开单长期磨损的情况下,可能会磨损掉长线策略资金。这次将短线策略集中于永续合约,长线策略集中于交割合约。代码已提供,还有很多可精简的地方。另外该策略未使用并行模式,个人觉得,多开几个策略和并行应该是差不多的效果。
9 r& ]7 m! ^2 f/ m! l永续合约版本, J' e) f6 J/ k+ F" M
"""
+ B& m2 c$ R5 {# 程序主要思路如下:
: J0 M6 K) f1 r通过while语句,每根K线不断的循环。
8 h- p2 K9 J, j5 A/ m+ h每次循环中需要做的操作步骤
M! s8 C, u8 {4 o' ] 1. 更新账户信息
$ z, j( x8 `8 ]) v+ l! C/ u. K ?( A0 f 2. 获取实时数据
/ a) S3 i1 i+ \, [: e; u 3. 根据最新数据计算买卖信号8 N. C1 b/ `& k% }) M; |/ {/ p9 A2 n: c
4. 根据目前仓位、买卖信息,结束本次循环,或者进行交易
d1 A: c4 {8 Z1 K 5. 交易7 p$ g4 C9 r$ n
"""
4 b" s+ m' M/ C, c Ifrom urllib.request import urlopen) V. N3 ~6 \9 q0 k5 }2 N. T# {
import ccxt8 d/ @' V2 H0 H, a
import time
- Q$ ]/ m* B) K5 t& Dimport pandas as pd# T9 ~+ j9 G; H
from datetime import datetime, timedelta# N( u7 O9 q' S5 P- W2 T6 n/ H
from time import sleep \/ q/ ]) F1 M8 y0 p
import json1 J. d) K* k/ ?% l5 `0 W
import warnings
$ ]. `: |0 w$ v+ ^+ O/ F' q! n- Vimport requests d- `' E& L) c" k3 k
import random% p u! V3 ?" |
import hashlib$ {, P/ z8 e, ^& [
import hmac2 a) z' ]# p& W
import base64
) x# V& \9 `, O3 \3 g# K) v3 P6 ffrom urllib import parse
* w- ~5 J. @3 L& i6 I9 }9 Fimport numpy as np
. o* q) l2 e P. v" h# e) i( N- zimport Signal6 s( q( b" t8 R( r& Y- P
warnings.filterwarnings("ignore")9 H, ~7 h- f0 v* O+ G4 g
pd.set_option('expand_frame_repr', False) # 当列太多时不换行: p% \; G3 @ |8 n
pd.set_option("display.max_columns",100)7 g& C4 m1 p% c2 v5 V, c5 \5 O+ V
: v, A( x0 J Hcoin="BTC-USDT"/ g" r( |! z0 k8 ^! Q* \! a
strategy = "vix"
- q/ t' S, O+ `
3 E I6 z. ]2 sconfig =pd.read_csv('swap_usdt.csv',index_col="coin")
5 n' G3 H( p; M" X! C2 jexchange = ccxt.okex3()
) }0 D* M! ]; t0 B- z) Q; h A( Yexchange.load_markets()
: D7 p9 c6 x/ ] {, g Nexchange.apiKey = config["apiKey"][coin]0 C. u' |5 k0 U2 u% H3 S+ ?5 T
exchange.secret = config["secret"][coin]5 F+ }! f& V7 d6 }
exchange.password=config["password"][coin] f- M9 X7 h% P
symbol =coin + '-SWAP'
& J9 M- u, [' @: l& Mtime_interval = config["time_interval"][coin]
8 o5 a5 Z- |4 _, w9 Q" F' Ctrade_coin = symbol.split("-")[0]
4 R8 p0 Y- i5 ]4 T8 z0 Nbase_coin = symbol.split("-")[1]) E% k+ x- `# P( @
secret = config["robot_secret"][coin]; O- k O `" X: S$ z
robot_id = config["robot_id"][coin]+ A- v/ H! h0 X6 \7 X; e+ _
+ E6 M/ K6 C$ h9 y F
def send_dingding_msg(content, secret=secret, robot_id=robot_id):
4 l' d; u& z# s3 a+ t5 ^! j timestamp = int(round(time.time() * 1000))/ S! w, P( p6 X% R0 e
secret_enc = bytes(secret.encode('utf-8')) o5 ]" S2 A' c1 Q; f
string_to_sign = '{}\n{}'.format(timestamp, secret)
9 Z: z V! v, k% K# U string_to_sign_enc = bytes(string_to_sign.encode('utf-8'))
( C9 H- c s* [+ K; F hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() l* y% V" P% v. \$ Y/ A8 i
sign = parse.quote_plus(base64.b64encode(hmac_code))1 y7 b5 P9 M# h! H5 _
query_string = 'access_token=' + str(robot_id) + "×tamp=" + str(timestamp) + "&sign=" + str(sign)
3 n: s; F: U0 K# ^ try:* k2 P3 N7 T5 E. w9 @7 V6 A. h
msg = {
4 i. ?! [6 {" o, | _, |2 P. s "msgtype": "text",
' I) q7 B3 W: J; B- \; r/ N "text": {"content": content + "\n" + datetime.now().strftime("%Y-%m-%d %H:%M:%S")}}6 v4 P6 Y- V; b) L
url = 'https://oapi.dingtalk.com/robot/send'
& D+ c7 Y" p( z, p url = url + '?' + query_string
( c4 F& m7 u4 w body = json.dumps(msg)8 d% g3 x2 p5 A% R3 E
# 设置重连次数2 a. ? ?% y1 R1 g; M3 w; z$ `/ [ ~
requests.adapters.DEFAULT_RETRIES = 15
5 w1 ^, f$ w4 B' M! X( Q* F # 设置连接活跃状态为False! F* }9 O# ~, x
s = requests.session()/ U( `# {# D, X' N& s) d
s.keep_alive = False
- I) ` _( G$ J Z # 进行请求
1 Y. w1 V3 D7 R+ B requests.post(url, data=body, headers={"Content-Type": "application/json ;charset=utf-8"}, timeout=5)
9 N8 w" A% I F) H* S% H print('成功发送钉钉')2 K9 X0 I/ z- W8 R ~5 Z; ]
except Exception as e:
2 m5 C3 C- l8 T5 a5 U, t5 {6 q- y print('钉钉发送失败,尝试重复发送', e)
# F" B" L% r4 s5 s5 G0 g+ k '''请求超时'''
8 ~- U$ g' R5 Q0 @/ ] for i in range(1, 5):
: |4 n" m) D% S/ L* H print('请求超时,第%s次重复请求' % i)4 ?: E2 ]/ _) i; K, k
response = requests.post(url, data=body, headers={"Content-Type": "application/json ;charset=utf-8"}, c/ ?( y; f: H3 ~% E' F' n1 A
timeout=5)5 S% |4 B& H) K- @+ ?" J
requests.adapters.DEFAULT_RETRIES = 5
4 i9 ?9 t. Z4 T+ i. N$ m if response.status_code == 200:
( A0 G$ L% {4 C. R print('成功发送钉钉')
6 L( W" t' B$ S return response
5 C7 L* g- f8 A, P2 W/ E+ j' r) S4 ^
def place_order(exchange, symbol,client_oid,order_type, buy_or_sell, price, match_price, size):( ]% E% A7 H9 H) L% e
"""/ d0 H5 ?1 t+ b$ R0 [# [
下单
4 Y1 u ~ @; M6 z4 Y0 U6 B :param lever_rate:3 h5 E' x5 a# Y7 l- S
:param exchange:
, f3 V0 ?5 _1 u8 T$ L- m+ z :param symbol:
. A- u! N) K5 v8 F5 T* f5 R :param order_type:' T! I, T& u3 d* M$ t; \' J3 W9 P3 a
:param buy_or_sell: 1:开多 2:开空 3:平多 4:平空" j; `7 _' D0 Z$ h3 T6 J
:param price:8 I0 q, j: N+ I, W' \
:param amount: 这里的amount是合约张数
c% f% x- U0 A, r7 V :return:
1 s8 z+ R! ]4 D; ?& x! Q """, k% T2 c+ ~( r( O
if order_type == '1':
% S; @, _; l* t$ b; ` match_price = '1'5 B' q& H: W- D2 D" j) w
elif order_type == '0':
$ G6 }1 e7 [) x; B match_price = '0'1 n/ _) D( E( B$ K8 ^' ?' W
if buy_or_sell == '开多':
4 c4 _( G! M3 c" d* f5 a9 k type = '1'8 u0 }3 J2 S% [0 `
elif buy_or_sell == '开空':) `. R& B# F; z/ v- m0 [
type = '2'6 o6 s" C- o2 R; b3 H
elif buy_or_sell == '平多':
. g9 t/ W7 n; B, O% @; r type = '3'
+ b, y8 ~! ?9 [: m) `$ x% n8 s elif buy_or_sell == '平空':9 I& T3 [2 s5 h; S6 M: e% s& f C
type = '4'
4 k$ k4 S' s i; u2 ^ # 下单
# M) Q: S" e# O% Z, ~ for i in range(20):6 A4 i+ Q6 f P- @! W" O# x8 m
try:
1 I! t" B0 D% L5 r$ ]/ r, R order_info = exchange.swap_post_order(params={'client_oid': client_oid,
' b! e, S% y" |) {; ]) b 'type': type,
$ t% O& v& l- [7 L/ o 'instrument_id': symbol,
G5 u: H V3 T! z% N* g* m 'order_type': 0,
2 }, x K5 x# M7 G6 N5 C 'price': price,9 O, W' ]- U. ^, \& Q1 _
'size': size,: e9 [* W; a A. T/ z8 ?
'match_price': match_price,7 r; u7 }" J9 u
'leverage': 3
: Y* X1 a! [6 R0 W } )
+ O+ |5 l' w+ A3 X/ ` print('okex 合约交易下单成功:', symbol, buy_or_sell, price, size)& C& a7 }5 R* a* ?/ m# O! x w
print('下单信息:', order_info, '\n')
- L, T: r" c; p return order_info9 l1 K9 ^1 ?* i; n
& c( C% e, l3 _8 c3 T. F; P9 [ except Exception as e:
9 |+ Z+ v h+ w. [ if "error_code" in str(e) and "35014" in str(e):% s; H6 D( E' H) o3 u( f
_df = exchange.swap_get_instruments_instrument_id_price_limit(params={'instrument_id': symbol})2 K. r* r6 G0 C2 w) F
limit_high = float(_df["highest"])7 f0 t, c+ Q( k/ ~! ^" M3 G* u
limit_low = float(_df["lowest"])) S* T% v, r6 b1 N
print("限价数据:", limit_high, limit_low, "下单价格:", price)
/ F5 M, ], R j! a& q0 F( g9 F+ j# S if buy_or_sell in ["开多", "平空"]:
! V: Y. D% R9 r( i( ^ price = limit_high * 0.995
+ B# G5 @5 s: C. t/ J else:
V/ P# _% T: S) \ price = limit_low * 1.005. I$ K& {+ |- K( [+ F* F$ A
print("修改后的下单价格:", price)
2 q" o- ^, D2 R9 k time.sleep(0.5), w* h6 h0 L7 V7 U) ^7 H% X
if "error_code" in str(e) and "35002" in str(e):3 F! g0 d: m) i1 `) E2 F& z2 v
time.sleep(15)
" {/ Q1 G+ X/ m( J. Z if "error_code" in str(e) and "35004" in str(e):6 m" S9 h+ o3 }5 C ?
time.sleep(15)
" i0 Y$ b6 x: ^. R- i( f print(e)
+ L+ v* B% {- ? raise ValueError('okex 合约交易下单报错次数过多,程序终止')# X( w' O" V! G" F3 x
print('下单报错次数过多,程序终止')! W- e/ B- V# F. C" M+ e$ F
send_dingding_msg('下单报错次数过多,程序终止')
; \2 _6 O. G' e% j
. m# r2 a& @+ ?4 Y/ v4 U' `$ d4 zdef fetch_account_info(exchange,symbol,client_oid):! p/ A/ i d4 E. M# f9 [7 @+ n. r' ^
"""
?" Y9 ^; [ e. e! `0 { 获取账户信息
; U# c+ x/ X) B1 w, z7 Q :param exchange: 交易所( n; N/ z& P6 N& d1 [4 }
:return:
' I6 d& e; M& N, E :param account_info: dict形式,包含账户和仓位信息) E. z3 m% L! h+ ` y
"""
/ c- d: C* E- R& ]1 } # 创建用于存放账户信息的变量
3 m# j9 v s6 u2 H3 {: N! ]5 x1 v) a account_info = {'账户余额': 0, '多头仓位数量': 0, '空头仓位数量': 0, '仓位成本': 0}
4 L/ F! [) b5 N while True:
* X' S5 O: U1 D0 v, E, C # 获取账户的资产信息" N D0 j3 J( m2 u# `; p0 X
try:* [% `3 ]6 F! a6 m( \" ?
data = exchange.swap_get_instrument_id_accounts(params={'instrument_id':symbol}) # 从交易所获取账户balance信息
- U* V; X5 D! l/ s1 M% | except Exception as e:
# h5 `3 ?5 L/ @) v1 y( f; N send_dingding_msg('获取账户信息失败')
# a8 c8 N; _5 t* f( E/ a print(e)( m2 m; q0 @+ K- Y! o
sleep(0.2)8 x5 F$ T9 J1 V6 V
continue7 a- Q/ D2 A5 x6 i2 a
# 将数据转化为df格式
" D [4 n/ A+ ?" T' V account_info['账户余额'] = float(data['info']["equity"])5 Y7 L4 w; X% z2 G( B: l" o
% O4 c! }5 o7 f' ~8 F8 l8 j* h( P # 获取账户的持仓信息- B5 @ s2 ]4 I; u% I
try:% ]( j8 E3 m; r) a! d
client_oid_info = exchange.swap_get_orders_instrument_id_client_oid(
0 t" z1 Y5 [3 [- L' x params={'instrument_id': symbol, "client_oid": client_oid})2 u. z D4 u$ l# b- a8 O* M
except Exception as e:
' U% P( J1 e9 o- i _ try:
1 D2 \ i& i/ U4 V if "error_code" in str(e) and "35029" in str(e):9 D: b' p' S8 I9 c) O
account_info['空头仓位数量'] = 02 P( n) O5 H: M+ |# K- i3 y" U
account_info['多头仓位数量'] = 0
7 G/ \3 F, `# p/ N account_info['仓位成本'] = 0
9 ~7 x B/ ], P3 E8 Z- Z" z break
5 e) K( C" A, a) U7 o time.sleep(0.05)
# p) g9 C# k% a) f- T except:3 i2 {% j* V- ^, |+ _5 ^1 x% F1 p
time.sleep(0.05)
5 `; L l( ?! T# q1 P- n continue
, a1 X- f( j$ m' P" {
; @4 ~" s" {: m; A if client_oid_info: # 当持仓信息信息不为空时/ _$ o# J4 U4 Q$ Y
if client_oid_info['state'] == "2":
8 E3 G; _# @4 O5 C, K if client_oid_info['type'] == "1": # 当持仓信息信息不为空时: b, r1 M/ q4 g- x& D# p1 ?
account_info['多头仓位数量'] = float(client_oid_info['filled_qty'])
, ~* G/ D2 U; H1 e# Y account_info['仓位成本'] = float(client_oid_info['price_avg']) Q( Z3 ^# M" s
elif client_oid_info['type'] == "2": # 当持仓信息信息不为空时) R. |' t8 p0 v8 Z4 U& ]
account_info['空头仓位数量'] = float(client_oid_info['filled_qty']), |8 h+ @; H8 t4 P5 }! R( ]" b; d
account_info['仓位成本'] = float(client_oid_info['price_avg']). t7 J9 R! ]! `( s! x# i& J9 v
elif client_oid_info['type'] == "3" or "4": # 当持仓信息信息不为空时
- X: V& Z1 H' Y+ _' b account_info['空头仓位数量'] = 0
& O* v8 m% J' S- G- g) E g account_info['多头仓位数量'] = 01 j3 s) E2 @$ G
account_info['仓位成本'] = 0, t$ [4 a- p6 |! J* `2 Q
break2 q+ t9 \7 I1 }' t% v& c9 f |
return account_info( `0 T' S0 T% m0 A4 r6 V% Y
( j3 R, i) N& N. X9 fdef get_history_candle_data(exchange, symbol, time_interval, max_len, max_try_amount=20):
" b/ e4 |8 K9 D. N8 H+ I( C """( T. u" Q1 Z$ ]) U+ {& \
获取某个币种在okex交易所所有能获取的历史数据,目前v3接口最多获取1440根0 p) N g0 W6 ?& f' q
:param exchange:, T& A* O9 b/ T2 L
:param symbol:+ n: d0 ~- a5 m
:param time_interval_str:
+ f0 k* y% e+ k1 Z :param now_milliseconds:# X! k. n T' C* n. e0 K
:param max_len:+ T# T$ I" x3 v' j" E2 x0 p* W
:param max_try_amount:
4 A/ E/ m: z4 x5 A- o :return:
% h" O9 S, T9 x' D """
3 g0 w. o$ J# z# R now_milliseconds = int(time.time() * 1e3) # 获取统一的数据截止时间
5 J# W5 w# R% C( k" k" E time_interval_int = int(time_interval[:-1]), R# V. e( Y7 H3 v; `) B
time_segment = time_interval_int * 60 * 1000
; B8 r9 O& P" F' D) f end = now_milliseconds - time_segment m) u6 C/ U. M
since = end - max_len * time_segment) e+ v7 j) T6 f1 `) b/ h; i9 G; y
all_kline_data = []0 G& }/ u& F" e9 \% e5 H6 p E
while end - since >= time_segment:) E/ `9 W' @9 D: j
kline_data = []- n d) g* u; s; H4 W- F, M
for i in range(max_try_amount):; E' p: h& e+ ^; z* @7 u
try:
1 m. o( _2 f9 w p kline_data = exchange.fetch_ohlcv(symbol=symbol, since=since, timeframe=time_interval)
) p9 v: Y+ ^: j( [ break" C$ [( C7 B9 K/ X# f. t
except Exception as e:
9 r" p" g7 v8 m) O3 ^ S& X print(e)
( Q: f/ _* s' v; c; j, O* c try:
, L) w) x( S2 Z* v1 H+ M G0 _ e_str = str(e)" F# x. ]( D! z( i1 n7 H) U/ u
if '35004' in e_str:
8 W% X. n* B) a5 U time.sleep(30): v4 |$ {, P9 W0 j9 U8 y7 W. s% X0 z0 N9 h
if '30014' in e_str:+ W4 S2 v; b: _* r6 i5 {
time.sleep(10)& [6 T0 W+ h( I5 Q) ?
except:
5 j% p" O, r2 [: C5 K& B+ g: n time.sleep(10)
8 v; i$ n% v a% \4 \7 Y& i4 K8 t {
if i == (max_try_amount - 1):$ t' Q c' z3 @; b5 j9 x6 g7 r
print('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')( }* l- b# ]" g
send_dingding_msg('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
6 _5 a' u5 X# J9 `) n0 Q2 L raise ValueError('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
1 m# g0 N8 C2 y1 E* }8 A" |+ A if kline_data:8 b& ~' P& o; \7 `+ C% X
# 更新获取时间
# \9 D2 Z2 W" ?& f( r/ ^& A; T5 v since = kline_data[-1][0]
! R# h2 l; Y+ ^" x/ t1 [ all_kline_data += kline_data
5 Y% |5 N1 C( Z! [ A5 ^% U$ k6 n: e3 j else:
8 R. ]$ @( Z5 n& h6 _ break
; w# c% J; c0 y( ^' c$ ?( ]& I$ N& Q
) H% h4 I' j3 a7 G ^" T # 对数据进行整理
3 k; G/ b! }$ F5 M) b8 | df = pd.DataFrame(all_kline_data, dtype=float)
% @, u3 o, W3 M# J D! P df.rename(columns={0: 'MTS', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'}, inplace=True)
7 c$ x) a: L% Z3 q7 a df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms')
0 T! e; d5 \% r' j T df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8)0 g8 x& a3 _6 q
df = df[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'volume']]
. D6 ~, v; X6 I6 ~$ n2 u N& @# u4 e& X5 r5 n. A
# 删除重复的数据
2 x7 B4 I/ N: a3 q1 U; A: N ^ df.drop_duplicates(subset=['candle_begin_time_GMT8'], keep='last', inplace=True)
/ W' c6 k# O' ?6 A! n df.reset_index(drop=True, inplace=True)& t+ H% U" z1 W% r3 O4 x% ~
df = df[:-1]! a& ]5 I/ u2 G
# p- ^. j# _. l H I0 J& E return df2 D3 a2 n% b& j: n5 o; v0 t
( m2 k# ^/ `: |! s5 ]- _
def ccxt_fetch_candle_data(exchange, symbol, time_interval, limit, max_try_amount=5):
, J* `0 x4 @% v2 @' _ `+ L8 H """
& e9 u) o/ a- o( H7 n# g. H :param exchange:- ^# x# o% s/ ^ `
:param symbol:
- f' C+ t7 q1 V7 e3 m :param time_interval:8 T7 @, N7 C- c3 j" V1 _
:param limit:: k9 V. i6 }( _+ c
:param max_try_amount:
6 C" l- y. B5 ?. ?$ ]4 B3 [ :return:
( [- D! Z7 ~8 x# d: Z 获取K线数据
" e7 ?" k. j* W% z, H """. t# X6 o3 N: Z9 z0 t+ `
for _ in range(max_try_amount):
1 b6 Y2 W% B( r, C+ ?& q- F+ S, j try:+ D7 b$ k/ _; V; {5 Y. Y; }, G9 e
# 获取数据. p! D. B9 W' k' p
data = exchange.fetch_ohlcv(symbol=symbol, timeframe=time_interval, limit=limit); Y! e& @5 [) p0 r8 v- r
# 整理数据8 b7 x. q- g+ v4 u1 p* q1 N
df = pd.DataFrame(data, dtype=float)
8 _3 X7 g( _3 e7 W+ U df.rename(columns={0: 'MTS', 1: 'open', 2: 'high',6 @- n0 x$ [( |2 }3 V) H$ M0 H
3: 'low', 4: 'close', 5: 'volume'}, inplace=True)9 s% K) b, W6 O( w( n5 r
df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms')
1 y3 |3 w! O3 @ f/ h+ P+ M$ M4 J2 Q df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8)
0 [- [( p( o. H7 ?1 A% l& `. c df = df[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'volume']]7 Z0 Z+ I: b0 L3 ]! K; c: b9 A8 C
return df- f" V8 I. S" I( _
except Exception as e:
. B( ?) y! z3 p* A4 O1 \ print(e)
7 a% ]0 j$ r; v. a print('获取fetch_ohlcv合约K线数据失败,稍后重试') ^- @2 C0 T8 z' U& t7 n z9 L2 E
time.sleep(short_sleep_time)
8 o2 G* L- u+ {' ^) T. E+ y Q1 c
/ U, f- {1 w- `) B% K print('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')# U5 k9 e) l9 T8 u5 p7 G
send_dingding_msg('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
$ I$ d& Y. l' c5 b7 I# v raise ValueError('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
4 M1 C* i8 S& s6 J3 g; O6 j
& W5 S. ^9 H5 z6 c2 @! t# l' v5 k
" a7 E+ D" s3 |9 s& U
% u( U5 L: w' A( e8 Y7 C" ^! Qdef get_candle_data(exchange, symbol, time_interval, run_time, max_try_amount, candle_num):6 g( l# `1 G; W$ I0 o W! L& ?
"""4 J7 ]9 S Z2 g/ n* u4 O' t1 z4 ]
:param exchange:
; |0 j7 Y ]+ p :param symbol_config:
7 N/ `% [. f. b. m4 m& ~+ y8 T& p :param time_interval:* X _) H- _! U6 L8 K* [8 C- J+ U
:param run_time:5 w1 M$ V1 {! k. [7 c r
:param max_try_amount:. c- V# ^& w( N8 Z" [) q9 h% l
:param symbol:3 s( b/ n1 p# q. F+ g# F5 S2 i& ?' r
:param candle_num:' c( V9 u/ r$ I. e
:return:
# B1 D Q' x9 k+ c0 O 尝试获取K线数据,并检验质量
, n! {, k$ g4 Z8 a """6 P; s6 y y. [6 D
# 标记开始时间; ~7 u5 o/ K+ A; F7 x/ X
start_time = datetime.now()
0 V& r$ Q& u7 O8 Z print('start get_candle_data', symbol, start_time)+ h0 V# u8 q0 H6 e* l5 j. y# J# w
/ P Q, V4 Z9 M( R8 l/ k # 尝试获取数据! E7 g i7 E* w: c2 z
for i in range(max_try_amount):
7 \ v0 ?, r' @: H5 i # 获取symbol该品种最新的K线数据3 y+ x' h5 ]8 T6 p9 A& I b5 r5 W% R
df = ccxt_fetch_candle_data(exchange, symbol, time_interval, limit=candle_num)
; j) W- u$ @2 z' c6 S if df.empty:
& F% ]! S [6 t# A continue
2 [8 ?* X7 P# @, z4 I) i; b( x b) s # 判断是否包含最新的数据: K1 W0 ^3 i0 T* G$ F/ K" I, s
_ = df[df['candle_begin_time_GMT8'] == (run_time - timedelta(minutes=int(time_interval.strip('m'))))]2 ~" X5 Y/ ?3 F1 T3 T5 L* W4 F+ A. D3 g
if _.empty:4 ?9 e/ A) d6 D/ s" z, b( _- T
print('获取数据不包含最新的数据,重新获取') ?) W, f' l* B R$ I, r d4 i7 o
time.sleep(short_sleep_time)$ g# I# r1 V1 C- b; A
continue
, G' c% j8 P8 a; m- `* Y! V- m4 _" z8 b
else:3 A8 p$ q$ Q, o6 e
# 获取到了最新数据$ U: Q" o( f, C A: l' U) k* a
$ U" W$ Q. E1 U& i8 v9 r df = df[df['candle_begin_time_GMT8'] < pd.to_datetime(run_time)] # 去除run_time周期的数据/ F% w0 g3 m9 C( y! }4 w
print('end get_candle_data', symbol, datetime.now() - start_time) C# H5 s/ b# l7 o
return df
A0 K j( o X- a% [8 X) M7 Q2 j& n( f- k& w9 }, W
print('获取candle_data数据次数超过max_try_amount,数据返回空值')
7 Z" U& f0 T& d# ]. A return pd.DataFrame()4 \4 P6 O- s+ b9 T" ]; M
) `+ w Q4 q. |# @ D+ `% W
3 z/ @6 N7 R* u) s, d* k# S( @ y' Y' M# Y) U3 S3 l
def next_run_time(time_interval, ahead_time=1):
7 v: V4 ]" j& i) u2 X if time_interval.endswith('m'):
1 g7 N Y3 X" E* \ now_time = datetime.now()
2 W+ x" g" K4 I time_interval = int(time_interval.strip('m'))
, I) \4 l: N4 d6 R, B target_min = (int(now_time.minute / time_interval) + 1) * time_interval
8 a0 r/ t( y" F' ]' ], V+ B if target_min < 60:" U: `5 T# m8 b: G5 ~
target_time = now_time.replace(minute=target_min, second=0, microsecond=0)
. A! a }4 W6 ] else:' m' H% k1 G6 k$ j) Q
if now_time.hour == 23:
/ A& V2 E' k" m4 q target_time = now_time.replace(hour=0, minute=0, second=0, microsecond=0), D; K5 r! k! ~' [+ n
target_time += timedelta(days=1)3 u; F, z0 s5 [: K
else:- L3 K' r# d, a# l [
target_time = now_time.replace(hour=now_time.hour + 1, minute=0, second=0, microsecond=0)+ e) k# x- D# m: L: _* q6 X9 A" |
# sleep直到靠近目标时间之前
3 m( }$ U, ` ?% o if (target_time - datetime.now()).seconds < ahead_time+1:( O1 Q ?, _4 c( a" J4 \
print('距离target_time不足', ahead_time, '秒,下下个周期再运行')) C0 R+ h% ?& R+ ^1 J2 `7 L
target_time += timedelta(minutes=time_interval)
; A3 u" V, v/ |7 e print('下次运行时间', target_time)0 O0 D/ U2 k4 W7 B- v
return target_time
6 Q% w1 @$ I% D. M2 a! [ else:
9 C) }% D* p7 W' P( u/ ]- D3 } exit('time_interval doesn\'t end with m')/ g. R4 k8 C4 m) H
% H- a K. G+ ]+ G' X7 M# =====主函数) g" }' H8 n8 o' ^% H7 H
def main():
/ C3 R1 o, U3 q # ===钉钉内容# v7 u: a! ?- I7 H- t5 c
max_len = 300
7 {1 |% e6 B6 E: [ @2 u7 G max_try_amount = 10
. i6 Z6 F& {, M: Z candle_num =3
" o8 G# y3 I# P0 p y5 W config = pd.read_csv('swap_usdt.csv', index_col="coin")- B& v8 E Q4 V7 v
strategy_name=strategy.upper()4 y2 P- Q- V- i, k" h# {
msg_content = 'SWAP_%s_%s策略报告:' % (trade_coin , strategy_name)
7 C1 C$ y a/ f! {# z closebyhand = config['closebyhand'][coin] # 手贱模式6 u7 }# Z$ C* Y/ }3 q: Z
leverage = config["leverage"][coin]
6 z4 x7 }7 |0 s# f: j e7 c, I- Y fv = config['fv'][coin]. o3 h# s/ Z4 E
stop_lose_pct = int(config["stop_lose_pct"][coin])
D( w9 i/ A% @7 f& o client_oid = trade_coin + time_interval + str(closebyhand) + 'usdt' + strategy; i5 j( ?$ T. [+ \
para = [int(config[strategy][coin])]
+ F2 `7 X3 ~9 l/ ]& \ account_info = fetch_account_info(exchange, symbol, client_oid=client_oid)
+ K( @7 {* n1 f4 z msg_content += '\n持有' + str(base_coin) + ':' + str(round(account_info['账户余额'], 2))
4 j0 Q/ K4 W) b) I# M+ I
$ P7 ~# [6 ~; C if account_info['多头仓位数量'] != 0:
/ w5 u2 j) J4 D8 u) d msg_content += '\n多头仓位数量:' + str(account_info['多头仓位数量'])
* ~0 v" F* y6 W2 n) O" v- Z6 l3 a msg_content += '\n仓位成本价:' + str(account_info['仓位成本']): s+ S9 h! Q: [+ E+ @+ \
elif account_info['空头仓位数量'] != 0:$ Y) Q0 C: x r6 p' t1 B& I
msg_content += '\n空头仓位数量:' + str(account_info['空头仓位数量'])
5 R! y% z. e' a& _+ m# } msg_content += '\n仓位成本价:' + str(account_info['仓位成本'])
4 r1 ^$ D# f( B# k+ l9 X! ?! g else:2 w' W- ^6 Y" S3 e
msg_content += '\n无持仓信息'
' h$ x( e6 C' @8 s7 a
5 U" [( o( D( m0 e1 _9 S history_candle_data = get_history_candle_data(exchange, symbol,time_interval, max_len= max_len)
) s% b7 l% d% E
, @$ r+ b7 \% W8 N( a! F/ a9 v run_time = next_run_time(time_interval): Q# o& _, ]- J0 \( D9 v
sleep(max(0, (run_time - datetime.now()).seconds))7 ]$ e7 @) M9 X8 j( `
while True: # 在靠近目标时间时' g4 z8 G6 S$ C& p
if datetime.now() < run_time:1 n: {1 H. f# K
continue
3 h+ C J" I. I0 l3 D$ R else:
2 O& F( K+ Y) M/ F, ~$ U break
$ Y+ M7 C$ s" S. M: e! U, x \4 g, h! o9 f4 g& N* r9 N |
recent_candle_data=get_candle_data(exchange, symbol, time_interval, run_time, max_try_amount, candle_num)
+ E( G& ~! \# s4 y3 s4 V
/ y- e2 }* `: t8 K df = history_candle_data.append(recent_candle_data, ignore_index=True)
5 T' T# p/ @" a. t. S' e+ Z
4 P+ W+ j( I: x# f) u+ c df.drop_duplicates(subset=['candle_begin_time_GMT8'], keep='last', inplace=True)& t* ~" x' u8 {7 V0 M1 E) r/ q4 }3 D
df.sort_values(by='candle_begin_time_GMT8', inplace=True) # 排序,理论上这步应该可以省略,加快速度
- J. E" G2 c p2 q& G2 [ `9 c8 V9 j8 k( j df = df.iloc[-max_len:] # 保持最大K线数量不会超过max_len个
$ J( m: W: F3 ]$ h df.reset_index(drop=True, inplace=True): }& P* `% k# h q# C
$ |" Z! V" ^" I1 E* n. \ new_price = df.iloc[-1]['close'] ?: G7 k# b- F0 ?
msg_content += '\n' + str(trade_coin) + '实时价格:' + str(new_price)
- H; P2 A6 |' i
5 G. i3 q/ w# |5 ~) S% Z. q df = getattr(Signal,strategy)(df, para=para)
|+ u/ D$ n, {/ y. ? signal = df.iloc[-1]['signal']& n2 ~* T! S: L: E' b- |! H2 t
msg_content += '\n策略信号:' + str(signal)
: V* ]0 f6 s5 ^
8 l' K3 `2 V# l: R5 b # ===判断交易方向并且下单,除了无交易信号之外,总共有6种情况/ W a% s: u) U; Y, w2 ~" g
if signal == -1:5 I* E9 _( k' c$ L! G
if account_info['多头仓位数量'] > 0:4 S) y, k0 T# V) {$ y
size = int(account_info['多头仓位数量'])
4 q6 E1 D7 J7 \% H place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',
4 I% D8 o1 f' D( O' Z price=new_price * 0.98,( G" r5 B! d6 x' e' @
match_price="0", size=size)
& }( M6 @$ k! ~! W/ ]3 i # 更新账户信息、价格信息
9 Z* D4 W4 ^ H% t/ D/ j sleep(1)
6 e$ \/ Q' c; T4 P1 g. S+ C account_info = fetch_account_info(exchange, symbol, client_oid=client_oid)* |& e+ c6 b3 _" z
new_price = exchange.fetch_ticker(symbol)['ask']
" l5 t: l6 n: ]4 s/ U4 O d size = int(account_info['账户余额'] * leverage / (new_price * fv))% b1 s2 K7 G+ a8 Y4 I Q
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开空',% q' L3 q) \) z, B/ k
price=new_price * 0.98,
. Z: ~& y+ y- i1 C" n1 L3 x match_price="0", size=size)
- K9 O8 Q0 }2 G: o8 p msg_content += '\n关闭多单并开空单'0 C* h& P+ G% |6 T. {
msg_content += '\n开空交易数量:' + str(size) + '\n'" M$ u, g! l( u: z/ e; q6 I& u/ U
elif account_info["空头仓位数量"] > 0:2 ?* l5 ^, w% _ S) d- Q
msg_content += '\n之前已开空单,本次不开单'$ K% G# l/ g# Y3 d( ~
pass
# \+ U; w& Q/ v6 Q) m; o; P else:
, ?3 F3 {0 j$ n8 Z. ~/ T size = int(account_info['账户余额'] * leverage / (new_price * fv)): z8 Q0 b- y8 s/ X
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开空',# n3 R% a& k1 J. n: S
price=new_price * 0.98,% P ]! p. h9 Z# F/ }" f$ _' B
match_price="0", size=size)
. T+ B9 @. n& D* O6 Q6 T' ^ C msg_content += '\n开空交易数量:' + str(size) + '\n') S, c# v V' B; Q
elif signal == 1:7 W7 `6 X( x/ X5 ~; A! u
if account_info["空头仓位数量"] > 0:
" \; x# X/ B) A2 w size = int(account_info['空头仓位数量'])1 P0 P( ]% H1 i6 y3 b% g
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',6 G/ F" ~6 h+ K. [0 T
price=new_price * 1.02,3 ] X% R0 P$ [: C! S- l) Z
match_price="0", size=size)
! c5 o8 ?! v- [
+ E$ V7 h4 Y1 n( K # 更新账户信息、价格信息# P, t ^/ \+ T, n8 e9 y! E
sleep(1)
" }" x n0 P) y5 `, H( [3 b* f account_info = fetch_account_info(exchange, symbol, client_oid=client_oid)' g6 V+ k5 `/ y' n
new_price = exchange.fetch_ticker(symbol)['bid']) b$ H( t% {3 o' y
size = int(account_info['账户余额'] * leverage / (new_price * fv))
?! J9 N6 {7 J$ [$ f place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开多',
# i1 o1 O6 g5 j! N3 k price=new_price * 1.02,
: j) ], N( Z r& N/ o. }1 N1 g& b match_price="0", size=size)
9 u& i& M# x- O msg_content += '\n关闭空单并开多单'' C$ e% f; @, U8 F) }' T9 p6 d- q
msg_content += '\n开多交易数量:' + str(size) + '\n'
) @/ e8 o% i; g! _ elif account_info["多头仓位数量"] > 0:! V- y" a% G" M- Q. M$ b3 G/ j
msg_content += '\n之前已开多单,本次不开单'
8 O' N! r. v$ x# y6 }8 F* F pass
; K! @2 n) X( D1 P! l+ }8 m else:
' K5 q6 {+ K1 ^$ k* A size = int(account_info['账户余额'] * leverage / (new_price * fv))6 h1 f+ ^ c! Z( v, o# f
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开多',/ W1 c' s$ [4 a2 J
price=new_price * 1.02,
2 I5 y/ K' C4 @) n+ _' d' [ match_price="0", size=size)
% P. n2 _9 ~: s8 ?$ P# K3 X msg_content += '\n开多交易数量:' + str(size) + '\n'
6 y( Q4 t5 W7 H: e# S) o* M, H elif signal == 0:
, z3 ?! s. X2 j! J, z0 a2 n$ W7 S o if account_info["空头仓位数量"] > 0:
3 K, X R4 ]3 {5 w2 v/ m$ r6 s9 u size = int(account_info['空头仓位数量'])" w2 \+ f/ Y- d6 w. f9 B
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',
1 A1 f- a: v* v price=new_price * 1.02,& L0 S& w. g6 \
match_price="0", size=size)
& ?& k3 f2 a z Q: d) e% x! k msg_content += '\n止损空单交易数量:' + str(size) + '\n'
# i2 o3 B, b/ U" k: b" v elif account_info["多头仓位数量"] > 0:2 Y, s) _1 u+ ~8 i% |: c
size = int(account_info['多头仓位数量'])
' z. z( k C7 [9 @7 Y3 N2 o- Y place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',
) r* a0 ^$ {: q price=new_price * 0.98,! ~* }6 i2 P1 F( b
match_price="0", size=size)- T* U/ I1 n7 r' r/ |
msg_content += '\n止损多单交易数量:' + str(size) + '\n'
1 x. p# O C. \" T2 q4 Y8 O else:
) A+ Y: O x: D, {" u; c: [0 ? msg_content += '\n之前已平仓,本次不开单'
1 k/ b, a& x2 P- S/ a pass. b1 Q* T# m" j) ]3 s
elif account_info["多头仓位数量"] > 0:# P! M0 j1 H( S3 E1 b" a4 s
if df.iloc[-1]['close'] < float(account_info['仓位成本']) * (1 - stop_lose_pct / 100):3 e; i6 o' s9 \1 s4 |# S
size = int(account_info['多头仓位数量'])
7 U* O) Q: }* N* Z3 e place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',
7 U! i# L9 h- x. x' a/ d ^ price=new_price * 0.98,
3 p: N) h$ O g' l; {1 D$ i match_price="0", size=size) B: Q# [# F) q+ Q _0 O1 e
msg_content += '\n止损多单交易数量:' + str(size) + '\n'
7 J6 }3 N2 u3 ~" |# [% F elif account_info["空头仓位数量"] > 0:
8 x+ V8 R, _0 p- p/ H if df.iloc[-1]['close'] > float(account_info['仓位成本']) * (1 + stop_lose_pct / 100):+ j7 G$ O/ t0 s
size = int(account_info['空头仓位数量'])
& K2 e% v: H7 S6 H" s place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',7 l9 A4 _: v& _6 U
price=new_price * 1.02,& `! m% ?9 M3 W' o6 O( N
match_price="0", size=size)
7 W5 `. D4 h: K9 w/ l9 ~& | msg_content += '\n止损空单交易数量:' + str(size) + '\n'5 q* E# {% r$ }, W) e
else:
5 r' J7 }. d1 ?& | msg_content += '\n本周期无操作'. T, d* c1 w3 q7 [. u: h6 J* H
print('本周期无操作')
. T5 e. S P$ a8 Q- X% H
4 T& h S) H0 R1 g
" {# S% ?1 g" B1 s. u) l3 k9 Y try:
[+ j8 N5 e) v& b# C0 K6 u2 |* O3 |* Y send_dingding_msg(msg_content)
_/ ~ _4 @) u v' r except Exception as e:
% r! l' Z1 G% C9 d; [; H3 D& { print(e)
; W! h% j9 t8 M, ]2 ]5 n5 |. X# # =====运行主体
. S. z3 q$ G l4 h* k9 Qwhile True:
& j! q* _; _, a( g6 e try:
$ {% I$ b. u" K0 E/ H main()/ y/ ^* {: J% W! Z* I. g. q
time.sleep(10)7 E4 O4 ?8 w1 i2 Z# x4 h V
except Exception as e:
& i. p# m3 c4 @! B* o8 i send_dingding_msg('系统出错,10s之后重新运行')1 i3 N+ b, A4 |" D$ J
print(e)" x0 n) X1 [1 [6 `
time.sleep(10)交割合约版本:
6 S' j6 Q ]6 ]$ r& s; @""". E6 J F2 b8 E8 x$ }
# 程序主要思路如下:
! W$ y2 F( B4 J3 X! \通过while语句,每根K线不断的循环。
( U, J( @- |8 o0 i3 y每次循环中需要做的操作步骤$ F; S4 p# d& L* W* _# r
1. 更新账户信息3 m& F9 ?* ]2 t- V4 ] ?& g6 {
2. 获取实时数据
% j+ O1 ~* v+ i! p: a 3. 根据最新数据计算买卖信号
! w& V0 i/ r- m3 i3 K* i 4. 根据目前仓位、买卖信息,结束本次循环,或者进行交易
, v. O- O: ?! r/ n v% T 5. 交易! d# a6 E9 q# x% g. X" `% H( h' j
"""
2 A" `$ _9 C! N- l+ B- Ffrom urllib.request import urlopen
) K# e2 O+ n' ]9 W( Rimport ccxt
+ K% I+ f7 _* K# Vimport time& S$ p0 K$ ? H( H- B( |% m0 Q% o
import pandas as pd) s& T% q4 g; j6 u( j' {. d, P
from datetime import datetime, timedelta$ [0 [- l! V$ X
from time import sleep( H- [7 `7 f. _% n$ E
import json, p( R2 j) ~3 Y$ F, y' j- C
import warnings
K6 y! L4 a, {( {1 Eimport requests0 K+ J V6 d$ M! X
import random, C3 n/ ?6 \& G8 G7 q! ~$ s, c/ ^- b
import hashlib$ R5 @0 `& H- b; H
import hmac) [/ \9 g/ r+ p9 S
import base64
3 y. r6 V; k/ }2 }. I% ofrom urllib import parse
; B; L& m! _- l0 H/ C3 F" wimport numpy as np* Y& ~) ?- N* q" d6 n
import Signal! w7 m2 A" t6 { z
warnings.filterwarnings("ignore")( S/ [( z" s3 X: t& ]: c6 ]; D
pd.set_option('expand_frame_repr', False) # 当列太多时不换行& r& g( X. d A2 l# ^
pd.set_option("display.max_columns",100)
# A- I" H/ Q# c1 n3 ` }1 U& E; M2 D9 @& U9 o% ]6 H1 E
2 ~- p- @% N C4 m# }
coin="BTC-USDT-15"6 u$ ?8 K K8 ^* Q& }
strategy_name ='boll' V. x8 O, u, b! O
$ X) u7 \4 }' A! K% b" l0 u6 Uconfig =pd.read_csv('futures_usdt.csv',index_col="coin")
$ w+ z* r6 W/ Q$ G5 @4 W0 l0 ~( O9 `7 @exchange = ccxt.okex3()- C2 b/ e; @: f a
exchange.load_markets()2 ^' j7 J; p: f H
exchange.apiKey = config["apiKey"][coin]
9 \; M( L! Y2 w) L( E& \# t" oexchange.secret = config["secret"][coin]* m: J" t! }" e# F8 }1 X8 `
exchange.password=config["password"][coin]! o5 p4 o: W) k; G
symbol = config["instrument_id"][coin]
! r. X2 P# U G! t& A# J0 x6 O
" g5 r5 }( A9 |secret = config["robot_secret"][coin]9 N" y0 w7 C/ Q0 a( s
robot_id = config["robot_id"][coin]
/ m( B% n' t3 Udef send_dingding_msg(content, secret=secret, robot_id=robot_id):
1 k" d* i% I& t/ o0 a6 p timestamp = int(round(time.time() * 1000))' y2 O: p7 @6 C6 S
secret_enc = bytes(secret.encode('utf-8'))
+ n3 H. [' f9 x5 ` string_to_sign = '{}\n{}'.format(timestamp, secret)
8 P9 w1 Z. ?/ i* p string_to_sign_enc = bytes(string_to_sign.encode('utf-8'))$ H4 i! r7 E! w: n$ S" j1 K
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
1 Z( ?( o( H/ `6 g sign = parse.quote_plus(base64.b64encode(hmac_code))9 y' U0 Q7 f# G+ X: i
query_string = 'access_token=' + str(robot_id) + "×tamp=" + str(timestamp) + "&sign=" + str(sign)
+ l5 I+ b8 U! `5 ]/ x3 U& Z$ T try:& K' Y ]0 A+ v" q; }3 |2 t
msg = {
: L8 [' I. D# f "msgtype": "text",
0 e. n5 E0 `$ P9 d' j# ]6 e "text": {"content": content + "\n" + datetime.now().strftime("%Y-%m-%d %H:%M:%S")}}, H; c6 c8 P3 q2 U$ Q
url = 'https://oapi.dingtalk.com/robot/send'
2 [3 P. p& S+ I- i8 w url = url + '?' + query_string# ~4 C0 ]' H& w! ]# Z2 d5 z" w, I. ]
body = json.dumps(msg)
7 Y. x" M6 X c e7 [3 G # 设置重连次数: P" V8 b2 N5 L9 S9 ?
requests.adapters.DEFAULT_RETRIES = 153 E& Q7 E% r( G% A( h7 f# w
# 设置连接活跃状态为False
2 W2 S2 ]0 y7 j1 r s = requests.session()5 ~ t Q! p0 k w: W+ b, u5 a
s.keep_alive = False
% p1 L3 ]* E- u6 E* { # 进行请求9 ^3 G0 q, [, k- T* W1 e- E
requests.post(url, data=body, headers={"Content-Type": "application/json ;charset=utf-8"}, timeout=5)
' Z8 ^" p' \# {; U- m3 m6 ~ print('成功发送钉钉')
+ X9 b( b0 F, F: |; s- R except Exception as e:( v) A% i& i! n5 p/ ~
print('钉钉发送失败,尝试重复发送', e)
0 b) h$ u: F6 Z+ F '''请求超时'''
! Q: ], N o; ?9 j8 K( [. X8 T for i in range(1, 5):
# {8 j- a( W! ] print('请求超时,第%s次重复请求' % i)9 q7 k$ X) ^; H: j0 t- m7 P
response = requests.post(url, data=body, headers={"Content-Type": "application/json ;charset=utf-8"},
+ K6 R1 ~+ m E9 h2 K' u* e3 C2 u timeout=5)1 R2 ?' F. w, Y
requests.adapters.DEFAULT_RETRIES = 58 [4 |3 R; t& G
if response.status_code == 200:
- b# [% q% L$ h4 k2 ~3 p" u J print('成功发送钉钉')6 R) x. H; v9 [+ f$ Q
return response+ }9 [+ A4 y8 s+ k: E& j, {/ g
# G, ?6 t( W$ q- v1 f
def place_order(exchange, symbol, client_oid, order_type, buy_or_sell, price, match_price, size):
$ y. S9 K4 `) }# L( K |1 Y5 k6 L """* p$ N- Q3 W9 J) Z
下单
5 u Q3 _/ L0 y' B! ^' W5 b :param lever_rate:! I' s4 c* J' A1 q& Y
:param exchange:
Z- _) n2 ]7 N7 ?! f :param symbol:
4 H. B4 U s# \$ ~) Y8 p P :param contract_type: 合约类型: this_week:当周 next_week:下周 quarter:季度
& x- {' c( G- y7 L4 b) D :param order_type:
+ i- x5 W! X# n4 ?" g( i* w4 h/ p :param buy_or_sell: 1:开多 2:开空 3:平多 4:平空
/ v; k% U0 w+ o" ?$ ? :param price:. E$ @( `& j/ ?" O, i1 O1 v
:param amount: 这里的amount是合约张数) Z& L* U1 H' p. l' ?0 B5 b
:return:" q7 K( V( a. ?7 V
"""
- {- B( o( s% X' c9 s if order_type == '1':( s' u2 c$ O i
match_price = '1'
# v' Q$ O& \1 P* a elif order_type == '0':
" e$ F z! ]0 H" ?, x7 ] match_price = '0'- x# `9 C b5 W3 X# w
if buy_or_sell == '开多':
1 S7 E8 [$ E9 k3 a9 _* Y type = '1'
9 }9 F/ [# h: |. Z4 G elif buy_or_sell == '开空':5 j: {" i) e& V& ~1 G9 |* T y5 ^! ]# P
type = '2'6 {+ K4 y( z2 e" p; C9 {
elif buy_or_sell == '平多':
$ C* C" Z# e: u( `: b* t2 e3 [+ n type = '3'. t- s/ ~1 R# k7 Q5 d' \
elif buy_or_sell == '平空':! M/ a! U3 P+ C7 _( m# ^
type = '4'+ k# X4 k l2 O1 |( ], ]
# 下单
( B# V7 d: K3 `0 K( i for i in range(10):
9 Q- b0 h# Q' c' W6 C- F$ ]7 w8 z9 ` g try:3 ?- f2 O7 U5 @& n) e: Q6 w. H
order_info = exchange.futures_post_order(params={'client_oid': client_oid,
/ w" F% X, r) G9 D, O 'type': type,- l' L, V+ {2 B* t* M- o6 J
'instrument_id': symbol,
1 H) ~$ A" s. m8 Z | E 'order_type': 0,
& g3 Q( P0 m7 j' O$ |4 ? 'price': price,+ V& h' J- }0 d' y0 j E/ ?5 W
'size': size,
* a0 i! B, c1 B5 [ v* {& _ c* @ 'match_price': match_price,
0 ~- D5 ~9 A% m$ ]$ R 'leverage': 3
* B! I1 B. U2 s4 U/ v }
. F) a3 Q' o! I& P! U )5 M0 E% b" A4 Y i
print('okex 合约交易下单成功:', symbol, buy_or_sell, price, size)5 A/ F R- k4 s( G0 ~4 ]
print('下单信息:', order_info, '\n')
( E5 C' q' }, U7 a C0 B return order_info
) ]: Y5 {0 u( D. Y9 Z1 G) T* P% ?( k' I1 v m5 C- g7 p/ {+ U4 I
except Exception as e:
! h; f8 D# S* d2 v6 {' V print(e)
# W$ C- ~. E' s! A% ]0 I ^ if "error_code" in str(e) and "32019" in str(e):
$ {& c2 ^& z! @% @ _df = exchange.futures_get_instruments_instrument_id_price_limit(params={'instrument_id': symbol})
1 w- i1 P' L# I; r- g4 r! U limit_high = float(_df["highest"])
- z, v- g$ d( Z6 Z' D limit_low = float(_df["lowest"])7 L3 z9 x2 O [! f
print("限价数据:", limit_high, limit_low, "下单价格:", price)6 K1 d# P6 G6 d( |6 R* l7 Y3 K/ t
if buy_or_sell in ["开多", "平空"]:
9 d/ y1 s: T+ h) i; Y7 k price = limit_high * 0.995
" g1 ]. x6 N2 f. ~# R7 r else:5 e* e0 r# N+ ^
price = limit_low * 1.005 b! i$ k n& B2 K
print("修改后的下单价格:", price) W+ O6 P* ?& G8 q4 `
time.sleep(0.5)/ z5 |" ]. J9 E9 [) @& |+ o
if "error_code" in str(e) and "32025" in str(e):9 n: \. L% o/ @* s# T- ?* y
time.sleep(30)
" c4 K# t e; } if "error_code" in str(e) and "30014" in str(e):
. M1 p7 ]( C3 {5 p time.sleep(2)7 l" n2 Q. Q+ B; k
raise ValueError('okex 合约交易下单报错次数过多,程序终止')
1 k0 _' Z9 a6 H print('下单报错次数过多,程序终止')
+ p; v! [% M0 V7 F5 n$ S send_dingding_msg('下单报错次数过多,程序终止')
- o6 [9 c( {& u# I
* {) N$ y8 t! {2 wdef next_run_time(time_interval, ahead_time=1):
, _, H4 b- A! V! o3 l9 I5 _ if time_interval.endswith('m'):. l' p0 T2 s& u2 g7 N6 _# ]: f
now_time = datetime.now()
# I, D6 M7 p$ ]* x time_interval = int(time_interval.strip('m'))
% W2 ?4 |8 r% h" [ target_min = (int(now_time.minute / time_interval) + 1) * time_interval$ M \7 w; b! y. X8 p" h g+ q
if target_min < 60:# e* r. g; G( o0 ~
target_time = now_time.replace(minute=target_min, second=0, microsecond=0)3 ^, K0 J- {9 l
else:
/ m- T4 B# h# W3 d2 O) [3 r- Q if now_time.hour == 23:
" B) |. r) X& h target_time = now_time.replace(hour=0, minute=0, second=0, microsecond=0)/ ?* q& u- V* K
target_time += timedelta(days=1)
2 a% V( C' i2 z$ Q0 R2 D& C else:
1 ]+ O. e8 E- z# O target_time = now_time.replace(hour=now_time.hour + 1, minute=0, second=0, microsecond=0)
) Q9 t9 m+ j. Q% ?) V: ^ # sleep直到靠近目标时间之前
/ V9 V$ r: r8 ^; p/ D z if (target_time - datetime.now()).seconds < ahead_time + 1:
f' u: Z3 W7 b print('距离target_time不足', ahead_time, '秒,下下个周期再运行')
- J; C# O; _, ]& x) U1 i target_time += timedelta(minutes=time_interval)1 `- t6 B. Y; x' |& A+ ^7 m, @
print('下次运行时间', target_time)
2 B, k ]. e, H! A0 ~ return target_time
! o& _+ f, U1 Y# ^ else:
5 Q6 Y' x0 A" V7 t exit('time_interval doesn\'t end with m') w+ `! g6 f2 C
; o/ I3 L; I7 j! T' ]$ s+ C
# ===获取账户和仓位信息
* u2 m& Q. L! Pdef fetch_account_info(exchange, trade_symbol, client_oid):0 G9 Y8 z5 e$ i7 F' d0 Z
""" \: T! A, g" J' e) M
获取账户信息' F* E9 c$ q( g! O" y
:param exchange: 交易所
4 O) h6 }5 g8 _% p* }+ \ :param base_coin: 基准币的名称% ], U/ x' I& X% C' I! h2 T Z* H9 b" w
:param trade_symbol: 交易对的名称
, S. K9 i& w0 n, h :return:! E/ I8 ]$ p$ F# d
:param account_info: dict形式,包含账户和仓位信息+ q% B% J6 u0 t, N' H& J
"""% F+ u$ r7 C2 y) i& V6 @* Z8 [) P
# 创建用于存放账户信息的变量$ a. s' P1 P% }; P" ^5 V
account_info = {'账户余额': 0, '多头仓位数量': 0, '空头仓位数量': 0, '仓位成本': 0}+ R% i4 M- G* X7 d& S c* F% ^
while True:
5 X* D/ X* h% Y u0 f/ w2 X # 获取账户的资产信息# j7 I f# f0 p- y+ E
try:
, R% `" ?+ G+ Y i( c* N data = exchange.futures_get_accounts_currency(params={'currency': trade_symbol}) # 从交易所获取账户balance信息8 \4 `1 F4 v/ s: z6 f
except Exception as e:' x( x5 b2 g. i: f; e3 o
send_dingding_msg('获取账户信息失败')
1 O i# M9 D- W3 q* M( I print(e)' N! n' }6 \( h
sleep(0.5)( M+ U1 o, R9 r0 _, w* Q# N! f/ K
continue
3 z% V. D) q! s# S+ l9 P6 i account_info['账户余额'] = float(data["equity"])) P0 ~% F; N" g8 P$ _
# 获取账户的order4 E1 Y2 T( j2 {
& K4 Y+ r' q' U4 D4 y# ]
try:
' u* T% `/ G8 l, V1 q6 |( l client_oid_info = exchange.futures_get_orders_instrument_id_client_oid(+ B. s1 ^% S# {; K
params={'instrument_id': symbol, "client_oid": client_oid})4 F6 i# f9 x+ h
9 Y. K+ e- k" P; f0 e
except Exception as e:
" ~, H+ I7 _1 x H& p send_dingding_msg('获取信息失败')
! F' p2 D3 A8 Y1 ]3 A: H print(e)- Y2 ]' F3 J; g, T
sleep(0.5)
+ G! S S) z- p+ x0 o$ O continue5 g0 _# e( b: U) Q
if client_oid_info:
8 [, X8 ~# D% N: X! _* K if client_oid_info['state'] == "2":& {1 y r6 \; P7 L
if client_oid_info['type'] == "1": # 当持仓信息信息不为空时
; r6 d3 M, _) L, e5 I/ ? account_info['多头仓位数量'] = float(client_oid_info['filled_qty'])
1 e" H* P7 N/ @8 P) V account_info['仓位成本'] = float(client_oid_info['price_avg'])* c+ P* X" {9 Z. k; Q- l. ?
elif client_oid_info['type'] == "2": # 当持仓信息信息不为空时
; n2 }* z. F, a T# F- r y/ G account_info['空头仓位数量'] = float(client_oid_info['filled_qty'])
0 W- I2 [0 q @; H$ D account_info['仓位成本'] = float(client_oid_info['price_avg'])
6 ]: A2 o; L9 q; N, E0 @ elif client_oid_info['type'] == "3" or "4": # 当持仓信息信息不为空时
6 \: i0 k6 a% v account_info['空头仓位数量'] = 0" X0 ?0 p3 l3 k }' @% g
account_info['多头仓位数量'] = 0
4 ]& k" {, d# U& B2 f/ \ account_info['仓位成本'] = 0# o& P2 r9 i$ h' D1 h
break0 M/ r, o+ ]$ r$ a" k
return account_info
& F0 |$ ?! W1 Q1 r/ \
' @' u3 h3 [! n( d( \3 _# xdef get_history_candle_data(exchange, symbol, time_interval, max_len, max_try_amount=20):7 t% w! I4 n9 W) ^- @
"""2 E0 ?: Z. \* [) Q/ P0 X8 m: K
获取某个币种在okex交易所所有能获取的历史数据,目前v3接口最多获取1440根5 h8 r% k& l) R+ h6 \
:param exchange:. {1 w* N* Q0 m5 D# B) X/ y. w
:param symbol:4 a1 {, ? n- u/ D* T5 u' `
:param time_interval_str:
! n8 b$ x2 p" o2 q/ i: Z8 ~( f( ?1 C :param now_milliseconds:6 T4 I7 s- G. h H, n
:param max_len:. X7 V8 c( i% {- j' R& m) j
:param max_try_amount:* y+ L7 v( {/ Y0 Z7 q8 M N3 |4 A
:return:
* S r4 b9 ]. l7 @+ `/ Y; F """- E9 r+ n3 j/ l* W3 O
now_milliseconds = int(time.time() * 1e3) # 获取统一的数据截止时间" e, E& ~3 I/ p# |/ p
time_interval_int = int(time_interval[:-1])
4 _0 `2 ?1 Y, C. t time_segment = time_interval_int * 60 * 1000
3 B! K! \, T3 R: O end = now_milliseconds - time_segment7 `2 e7 s! x$ R, u
since = end - max_len * time_segment
9 E" W0 p+ F; ^% D- m! a! E7 S3 o# R3 B all_kline_data = []
& \" l. ?$ |$ i' b6 k while end - since >= time_segment:% O- T1 Y* S* k) d8 S7 H- M U% F
kline_data = [], n$ _* [3 ^1 c6 A1 W' D; z
for i in range(max_try_amount):2 q7 b. Y9 y: l2 S, A# m
try:* t/ A. d8 W. V! a( O4 B
kline_data = exchange.fetch_ohlcv(symbol=symbol, since=since, timeframe=time_interval)+ t& Y) Q% f+ B; m" p0 x
break
1 c& E1 M/ G! f( R8 @% { except Exception as e:, d' |/ w8 K/ ]5 `% L7 d" l I
print(e)0 Y4 \5 R6 e& _0 ~
try:. k% a5 y; x/ D
e_str = str(e)* j* U) p9 [& J0 q, L
if '35004' in e_str:
7 C( J; N# f5 w time.sleep(30)
; l$ _7 b6 ~6 S7 `( m if '30014' in e_str:
& q/ T6 X' Q6 W* Q8 e- u1 B time.sleep(10); ^6 k' i; x3 ]. |8 \
except:
" O' e; V; L- g! ~ time.sleep(10)
: `9 \: d% r3 G
$ F- l' M& l" ?: x; a if i == (max_try_amount - 1):
9 H4 a U5 o4 f( p$ g print('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
: C0 w) L) r( Q+ f8 l send_dingding_msg('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
! @+ O# K+ Z; |* ?3 k raise ValueError('获取历史数据时,fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')) x! S0 i% K$ G% T
if kline_data:
' i9 k. Y& ]0 L # 更新获取时间8 x0 U% _ R3 Z
since = kline_data[-1][0]
8 c/ |% f7 P- M0 K6 y. j% ]' n% ]/ v all_kline_data += kline_data6 ^ A- f# j+ f: ^6 t$ N% `/ \/ x
else:
" K- o6 `& y0 Z/ B2 I break
9 \' k8 s9 W. |0 t) J/ g, O. u* Y7 y. L' W# F
# 对数据进行整理
( C! H) O7 p! a" B1 ~/ _ df = pd.DataFrame(all_kline_data, dtype=float)
& x) Q4 p- p+ T6 Y2 Q df.rename(columns={0: 'MTS', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'}, inplace=True), T3 t6 H5 g8 N. e8 I
df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms')
I/ l8 [ j: Q4 ^0 j df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8)* B, G1 m! h6 f7 t: k! X7 O
df = df[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'volume']]
_. w* I2 E2 E; {' U1 ~+ o" y* S/ D/ D
# 删除重复的数据
0 U$ \7 g# x3 n" r3 p- O( \ df.drop_duplicates(subset=['candle_begin_time_GMT8'], keep='last', inplace=True)
7 O% F `4 V; K N/ c df.reset_index(drop=True, inplace=True)9 y+ j% n& K$ J( k. Z% G- r
df = df[:-1]
( }" E: ~5 c: G* I7 ~
# Z" L$ t Z( A% |2 K: j return df$ m5 A+ k2 h0 \$ _, k
$ W0 F4 C6 t8 p6 x& j7 @3 m9 Z" l
def ccxt_fetch_candle_data(exchange, symbol, time_interval, limit, max_try_amount=5):
- C7 s( N$ M. @$ b """6 q' P9 |/ T V
:param exchange:
; j3 a9 u6 f4 B% M! F, T :param symbol:
# t( d+ \7 c! u :param time_interval:
2 B( Y, c: [9 p :param limit:' |% W4 u5 i( O
:param max_try_amount:
# _7 K, N; {: ]; `6 } :return:' a* ]6 q: Q; S C. Q* l
获取K线数据
2 I& a8 c( F# o& @* x+ K1 q """( I; C, V$ d; X; M. ^
for _ in range(max_try_amount):
' o8 I1 ?4 H# h try:# j- t4 z# o; N" B0 h6 C! c
# 获取数据
" q' _- W {& }' c( [ data = exchange.fetch_ohlcv(symbol=symbol, timeframe=time_interval, limit=limit) N5 D, | F/ e7 \, l
# 整理数据8 g, d& k$ x% ^" Y' F0 R _( s/ z
df = pd.DataFrame(data, dtype=float), a. x; J# W% c
df.rename(columns={0: 'MTS', 1: 'open', 2: 'high',& ] t- i1 x' B/ j
3: 'low', 4: 'close', 5: 'volume'}, inplace=True), g- b& _4 S5 B
df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms')4 }9 Y2 @" h+ y& O
df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8)$ k; j0 L/ X# n' ]' ~% a- @
df = df[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'volume']]
3 U0 h, ]; z6 J) k return df+ i- g# q8 {: u8 U5 N# E
except Exception as e:4 L( N0 z% t. ]2 N( L1 b
print(e)
! u. p$ ?$ m# P# [+ H print('获取fetch_ohlcv合约K线数据失败,稍后重试')6 S0 T ~* t) A) e+ r7 P1 H0 Y
time.sleep(short_sleep_time)0 ?& v2 X9 `/ \, d+ Y! x' F R: s
" k1 j6 `! i4 r( y9 V8 _ print('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
: Y6 C9 e3 m; t1 i6 ]3 S( k+ ~ send_dingding_msg('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
/ L1 w0 U! {+ P1 Q raise ValueError('获取fetch_ohlcv合约K线数据失败,失败次数过多,程序退出')
7 V/ c1 S/ V$ A, ?7 ]- ?
: G9 y& p% m3 Z* D1 m
: p. {4 s) `' k0 m( n
0 G5 x3 C, d' L2 a. U+ i' Tdef get_candle_data(exchange, symbol, time_interval, run_time, max_try_amount, candle_num):
# I" M# k0 g" u0 @ """
! ]; \. l+ S2 B# p :param exchange:2 R7 J& |: { Q7 u
:param symbol_config:
$ U8 S! e- S- _( G8 V5 w. t8 t6 C :param time_interval:5 c2 W1 [# Y# v' p8 M% [- S6 m! h
:param run_time:* I( `4 T D6 z0 e6 V
:param max_try_amount:
7 c! a" ~" K7 V/ G e3 ~* L( Q7 \- S :param symbol:
: n/ w2 t$ `$ r( F1 `2 K :param candle_num:
+ H2 i! d S4 \9 T% p5 ` :return:
; q3 g. }% V; b9 o/ d0 _ 尝试获取K线数据,并检验质量+ O6 f3 q6 ]9 |, L0 z) U
""" V! ~8 m- S" M" j5 @0 }* [
# 标记开始时间% q, b4 X* ~/ M0 ?7 R
start_time = datetime.now()# G2 ` u4 C/ q+ d4 k6 N
print('start get_candle_data', symbol, start_time)2 y7 ^ y Q" e: n( U+ ^# k5 G
8 y b6 q5 _& e* d8 U # 尝试获取数据
: ^/ d) W: W) t( I. D) f. x( z" a for i in range(max_try_amount):
; m9 s9 R" A+ t# u; g" r& K4 W # 获取symbol该品种最新的K线数据( A4 X) t7 \: ]6 N% @
df = ccxt_fetch_candle_data(exchange, symbol, time_interval, limit=candle_num)
9 q0 ?. x$ k9 u1 ~- F1 m if df.empty:9 Q9 C6 U: Q1 F
continue8 O X' j$ Q" D& P1 I5 A
# 判断是否包含最新的数据3 m- W. \5 ^. R% ]5 l& i) v
_ = df[df['candle_begin_time_GMT8'] == (run_time - timedelta(minutes=int(time_interval.strip('m'))))]
- {4 v' R7 c$ s- z1 {2 ]8 M4 p& f if _.empty: {( Y) p$ O- z3 _" `. B
print('获取数据不包含最新的数据,重新获取')7 S: \6 V) X/ l/ R
time.sleep(short_sleep_time)
+ p0 n4 {. @3 I0 p9 Z continue/ {. S& r! I- g0 m" ?( s" j/ L
* p0 x5 _. K; o7 H; \3 N' s2 _. R& T
else:
1 U( W. @$ a; [8 o# U* Y # 获取到了最新数据
4 p1 k& v7 q' K3 r9 {% ?2 N8 i% U: ~: \3 j) N
df = df[df['candle_begin_time_GMT8'] < pd.to_datetime(run_time)] # 去除run_time周期的数据. w8 a; S* y9 K0 L D, V6 k
print('end get_candle_data', symbol, datetime.now() - start_time)
& q+ ?: E6 r h/ H& u% B- Z return df
& M; @$ ]! ?. k. T9 n5 p) `5 [7 F6 O
print('获取candle_data数据次数超过max_try_amount,数据返回空值')
, o5 ]' ~: W7 {, b/ E9 j6 I return pd.DataFrame(), J' H% ]: b1 X6 V% x. O) e
" v! y7 B, _1 {% M7 R N& b' N! K' V
% Y }; w `3 ~$ `$ C7 x* }, E
def main():
6 W5 U/ C! h- L& v5 \8 x+ ~ max_len = 14007 o* x8 d' f- [1 M/ J' \2 X
max_try_amount = 10/ H& c( }" g& W
candle_num = 5
+ ~$ B/ W0 A: I/ ]2 o- Y3 D config = pd.read_csv('futures_usdt.csv', index_col="coin")- o* }7 r* E+ ?/ X
time_interval = config["time_interval"][coin]' @ N; j5 C! b
trade_coin = symbol.split("-")[0]
1 R9 @/ c) {0 O trade_symbol = symbol[0:8].lower()
% {4 P Y* e2 E base_coin = symbol.split("-")[1]0 B+ i" r0 T$ a z" T u
msg_content = 'Fr_%s_%s策略%s报告:' % (trade_coin, strategy_name.upper(),time_interval)
6 i! ^! s/ L/ `; }( W0 \! w& r6 L: r, q! b
closebyhand = config['closebyhand'][coin] # 手贱模式7 _6 s: |, l9 d h9 K
leverage = config["leverage"][coin]
$ v0 l u$ G% }: | fv = config['fv'][coin]" z; m. b+ ~, S% y8 `3 \
stop_lose_pct = int(config["stop_lose_pct"][coin])
# ?6 N: ]& g: V+ R' i( q7 F/ F* a* e0 K0 ]8 \5 K2 E1 B! A: Q
client_oid = trade_coin + time_interval + str(closebyhand) + strategy_name. L1 x$ W; U" }+ F- b$ C" G8 C& O' I( s
if strategy_name =='boll':
$ }& a" a3 g( b* m t* q para = [int(config["boll_1"][coin]), float(config['boll_2'][coin])]" ?* t# y; k7 x8 a& R I2 c
else:
0 B* H9 f2 K. N$ n* r; b' J1 H para = [int(config[strategy_name][coin])]( u) q H N$ b4 R8 b! A) e
" X w! b. m6 T' {, Y* \
account_info = fetch_account_info(exchange, trade_symbol, client_oid=client_oid)
6 J5 n3 [ E9 ~) n$ W msg_content += '\n持有' + str(base_coin) + ':' + str(round(account_info['账户余额'], 2))0 x1 r+ n ^6 l( D6 G
% c0 H; T4 v; o: ^2 G if account_info['多头仓位数量'] != 0:! }7 C# j/ t* Q) r, u, x! ?
msg_content += '\n多头仓位数量:' + str(account_info['多头仓位数量'])
/ C3 M0 M2 N) b5 C v" x msg_content += '\n仓位成本价:' + str(account_info['仓位成本'])* q& R8 v, F$ c: I& D
elif account_info['空头仓位数量'] != 0:, d. Y5 ^0 N$ z9 m/ L" _
msg_content += '\n空头仓位数量:' + str(account_info['空头仓位数量'])# \7 ~! ?3 _- o, d4 [: \' n
msg_content += '\n仓位成本价:' + str(account_info['仓位成本'])/ _. z! S6 y: W3 K4 I# h
else:$ b, u. o* z% u% W P8 J. M z r
msg_content += '\n无持仓信息'
6 I! }/ f/ ^$ u: q- s; q5 L" d+ q E# v2 S7 b" {2 _! R/ q- k
history_candle_data = get_history_candle_data(exchange, symbol, time_interval, max_len=max_len)
" {" t8 `( ^1 p3 h) X# e+ r
: }& S, ]7 {! C* ^1 h run_time = next_run_time(time_interval)0 E" O; V) s/ }4 J' D4 u
sleep(max(0, (run_time - datetime.now()).seconds))
, e2 n. d) R/ A3 l% L while True: # 在靠近目标时间时! P: e- N! ]$ F$ q
if datetime.now() < run_time:2 H( w0 d' U/ \% u4 ]5 O
continue% Y/ P' H) A6 x! d% s: E7 ~
else:
, Y8 [' U- X B/ t( P break
7 n5 q w* b" x8 q2 o ^' p$ c# v; O: f
- k) E' ?5 O% D# o! X recent_candle_data = get_candle_data(exchange, symbol, time_interval, run_time, max_try_amount, candle_num)
5 z! C( i( S+ B: F2 J) [* C: B. {) v df = history_candle_data.append(recent_candle_data, ignore_index=True)1 M! {" z! i# k7 x+ B& p/ b
df.drop_duplicates(subset=['candle_begin_time_GMT8'], keep='last', inplace=True)
' D) }7 p m2 G, f# a df = df.iloc[-max_len:] # 保持最大K线数量不会超过max_len个
1 p' `$ F! A, |2 [; V% [+ u; o df.reset_index(drop=True, inplace=True)
8 h2 ~, {' f& `/ U new_price = df.iloc[-1]['close']
* j' A1 L% q" a% y4 l9 P! A* l+ G msg_content += '\n' + str(trade_coin) + '实时价格:' + str(new_price)- L9 a" Z1 g ~8 c7 e' p
e- F1 @4 `4 M! Q/ U, w. W7 @
df = getattr(Signal, strategy_name)(df, para=para)
8 N' h6 t" [, I! [8 o signal = df.iloc[-1]['signal']
7 \, r9 B. a* o3 U5 X
: F( _+ g. _% h. B msg_content += '\n策略信号:' + str(signal), _" j/ ~# e! |6 U# D. a) q
# T, {9 w e* p/ e9 ~% i6 {/ V
# ===判断交易方向并且下单,除了无交易信号之外,总共有6种情况
+ N& s/ B4 L, _, r3 p8 b5 r if signal == -1:
: n9 {5 M8 U7 ~ if account_info['多头仓位数量'] > 0:2 h: k, p" i8 ?: u/ y; e6 H
size = int(account_info['多头仓位数量'])6 g9 @" t5 e( @( d% ^- K' [
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',
+ f' A& U8 b0 c% d; e2 r price=new_price * 0.98,7 |" }5 L4 p1 u! ?" y
match_price="0", size=size)
5 }. y) q) Q+ f5 }9 P: K # 更新账户信息、价格信息
: b( E, T3 ^# j* j2 K: r sleep(1)4 M# O v$ J" R: o. |
account_info = fetch_account_info(exchange, trade_symbol, client_oid=client_oid)1 Y! s: f6 Q$ F L5 k+ @ \
new_price = exchange.fetch_ticker(symbol)['ask']
8 @5 r$ Z: Q% Z7 H, o! D, z size = int(account_info['账户余额'] * leverage / (new_price * fv))
; q+ U6 o: E/ T3 k place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开空',
! U- Q4 r6 M: c0 O0 c7 ] price=new_price * 0.98,
& T' K9 a3 Y" w$ a match_price="0", size=size)
6 R2 g2 d! y7 U; t' M& D: p msg_content += '\n关闭多单并开空单'
+ H0 J9 N; f0 m7 A1 ^% E6 y0 P! N: @ msg_content += '\n开空交易数量:' + str(size) + '\n'
`/ _2 k8 I* A. w; z7 Z elif account_info["空头仓位数量"] > 0:
m: o, ^2 F4 I* g7 i msg_content += '\n之前已开空单,本次不开单'- \4 P9 r! {0 v k
pass
1 L8 A& v+ ^8 e else:/ u; p R# F2 i6 W2 k
size = int(account_info['账户余额'] * leverage / (new_price * fv))
6 A D3 V& V# ~/ L; p0 ^) ] place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开空',% Y- J, r8 @; L$ i& a+ }( [
price=new_price * 0.98,
, k! j$ F: A/ j# n! Z3 A- M match_price="0", size=size)
; W: a m2 o) s7 a' D" M: u$ } msg_content += '\n开空交易数量:' + str(size) + '\n'2 A2 a6 d, B+ E/ d' t
elif signal == 1:$ {; _. f4 q( z, u6 Z0 ]
if account_info["空头仓位数量"] > 0:
" B0 _3 \2 u( h4 `7 W- H8 U4 c size = int(account_info['空头仓位数量'])
' Z" T" w' ?3 f! H1 c place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',5 J& P7 r% c) E) e" ^3 _ s
price=new_price * 1.02,
0 W. B0 d! G. n% ~( N/ S match_price="0", size=size)! {$ v; \ Y% w
# 更新账户信息、价格信息5 k0 [ o; z- K5 e% p9 k) Q
sleep(1)
# s- N# y3 i6 {8 C! K account_info = fetch_account_info(exchange, trade_symbol, client_oid=client_oid)3 o+ K% x5 l$ X; W' J0 Y
new_price = exchange.fetch_ticker(symbol)['bid']
& C- [9 s5 O4 D" e size = int(account_info['账户余额'] * leverage / (new_price * fv))1 B9 v% p; k% |5 G6 i; q+ @( q5 c
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开多',
( o/ N6 w8 |/ c5 n$ c price=new_price * 1.02,
+ f) D1 B) }9 @+ n* { match_price="0", size=size)- r; `! _$ b- X, _* V' X# E5 j$ l
msg_content += '\n关闭空单并开多单'
0 T8 o7 q, h" [+ q( ~, o9 C msg_content += '\n开多交易数量:' + str(size) + '\n'5 D+ Z3 x9 `& V5 ~! l1 g3 N9 S
elif account_info["多头仓位数量"] > 0:
: Y* x! S7 d2 o; R! D& Z: R+ R msg_content += '\n之前已开多单,本次不开单'% W9 D& E: R3 x! X
pass5 G8 g5 I c$ z3 E+ {1 k
else:5 T# s2 K4 X' U1 O) Q" U# Y
size = int(account_info['账户余额'] * leverage / (new_price * fv))* C. { l1 z0 o; C9 k7 n4 z- Q% s
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='开多',) o3 F0 m% E( e" T, ^1 ]3 @
price=new_price * 1.02,
' G& D& A% ~1 c# d match_price="0", size=size)
* z7 f1 S+ K i- F! W# g msg_content += '\n开多交易数量:' + str(size) + '\n'( E2 U9 O, m0 }: F& U% \
elif signal == 0:- Y ?; n/ w! D! t1 Q; s
if account_info["空头仓位数量"] > 0:) K% Z0 w! Z& {- z0 o- U4 J
size = int(account_info['空头仓位数量'])
0 J0 E6 }% k& u# ]+ q' c" J+ e place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',- l3 Y4 U. e! j6 D' K
price=new_price * 1.02,
! A) U+ M4 X4 O match_price="0", size=size)
4 v! ~& |5 _" C5 s$ o msg_content += '\n止损空单交易数量:' + str(size) + '\n'
+ E' D; ?' O, B s! F, R) G2 s elif account_info["多头仓位数量"] > 0:% A6 \# p; u9 R3 x( I, l' {
size = int(account_info['多头仓位数量'])% ^9 K) g" \: ^+ V, q! A
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',8 z) _5 }+ {5 i1 `2 A6 G9 E
price=new_price * 0.98,
8 b+ E+ D& t k' _9 \ match_price="0", size=size)# X# K4 d& ]6 o0 {, v; G+ P
msg_content += '\n止损多单交易数量:' + str(size) + '\n'9 d1 H1 M& B& ^' H( _
else:
1 h5 Q9 q7 \! K2 s msg_content += '\n之前已平仓,本次不开单'
4 ]- _* M' U, [: k; U7 A pass
5 @2 G8 E/ u' o, K elif account_info["多头仓位数量"] > 0:6 M; z5 w% ^/ |! @% [7 f# I' n
if df.iloc[-1]['close'] < float(account_info['仓位成本']) * (1 - stop_lose_pct / 100):# k5 ^2 ~1 \( i6 C" b
size = int(account_info['多头仓位数量'])& r# L1 I9 G- [* D' Q; {( n
place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平多',
# ~* o6 z! o" V9 L' N price=new_price * 0.98,
+ E& \& K# k2 g; w match_price="0", size=size)2 Z2 h7 {; E+ {# `3 j
msg_content += '\n止损多单交易数量:' + str(size) + '\n'$ E* w+ p" R- K1 V$ P7 _
elif account_info["空头仓位数量"] > 0:
2 w+ F2 ^6 a n5 b& C, V" \0 ~ if df.iloc[-1]['close'] > float(account_info['仓位成本']) * (1 + stop_lose_pct / 100):, f1 X9 }9 j0 k$ ]5 W
size = int(account_info['空头仓位数量'])
' J6 X* b* h( N# _" q+ t; V place_order(exchange=exchange, client_oid=client_oid, symbol=symbol, order_type='0', buy_or_sell='平空',
; V0 |" J) @3 o" B1 A2 T0 d price=new_price * 1.02,
8 R( n7 Z9 @) W+ k5 ~2 t8 c# Y/ Y match_price="0", size=size)% G" x( i. J* W% g1 [( A
msg_content += '\n止损空单交易数量:' + str(size) + '\n'4 t2 [& X2 X( ^; f+ z8 L1 v# q/ A5 T
else:% z3 R, \8 G. ~6 Q. s2 E7 X
msg_content += '\n本周期无操作'
; V$ R0 Z I. C4 d print('本周期无操作')4 F4 D+ y" M6 I3 j
. a; |9 Y5 ~8 e. y8 I0 O; ~
try:
# E4 ]- u1 T: h) N send_dingding_msg(msg_content)
/ ~" |- u2 j [; E0 F except Exception as e:
1 F$ Y7 r( L6 t% P; ? print(e)
5 z- V) b U, a! X+ g# o. M6 f$ H1 C' r/ q4 L: ]. L
sleep(10 * 1)2 [2 [# b. s% I2 I9 e0 y! u" h
# # =====运行主体' T. x# D) v" g
while True:
. E+ o/ X, X: w# ?/ R0 q% b try:! q7 g$ p! b% [ i: y2 E
main()
! m( e7 H& Q: h& N! V M time.sleep(10)
9 c1 W1 ^* F# I9 }7 N: x except Exception as e:
; Y4 K: `9 y8 | send_dingding_msg('系统出错,10s之后重新运行')7 v7 n6 Z- z7 R- v+ ^7 m9 d7 _* V
print(e, type(e)). |$ J& ~% o6 k( M
time.sleep(10)csv格式:
' u) \% Q$ J% s9 ]# t0 w: m交割合约:1 c, w# R6 X" b6 S
/ e, V( ^9 U2 X, D3 J I
% i9 q+ k# ~+ R, r5 S f4 r$ {
永续合约:
4 e2 x4 ^' h, l: P: K ^' P# K5 }" R7 H

, C& g/ q0 C- H, G* l上架时就把) L: T1 `( Z% F/ A: @
“
- E0 _0 W' x* z! Fcoin="BTC-USDT-15"
0 K. S. {/ i1 ^3 Wstrategy_name ='boll'3 x0 p5 d- U2 h! }; p. I6 Q
”9 E+ J& \) ^2 D. K
这个部分修改就行9 Q% p# n" n W- \
然后在Linux服务器上用nohup命令:
& N$ C/ R" x; { X$ Inohup python -u btc_vix.py >1btc_vix.log 2>&1 && F. g8 {" v' C) C& U, q) `& E
搞定。这个完全是我实盘框架。比较粗糙,里面的函数可封装、模块化。框架可优化、简洁的地方比较多。 [9 n$ i6 q# @* H" {- b/ b" V5 {
另外就差一个Signal文件啦,这个就由各位自行搞定啦。 |