Skip to content

Commit

Permalink
Fix asset channels
Browse files Browse the repository at this point in the history
close #379
  • Loading branch information
StephanAkkerman committed Nov 13, 2023
1 parent bb468c5 commit d719902
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 77 deletions.
47 changes: 26 additions & 21 deletions src/cogs/commands/portfolio.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# > 3rd Party Dependencies
import pandas as pd
import ccxt
import traceback

# > Discord dependencies
from discord.ext import commands
Expand Down Expand Up @@ -81,7 +82,9 @@ async def add(
if exchange.lower() == "kucoin":
if not passphrase:
raise commands.UserInputError()
ccxt_exchange = ccxt.kucoin({"apiKey": key, "secret": secret})
ccxt_exchange = ccxt.kucoin(
{"apiKey": key, "secret": secret, "password": passphrase}
)

elif exchange.lower() == "binance":
ccxt_exchange = ccxt.binance({"apiKey": key, "secret": secret})
Expand Down Expand Up @@ -111,16 +114,16 @@ async def add(
# Check for duplicates based on a subset of columns that should be unique together
# Adjust the subset columns as per your data's unique constraints
duplicate_entries = util.vars.portfolio_db[
(util.vars.portfolio_db["user"] == new_data["user"].iloc[0])
& (util.vars.portfolio_db["exchange"] == new_data["exchange"].iloc[0])
& (util.vars.portfolio_db["key"] == new_data["key"].iloc[0])
& (util.vars.portfolio_db["secret"] == new_data["secret"].iloc[0])
(util.vars.portfolio_db["user"] == ctx.author.name)
& (util.vars.portfolio_db["exchange"] == exchange.lower())
& (util.vars.portfolio_db["key"] == key)
& (util.vars.portfolio_db["secret"] == secret)
]

if not duplicate_entries.empty:
# Handle the case where a duplicate is found
await ctx.respond("This portfolio already exists in the database.")
return
if not duplicate_entries.empty:
# Handle the case where a duplicate is found
await ctx.respond("This portfolio already exists in the database.")
return

# Update the databse
util.vars.portfolio_db = pd.concat(
Expand Down Expand Up @@ -153,16 +156,18 @@ async def remove(
`/portfolio remove (<exchange>)` if exchange is not specified, all your portfolio(s) will be removed.
"""

old_db = util.vars.portfolio_db
if exchange:
rows = old_db.index[
(old_db["id"] == ctx.author.id) & (old_db["exchange"] == exchange)
rows = util.vars.portfolio_db.index[
(util.vars.portfolio_db["id"] == ctx.author.id)
& (util.vars.portfolio_db["exchange"] == exchange)
].tolist()
else:
rows = old_db.index[old_db["id"] == ctx.author.id].tolist()
rows = util.vars.portfolio_db.index[
util.vars.portfolio_db["id"] == ctx.author.id
].tolist()

# Update database
self.update_portfolio_db(old_db.drop(rows))
self.update_portfolio_db(util.vars.portfolio_db.drop(rows))

await ctx.respond("Succesfully removed your portfolio from the database!")

Expand All @@ -179,14 +184,14 @@ async def show(
"""
`/portfolio show` to show your portfolio(s) in our database.
"""

db = util.vars.portfolio_db
rows = db.loc[db["id"] == ctx.author.id]
rows = util.vars.portfolio_db[util.vars.portfolio_db["id"] == ctx.author.id]
if not rows.empty:
for _, row in rows.iterrows():
await ctx.respond(
f"Exchange: {row['exchange']} \nKey: {row['key']} \nSecret: {row['secret']}"
)
response = f"Exchange: {row['exchange']} \nKey: {row['key']} \nSecret: {row['secret']}"

if row["passphrase"]:
response += f"\nPassphrase: {row['passphrase']}"
await ctx.respond(response)
else:
await ctx.respond("Your portfolio could not be found")

Expand Down Expand Up @@ -216,7 +221,7 @@ async def remove_error(self, ctx: commands.Context, error: Exception) -> None:

@show.error
async def show_error(self, ctx: commands.Context, error: Exception) -> None:
# print(traceback.format_exc())
print(traceback.format_exc())
if isinstance(error, commands.PrivateMessageOnly):
await ctx.respond(
"Please only use the `/portfolio` command in private messages for security reasons."
Expand Down
17 changes: 8 additions & 9 deletions src/cogs/loops/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ async def assets(self, portfolio_db: pd.DataFrame) -> None:

if portfolio_db.equals(util.vars.portfolio_db):
# Drop all crypto assets
old_db = util.vars.assets_db
if not old_db.empty:
crypto_rows = old_db.index[old_db["exchange"] != "stock"].tolist()

assets_db = old_db.drop(index=crypto_rows)
if not util.vars.assets_db.empty:
crypto_rows = util.vars.assets_db.index[
util.vars.assets_db["exchange"] != "stock"
].tolist()
assets_db = util.vars.assets_db.drop(index=crypto_rows)
else:
assets_db = pd.DataFrame(
columns=["asset", "buying_price", "owned", "exchange", "id", "user"]
Expand All @@ -101,7 +101,6 @@ async def assets(self, portfolio_db: pd.DataFrame) -> None:
for _, row in portfolio_db.iterrows():
# Add this data to the assets.db database
exch_data = await get_data(row)

assets_db = pd.concat([assets_db, exch_data], ignore_index=True)

# Ensure that the db knows the right types
Expand Down Expand Up @@ -230,14 +229,14 @@ async def post_assets(self) -> None:
"""

# Use the user name as channel
for name in util.vars.assets_db["user"].unique():
for id in util.vars.assets_db["id"].unique():
# Get the assets of this user
user_assets = util.vars.assets_db.loc[util.vars.assets_db["user"] == name]
user_assets = util.vars.assets_db.loc[util.vars.assets_db["id"] == id]

# Only post if there are assets
if not user_assets.empty:
# Get the Discord objects
channel = await self.get_user_channel(name)
channel = await self.get_user_channel(user_assets["user"].values[0])
disc_user = await self.get_user(user_assets)

e = discord.Embed(
Expand Down
9 changes: 5 additions & 4 deletions src/util/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ def __init__(self, bot: commands.Bot) -> None:

def set_portfolio_db(self):
util.vars.portfolio_db = get_db("portfolio")
if not util.vars.portfolio_db.empty:
util.vars.portfolio_db["id"] = util.vars.portfolio_db["id"].astype(np.int64)

def set_assets_db(self):
assets_db = get_db("assets")
if not assets_db.empty:
assets_db["id"] = assets_db["id"].astype(np.int64)
util.vars.assets_db = assets_db
util.vars.assets_db = get_db("assets")
if not util.vars.assets_db.empty:
util.vars.assets_db["id"] = util.vars.assets_db["id"].astype(np.int64)

def set_tweets_db(self):
util.vars.tweets_db = get_db("tweets")
Expand Down
100 changes: 57 additions & 43 deletions src/util/exchange_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,91 @@
import numpy as np
from util.vars import stables


async def get_data(row) -> pd.DataFrame:

exchange_info = {'apiKey': row["key"], 'secret': row["secret"]}

if row['exchange'] == 'binance':
exchange_info = {"apiKey": row["key"], "secret": row["secret"]}

if row["exchange"] == "binance":
exchange = ccxt.binance(exchange_info)
exchange.options['recvWindow'] = 60000
elif row['exchange'] == 'kucoin':
exchange_info['password'] = row['passphrase']
exchange.options["recvWindow"] = 60000
elif row["exchange"] == "kucoin":
exchange_info["password"] = row["passphrase"]
exchange = ccxt.kucoin(exchange_info)

try:
balances = await get_balance(exchange)

if balances == "invalid API key":
await exchange.close()
return "invalid API key"

# Create a list of dictionaries
owned = []

for symbol, amount in balances.items():
usd_val = await get_usd_price(exchange, symbol)
worth = amount * usd_val

if worth < 5:
continue

buying_price = await get_buying_price(exchange, symbol)

if buying_price != 0:
owned.append({
"asset": symbol,
"buying_price" : buying_price,
"owned": amount,
"exchange": exchange.id,
"id": row["id"],
"user": row["user"],
})

owned.append(
{
"asset": symbol,
"buying_price": buying_price,
"owned": amount,
"exchange": exchange.id,
"id": row["id"],
"user": row["user"],
}
)

df = pd.DataFrame(owned)

if not df.empty:
df = df.astype({"asset": str, "buying_price": float, "owned": float, "exchange": str, "id": np.int64, "user": str})

df = df.astype(
{
"asset": str,
"buying_price": float,
"owned": float,
"exchange": str,
"id": np.int64,
"user": str,
}
)

await exchange.close()
return df
except Exception as e:
await exchange.close()
print("Error in get_data(). Error:", e)


async def get_balance(exchange) -> dict:
try:
balances = await exchange.fetchBalance()
total_balance = balances['total']
total_balance = balances["total"]
if total_balance is None:
return "invalid API key"
return {k: v for k, v in total_balance.items() if v > 0}
except Exception:
return {}


async def get_usd_price(exchange, symbol) -> float:
"""
Returns the price of the symbol in USD
Symbol must be in the format 'BTC/USDT'
"""
"""
if symbol not in stables:
for usd in stables:
try:
price = await exchange.fetchTicker(f"{symbol}/{usd}")
if price != 0:
return float(price['last'])
return float(price["last"])
except ccxt.BadSymbol:
continue
except ccxt.ExchangeError as e:
Expand All @@ -83,37 +96,38 @@ async def get_usd_price(exchange, symbol) -> float:
continue
else:
try:
price = await exchange.fetchTicker(symbol + '/DAI')
return float(price['last'])
price = await exchange.fetchTicker(symbol + "/DAI")
return float(price["last"])
except ccxt.BadSymbol:
return 1

return 0

async def get_buying_price(exchange, symbol, full_sym : bool = False) -> float:

async def get_buying_price(exchange, symbol, full_sym: bool = False) -> float:
# Maybe try different quote currencies when returned list is empty
if symbol in stables:
return 1
symbol = symbol + '/USDT' if not full_sym else symbol

symbol = symbol + "/USDT" if not full_sym else symbol

params = {}
if exchange.id == 'kucoin':
params = {"side" : 'buy'}
if exchange.id == "kucoin":
params = {"side": "buy"}
try:
trades = await exchange.fetchClosedOrders(symbol, params = params)
trades = await exchange.fetchClosedOrders(symbol, params=params)
except ccxt.BadSymbol:
return 0
except ccxt.RequestTimeout:
return 0
if type(trades) == list:
if len(trades) > 1:
if exchange.id == 'binance':
if exchange.id == "binance":
# Filter list for side:buy
trades = [trade for trade in trades if trade['info']['side'] == 'BUY']
trades = [trade for trade in trades if trade["info"]["side"] == "BUY"]
if len(trades) == 0:
return 0
return float(trades[-1]['price'])
return 0

return float(trades[-1]["price"])

return 0

0 comments on commit d719902

Please sign in to comment.