目錄
- 一、計算交易信號
- 二、交易相關方法
- 三、主程式
一、計算交易信號
這里以雙均線策略為例,金叉買入,死叉賣出,
import pandas as pd
def signal_moving_average(df: pd.DataFrame, para=[5, 60]):
"""
簡單的移動平均線策略
當短期均線由下向上穿過長期均線的時候,買入;然后由上向下穿過的時候,賣出,
:param df: 原始資料
:param para: 引數,[ma_short, ma_long]
:return:
"""
# ===計算指標
ma_short = para[0]
ma_long = para[1]
# 計算均線
df['ma_short'] = df['close'].rolling(ma_short, min_periods=1).mean()
df['ma_long'] = df['close'].rolling(ma_long, min_periods=1).mean()
# ===找出買入信號
condition1 = df['ma_short'] > df['ma_long'] # 短期均線 > 長期均線
condition2 = df['ma_short'].shift(1) <= df['ma_long'].shift(1) # 之前的短期均線 <= 長期均線
df.loc[condition1 & condition2, 'signal'] = 1
# ===找出賣出信號
condition1 = df['ma_short'] < df['ma_long'] # 短期均線 < 長期均線
condition2 = df['ma_short'].shift(1) >= df['ma_long'].shift(1) # 之前的短期均線 >= 長期均線
df.loc[condition1 & condition2, 'signal'] = 0 # 將產生平倉信號當天的signal設定為0,0代表平倉
df.drop(['ma_short', 'ma_long'], axis=1, inplace=True)
# ===由signal計算出實際的每天持有倉位
# signal的計算運用了收盤價,是每根K線收盤之后產生的信號,到第二根開盤的時候才買入,倉位才會改變,
df['pos'] = df['signal'].shift()
df['pos'].fillna(method='ffill', inplace=True)
df['pos'].fillna(value=0, inplace=True)
return df
二、交易相關方法
from datetime import datetime, timedelta
import time
import pandas as pd
from email.mime.text import MIMEText
from smtplib import SMTP_SSL
# 計算當前時間到下一個交易周期的 sleep 時間
def next_run_time(time_interval, ahead_time=1):
if time_interval.endswith('m'):
now_time = datetime.now()
time_interval = int(time_interval.strip('m'))
target_min = (int(now_time.minute / time_interval) + 1) * time_interval
if target_min < 60:
target_time = now_time.replace(minute=0, second=0, microsecond=0)
else:
if now_time.hour == 23:
target_time = now_time.replace(hour=0, minute=0, second=0, microsecond=0)
target_time += timedelta(days=1)
else:
target_time = now_time.replace(hour=now_time.hour + 1, minute=0, second=0, microsecond=0)
# sleep直到靠近目標時間之前
if (target_time - datetime.now()).seconds < ahead_time + 1:
print('距離target_time不足', ahead_time, '秒,下下個周期再運行')
target_time += timedelta(minutes=time_interval)
print('下次運行時間', target_time)
return target_time
else:
exit('time_interval doesn\'t end with m')
return datetime.now()
# 獲取okex的k線資料
def get_okex_candle_data(exchange, symbol, time_interval):
# 抓取資料
content = exchange.fetch_ohlcv(symbol, timeframe=time_interval, since=0)
# 整理資料
df = pd.DataFrame(content, dtype=float)
df.rename(columns={0: 'MTS', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'}, inplace=True)
df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms')
# 北京時間 = 格林威治時間 + 8小時
df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8)
df = df[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'volume']]
return df
def place_order(exchange, order_type, buy_or_sell, symbol, price, amount):
"""
下單
:param exchange: 交易所
:param order_type: limit, market
:param buy_or_sell: buy, sell
:param symbol: 買賣品種
:param price: 當market訂單的時候,price無效
:param amount: 買賣量
:return:
"""
for i in range(5):
try:
# 限價單
if order_type == 'limit':
# 買
if buy_or_sell == 'buy':
order_info = exchange.create_limit_buy_order(symbol, amount, price) # 買單
# 賣
elif buy_or_sell == 'sell':
order_info = exchange.create_limit_sell_order(symbol, amount, price) # 賣單
# 市價單
elif order_type == 'market':
# 買
if buy_or_sell == 'buy':
order_info = exchange.create_market_buy_order(symbol=symbol, amount=amount) # 買單
# 賣
elif buy_or_sell == 'sell':
order_info = exchange.create_market_sell_order(symbol=symbol, amount=amount) # 賣單
else:
pass
print('下單成功:', order_type, buy_or_sell, symbol, price, amount)
print('下單資訊:', order_info, '\n')
return order_info
except Exception as e:
print('下單報錯,1s后重試', e)
time.sleep(1)
print('下單報錯次數過多,程式終止')
exit()
class QQMail:
user = 'xxx@qq.com' # QQ郵箱地址
pwd = '授權碼' # 授權碼 https://jingyan.baidu.com/article/29697b91072c51ab20de3c3f.html
def __init__(self):
self.smtp = SMTP_SSL('smtp.qq.com', 465)
self.smtp.login(self.user, self.pwd)
def send_message(self, to, subject, content):
msg = MIMEText(content)
msg['Subject'] = subject # 標題
msg['From'] = self.user # 發件人
msg['To'] = to # 收件人
self.smtp.send_message(msg)
def quit(self):
self.smtp.quit()
# 自動發送郵件
def auto_send_email(to_address, subject, content):
mail = QQMail()
mail.send_message(to_address, subject, content)
mail.quit()
三、主程式
import ccxt
from datetime import datetime, timedelta
from time import sleep
import pandas as pd
from .trade import next_run_time, auto_send_email, place_order, get_okex_candle_data
from .signals import signal_moving_average
"""
自動交易主要流程
# 通過while陳述句,不斷的回圈
# 每次回圈中需要做的操作步驟
1. 更新賬戶資訊
2. 獲取實時資料
3. 根據最新資料計算買賣信號
4. 根據目前倉位、買賣資訊,結束本次回圈,或者進行交易
5. 交易
"""
time_interval = '1m' # 運行時間間隔
# 創建交易所物件
exchange = ccxt.okex5()
# 設定代理
exchange.proxies = {
'http': 'http://127.0.0.1:6666',
'https': 'http://127.0.0.1:6666',
}
# 設定apiKey和apiSecret
exchange.apiKey = ''
exchange.secret = ''
exchange.password = '' # okex特有的引數Passphrase,如果不設定會報錯:AuthenticationError: requires `password`
symbol = 'ETH/USDT' # 交易對
base_coin = symbol.split('/')[-1]
trade_coin = symbol.split('/')[0]
para = [20, 200] # 策略引數
# ====主程式
while True:
# ===監控郵件內容
email_title = '策略報表'
email_content = ''
# ===從服務器更新賬戶balance資訊
balance = exchange.fetch_balance()['total']
base_coin_amount = float(balance[base_coin])
trade_coin_amount = float(balance[trade_coin])
print('當前資產:\n', base_coin, base_coin_amount, trade_coin, trade_coin_amount)
# ===sleep直到運行時間
run_time = next_run_time(time_interval)
sleep(max(0, (run_time - datetime.now()).seconds))
while True: # 在靠近目標時間時
if datetime.now() < run_time:
continue
else:
break
# ===獲取最新資料
while True:
# 獲取資料
df = get_okex_candle_data(exchange, symbol, time_interval)
# 判斷是否包含最新的資料
_temp = df[df['candle_begin_time_GMT8'] == (run_time - timedelta(minutes=int(time_interval)))]
if _temp.empty:
print('獲取資料不包含最新的資料,重新獲取')
continue
else:
break
# ===產生交易信號
df = df[df['candle_begin_time_GMT8'] < pd.to_datetime(run_time)] # 去除target_time周期的資料
df = signal_moving_average(df, para=para)
signal = df.iloc[-1]['signal']
# signal = 1
print('\n 交易信號', signal)
# ====賣出品種
if trade_coin_amount > 0 and signal == 0:
print('\n賣出')
# 獲取最新的賣出價格
price = exchange.fetch_ticker(symbol)['bid'] # 獲取買一價格
# 下單
place_order(exchange, order_type='limit', buy_or_sell='sell', symbol=symbol, price=price * 0.98, amount=trade_coin_amount)
# 郵件標題
email_title += '_賣出_' + trade_coin
# 郵件內容
email_content += '賣出資訊:\n'
email_content += '賣出數量:' + str(trade_coin_amount) + '\n'
email_content += '賣出價格:' + str(price) + '\n'
# ====買入品種
if trade_coin_amount == 0 and signal == 1:
print('\n買入')
# 獲取最新的買入價格
price = exchange.fetch_ticker(symbol)['ask'] # 獲取賣一價格
# 計算買入數量
buy_amount = base_coin_amount / price
# 獲取最新的賣出價格
place_order(exchange, order_type='limit', buy_or_sell='buy', symbol=symbol, price=price * 1.02, amount=buy_amount)
# 郵件標題
email_title += '_買入_' + trade_coin
# 郵件內容
email_content += '買入資訊:\n'
email_content += '買入數量:' + str(buy_amount) + '\n'
email_content += '買入價格:' + str(price) + '\n'
# ====發送郵件
# 每個半小時發送郵件
if run_time.minute % 30 == 0:
# 發送郵件
auto_send_email('462915202@qq.com', email_title, email_content)
# ====本次交易結束
print(email_title)
print(email_content)
print('====本次運行完畢\n')
sleep(6 * 1)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/292101.html
標籤:區塊鏈
