Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

L5 #149

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

L5 #149

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Lista5/apis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"API": {
"bitbay": {
"name": "bitbay",
"url_orderbook": "https://bitbay.net/API/Public/",
"url_markets": "https://api.bitbay.net/rest/trading/ticker",
"orderbook_ending": "/orderbook.json",
"taker_fee": 0.0043
},
"bitrex": {
"name": "bitrex",
"url_orderbook": "https://api.bittrex.com/api/v1.1/public/getorderbook?market=",
"url_markets": "https://api.bittrex.com/api/v1.1/public/getmarkets",
"url_currencies": "https://api.bittrex.com/api/v1.1/public/getcurrencies",
"orderbook_ending": "&type=both",
"orderbook_separator": "-",
"taker_fee": 0.0075
},
"nbp": {
"name": "nbp",
"url_exchange": "http://api.nbp.pl/api/exchangerates/rates/a/",
"json_format": "?format=json"
},
"stooq" : {
"name": "stooq",
"url_stock" : "https://stooq.pl/q/?s="
}
},
"FEES": {
"bitbay_fees": {
"AAVE": 0.54,
"ALG": 426,
"AMLT": 1743,
"BAT": 156,
"BCC": 0.001,
"BCP": 1237,
"BOB": 11645,
"BSV": 0.003,
"BTC": 0.0005,
"BTG": 0.001,
"COMP": 0.1,
"DAI": 81.0,
"DASH": 0.01,
"DOT": 0.1,
"EOS": 0.1,
"ETH": 0.006,
"EXY": 520,
"GAME": 479,
"GGC": 112,
"GNT": 403,
"GRT": 84,
"LINK": 2.7,
"LML": 1500,
"LSK": 0.3,
"LTC": 0.001,
"LUNA": 0.02,
"MANA": 100,
"MKR": 0.025,
"NEU": 572,
"NPXS": 17229.0,
"OMG": 14,
"PAY": 1523,
"QARK": 1019,
"REP": 3.2,
"SRN": 5717,
"SUSHI": 8.8,
"TRX": 1.0,
"UNI": 2.5,
"USDC": 125,
"USDT": 190,
"XBX": 5508,
"XIN": 5,
"XLM": 0.005,
"XRP": 0.1,
"XTZ": 0.1,
"ZEC": 0.004,
"ZRX": 56
}
}
}
194 changes: 194 additions & 0 deletions Lista5/apisUtilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import time
import requests
import yfinance as yf
from bs4 import BeautifulSoup


NORMALIZED_OPERATIONS = ['bids', 'asks']
WAITING_TIME = 2


def get_api_response(url):
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
# print("Request not successful: " + response.reason)
return None


def get_transfer_fees(stockFst, stockSnd, pairs):
fees = {stockFst.get_name(): {}, stockSnd.get_name(): {}}
fstStockFees = stockFst.get_withdrawal_fees()
sndStockFees = stockSnd.get_withdrawal_fees()
for pair in pairs:
fees[stockFst.get_name()][pair[0]] = fstStockFees[pair[0]]
fees[stockSnd.get_name()][pair[0]] = sndStockFees[pair[0]]
return fees


def find_common_currencies_pairs(fstStockPairs, sndStockPairs):
common = []
for pair in fstStockPairs:
if pair in sndStockPairs:
common.append(pair)
return common


def exchange_base_currency(sourceCurrency, targetCurrency, quantity, apiInfo):
exchangeRate = get_currency_exchange_rate(sourceCurrency, targetCurrency, apiInfo)
return exchangeRate * quantity


def get_currency_exchange_rate(sourceCurrency, targetCurrency, apiInfo):
if sourceCurrency == targetCurrency:
return 1
if sourceCurrency == 'PLN':
url = f"{apiInfo['API']['nbp']['url_exchange']}{targetCurrency}/{apiInfo['API']['nbp']['json_format']}"
response = get_api_response(url)
if response is not None:
return 1 / float(response['rates'][0]['mid'])
else:
return None
elif targetCurrency == 'PLN':
url = f"{apiInfo['API']['nbp']['url_exchange']}{sourceCurrency}/{apiInfo['API']['nbp']['json_format']}"
response = get_api_response(url)
if response is not None:
return float(response['rates'][0]['mid'])
else:
return None

plnValue = get_currency_exchange_rate(sourceCurrency, 'PLN', apiInfo)
targetValue = get_currency_exchange_rate('PLN', targetCurrency, apiInfo)
if plnValue and targetValue:
return plnValue * targetValue
else:
return None


