[PYTHON] J'ai essayé de démarrer avec Bitcoin Systre le week-end

Contexte

――Lorsque je surfais sur le net, j'ai trouvé un site très utile sur Systre.

Ce que tu as fait

1. Obtenez des données bitcoin à partir de l'API CryptWatch

--Défini les classes suivantes

import json
import requests
import pandas as pd
import datetime
from pprint import pprint
import time

class PriceGetterCryptwatch():
#Omission
    def get_price(self, periods, after=None, before=None, exchange="bitflyer", currency="btcjpy"):
        """
        input
        periods:Cycle 86400 pour tous les jours
        after:Obtenir le graphique après après avec unixTime
        before:Obtenir un graphique avant avant avec unixTime
        exchange:Échange de devises virtuel
        currency:Paire de devises

        output
Résultats de l'API:Format JSON
        """
        url = "https://api.cryptowat.ch/markets/"+exchange+"/"+currency+"/ohlc?periods="+str(periods)

        if after:
            after=int(after)
            url += "&after=" + str(after)
        elif before:
            before=int(before)
            url += "&before=" + str(before)

        print(url)
        responce = requests.request("GET", url)
        return responce.json()

    def apidata_to_dataframe(self, data, periods):
        """
        [ CloseTime , OpenPrice , HighPrice , LowPrice , ClosePrice , Volume]
        [Date et l'heure,Prix ouvert,Prix élevé,Bas prix,le dernier prix,Le volume]
        """
        #La dernière colonne étant inconnue, supprimez-la pour le moment
        df = pd.DataFrame(data["result"][str(periods)],columns=[ "CloseTime" , "OpenPrice" , "HighPrice" , "LowPrice" , "ClosePrice" , "Volume", "None"])
        df = df.drop("None", axis=1)
        #Convertir le temps Unix en temps réel
        df["CloseTime"] = df["CloseTime"].map(self.unixtime_to_datetime)
        return df

--Si vous l'utilisez, vous obtiendrez le tableau suivant スクリーンショット 2019-11-11 13.11.48.png

Sauvegarde des données passées

une fonction

def save_data(periods):
    getter = PriceGetterCryptwatch()
    date = getter.datetime_to_obj(2017,1,1)
    date = getter.datetime_to_unixtime(date)

    # designate start date
    data = getter.get_price(periods, after=date)
    df = getter.apidata_to_dataframe(data, periods)

    start_time = (df["CloseTime"].iloc[0])
    last_time = (df["CloseTime"].iloc[-1]) # unixtime
    filename = str(start_time) + " - " + str(last_time)  + "period " + str(periods)
    filename += ".csv"
    filename = filename.replace(":", "-")

    df.to_csv(filename, index=False, float_format="%.6f")

def update_data(old_filename, periods):
    df_old = pd.read_csv(old_filename)
    old_size = df_old.shape[0]

    # get new data
    getter = PriceGetterCryptwatch()
    date = getter.datetime_to_obj(2018,1,1)
    date = getter.datetime_to_unixtime(date)

    # designate start date
    data = getter.get_price(periods,after=date)
    df = getter.apidata_to_dataframe(data, periods)
    
    df_temp = pd.concat([df_old, df], axis=0)
    df_temp["CloseTime"] = pd.to_datetime(df_temp["CloseTime"]) 
    df_save = df_temp.drop_duplicates(subset="CloseTime")
    df_save = df_save.sort_values("CloseTime")
    df_save = df_save.reset_index(drop=True)
    new_size = df_save.shape[0] - old_size

    if new_size > 0:
        print("NewData : {}".format(new_size))

        start_time = df_save["CloseTime"].iloc[0]
        last_time = df_save["CloseTime"].iloc[-1] # unixtime
        filename = str(start_time) + " - " + str(last_time)  + "period " + str(periods)
        filename += ".csv"
        filename = filename.replace(":", "-")

        dir_name = os.path.dirname(old_filename)
        filename = os.path.join(dir_name, filename)
        df_save.to_csv(filename, index=False, float_format="%.6f")
    else:
        print("NewData None")

--Exécuter --Lorsque acquis en 1 minute

    PERIOD_1MIN = 60
    FILENAME_OLD_1MIN = "./Data/2019-11-06 12-45-00 - 2019-11-11 09-03-00period 60.csv"
    update_data(FILENAME_OLD_1MIN, PERIOD_1MIN)

--Résultat --Il semble que 258 nouvelles données ont été ajoutées. La mise à jour est rapide car elle dure 1 minute

https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc?periods=60&after=1514732400
NewData : 258
CloseTime,OpenPrice,HighPrice,LowPrice,ClosePrice,Volume
2019-11-06 12:45:00,1013343,1013343,1013343,1013343,0.090000
2019-11-06 12:46:00,1013343,1013622,1012862,1013091,2.730759
2019-11-06 12:47:00,1013375,1013839,1013345,1013345,1.170000
...

