一、概述

1.1 第 1 部分回顾

在第一部分中,我们使用 WaveNet 创建了一个预测比特币每小时收益率的模型,并对其进行了回测。结果发现 WaveNet 的预测超出了基准。在第二部分,我们将使用 WaveNet 编写自动交易的逻辑。

1.2 第二部分内容

我们将描述使用上次创建的预测模型进行预测并实际下订单的机制。
有关使用的代码的详细信息,请参阅下面的 GitHub。
https://github.com/SY122095/BTC_bot

1.3 开发环境和使用的工具

  • Python 3.10
  • SQLite3
    用于存储交易结果
  • GMO 硬币帐户和 API 密钥
    用于数据检索和比特币订单
  • LINE 通知 API
    用于在进行交易时向 LINE 发送通知
    从以下网址注册
     https://notify-bot.line.me/ja/

2.待实施的交易流程

交易每隔 1 小时进行一次。流程如下

  1. 平仓
  2. 获取最新数据并转换为模型输入格式
  3. 使用 WaveNet 预测回报率
  4. 如果预测值为正则买入,如果为负则卖出
  5. 下订单
  6. 通知 LINE 迄今为止的利润和余额
  7. 等待 60 分钟

    3.代码详情

    3.1 目录

    ┣━ main.py                      # bot本体を記述
    ┣━ src
    ┃   ┣━ data
    ┃   ┃    ┗━ make_datasets.py    # 学習等データと予測用データ作成する関数を記述
    ┃   ┣━ features                 
    ┃   ┃    ┗━ build_features.py   # 特徴量を作成する関数を記述
    ┃   ┗━ models
    ┃        ┣━ predict_model.py    
    ┃        ┗━ train_model.py      # モデル構築のための関数を記述
    ┣━ trading
    ┃   ┗━ trading.py               # GMOコインにて取引を行うための関数を記述
    ┣━ models
    ┃   ┗━ model.h5                 # WaveNetモデル
    ┣━ line
    ┃   ┗━ line_notify              # LINEにメッセージを送るための関数を記述
    ┗━ sql
        ┗━ trading.db               # 取引結果を記録するデータベース
    

    3.2 通知LINE

    本文创建参考。

    线/line_notify.py
    import requests
    
    class LineNotify:
        def __init__(self):
            # LineNotify登録時に取得したTOKENを指定
            self.line_notify_token = LINE_NOTIFY_TOKEN
            self.line_notify_api = "https://notify-api.line.me/api/notify"
            self.headers = {
              "Authorization": f"Bearer {self.line_notify_token}"
            }
    
        def send(self, msg):
            '''lineにメッセージを送るメソッド'''
            msg = { "message": f" {msg}" }
            requests.post(self.line_notify_api, headers = self.headers, data = msg)
    

    3.3 执行交易并获得结果

    GMO硬币官方文件什么时候博文创建参考。

    交易/trading.py
    import hashlib
    import hmac
    import json
    import requests
    import time
    import pandas as pd
    from pytz import timezone
    from datetime import datetime
    from line.line_notify import LineNotify
    
    line_notify = LineNotify()
    api_Key = # 取得したAPIキーを入れる
    secretKey = # 取得したシークレットキーを入れる
        
    def get_availableAmount():
        '''取引余力を取得する関数'''
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        method    = 'GET'
        endPoint  = 'https://api.coin.z.com/private'
        path      = '/v1/account/margin'
        text = timestamp + method + path
        sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
        
        headers = {
            "API-KEY": api_Key,
            "API-TIMESTAMP": timestamp,
            "API-SIGN": sign
        }
        
        res = requests.get(endPoint + path, headers=headers)
        r =  json.dumps(res.json(), indent=2)
        return json.loads(r)['data']['availableAmount']
        
    
    def build_position(symbol, side, executionType, size, price='', losscutPrice='', timeInForce='FAK'):
        '''
        ポジションを決める関数
        '''
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        method    = 'POST'
        endPoint  = 'https://api.coin.z.com/private'
        path      = '/v1/order'
        reqBody = {
            "symbol": symbol,
            "side": side,
            "executionType": executionType,
            "timeInForce": timeInForce,
            "price": price,
            "losscutPrice": losscutPrice,
            "size": size
        }
    
        text = timestamp + method + path + json.dumps(reqBody)
        sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
    
        headers = {
            "API-KEY": api_Key,
            "API-TIMESTAMP": timestamp,
            "API-SIGN": sign
        }
    
        res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
        return res.json()
        
    
    def get_position():
        '''建玉一覧を取得'''
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        method    = 'GET'
        endPoint  = 'https://api.coin.z.com/private'
        path      = '/v1/openPositions'
    
        text = timestamp + method + path
        sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
        parameters = {
            "symbol": "BTC_JPY",
            "page": 1,
            "count": 100
        }
    
        headers = {
            "API-KEY": api_Key,
            "API-TIMESTAMP": timestamp,
            "API-SIGN": sign
        }
    
        res = requests.get(endPoint + path, headers=headers, params=parameters)
        return res.json()
    
    def close_position(ticker, side, size, executionType, position_id):
        '''決済注文を出す'''
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        method    = 'POST'
        endPoint  = 'https://api.coin.z.com/private'
        path      = '/v1/closeOrder'
        reqBody = {
            "symbol": ticker,
            "side": side,
            "executionType": executionType,
            "timeInForce": "",
            "price": "",
            "settlePosition": [
                {
                    "positionId": position_id,
                    "size": size
                }
            ]
        }
    
        text = timestamp + method + path + json.dumps(reqBody)
        sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
    
        headers = {
            "API-KEY": api_Key,
            "API-TIMESTAMP": timestamp,
            "API-SIGN": sign
        }
    
        res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
        return res.json()
    
    def exe_all_position():
        '''すべてのポジションを決済する'''
        position = get_position()
        if position['data'] == {}:
            print('ポジションはありません')
        else:
            for i in position['data']['list']:
                if i['side'] == 'BUY':
                    close_res = close_position(i['symbol'], 'SELL', i['size'], 'MARKET', i['positionId'])
                    if close_res['status'] == 0:
                        print('レバレッジ取引(買い注文)は決済されました')
                        line_notify.send('レバレッジ取引(買い注文)は決済されました')
                    else:
                        print(close_res)
                elif i['side'] == 'SELL':
                    close_res = close_position(i['symbol'], 'BUY', i['size'], 'MARKET', i['positionId'])
                    if close_res['status'] == 0:
                        print('レバレッジ取引(売り注文)は決済されました')
                        line_notify.send('レバレッジ取引(買い注文)は決済されました')
                    else:
                        print(close_res)
                        
                        
    def order_process(symbol, side, executionType, size, price='', losscutPrice='', timeInForce='FAK'):
        '''注文を出す'''
        if side == 'BUY':
            build_position(symbol, side, executionType, size, price, losscutPrice, timeInForce='FAK')
            time.sleep(1)
            print('ビットコインを' + str(get_position()['data']['list'][0]['price']) + '円でロングしました')
            line_notify.send('ビットコインを' + str(get_position()['data']['list'][0]['price']) + '円でロングしました')
        elif side == 'SELL':
            build_position(symbol, side, executionType, size, price, losscutPrice, timeInForce='FAK')
            time.sleep(1)
            print('ビットコインを' + str(get_position()['data']['list'][0]['price']) + '円でショートしました')
            line_notify.send('ビットコインを' + str(get_position()['data']['list'][0]['price']) + '円でショートしました')
    
    def get_trade_result():
        '''取引の記録を取得'''
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        method    = 'GET'
        endPoint  = 'https://api.coin.z.com/private'
        path      = '/v1/latestExecutions'
    
        text = timestamp + method + path
        sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
        parameters = {
            "symbol": "BTC_JPY",
            "page": 1,
            "count": 2
        }
    
        headers = {
            "API-KEY": api_Key,
            "API-TIMESTAMP": timestamp,
            "API-SIGN": sign
        }
    
        res = requests.get(endPoint + path, headers=headers, params=parameters)
        time_ = res.json()['data']['list'][1]['timestamp']
        time_ = pd.Timestamp(time_)
        time_ = time_.astimezone(timezone('Asia/Tokyo'))
        year = time_.year
        month = time_.month
        day = time_.day
        hour = time_.hour
        date = str(year) + '-' + str(month)+ '-' + str(day)+ ' ' + str(hour) + ':00:00'
        side = res.json()['data']['list'][1]['side']
        if side == 'SELL':
            position = -1
        else:
            position = 1
        order_price = int(res.json()['data']['list'][1]['price'])
        close_price = int(res.json()['data']['list'][0]['price'])
        loss_gain = int(res.json()['data']['list'][0]['lossGain'])
        id = int(res.json()['data']['list'][0]['executionId'])
        
        return id, date, position, order_price, close_price, loss_gain
    

    3.4 模型构建

    数据/make_datasets.py
    import json
    import numpy as np
    import pandas as pd
    import requests
    from datetime import datetime
    from datetime import timedelta
    from pandas import json_normalize
    
    def get_data(symbol='BTC_JPY', interval='1hour', date=''):
        '''1日分の1時間足データを取得する'''
        endPoint = 'https://api.coin.z.com/public'
        path     = f'/v1/klines?symbol={symbol}&interval={interval}&date={date}'
    
        response = requests.get(endPoint + path)
        r = json.dumps(response.json(), indent=2)
        r2 = json.loads(r)
        df = json_normalize(r2['data'])
        if len(df):
            date = []
            for i in df['openTime']:
                i = int(i)
                tsdate = int (i / 1000)
                loc = datetime.utcfromtimestamp(tsdate)
                date.append(loc)
            df.index = date
            df.index = df.index.tz_localize('UTC')
            df.index = df.index.tz_convert('Asia/Tokyo')
            df.drop('openTime', axis=1, inplace=True)
        return df
    
    def get_today():
        '''YYYYMMDD形式で本日の日付を取得'''
        now_time = datetime.now()
        current_year = now_time.year
        current_month = now_time.month
        current_day = now_time.day
        if current_month >= 10 and current_day >= 10:
            today = str(current_year) + str(current_month) + str(current_day)
        elif current_month < 10 and current_day >= 10:
            today = str(current_year) + '0' + str(current_month) + str(current_day)
        elif current_month < 10 and current_day < 10:
            today = str(current_year) + '0' + str(current_month) + '0' + str(current_day)
        elif current_month >= 10 and current_day < 10:
            today = str(current_year) + str(current_month) + '0' + str(current_day)
        return today
    
    def data_for_prediction():
        '''モデルへの入力のため過去20時間分のデータを用意する関数'''
        today = get_today()
        yesterday = datetime.strptime(today, '%Y%m%d')
        yesterday -= timedelta(days=1)
        yesterday = str(yesterday)
        yesterday = yesterday.replace('-', '')
        yesterday = yesterday.replace(' 00:00:00', '')
        two_days_before = datetime.strptime(yesterday, '%Y%m%d')
        two_days_before -= timedelta(days=1)
        two_days_before = str(two_days_before)
        two_days_before = two_days_before.replace('-', '')
        two_days_before = two_days_before.replace(' 00:00:00', '')
        if datetime.now().hour > 6:
            btc_today = get_data(symbol='BTC_JPY', interval='1hour', date=today)
            eth_today = get_data(symbol='ETH_JPY', interval='1hour', date=today)
            btc_yesterday = get_data(symbol='BTC_JPY', interval='1hour', date=yesterday)
            eth_yesterday = get_data(symbol='ETH_JPY', interval='1hour', date=yesterday)
            btc_data = pd.concat([btc_yesterday, btc_today], axis=0)
            eth_data = pd.concat([eth_yesterday, eth_today], axis=0)
            eth_data.columns = ['eth_open', 'eth_high', 'eth_low', 'eth_close', 'eth_volume']
            df = pd.concat([btc_data, eth_data], axis=1)
            df = df.tail(20)
        else:
            btc_yesterday = get_data(symbol='BTC_JPY', interval='1hour', date=yesterday)
            eth_yesterday = get_data(symbol='ETH_JPY', interval='1hour', date=yesterday)
            btc_day2 = get_data(symbol='BTC_JPY', interval='1hour', date=two_days_before)
            eth_day2 = get_data(symbol='ETH_JPY', interval='1hour', date=two_days_before)
            btc_data = pd.concat([btc_day2, btc_yesterday], axis=0)
            eth_data = pd.concat([eth_day2, eth_yesterday], axis=0)
            eth_data.columns = ['eth_open', 'eth_high', 'eth_low', 'eth_close', 'eth_volume']
            df = pd.concat([btc_data, eth_data], axis=1)
            df = df.tail(20)
        return df
    
    功能/build_features.py
    import numpy as np
    from numpy.lib.stride_tricks import sliding_window_view
    from sklearn.model_selection import train_test_split
    
    def normalise_windows(window_data, single_window=False):
        ''' window normalization'''
        normalised_data = [] # 正規化したデータを格納
        window_data = [window_data] if single_window else window_data
        for window in window_data:
            normalised_window = []
            for col_i in range(window.shape[1]): # Windowの幅
                # 各値を初期の値で割る
                normalised_col = [((float(p) / float(window[0, col_i])) - 1) for p in window[:, col_i]]
                normalised_window.append(normalised_col)
                # reshape and transpose array back into original multidimensional format
            normalised_window = np.array(normalised_window).T
            normalised_data.append(normalised_window)
        return np.array(normalised_data)
    
    def create_x_for_prediction(df, seqence_width=20):
        '''予測値算出のためのデータセット作成'''
        open = df['open'].values
        close = df['close'].values
        high = df['high'].values
        low = df['low'].values
        eth_open = df['eth_open'].values
        eth_close = df['eth_close'].values
    
        seqence_width = seqence_width
        open_df = sliding_window_view(open, seqence_width)
        close_df = sliding_window_view(close, seqence_width)
        high_df = sliding_window_view(high, seqence_width)
        low_df = sliding_window_view(low, seqence_width)
        eth_open_df = sliding_window_view(eth_open, seqence_width)
        eth_close_df = sliding_window_view(eth_close, seqence_width)
    
        x_open = open_df[:, :, np.newaxis]
        x_close = close_df[:, :, np.newaxis]
        x_high = high_df[:, :, np.newaxis]
        x_low = low_df[:, :, np.newaxis]
        x_eth_open = eth_open_df[:, :, np.newaxis]
        x_eth_close = eth_close_df[:, :, np.newaxis]
    
        x_data = np.concatenate([x_open, x_close], axis=2)
        x_data = np.concatenate([x_data, x_high], axis=2)
        x_data = np.concatenate([x_data, x_low], axis=2)
        x_data = np.concatenate([x_data, x_eth_open], axis=2)
        x_data = np.concatenate([x_data, x_eth_close], axis=2)
    
        x_data = normalise_windows(x_data)
        return x_data
    

    3.5 机器人本体

    详细信息请参考 GitHub,因为我们省略了更新模型和停止机器人的条件。

    矿。 py
    import pandas as pd
    import sqlite3
    import tensorflow as tf
    import time
    from datetime import datetime
    from line.line_notify import LineNotify
    from src.data.make_dataset import data_for_prediction
    from src.features.build_features import create_x_for_prediction
    from src.models.train_model import mish
    from trading.trading import get_availableAmount, exe_all_position, order_process, get_trade_result
    
    
    ####-----------------------------初期設定-----------------------------####
    ticker = "BTC_JPY" # 売買対象の仮想通貨(ビットコインのレバレッジ取引)
    exe_type = 'MARKET' # 注文方式(成行)
    default_balance = float(get_availableAmount()) # デフォルトの残高
    line_notify = LineNotify() # 取引発生時にLINEで知らせるためのインスタンス
    line_notify.send('取引ボットの稼働を開始します。') # ボットの稼働開始を知らせる
    trade_num = 0 # 取引回数
    profit = 0 # 利益を格納する
    tf.keras.utils.get_custom_objects().update({'mish': mish})
    dbname = 'sql/trading.db' # 取引結果を格納するデータベース
    
    
    ####-----------------------------Bot本体の処理-----------------------------####
    while True:
        # ポジションを決済
        exe_all_position()
        
        ##--------ポジションを決めるための予測を行う--------##
        df = data_for_prediction()
        x = create_x_for_prediction(df)
        model = tf.keras.models.load_model('.\models\model.h5', custom_objects={'mish': mish})
        prediction = model.predict(x)
        print(prediction)
        if prediction > 0:
            side = 'BUY'
            tmp_position = 'long'
            hour = datetime.now().hour
        elif prediction <= 0:
            side = 'SELL'
            tmp_position = 'short'
            hour = datetime.now().hour
        else:
            continue
        print(str(datetime.now().year) + '年' + str(datetime.now().month) + '月' + str(datetime.now().day) + '日' + str(datetime.now().hour) + '時のポジションは' + tmp_position + 'です。')
        time.sleep(1)
        
        ##--------取引結果をデータベースに登録--------##
        if trade_num != 0:
            tmp_id, tmp_date, tmp_position_, tmp_order_price, tmp_close_price, tmp_loss_gain = get_trade_result() # 取引記録の取得
            tmp_df = pd.DataFrame(columns=['id', 'date', 'position', 'order_price', 'close_price', 'loss_gain'],
                        data=[[tmp_id, tmp_date, tmp_position_, tmp_order_price, tmp_close_price, tmp_loss_gain]])
            result_df = pd.concat([result_df, tmp_df])
            profit += tmp_loss_gain
            # データベースに登録
            conn = sqlite3.connect(dbname)
            cur = conn.cursor()
            cur.execute('INSERT INTO trading values(?, ?, ?, ?, ?, ?)', (tmp_id, tmp_date, tmp_position_, tmp_order_price, tmp_close_price, tmp_loss_gain))
            conn.commit()
            conn.close()
            print('取引結果をデータベースに登録しました。
    ')
        
        ##--------利益と余力をLINEに通知--------##
        available = int(get_availableAmount())
        profit_loss = available - default_balance
        profit_rate = profit / default_balance
        if profit_loss > 0:
            line_notify.send('現在の残高は' + str(available) + '円で、' + str(profit_loss) + '円の利益です')
        elif profit_loss == 0:
            line_notify.send('現在の残高は' + str(available) + '円で、' + '損益無しです')
        else:
            line_notify.send('現在の残高は' + str(available) + '円で、' + str(-profit_loss) + '円の損失です')
        
        ##--------注文を出す--------##
        order_process(symbol=ticker, side=side, executionType=exe_type, size=0.01)
        time.sleep(1)
        trade_num += 1
        
        ##--------1時間経過するまで待つ--------##
        minutes = 60 - datetime.now().minute
        sleep_time = 60 * minutes
        print(f'{minutes}分スリープします。
    ')
        time.sleep(sleep_time)
    

    4.总结

    我分两部分写了关于比特币自动交易的文章,第一部分和第二部分。
    毕竟,我觉得最重要和最困难的部分是思考产生利润的逻辑。
    还有很多改进需要做,比如特征创建和模型选择,所以我会继续试错。

    参考

    使用 Python 创建一个自动交易程序
    如何从 Python 自动交易机器人向 LINE 发送通知


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628796.html

相关文章:

  • 2022-01-05
  • 2021-10-12
  • 2021-12-27
  • 2021-12-28
  • 2021-12-13
  • 2021-07-05
  • 2021-10-04
猜你喜欢
  • 2021-12-28
  • 2021-08-29
  • 2021-06-14
  • 2021-12-20
  • 2021-10-06
  • 2022-01-04
  • 2021-07-03
相关资源
相似解决方案