def get_current_foreign_stock_price(stock, baseCurrency, apiInfo):
# foreign stock prices are checked using yahoo finance API replacement
try:
tickerInfo = yf.Ticker(stock).info
currency = tickerInfo['currency'] if tickerInfo['currency'] else "USD"
return exchange_base_currency(currency, baseCurrency,
(float(tickerInfo['dayLow']) + float(tickerInfo['dayHigh'])) / 2, apiInfo)
except Exception:
raise ValueError(f'Wrong symbol: {stock}')


def get_foreign_exchange(stock):
# foreign market name is checked using yahoo finance API replacement
try:
tickerInfo = yf.Ticker(stock).info
return tickerInfo['exchange']

except Exception:
raise ValueError(f'Wrong symbol: {stock}')


def get_current_pl_stock_price(stock, baseCurrency, apiInfo):
# due to lack of free API for checking pl stock prices is resolved using HTML scrapping
stooqUrl = apiInfo["API"]["stooq"]["url_stock"]
try:
response = requests.get(f"{stooqUrl}{stock.lower()}").text
soup = BeautifulSoup(response, 'html.parser')
price = float(soup.find(id="t1").find(id='f13').find('span').get_text())
return exchange_base_currency("PLN", baseCurrency, price, apiInfo)

except Exception:
raise ValueError(f'Wrong symbol: {stock}')


def calculate_percentage_difference(order1, order2):
return round(((order1 - order2) / order1) * 100, 2)


def include_taker_fee(cost, stock, operation):
if operation == NORMALIZED_OPERATIONS[1]:
return cost * (1 + stock.get_taker_fee())
elif operation == NORMALIZED_OPERATIONS[0]:
return cost * (1 - stock.get_taker_fee())


def calculate_order_value(price, amount):
return price * amount


def calculate_arbitrage(sourceStock, targetStock, currency, baseCurrency, fees):
offersToBuyFrom = sourceStock.get_offers(currency, baseCurrency)[NORMALIZED_OPERATIONS[1]]
offersToSellTo = targetStock.get_offers(currency, baseCurrency)[NORMALIZED_OPERATIONS[0]]
volume = 0.0
spentMoney = 0.0
gainedMoney = 0.0
transferFee = fees[sourceStock.get_name()][currency]
transferFeePaidNumber = 0
operationNumber = 0

while offersToBuyFrom and offersToSellTo and offersToBuyFrom[0][0] < offersToSellTo[0][0]:
operationNumber += 1
buyVolume = min(offersToBuyFrom[0][1], offersToSellTo[0][1])
sellVolume = buyVolume
if transferFee > 0:
if sellVolume > transferFee:
sellVolume -= transferFee
transferFee = 0
transferFeePaidNumber = operationNumber
else:
transferFee -= buyVolume
sellVolume = 0
buyValue = calculate_order_value(offersToBuyFrom[0][0], buyVolume)
buyCost = include_taker_fee(buyValue, sourceStock, NORMALIZED_OPERATIONS[1])
sellValue = calculate_order_value(offersToSellTo[0][0], sellVolume)
sellGain = include_taker_fee(sellValue, targetStock, NORMALIZED_OPERATIONS[0])

if buyCost < sellGain or transferFee > 0 or (transferFee == 0.0 and operationNumber == transferFeePaidNumber):
volume += buyVolume
spentMoney += buyCost
gainedMoney += sellGain

offersToSellTo[0][1] -= sellVolume
if offersToSellTo[0][1] == 0:
del offersToSellTo[0]
offersToBuyFrom[0][1] -= buyVolume
if offersToBuyFrom[0][1] == 0:
del offersToBuyFrom[0]

else:
break

if gainedMoney < spentMoney:
gainedMoney = spentMoney = 0
if spentMoney == 0 and gainedMoney == 0:
profitability = 0
else:
profitability = round(((gainedMoney - spentMoney) / spentMoney) * 100, 2)

return {'volume': volume, 'profitability': profitability, 'profit': round(gainedMoney - spentMoney, 5)}