2019-11-11 13:26:00,981670,982509,981670,982509,0.778839
2019-11-11 13:27:00,982489,982489,982003,982003,0.205953
2019-11-11 13:28:00,982052,982052,982052,982052,0.010000

2. Simulation de trading

Graphique de prix BitCoin (4 heures et moyennes mobiles (12h, 20h))

スクリーンショット 2019-11-11 13.34.02.png

――Je vais l'élargir

スクリーンショット 2019-11-11 13.39.36.png

Implémentation d'algorithme de trading

--Création d'une classe d'achat et de vente de pièces de base et en a hérité pour créer un algo commercial ――Le moment où la ligne à court terme croise la ligne à long terme et dépasse ACHAT

class TradeBase():
#Omission

    def __init__(self, jpy_asset, bitcoin_asset, period):
        self.jpy_asset = jpy_asset
        self.bitcoin_asset = bitcoin_asset
        self.period = period
        self.df = None

    def buy_coin(self, bitcoin_price, jpy_asset, bitcoin_asset, jpy_buy_amount=None):
        """
Pour le backtesting
        """
        if jpy_buy_amount == None:
            jpy_buy_amount = jpy_asset

        bitcoin_buy_amount = jpy_buy_amount / bitcoin_price
        
        new_jpy_asset =  jpy_asset-jpy_buy_amount
        new_bitcoin_asset =  bitcoin_asset+bitcoin_buy_amount
        
        return new_jpy_asset, new_bitcoin_asset
        
    def sell_coin(self, bitcoin_price, jpy_asset, bitcoin_asset, bitcoin_sell_amount=None):
        """
Pour le backtesting
        """
        if bitcoin_sell_amount== None:
            bitcoin_sell_amount = bitcoin_asset

        jpy_sell_amount = bitcoin_price * bitcoin_sell_amount
        
        new_jpy_asset =  jpy_asset + jpy_sell_amount
        new_bitcoin_asset =  bitcoin_asset-bitcoin_sell_amount
        
        return new_jpy_asset, new_bitcoin_asset


class TradeWithMovingAverage(TradeBase):
    """
Lors de l'achat et de la vente uniquement à l'intersection de deux types de moyenne mobile
    """
    def __init__(self, jpy_asset, bitcoin_asset, period, period_ma_type1, period_ma_type2):
        super().__init__(jpy_asset, bitcoin_asset, period)
        self.period_ma_type1 = period_ma_type1
        self.period_ma_type2 = period_ma_type2
        self.rolling_win_type1 = int(period_ma_type1 / period)
        self.rolling_win_type2 = int(period_ma_type2 / period)
        # for trade func
        self.prev_mean_type1 = None
        self.prev_mean_type2 = None
        self.jpy_asset_result = []
        self.data = None
    
    def judge_sell_or_buy(self, new_mean_fast, prev_mean_fast, new_mean_slow, prev_mean_slow):
        """
Jugement d'achat et de vente en regardant la ligne de moyenne mobile
        """
        if prev_mean_fast > prev_mean_slow:
            if new_mean_fast < new_mean_slow:
                return "SELL"
            else:
                return "NOTHING"

        elif prev_mean_fast < prev_mean_slow:
            if new_mean_fast > new_mean_slow:
                return "BUY"
            else:
                return "NOTHING"
        else:
            return "NOTHING"
        
    def trade(self, init_var=True):

        if self.data is None:
            print(" Data is None")
            return
        
        if init_var:
            self.prev_mean_type1 = None
            self.prev_mean_type2 = None
            self.jpy_asset_result = []

        mean_type1 = None
        mean_type2 = None
        
        last_sell_jpyasset = None
        for i, value in enumerate(self.data):
            # Average
            if i > self.rolling_win_type1:
                mean_type1 = self.data.iloc[int(i-self.rolling_win_type1):i].mean()

            if i > self.rolling_win_type2:
                mean_type2 = self.data.iloc[int(i-self.rolling_win_type2):i].mean()
            
            if mean_type1 is None or mean_type2 is None:
                self.jpy_asset_result.append(self.jpy_asset)
                continue
            
            # Trade
            bitcoin_price = value
            if self.prev_mean_type1 and self.prev_mean_type2:
                judge = self.judge_sell_or_buy(mean_type1, self.prev_mean_type1,
                    mean_type2, self.prev_mean_type2)
                
                if judge == "SELL":
                    if self.bitcoin_asset > 0:
                        self.jpy_asset, self.bitcoin_asset = self.sell_coin(bitcoin_price, self.jpy_asset, self.bitcoin_asset)
                        last_sell_jpyasset = self.jpy_asset

                elif judge == "BUY":
                    if self.jpy_asset > 0:
                        self.jpy_asset, self.bitcoin_asset = self.buy_coin(bitcoin_price, self.jpy_asset, self.bitcoin_asset, jpy_buy_amount=self.jpy_asset)

                else:
                    pass

            self.prev_mean_type1 = mean_type1
            self.prev_mean_type2 = mean_type2
            self.jpy_asset_result.append(self.jpy_asset)
            
        self.last_sell_jpyasset = last_sell_jpyasset
        print("Result: Jpy{:.1f} bitcoin{:.4f} (LAST JPY: {})".format(self.jpy_asset, self.bitcoin_asset, self.last_sell_jpyasset))
        return self.jpy_asset, self.bitcoin_asset

