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

$ X0 v3 `" L1 ~上架时就把$ q( K9 S! s) U! P# G0 Q
“
, \7 a, H# D4 T! z; E2 T! vcoin="BTC-USDT-15"
, e* I& U$ Y$ H1 e( b, u/ }strategy_name ='boll'; p6 }1 U8 b3 X3 @. z' X' E9 J
”+ E: |. ]* _ K+ E; t
这个部分修改就行1 {9 W: x* J- L4 `8 u5 h" S
然后在Linux服务器上用nohup命令:' ~/ f: g" h) T# Z a
nohup python -u btc_vix.py >1btc_vix.log 2>&1 &+ z2 y) P' N. r$ o0 U5 E8 s
搞定。这个完全是我实盘框架。比较粗糙,里面的函数可封装、模块化。框架可优化、简洁的地方比较多。
5 ?, p) |/ V5 ?5 \/ J4 s另外就差一个Signal文件啦,这个就由各位自行搞定啦。 |