def zad6(fstStock, sndStock, transferFees, currencyPairs):
resultsFromFstToSnd = []
resultsFromSndToFst = []
for currency in currencyPairs:
crypto = currency[0]
baseCurrency = currency[1]
print(f"Checking arbitrage for Cryptocurrency: {crypto}, base currency: {baseCurrency}")
offer1 = fstStock.get_offers(crypto, baseCurrency)
offer2 = sndStock.get_offers(crypto, baseCurrency)
if offer1 is not None and offer2 is not None:
if offer1.get(NORMALIZED_OPERATIONS[0], None) and offer1.get(NORMALIZED_OPERATIONS[1], None) \
and offer2.get(NORMALIZED_OPERATIONS[0], None) and offer2.get(NORMALIZED_OPERATIONS[1], None):
resultFrom1To2 = calculate_arbitrage(fstStock, sndStock, crypto, baseCurrency, transferFees)
resultsFromFstToSnd.append((fstStock.get_name(),
sndStock.get_name(), crypto, baseCurrency, resultFrom1To2))
resultFrom2To1 = calculate_arbitrage(sndStock, fstStock, crypto, baseCurrency, transferFees)
resultsFromSndToFst.append((sndStock.get_name(),
fstStock.get_name(), crypto, baseCurrency, resultFrom2To1))
else:
print("Orderbooks do not contain buying and selling prices!")
else:
print("Something gone wrong during data acquisition")
time.sleep(WAITING_TIME)
print()
return resultsFromFstToSnd + resultsFromSndToFst
39 changes: 39 additions & 0 deletions Lista5/cryptoApis/Bitbay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from jsonUtilities import load_data_from_json
from apisUtilities import get_api_response
from cryptoApis.CryptoApi import CryptoApiInterface


def parse_bitbay_currencies(jsonData):
result = []
if jsonData.get("items", None):
items = jsonData['items']
for entry in items.keys():
pair = entry.split('-')
result.append((pair[0], pair[1]))
return result


class Bitbay(CryptoApiInterface):
def __init__(self):
self.__data = load_data_from_json("apis.json")['API']['bitbay']
self.__fees = load_data_from_json("apis.json")['FEES']["bitbay_fees"]

def get_offers(self, currency, baseCurrency):
offers = get_api_response(f'{self.__data["url_orderbook"]}{currency}'
f'{baseCurrency}{self.__data["orderbook_ending"]}')
if offers is not None:
return offers

def get_taker_fee(self):
return self.__data['taker_fee']

def get_markets(self):
marketInfo = get_api_response(self.__data['url_markets'])
if marketInfo is not None:
return parse_bitbay_currencies(marketInfo)

def get_withdrawal_fees(self):
return self.__fees

def get_name(self):
return self.__data['name']
71 changes: 71 additions & 0 deletions Lista5/cryptoApis/Bitrex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from apisUtilities import NORMALIZED_OPERATIONS, get_api_response
from cryptoApis.CryptoApi import CryptoApiInterface
from jsonUtilities import load_data_from_json


def parse_bitrex_orderbook(jsonData):
resultDictionary = {}
if jsonData.get("result", None):
table = []
if jsonData["result"].get("buy", None):
pair = []
for dictionary in jsonData["result"]["buy"]:
pair.append(dictionary["Rate"])
pair.append(dictionary["Quantity"])
table.append(pair.copy())
pair.clear()
resultDictionary[NORMALIZED_OPERATIONS[0]] = table.copy()
table.clear()
if jsonData["result"].get("sell", None):
pair = []
for dictionary in jsonData["result"]["sell"]:
pair.append(dictionary["Rate"])
pair.append(dictionary["Quantity"])
table.append(pair.copy())
pair.clear()
resultDictionary[NORMALIZED_OPERATIONS[1]] = table.copy()
table.clear()
return resultDictionary


def parse_bitrex_currencies(jsonData):
result = []
if jsonData.get("result", None):
for entry in jsonData['result']:
if entry.get("MarketCurrency", None) and entry.get("BaseCurrency", None):
result.append((entry['MarketCurrency'], entry['BaseCurrency']))
return result


class Bitrex(CryptoApiInterface):
def __init__(self):
self.__data = load_data_from_json("apis.json")['API']['bitrex']

def get_offers(self, currency, baseCurrency):
offers = get_api_response(f'{self.__data["url_orderbook"]}{baseCurrency}'
f'{self.__data["orderbook_separator"]}'
f'{currency}{self.__data["orderbook_ending"]}')
if offers is not None:
return parse_bitrex_orderbook(offers)

def get_taker_fee(self):
return self.__data['taker_fee']

def get_markets(self):
marketInfo = get_api_response(self.__data['url_markets'])
if marketInfo is not None:
return parse_bitrex_currencies(marketInfo)

def get_withdrawal_fees(self):
fees = get_api_response(self.__data['url_currencies'])
dictionary = {}
if fees.get("result", None):
items = fees['result']
for entry in items:
if entry.get('Currency', None) and entry.get('TxFee', None):
dictionary[entry['Currency']] = entry["TxFee"]

return dictionary

def get_name(self):
return self.__data['name']
Loading