Résultat d'exécution

jpy = 10000
bitcoin = 0
period = 60*60*4    #4 heures

trader = TradeWithMovingAverage(jpy_asset=jpy, bitcoin_asset=bitcoin, period=period,
                                period_ma_type1=period*3, period_ma_type2=period*5)
#Ensemble de données(Manuel)
trader.df = df
trader.data = df["ClosePrice"]
trader.trade()
trader.show_trade_result()

Transition d'actifs (yen japonais)

Il a plus que triplé par rapport au point de départ de 10 000 yens et semble fonctionner de manière inattendue.

スクリーンショット 2019-11-11 13.52.02.png

--J'ai essayé de l'utiliser pour les dernières données (2019/9 ~ 2019/11)

スクリーンショット 2019-11-11 14.04.50.png

3. Impressions

«J'ai pu voir avec mes yeux que le trading mécanique semble être plus stable sans biais que les humains. «Je suis content d'avoir eu l'idée de créer un algorithme de trading. «Je pense que nous allons apporter des améliorations qui intègrent d'autres indicateurs. ――Il semble qu'il faudra beaucoup de temps pour l'utiliser dans la pratique --Apprentissage approfondi, je voudrais intégrer un apprentissage amélioré dans l'algorithme de jugement commercial. --Merci pour la lecture.

Recommended Posts

J'ai essayé de démarrer avec Bitcoin Systre le week-end
J'ai essayé de commencer avec Hy
J'ai essayé de démarrer avec le script python de blender_Part 01
J'ai essayé de démarrer avec le script python de blender_Partie 02
J'ai essayé de commencer avec Hy ・ Définir une classe
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
Le moyen le plus simple de démarrer avec Django
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé d'obtenir des données CloudWatch avec Python
J'ai essayé de jouer avec la calculatrice avec tkinter
J'ai essayé d'obtenir le code d'authentification de l'API Qiita avec Python.
J'ai essayé avec les 100 meilleurs packages PyPI> J'ai essayé de représenter graphiquement les packages installés sur Python
J'ai essayé d'obtenir les informations sur le film de l'API TMDb avec Python
J'ai essayé d'implémenter Mine Sweeper sur un terminal avec python
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé de résoudre le problème avec Python Vol.1
Je veux commencer avec le noyau Linux, quelle est la structure de la tête de liste?
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai essayé d'analyser les émotions de tout le roman "Weather Child" ☔️
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
Mémo pour obtenir la valeur côté html-javascript avec jupyter
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de notifier les informations de retard de train avec LINE Notify
Connaissances minimales pour démarrer avec le module de journalisation Python
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10
J'ai essayé de lancer le cluster ipython au minimum sur AWS
J'ai essayé d'obtenir diverses informations de l'API codeforces
Lien pour commencer avec python
Premiers pas avec MicroPython (sur macOS)
Comment démarrer avec Scrapy
Comment démarrer avec Python
Comment démarrer avec Django
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
[Démarrage du shell] J'ai essayé d'afficher le shell sur le téléviseur avec un G-cluster à carte Linux bon marché
J'ai essayé de décrire le trafic en temps réel avec WebSocket
J'ai essayé de résoudre l'édition du débutant du livre des fourmis avec python
Premiers pas avec le framework Python Django sur Mac OS X
J'ai essayé d'obtenir l'index de la liste en utilisant la fonction énumérer
J'ai essayé d'automatiser l'arrosage du pot avec Raspberry Pi
Un mémorandum lors de l'acquisition automatique avec du sélénium
J'ai essayé la validation croisée basée sur le résultat de la recherche de grille avec scikit-learn
J'ai écrit un script pour vous aider à démarrer avec AtCoder à grande vitesse!
J'ai essayé de traiter l'image en "style croquis" avec OpenCV
J'ai essayé de numériser le tampon estampé sur papier en utilisant OpenCV
J'ai essayé d'enregistrer une station sur la plateforme IoT "Rimotte"
J'ai essayé d'afficher l'interface graphique sur Mac avec le système X Window
J'ai essayé de traiter l'image dans un "style de dessin au crayon" avec OpenCV
J'ai essayé d'agrandir la taille du volume logique avec LVM
J'ai essayé de résumer les remarques de tout le monde sur le slack avec wordcloud (Python)
J'ai essayé d'améliorer l'efficacité du travail quotidien avec Python
PhytoMine-I a essayé d'obtenir les informations génétiques de la plante avec Python
Notes d'étape pour démarrer avec django
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé de résumer la commande umask