[PYTHON] Reconstruction d'un carnet de commandes / positions avec oandapy V20

1.Tout d'abord

Je pense que beaucoup de gens n'utilisent que le prix (+ volume) pour créer des bots d'investissement. Mais ce ne sont pas les seules données disponibles. Par exemple, le statut des ordres et le statut des positions détenues peuvent être pris en compte.

Cette fois, j'ai sorti les données de commande et de position (carnet de commandes / carnet de positions) d'Oanda de FX Exchange et créé un outil pour les traiter, donc je vais le partager.

2. Qu'est-ce qu'un carnet de commandes / carnet de positions?

Le carnet de commandes indique le nombre de commandes non exécutées pour chaque tarif (planche en stock). Les positions ouvertes indiquent combien de positions sont actuellement détenues à quel taux. Pour plus de détails, rendez-vous sur le site oanda à partir du lien et vérifiez.

スクリーンショット 0002-09-22 16.28.46.png ( https://www.oanda.jp/lab-education/oanda_lab/oanda_rab/open_position/ )

3. À propos du livre d'Oanda

Avec plus de 500 000 $ de transactions au cours du dernier mois, oanda mettra à jour votre carnet de commandes et votre carnet de positions toutes les 5 minutes. Cette fréquence ne change pas que vous la visualisiez sur le Web ou que vous l'obteniez avec l'API.

Si vous créez simplement un compte, la fréquence de mise à jour passera à une fois toutes les 20 minutes. Cependant, si vous n'échangez pas fréquemment, je pense que c'est une fréquence de mise à jour suffisante.

Nous n'expliquerons pas ici comment ouvrir un compte ou obtenir une clé API.

4. Corps

Vous pouvez voir que les commandes ouvertes que vous voyez sur le site oanda sont réparties sur une certaine largeur (chaque 0,05 yen pour un yen dollar), centrée sur le taux au moment où l'instantané de la commande ouverte a été créé.

Mais les données obtenues par api ne sont pas de cette façon. Le carnet de commandes contient un large éventail d'informations sur les taux, y compris des informations impossibles en réalité, telles que 1 dollar = 0 yen à 1 dollar = 200 yens. De plus, le taux au cœur des commandes ouvertes fluctue constamment. Par conséquent, même si vous essayez d'analyser les données obtenues telles quelles, vous ne pouvez pas voir à quelle distance chaque taux est du taux lorsque la commande ouverte a été créée.

Par conséquent, cette fois, nous avons préparé un code capable de collecter et de traiter des données afin que vous puissiez saisir intuitivement le taux central et à quelle distance il en est, comme indiqué ci-dessous. Veuillez réécrire le accountID et access_token avec les vôtres.

スクリーンショット 0002-09-22 17.06.44.png

Il ne semble y avoir aucune donnée avant le 20 janvier 2017. En outre, il faut environ 20 à 30 minutes pour obtenir le livre toutes les heures.

def fetch_orderbook(instrument="USD_JPY", levels = 5, timeframe="15m"):
  '''
  instrument :Nom de la paire de devises
  levels :Combien de haut (bas) pour obtenir du taux central, >0
  timeframe :Combien de minutes pour obtenir le livre, "m":minute, "H":hour, "D":day, >="5m"
              ex) "1H":Obtenez toutes les heures, "2D":Obtenez quotidiennement
  '''
  accountID = "×××-×××-×××-×××"
  access_token ="××××××××××××"
  api = API(access_token=access_token, environment="live")

  if timeframe[-1]=="m":
    diff = 60 * int(timeframe[:-1])
  elif timeframe[-1]=="H":
    diff = 60 * 60 * int(timeframe[:-1])
  elif timeframe[-1]=="D":
    diff = 60 * 60 * 24 * int(timeframe[:-1])
    
  timestamp = datetime.datetime(year=2017, month=1, day=20)
  now = datetime.datetime.utcnow()
  processed_book, book_timestamps = [], []
  counter = 0
  start = datetime.datetime.utcnow()
  while True:
    try:
      #Terminez lorsque vous avez terminé jusqu'à maintenant
      if now < timestamp:
        break
      #Prévention des accès excessifs au serveur et à la sortie du journal
      counter+=1
      if counter%100==0:
        print(timestamp, datetime.datetime.utcnow()-start)
        start = datetime.datetime.utcnow()
        counter=0
        time.sleep(1)
      
      #Obtenir le carnet de commandes
      r=instruments.InstrumentsOrderBook(instrument=instrument, params={"time":timestamp.strftime("%Y-%m-%dT%H:%M:00Z")})
      api.request(r)
      book_created_rate, buckets, book_created_time = float(r.response["orderBook"]["price"]), r.response["orderBook"]["buckets"], r.response["orderBook"]["time"]
      #Obtenez un index proche du tarif au moment de la création du livre
      bucket_rate = [round(float(b["price"]), 2) for b in buckets]
      upper_idx = np.searchsorted(bucket_rate, book_created_rate)
      lower_idx = upper_idx - 1
      #Niveaux ajoutés séparés du prix actuel
      row_book = [book_created_rate]
      for i in range(levels):
        upper, lower = buckets[upper_idx + i], buckets[lower_idx - i]
        bucket = [upper["price"], upper['longCountPercent'], upper['shortCountPercent'], 
                  lower["price"], lower['longCountPercent'], lower['shortCountPercent']]
        bucket = [float(num) for num in bucket]
        row_book.extend(bucket)
      processed_book.append(row_book)
      book_timestamps.append(book_created_time)
    except:
      #samedi et dimanche
      pass
    timestamp+=datetime.timedelta(seconds = diff)


  #nom de colonne
  column_names = ["rate"]
  for i in range(levels):
    column_names.extend(["price_+{}".format(i+1), "OrderLongPercent_+{}".format(i+1), "OrderShortPercent_+{}".format(i+1),
    "price_-{}".format(i+1), "OrderLongPercent_-{}".format(i+1), "OrderShortPercent_-{}".format(i+1)])

  #Combiner des données
  orderbook = pd.DataFrame(processed_book, columns=column_names, index=book_timestamps)
  return orderbook

Il en va de même pour le carnet de positions

def fetch_positionbook(instrument="USD_JPY", levels = 5, timeframe="15m"):
  '''
  instrument :Nom de la paire de devises
  levels :Combien de haut (bas) pour obtenir du taux central, >0
  timeframe :Combien de minutes pour obtenir le livre, "m":minute, "H":hour, "D":day, >="5m"
              ex) "1H":Obtenez toutes les heures, "2D":Obtenez quotidiennement
  '''
  accountID = "×××-×××-×××-×××"
  access_token ="××××××××××××"
  api = API(access_token=access_token, environment="live")

  if timeframe[-1]=="m":
    diff = 60 * int(timeframe[:-1])
  elif timeframe[-1]=="H":
    diff = 60 * 60 * int(timeframe[:-1])
  elif timeframe[-1]=="D":
    diff = 60 * 60 * 24 * int(timeframe[:-1])
    
  timestamp = datetime.datetime(year=2017, month=1, day=20)
  now = datetime.datetime.utcnow()
  processed_book, book_timestamps = [], []
  counter = 0
  start = datetime.datetime.utcnow()
  while True:
    try:
      #Terminez lorsque vous avez terminé jusqu'à maintenant
      if now < timestamp:
        break
      #Prévention des accès excessifs au serveur et à la sortie du journal
      counter+=1
      if counter%100==0:
        print(timestamp, datetime.datetime.utcnow()-start)
        start = datetime.datetime.utcnow()
        counter=0
        time.sleep(1)
      
      #Obtenir le carnet de position
      r=instruments.InstrumentsPositionBook(instrument=instrument, params={"time":timestamp.strftime("%Y-%m-%dT%H:%M:00Z")})
      api.request(r)
      book_created_rate, buckets, book_created_time = float(r.response["positionBook"]["price"]), r.response["positionBook"]["buckets"], r.response["positionBook"]["time"]
      #Obtenez un index proche du tarif au moment de la création du livre
      bucket_rate = [round(float(b["price"]), 2) for b in buckets]
      upper_idx = np.searchsorted(bucket_rate, book_created_rate)
      lower_idx = upper_idx - 1
      #Niveaux ajoutés séparés du prix actuel
      row_book = [book_created_rate]
      for i in range(levels):
        upper, lower = buckets[upper_idx + i], buckets[lower_idx - i]
        bucket = [upper["price"], upper['longCountPercent'], upper['shortCountPercent'], 
                  lower["price"], lower['longCountPercent'], lower['shortCountPercent']]
        bucket = [float(num) for num in bucket]
        row_book.extend(bucket)
      processed_book.append(row_book)
      book_timestamps.append(book_created_time)
    except:
      #samedi et dimanche
      pass
    timestamp+=datetime.timedelta(seconds = diff)


  #nom de colonne
  column_names = ["rate"]
  for i in range(levels):
    column_names.extend(["price_+{}".format(i+1), "PositionLongPercent_+{}".format(i+1), "PositionShortPercent_+{}".format(i+1),
    "price_-{}".format(i+1), "PositionLongPercent_-{}".format(i+1), "PositionShortPercent_-{}".format(i+1)])

  #Combiner des données
  positionbook = pd.DataFrame(processed_book, columns=column_names, index=book_timestamps)
  return positionbook

5. Enfin

J'aimerais le présenter, mais je pense qu'il est difficile de créer un bot en utilisant uniquement ces données. J'ai pu gagner (sans les frais), mais je n'arrivais pas à gagner avec les frais. Peut-être que cela fonctionne bien lorsqu'il est combiné avec une anomalie de temps ...

Recommended Posts

Reconstruction d'un carnet de commandes / positions avec oandapy V20