Coinbase pro api (#743)

* Add separate view for finbrain for crypto curreny sentiment analysis

* Move json with symbols to separate directory, little refactoring, update readme

* Add screenshot of finbrain for PolkaDot

* Coinbase menu added

* Add more methods for coinbase

* Add coinbase views, coinbase models. Add coinbase to load, chart, find commands. Add coinbase to controller

* Add auth client for coinbase pro endpoints that needs auth

* Add more views for coinbase to crypto menu

* Add some print tests for Coinbase view

* Add 2 more tests for coinbase view

* Update README with Coinbase API keys

* Cleaning empty lines, spaces etc.

* Use os.path.join in imports, change column names in one normalized df

* Move Coinbase to portfolio brokers. Adjust tests, and imports

* precommit config return to prev ver

* Fix coinbase accounts command, del column filter for deposits
This commit is contained in:
jp
2021-09-13 20:07:00 +02:00
committed by GitHub
parent 6a2792ea8e
commit 0b554c6893
22 changed files with 2436 additions and 222 deletions

View File

@@ -26,7 +26,7 @@ repos:
rev: 'v0.812'
hooks:
- id: mypy
args: [ --ignore-missing-imports ]
args: [ --ignore-missing-imports ]
- repo: https://github.com/pre-commit/mirrors-pylint
rev: 'v3.0.0a4'
hooks:

2
README.md vendored
View File

@@ -266,6 +266,7 @@ These are the ones where a key is necessary:
* SentimentInvestor: https://sentimentinvestor.com
* Tradier: https://developer.tradier.com/getting_started
* Twitter: https://developer.twitter.com
* Coinbase Pro API: https://docs.pro.coinbase.com/
When these are obtained, don't forget to update [config_terminal.py](/gamestonk_terminal/config_terminal.py).
@@ -288,6 +289,7 @@ Alternatively, you can also set them to the following environment variables:
| [SentimentInvestor](https://sentimentinvestor.com) | GT_API_SENTIMENTINVESTOR_TOKEN <br> GT_API_SENTIMENTINVESTOR_KEY |
| [Tradier](https://developer.tradier.com) | GT_TRADIER_TOKEN |
| [Twitter](https://developer.twitter.com) | GT_API_TWITTER_KEY <br/> GT_API_TWITTER_SECRET_KEY <br/> GT_API_TWITTER_BEARER_TOKEN |
| [Coinbase](https://docs.pro.coinbase.com/) | GT_API_COINBASE_KEY <br/> GT_API_COINBASE_SECRET <br/> GT_API_COINBASE_PASS_PHRASE |
Example:
```

View File

@@ -85,3 +85,8 @@ API_SENTIMENTINVESTOR_KEY = os.getenv("GT_API_SENTIMENTINVESTOR_KEY") or "REPLAC
API_SENTIMENTINVESTOR_TOKEN = (
os.getenv("GT_API_SENTIMENTINVESTOR_TOKEN") or "REPLACE_ME"
)
# https://pro.coinbase.com/profile/api
API_COINBASE_KEY = os.getenv("GT_API_COINBASE_KEY") or "REPLACE_ME"
API_COINBASE_SECRET = os.getenv("GT_API_COINBASE_SECRET") or "REPLACE_ME"
API_COINBASE_PASS_PHRASE = os.getenv("GT_API_COINBASE_PASS_PHRASE") or "REPLACE_ME"

View File

@@ -0,0 +1,167 @@
"""Coinbase helpers model"""
__docformat__ = "numpy"
import argparse
import binascii
from typing import Optional, Any, Union
import hmac
import hashlib
import time
import base64
import requests
from requests.auth import AuthBase
import gamestonk_terminal.config_terminal as cfg
class CoinbaseProAuth(AuthBase):
"""Authorize CoinbasePro requests. Source: https://docs.pro.coinbase.com/?python#signing-a-message"""
def __init__(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase
def __call__(self, request):
timestamp = str(time.time())
message = timestamp + request.method + request.path_url + (request.body or "")
message = message.encode("ascii")
try:
hmac_key = base64.b64decode(self.secret_key)
signature = hmac.new(hmac_key, message, hashlib.sha256)
signature_b64 = base64.b64encode(signature.digest())
except binascii.Error:
signature_b64 = ""
request.headers.update(
{
"CB-ACCESS-SIGN": signature_b64,
"CB-ACCESS-TIMESTAMP": timestamp,
"CB-ACCESS-KEY": self.api_key,
"CB-ACCESS-PASSPHRASE": self.passphrase,
"Content-Type": "application/json",
}
)
return request
class CoinbaseRequestException(Exception):
"""Coinbase Request Exception object"""
def __init__(self, message: str):
super().__init__(message)
self.message = message
def __str__(self) -> str:
return "CoinbaseRequestException: %s" % self.message
class CoinbaseApiException(Exception):
"""Coinbase API Exception object"""
def __init__(self, message: str):
super().__init__(message)
self.message = message
def __str__(self) -> str:
return "CoinbaseApiException: %s" % self.message
def check_validity_of_product(product_id: str) -> str:
"""Helper method that checks if provided product_id exists. It's a pair of coins in format COIN-COIN.
If product exists it return it, in other case it raise an error. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
Returns
-------
str
pair of coins in format COIN-COIN
"""
products = [pair["id"] for pair in make_coinbase_request("/products")]
if product_id.upper() not in products:
raise argparse.ArgumentTypeError(
f"You provided wrong pair of coins {product_id}. "
f"It should be provided as a pair in format COIN-COIN e.g UNI-USD"
)
return product_id.upper()
def make_coinbase_request(
endpoint, params: Optional[dict] = None, auth: Optional[Any] = None
) -> dict:
"""Request handler for Coinbase Pro Api. Prepare a request url, params and payload and call endpoint.
[Source: Coinbase]
Parameters
----------
endpoint: str
Endpoint path e.g /products
params: dict
Parameter dedicated for given endpoint
auth: any
Api credentials for purpose of using endpoints that needs authentication
Returns
-------
dict
response from Coinbase Pro Api
"""
url = "https://api.pro.coinbase.com"
response = requests.get(url + endpoint, params=params, auth=auth)
if not 200 <= response.status_code < 300:
raise CoinbaseApiException("Invalid Authentication: %s" % response.text)
try:
return response.json()
except ValueError as e:
raise CoinbaseRequestException("Invalid Response: %s" % response.text) from e
def _get_account_coin_dict() -> dict:
"""Helper method that returns dictionary with all symbols and account ids in dictionary format. [Source: Coinbase]
Returns
-------
dict:
Your accounts in coinbase
{'1INCH': '0c29b708-d73b-4e1c-a58c-9c261cb4bedb', 'AAVE': '0712af66-c069-45b5-84ae-7b2347c2fd24', ..}
"""
auth = CoinbaseProAuth(
cfg.API_COINBASE_KEY, cfg.API_COINBASE_SECRET, cfg.API_COINBASE_PASS_PHRASE
)
accounts = make_coinbase_request("/accounts", auth=auth)
return {acc["currency"]: acc["id"] for acc in accounts}
def _check_account_validity(account: str) -> Union[str, Any]:
"""Helper methods that checks if given account exists. [Source: Coinbase]
Parameters
----------
account: str
coin or account id
Returns
-------
Union[str, Any]
Your account id or None
"""
accounts = _get_account_coin_dict()
if account in list(accounts.keys()):
return accounts[account]
if account in list(accounts.values()):
return account
print("Wrong account id or coin symbol")
return None

View File

@@ -38,10 +38,9 @@ from gamestonk_terminal.cryptocurrency.cryptocurrency_helpers import (
plot_chart,
)
from gamestonk_terminal.cryptocurrency.report import report_controller
from gamestonk_terminal.cryptocurrency.due_diligence import binance_model
from gamestonk_terminal.cryptocurrency.due_diligence import coinbase_model
from gamestonk_terminal.cryptocurrency.onchain import onchain_controller
from gamestonk_terminal.cryptocurrency.due_diligence.binance_model import (
show_available_pairs_for_given_symbol,
)
import gamestonk_terminal.config_terminal as cfg
@@ -67,6 +66,7 @@ class CryptoController:
"bin": "Binance",
"cg": "CoinGecko",
"cp": "CoinPaprika",
"cb": "Coinbase",
}
DD_VIEWS_MAPPING = {
@@ -178,7 +178,7 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="load",
description="Load crypto currency to perform analysis on. "
"Available data sources are CoinGecko, CoinPaprika, and Binance"
"Available data sources are CoinGecko, CoinPaprika, Binance, Coinbase"
"By default main source used for analysis is CoinGecko (cg). To change it use --source flag",
)
@@ -196,7 +196,7 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
"--source",
help="Source of data",
dest="source",
choices=("cp", "cg", "bin"),
choices=("cp", "cg", "bin", "cb"),
default="cg",
required=False,
)
@@ -236,11 +236,8 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="chart",
description="""Loads data for technical analysis. You can specify currency vs which you want
to show chart and also number of days to get data for.
By default currency: usd and days: 30.
E.g. if you loaded in previous step Ethereum and you want to see it's price vs btc
in last 90 days range use `ta --vs btc --days 90`""",
description="""Display chart for loaded coin. You can specify currency vs which you want
to show chart and also number of days to get data for.""",
)
if self.source == "cp":
@@ -295,7 +292,9 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
"1month": client.KLINE_INTERVAL_1MONTH,
}
_, quotes = show_available_pairs_for_given_symbol(self.current_coin)
_, quotes = binance_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser.add_argument(
"--vs",
@@ -325,13 +324,61 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
type=check_positive,
)
if self.source == "cb":
interval_map = {
"1min": 60,
"5min": 300,
"15min": 900,
"1hour": 3600,
"6hour": 21600,
"24hour": 86400,
"1day": 86400,
}
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"-i",
"--interval",
help="Interval to get data",
choices=list(interval_map.keys()),
dest="interval",
default="1day",
type=str,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
default=100,
help="Number to get",
type=check_positive,
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if self.source == "bin":
if self.source in ["bin", "cb"]:
limit = ns_parser.limit
interval = ns_parser.interval
days = 0
@@ -424,7 +471,9 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
"1month": client.KLINE_INTERVAL_1MONTH,
}
_, quotes = show_available_pairs_for_given_symbol(self.current_coin)
_, quotes = binance_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
@@ -453,13 +502,109 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
type=check_positive,
)
if self.source == "cb":
interval_map = {
"1min": 60,
"5min": 300,
"15min": 900,
"1hour": 3600,
"6hour": 21600,
"24hour": 86400,
"1day": 86400,
}
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"-i",
"--interval",
help="Interval to get data",
choices=list(interval_map.keys()),
dest="interval",
default="1day",
type=str,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
default=100,
help="Number to get",
type=check_positive,
)
if self.source == "cb":
interval_map = {
"1min": 60,
"5min": 300,
"15min": 900,
"1hour": 3600,
"6hour": 21600,
"24hour": 86400,
"1day": 86400,
}
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"-i",
"--interval",
help="Interval to get data",
choices=list(interval_map.keys()),
dest="interval",
default="1day",
type=str,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
default=100,
help="Number to get",
type=check_positive,
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if self.source == "bin":
if self.source in ["bin", "cb"]:
limit = ns_parser.limit
interval = ns_parser.interval
days = 0
@@ -599,7 +744,7 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Find similar coin by coin name,symbol or id. If you don't remember exact name or id of the Coin at CoinGecko,
Binance or CoinPaprika you can use this command to display coins with similar name, symbol or id
Binance, Coinbase or CoinPaprika you can use this command to display coins with similar name, symbol or id
to your search query.
Example of usage: coin name is something like "polka". So I can try: find -c polka -k name -t 25
It will search for coin that has similar name to polka and display top 25 matches.
@@ -639,7 +784,7 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
parser.add_argument(
"--source",
dest="source",
choices=["cp", "cg", "bin"],
choices=["cp", "cg", "bin", "cb"],
default="cg",
help="Source of data.",
type=str,

View File

@@ -2,9 +2,11 @@
__docformat__ = "numpy"
import os
import json
from typing import Tuple, Any, Optional, Union
import difflib
import pandas as pd
import numpy as np
from binance.client import Client
import matplotlib.pyplot as plt
from tabulate import tabulate
@@ -13,15 +15,12 @@ from gamestonk_terminal.helper_funcs import (
plot_autoscale,
export_data,
)
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal.cryptocurrency.due_diligence import (
pycoingecko_model,
coinpaprika_model,
)
from gamestonk_terminal.cryptocurrency.discovery.pycoingecko_model import (
get_coin_list,
get_mapping_matrix_for_binance,
load_binance_map,
)
from gamestonk_terminal.cryptocurrency.discovery.pycoingecko_model import get_coin_list
from gamestonk_terminal.cryptocurrency.overview.coinpaprika_model import (
get_list_of_coins,
)
@@ -30,11 +29,35 @@ from gamestonk_terminal.cryptocurrency.due_diligence.binance_model import (
show_available_pairs_for_given_symbol,
plot_candles,
)
from gamestonk_terminal.cryptocurrency.due_diligence import coinbase_model
import gamestonk_terminal.config_terminal as cfg
from gamestonk_terminal.feature_flags import USE_ION as ion
from gamestonk_terminal import feature_flags as gtff
def _load_coin_map(file_name: str) -> pd.DataFrame:
if file_name.split(".")[1] != "json":
raise TypeError("Please load json file")
current_dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(current_dir, "data", file_name)
with open(path, encoding="utf8") as f:
coins = json.load(f)
coins_df = pd.Series(coins).reset_index()
coins_df.columns = ["symbol", "id"]
return coins_df
def load_binance_map():
return _load_coin_map("binance_gecko_map.json")
def load_coinbase_map():
return _load_coin_map("coinbase_gecko_map.json")
def prepare_all_coins_df() -> pd.DataFrame:
"""Helper method which loads coins from all sources: CoinGecko, CoinPaprika, Binance and
merge those coins on keys:
@@ -47,12 +70,17 @@ def prepare_all_coins_df() -> pd.DataFrame:
CoinGecko - id for coin in CoinGecko API: uniswap
CoinPaprika - id for coin in CoinPaprika API: uni-uniswap
Binance - symbol (baseAsset) for coin in Binance API: UNI
Coinbase - symbol for coin in Coinbase Pro API e.g UNI
Symbol: uni
"""
gecko_coins_df = get_coin_list()
paprika_coins_df = get_list_of_coins()
# TODO: Think about scheduled job, that once a day will update data
binance_coins_df = load_binance_map().rename(columns={"symbol": "Binance"})
coinbase_coins_df = load_coinbase_map().rename(columns={"symbol": "Coinbase"})
gecko_paprika_coins_df = pd.merge(
gecko_coins_df, paprika_coins_df, on="name", how="left"
)
@@ -72,7 +100,15 @@ def prepare_all_coins_df() -> pd.DataFrame:
inplace=True,
)
return df_merged[["CoinGecko", "CoinPaprika", "Binance", "Symbol"]]
df_merged = pd.merge(
left=df_merged,
right=coinbase_coins_df,
left_on="CoinGecko",
right_on="id",
how="left",
)
return df_merged[["CoinGecko", "CoinPaprika", "Binance", "Coinbase", "Symbol"]]
def _create_closest_match_df(
@@ -109,7 +145,7 @@ def load(
coin: str,
source: str,
) -> Tuple[Union[Optional[str], pycoingecko_model.Coin], Any]:
"""Load cryptocurrency from given source. Available sources are: CoinGecko, CoinPaprika and Binance.
"""Load cryptocurrency from given source. Available sources are: CoinGecko, CoinPaprika, Coinbase and Binance.
Loading coin from Binance and CoinPaprika means validation if given coins exists in chosen source,
if yes then id of the coin is returned as a string.
@@ -151,6 +187,17 @@ def load(
current_coin, _ = coinpaprika_model.validate_coin(coin, paprika_coins_dict)
return current_coin, source
if source == "cb":
coinbase_coin = coin.upper()
current_coin, pairs = coinbase_model.show_available_pairs_for_given_symbol(
coinbase_coin
)
if len(pairs) > 0:
print(f"Coin found : {current_coin}\n")
else:
print(f"Couldn't find coin with symbol {current_coin}\n")
return current_coin, source
return current_coin, None
@@ -158,7 +205,7 @@ def load(
def find(source: str, coin: str, key: str, top: int, export: str) -> None:
"""Find similar coin by coin name,symbol or id.
If you don't remember exact name or id of the Coin at CoinGecko or CoinPaprika
If you don't remember exact name or id of the Coin at CoinGecko CoinPaprika, Binance or Coinbase
you can use this command to display coins with similar name, symbol or id to your search query.
Example of usage: coin name is something like "polka". So I can try: find -c polka -k name -t 25
It will search for coin that has similar name to polka and display top 25 matches.
@@ -175,7 +222,7 @@ def find(source: str, coin: str, key: str, top: int, export: str) -> None:
key: str
Searching key (symbol, id, name)
source: str
Data source of coins. CoinGecko (cg) or CoinPaprika (cp) or Binance (bin)
Data source of coins. CoinGecko (cg) or CoinPaprika (cp) or Binance (bin), Coinbase (cb)
export : str
Export dataframe data to csv,json,xlsx file
"""
@@ -183,76 +230,35 @@ def find(source: str, coin: str, key: str, top: int, export: str) -> None:
if source == "cg":
coins_df = get_coin_list()
coins_list = coins_df[key].to_list()
if key in ["symbol", "id"]:
coin = coin.lower()
sim = difflib.get_close_matches(coin, coins_list, top)
df = pd.Series(sim).to_frame().reset_index()
df.columns = ["index", key]
coins_df.drop("index", axis=1, inplace=True)
df = df.merge(coins_df, on=key)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".1f",
showindex=False,
tablefmt="fancy_grid",
)
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"find",
df,
)
elif source == "cp":
coins_df = get_list_of_coins()
coins_list = coins_df[key].to_list()
keys = {"name": "title", "symbol": "upper", "id": "lower"}
key = keys[key]
coin = getattr(coin, str(key))()
func_key = keys[key]
coin = getattr(coin, str(func_key))()
sim = difflib.get_close_matches(coin, coins_list, top)
df = pd.Series(sim).to_frame().reset_index()
df.columns = ["index", key]
df = df.merge(coins_df, on=key)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".1f",
showindex=False,
tablefmt="fancy_grid",
)
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"find",
df,
)
elif source == "bin":
# TODO: Fix it in future. Determine if user looks for symbol like ETH or ethereum
if len(coin) > 4:
if len(coin) > 5:
key = "id"
coins_df_gecko = get_coin_list()
coins_bin = get_mapping_matrix_for_binance()
coins_df_bin = pd.Series(coins_bin).reset_index()
coins_df_bin.columns = ["symbol", "id"]
coins_df_bin = load_binance_map()
coins = pd.merge(
coins_df_bin, coins_df_gecko[["id", "name"]], how="left", on="id"
)
@@ -263,29 +269,48 @@ def find(source: str, coin: str, key: str, top: int, export: str) -> None:
df.columns = ["index", key]
df = df.merge(coins, on=key)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".1f",
showindex=False,
tablefmt="fancy_grid",
)
)
else:
print(df.to_string, "\n")
elif source == "cb":
if len(coin) > 5:
key = "id"
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"find",
df,
coins_df_gecko = get_coin_list()
coins_df_bin = load_coinbase_map()
coins = pd.merge(
coins_df_bin, coins_df_gecko[["id", "name"]], how="left", on="id"
)
coins_list = coins[key].to_list()
sim = difflib.get_close_matches(coin, coins_list, top)
df = pd.Series(sim).to_frame().reset_index()
df.columns = ["index", key]
df = df.merge(coins, on=key)
else:
print("Couldn't execute find methods for CoinPaprika, Binance or CoinGecko")
print("")
print(
"Couldn't execute find methods for CoinPaprika, Binance, Coinbase or CoinGecko\n"
)
df = pd.DataFrame()
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".1f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"find",
df,
)
def display_all_coins(
@@ -293,7 +318,7 @@ def display_all_coins(
) -> None:
"""Find similar coin by coin name,symbol or id.
If you don't remember exact name or id of the Coin at CoinGecko or CoinPaprika
If you don't remember exact name or id of the Coin at CoinGecko, CoinPaprika, Coinbase, Binance
you can use this command to display coins with similar name, symbol or id to your search query.
Example of usage: coin name is something like "polka". So I can try: find -c polka -k name -t 25
It will search for coin that has similar name to polka and display top 25 matches.
@@ -307,7 +332,7 @@ def display_all_coins(
coin: str
Cryptocurrency
source: str
Data source of coins. CoinGecko (cg) or CoinPaprika (cp) or Binance (bin)
Data source of coins. CoinGecko (cg) or CoinPaprika (cp) or Binance (bin), Coinbase (cb)
skip: int
Skip N number of records
show_all: bool
@@ -315,12 +340,13 @@ def display_all_coins(
export : str
Export dataframe data to csv,json,xlsx file
"""
sources = ["cg", "cp", "bin", "cb"]
limit, cutoff = 30, 0.75
coins_func_map = {
"cg": get_coin_list,
"cp": get_list_of_coins,
"bin": load_binance_map,
"cb": load_coinbase_map,
}
if show_all:
@@ -330,7 +356,7 @@ def display_all_coins(
else:
df = prepare_all_coins_df()
elif not source or source not in ["cg", "cp", "bin"]:
elif not source or source not in sources:
df = prepare_all_coins_df()
cg_coins_list = df["CoinGecko"].to_list()
sim = difflib.get_close_matches(coin.lower(), cg_coins_list, limit, cutoff)
@@ -362,6 +388,17 @@ def display_all_coins(
df = df[["index", "symbol", "name"]]
df.columns = ["index", "id", "name"]
elif source == "cb":
coins_df_gecko = get_coin_list()
coins_df_cb = load_coinbase_map()
coins_df_cb.columns = ["symbol", "id"]
coins_df = pd.merge(
coins_df_cb, coins_df_gecko[["id", "name"]], how="left", on="id"
)
df = _create_closest_match_df(coin.lower(), coins_df, limit, cutoff)
df = df[["index", "symbol", "name"]]
df.columns = ["index", "id", "name"]
else:
df = pd.DataFrame(columns=["index", "id", "symbol"])
print("Couldn't find any coins")
@@ -417,7 +454,7 @@ def load_ta_data(
Returns
----------
Tuple[pd.DataFrame, str]
dataframe with prices
df with prices
quoted currency
"""
@@ -505,6 +542,24 @@ def load_ta_data(
df.index.name = "date"
return df, currency
if source == "cb":
assert isinstance(coin, str)
coin, currency = coin.upper(), currency.upper()
pair = f"{coin}-{currency}"
if coinbase_model.check_validity_of_product(pair):
print(f"{coin} loaded vs {currency}")
df = coinbase_model.get_candles(
product_id=pair,
interval=interval or "24hour",
).head(limit)
df_coin = df.set_index(pd.to_datetime(df["Time0"], unit="s")).drop(
"Time0", axis=1
)
return df_coin, currency
return pd.DataFrame(), currency
@@ -668,3 +723,79 @@ def plot_chart(
plt.ion()
plt.show()
print("")
if source == "cb":
assert isinstance(coin, str)
coin, currency = coin.upper(), currency.upper()
pair = f"{coin}-{currency}"
if coinbase_model.check_validity_of_product(pair):
print(f"{coin} loaded vs {currency}")
df = coinbase_model.get_candles(
product_id=pair,
interval=interval or "24hour",
).head(limit)
df = df.astype(float).iloc[:, :6]
df.sort_values(by="Time0", inplace=True, ascending=True)
df = df.set_index(pd.to_datetime(df["Time0"], unit="s")).drop(
"Time0", axis=1
)
title = (
f"\n{coin}/{currency} from {df.index[0].strftime('%Y/%m/%d')} to {df.index[-1].strftime('%Y/%m/%d')}",
)
df["Volume"] = df["Volume"] / 1_000
mpf.plot(
df,
type="candle",
volume=True,
ylabel_lower="Volume [1K]",
title=str(title[0]) if isinstance(title, tuple) else title,
xrotation=20,
style="binance",
figratio=(10, 7),
figscale=1.10,
figsize=(plot_autoscale()),
update_width_config=dict(
candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0
),
)
if ion:
plt.ion()
plt.show()
print("")
def plot_order_book(bids: np.array, asks: np.array, coin: str) -> None:
"""
Plots Bid/Ask. Can be used for Coinbase and Binance
Parameters
----------
bids : np.array
array of bids with columns: price, size, cumulative size
asks : np.array
array of asks with columns: price, size, cumulative size
coin : str
Coin being plotted
"""
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
ax.plot(bids[:, 0], bids[:, 2], "g", label="bids")
ax.fill_between(bids[:, 0], bids[:, 2], color="g", alpha=0.4)
ax.plot(asks[:, 0], asks[:, 2], "r", label="asks")
ax.fill_between(asks[:, 0], asks[:, 2], color="r", alpha=0.4)
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.minorticks_on()
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
plt.legend(loc=0)
plt.xlabel("Price")
plt.ylabel("Size (Coins) ")
plt.title(f"Order Book for {coin}")
if ion:
plt.ion()
plt.show()
print("")

View File

@@ -0,0 +1,101 @@
{
"BTC": "bitcoin",
"ETH": "ethereum",
"SOL": "solana",
"ADA": "cardano",
"ALGO": "algorand",
"USDT": "tether",
"WBTC": "wrapped-bitcoin",
"LTC": "litecoin",
"XTZ": "tezos",
"LINK": "chainlink",
"MATIC": "matic-network",
"DAI": "dai",
"DOT": "polkadot",
"ATOM": "cosmos",
"BCH": "bitcoin-cash",
"XLM": "stellar",
"DOGE": "dogecoin",
"FIL": "filecoin",
"USDC": "usd-coin",
"EOS": "eos",
"GRT": "the-graph",
"ETC": "ethereum-classic",
"AXS": "axie-infinity",
"YFI": "yearn-finance",
"UNI": "uniswap",
"ICP": "internet-computer",
"AAVE": "aave",
"OMG": "omisego",
"DASH": "dash",
"QNT": "quant-network",
"UST": "terrausd",
"SUSHI": "sushi",
"ACH": "alchemy-pay",
"MKR": "maker",
"COMP": "compound-governance-token",
"FET": "fetch-ai",
"CGLD": "celo",
"AMP": "amp-token",
"WLUNA": "wrapped-terra",
"CRV": "curve-dao-token",
"ZRX": "0x",
"POLY": "polymath",
"LRC": "loopring",
"SNX": "havven",
"ENJ": "enjincoin",
"ANKR": "ankr",
"XYO": "xyo-network",
"REN": "republic-protocol",
"MANA": "decentraland",
"STORJ": "storj",
"PAX": "paxos-standard",
"CHZ": "chiliz",
"1INCH": "1inch",
"ZEC": "zcash",
"CVC": "civic",
"SKL": "skale",
"RLC": "iexec-rlc",
"UMA": "uma",
"BAND": "band-protocol",
"LPT": "livepeer",
"NKN": "nkn",
"BAT": "basic-attention-token",
"TRB": "tellor",
"IOTX": "iotex",
"MASK": "mask-network",
"KEEP": "keep-network",
"BAL": "balancer",
"RLY": "rally-2",
"NU": "nucypher",
"BOND": "barnbridge",
"CTSI": "cartesi",
"OGN": "origin-protocol",
"OXT": "orchid-protocol",
"COTI": "coti",
"MIR": "mirror-protocol",
"KNC": "kyber-network",
"TRU": "truefi",
"GTC": "gitcoin",
"NMR": "numeraire",
"YFII": "yfii-finance",
"MLN": "melon",
"REQ": "request-network",
"CLV": "clover-finance",
"FARM": "harvest-finance",
"RAD": "radicle",
"FORTH": "ampleforth-governance-token",
"DNT": "district0x",
"RGT": "rari-governance-token",
"ORN": "orion-protocol",
"QUICK": "quick",
"PLA": "playdapp",
"TRIBE": "tribe-2",
"BNT": "bancor",
"LOOM": "loom-network",
"DDX": "derivadao",
"GNT": "golem",
"RAI": "rai",
"REP": "augur",
"SHIB": "shiba-inu"
}

View File

@@ -1,8 +1,6 @@
"""CoinGecko model"""
__docformat__ = "numpy"
import os
import json
import pandas as pd
from pycoingecko import CoinGeckoAPI
from gamestonk_terminal.cryptocurrency.dataframe_helpers import (
@@ -469,9 +467,16 @@ def get_coins_for_given_exchange(exchange_id: str = "binance", page: int = 1) ->
return binance_coins["tickers"]
def get_mapping_matrix_for_binance() -> dict:
def get_mapping_matrix_for_exchange(exchange_id: str, pages: int = 12) -> dict:
"""Creates a matrix with all coins available on Binance with corresponding coingecko coin_id. [Source: CoinGecko]
Parameters
----------
exchange_id: str
id of exchange: binance
pages: int
number of pages. One page contains 100 records
Returns
-------
dict
@@ -479,28 +484,10 @@ def get_mapping_matrix_for_binance() -> dict:
"""
coins_dct = {}
for i in range(12):
coins = get_coins_for_given_exchange(page=i)
for i in range(pages):
coins = get_coins_for_given_exchange(exchange_id=exchange_id, page=i)
for coin in coins:
bin_symbol, gecko_id = coin["base"], coin["coin_id"]
if bin_symbol not in coins_dct:
coins_dct[bin_symbol] = gecko_id
return coins_dct
def load_binance_map() -> pd.DataFrame:
"""Loads a data with all coins available on Binance with corresponding coingecko coin_ids. [Source: CoinGecko]
Returns
-------
pd.DataFrame
Columns: symbol, id
"""
path = os.path.abspath(__file__ + "/../../")
with open(path + "/data/binance_gecko_map.json", encoding="utf8") as f:
coins = json.load(f)
coins_df = pd.Series(coins).reset_index()
coins_df.columns = ["symbol", "id"]
return coins_df

View File

@@ -8,12 +8,10 @@ from collections import defaultdict
import matplotlib.pyplot as plt
import mplfinance as mpf
import pandas as pd
import numpy as np
from binance.client import Client
from binance.exceptions import BinanceAPIException
import gamestonk_terminal.config_terminal as cfg
from gamestonk_terminal.helper_funcs import plot_autoscale
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal.feature_flags import USE_ION as ion
@@ -125,39 +123,6 @@ def show_available_pairs_for_given_symbol(
return None, []
def plot_order_book(bids: np.array, asks: np.array, coin: str) -> None:
"""
Plots Bid/Ask. [Source: Binance]
Parameters
----------
bids : np.array
array of bids with columns: price, size, cumulative size
asks : np.array
array of asks with columns: price, size, cumulative size
coin : str
Coin being plotted
"""
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
ax.plot(bids[:, 0], bids[:, 2], "g", label="bids")
ax.fill_between(bids[:, 0], bids[:, 2], color="g", alpha=0.4)
ax.plot(asks[:, 0], asks[:, 2], "r", label="asks")
ax.fill_between(asks[:, 0], asks[:, 2], color="r", alpha=0.4)
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.minorticks_on()
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
plt.legend(loc=0)
plt.xlabel("Price")
plt.ylabel("Size (Coins) ")
plt.title(f"Order Book for {coin}")
if ion:
plt.ion()
plt.show()
print("")
def plot_candles(candles_df: pd.DataFrame, title: str) -> None:
"""Plot candle chart from dataframe. [Source: Binance]

View File

@@ -9,9 +9,7 @@ import pandas as pd
from gamestonk_terminal.helper_funcs import (
export_data,
)
from gamestonk_terminal.cryptocurrency.due_diligence.binance_model import (
plot_order_book,
)
from gamestonk_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book
import gamestonk_terminal.config_terminal as cfg
from gamestonk_terminal import feature_flags as gtff

View File

@@ -0,0 +1,193 @@
"""Coinbase model"""
__docformat__ = "numpy"
from typing import Optional, Any, Tuple
import pandas as pd
import numpy as np
from gamestonk_terminal.cryptocurrency.coinbase_helpers import (
check_validity_of_product,
make_coinbase_request,
)
def show_available_pairs_for_given_symbol(symbol: str = "ETH") -> Tuple[str, list]:
pairs = make_coinbase_request("/products")
df = pd.DataFrame(pairs)[["base_currency", "quote_currency"]]
if not isinstance(symbol, str):
print(
f"You did not provide correct symbol {symbol}. Symbol needs to be a string.\n"
)
return symbol, []
coin_df = df[df["base_currency"] == symbol.upper()]
return symbol, coin_df["quote_currency"].to_list()
def get_trading_pair_info(product_id: str) -> pd.DataFrame:
"""Get information about chosen trading pair. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
Returns
-------
pd.DataFrame
Basic information about given trading pair
"""
product_id = check_validity_of_product(product_id)
pair = make_coinbase_request(f"/products/{product_id}")
df = pd.Series(pair).to_frame().reset_index()
df.columns = ["Metric", "Value"]
print(df)
return df
def get_order_book(product_id: str) -> Tuple[np.array, np.array, str, dict]:
"""Get orders book for chosen trading pair. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
Returns
-------
Tuple[np.array, np.array, str, dict]
array with bid prices, order sizes and cumulative order sizes
array with ask prices, order sizes and cumulative order sizes
trading pair
dict with raw data
"""
# TODO: Order with price much higher then current price. E.g current price 200 USD, sell order with 10000 USD
# makes chart look very ugly (bad scaling). Think about removing outliers or add log scale ?
product_id = check_validity_of_product(product_id)
market_book = make_coinbase_request(f"/products/{product_id}/book?level=2")
size = min(
len(market_book["bids"]), len(market_book["asks"])
) # arrays needs to have equal size.
market_book["bids"] = market_book["bids"][:size]
market_book["asks"] = market_book["asks"][:size]
market_book.pop("sequence")
bids = np.asarray(market_book["bids"], dtype=float)[:size]
asks = np.asarray(market_book["asks"], dtype=float)[:size]
bids = np.insert(bids, 3, (bids[:, 1] * bids[:, 2]).cumsum(), axis=1)
asks = np.insert(asks, 3, np.flipud(asks[:, 1] * asks[:, 2]).cumsum(), axis=1)
bids = np.delete(bids, 2, axis=1)
asks = np.delete(asks, 2, axis=1)
return bids, asks, product_id, market_book
def get_trades(
product_id: str, limit: int = 1000, side: Optional[Any] = None
) -> pd.DataFrame:
"""Get last N trades for chosen trading pair. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
limit: int
Last <limit> of trades. Maximum is 1000.
side: str
You can chose either sell or buy side. If side is not set then all trades will be displayed.
Returns
-------
pd.DataFrame
Last N trades for chosen trading pairs.
"""
params = {"limit": limit}
if side is not None and side in ["buy", "sell"]:
params["side"] = side
product_id = check_validity_of_product(product_id)
product = make_coinbase_request(f"/products/{product_id}/trades", params=params)
return pd.DataFrame(product)[["time", "price", "size", "side"]]
def get_candles(product_id: str, interval: str = "24h") -> pd.DataFrame:
"""Get candles for chosen trading pair and time interval. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
interval: str
Time interval. One from 1min, 5min ,15min, 1hour, 6hour, 24hour
Returns
-------
pd.DataFrame
Candles for chosen trading pair.
"""
interval_map = {
"1min": 60,
"5min": 300,
"15min": 900,
"1hour": 3600,
"6hour": 21600,
"24hour": 86400,
"1day": 86400,
}
if interval not in interval_map:
print(f"Wrong interval. Please use on from {list(interval_map.keys())}\n")
return pd.DataFrame()
granularity: int = interval_map[interval]
product_id = check_validity_of_product(product_id)
candles = make_coinbase_request(
f"/products/{product_id}/candles", params={"granularity": granularity}
)
df = pd.DataFrame(candles)
df.columns = [
"Time0",
"Low",
"High",
"Open",
"Close",
"Volume",
]
return df[
[
"Time0",
"Open",
"High",
"Low",
"Close",
"Volume",
]
]
def get_product_stats(product_id: str) -> pd.DataFrame:
"""Get 24 hr stats for the product. Volume is in base currency units.
Open, high and low are in quote currency units. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
Returns
-------
pd.DataFrame
24h stats for chosen trading pair
"""
product_id = check_validity_of_product(product_id)
product = make_coinbase_request(f"/products/{product_id}/stats")
df = pd.Series(product).reset_index()
df.columns = ["Metric", "Value"]
return df

View File

@@ -0,0 +1,152 @@
"""Coinbase view"""
__docformat__ = "numpy"
import os
from typing import Optional
import pandas as pd
from tabulate import tabulate
from pandas.plotting import register_matplotlib_converters
from gamestonk_terminal.helper_funcs import export_data
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.cryptocurrency.due_diligence import coinbase_model
from gamestonk_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book
register_matplotlib_converters()
def display_order_book(product_id: str, export: str = "") -> None:
"""Displays a list of available currency pairs for trading. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
export : str
Export dataframe data to csv,json,xlsx file
"""
bids, asks, pair, market_book = coinbase_model.get_order_book(product_id)
plot_order_book(bids, asks, pair)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"book",
pd.DataFrame(market_book),
)
def display_trades(
product_id: str, limit: int = 1000, side: Optional[str] = None, export: str = ""
) -> None:
"""Display last N trades for chosen trading pair. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
limit: int
Last <limit> of trades. Maximum is 1000.
side: Optional[str]
You can chose either sell or buy side. If side is not set then all trades will be displayed.
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_trades(product_id, limit, side)
df_data = df.copy()
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"trades",
df_data,
)
def display_candles(product_id: str, interval: str, export) -> None:
"""Get candles for chosen trading pair and time interval. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
interval: str
Time interval. One from 1m, 5m ,15m, 1h, 6h, 24h
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_candles(product_id, interval)
df_data = df.copy()
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".1f",
showindex=True,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"candles",
df_data,
)
def display_stats(product_id: str, export: str) -> None:
"""Get 24 hr stats for the product. Volume is in base currency units.
Open, high and low are in quote currency units. [Source: Coinbase]
Parameters
----------
product_id: str
Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_product_stats(product_id)
df_data = df.copy()
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"stats",
df_data,
)

View File

@@ -13,15 +13,16 @@ from gamestonk_terminal.cryptocurrency.due_diligence import (
pycoingecko_view,
coinpaprika_view,
binance_view,
coinbase_model,
binance_model,
coinbase_view,
)
from gamestonk_terminal.helper_funcs import (
get_flair,
parse_known_args_and_warn,
check_positive,
)
from gamestonk_terminal.cryptocurrency.due_diligence.binance_model import (
show_available_pairs_for_given_symbol,
)
from gamestonk_terminal.cryptocurrency.due_diligence.coinpaprika_view import CURRENCIES
from gamestonk_terminal.cryptocurrency.cryptocurrency_helpers import plot_chart
import gamestonk_terminal.config_terminal as cfg
@@ -62,6 +63,7 @@ class DueDiligenceController:
"book",
"balance",
],
"cb": ["book", "trades", "stats"],
}
DD_VIEWS_MAPPING = {
@@ -122,6 +124,13 @@ Binance:
book show order book
balance show coin balance
"""
if self.source == "cb":
help_text += """
Coinbase:
book show order book
trades show last trades
stats show coin stats
"""
help_text += " chart display chart\n"
print(help_text)
@@ -527,69 +536,103 @@ Binance:
# binance
def call_book(self, other_args):
"""Process book command"""
limit_list = [5, 10, 20, 50, 100, 500, 1000, 5000]
_, quotes = show_available_pairs_for_given_symbol(self.current_coin)
parser = argparse.ArgumentParser(
prog="book",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Get the order book for selected coin",
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
help="Limit parameter. Adjusts the weight",
default=100,
type=int,
choices=limit_list,
)
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT",
choices=quotes,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
binance_view.display_order_book(
coin=self.current_coin,
limit=ns_parser.limit,
currency=ns_parser.vs,
export=ns_parser.export,
if self.current_coin:
parser = argparse.ArgumentParser(
prog="book",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Get the order book for selected coin",
)
except Exception as e:
print(e, "\n")
if self.source == "bin":
limit_list = [5, 10, 20, 50, 100, 500, 1000, 5000]
_, quotes = binance_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
help="Limit parameter. Adjusts the weight",
default=100,
type=int,
choices=limit_list,
)
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT",
choices=quotes,
)
if self.source == "cb":
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if self.source == "bin":
binance_view.display_order_book(
coin=self.current_coin,
limit=ns_parser.limit,
currency=ns_parser.vs,
export=ns_parser.export,
)
elif self.source == "cb":
pair = f"{self.current_coin.upper()}-{ns_parser.vs.upper()}"
coinbase_view.display_order_book(
product_id=pair,
export=ns_parser.export,
)
except Exception as e:
print(e, "\n")
def call_balance(self, other_args):
"""Process balance command"""
_, quotes = show_available_pairs_for_given_symbol(self.current_coin)
_, quotes = binance_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser = argparse.ArgumentParser(
prog="balance",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Chart",
description="Display balance",
)
parser.add_argument(
@@ -622,18 +665,131 @@ Binance:
except Exception as e:
print(e, "\n")
def call_trades(self, other_args):
"""Process trades command"""
parser = argparse.ArgumentParser(
prog="trades",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Show last trades on Coinbase",
)
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"--side",
help="Side of trade: buy, sell, all",
dest="side",
type=str,
default="all",
choices=["all", "buy", "sell"],
)
parser.add_argument(
"-t",
"--top",
default=15,
dest="top",
help="Limit of records",
type=check_positive,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
pair = f"{self.current_coin.upper()}-{ns_parser.vs.upper()}"
if ns_parser.side.upper() == "all":
side = None
else:
side = ns_parser.side
coinbase_view.display_trades(
product_id=pair, limit=ns_parser.top, side=side, export=ns_parser.export
)
except Exception as e:
print(e, "\n")
def call_stats(self, other_args):
"""Process stats command"""
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser = argparse.ArgumentParser(
prog="stats",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Display coin stats",
)
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
pair = f"{self.current_coin.upper()}-{ns_parser.vs.upper()}"
coinbase_view.display_stats(pair, ns_parser.export)
except Exception as e:
print(e, "\n")
def call_chart(self, other_args):
"""Process chart command"""
if self.current_coin:
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="ta",
description="""Loads data for technical analysis. You can specify currency vs which you want
to show chart and also number of days to get data for.
By default currency: usd and days: 30.
E.g. if you loaded in previous step Ethereum and you want to see it's price vs btc
in last 90 days range use `ta --vs btc --days 90`""",
prog="chart",
description="""Display chart for loaded coin. You can specify currency vs which you want
to show chart and also number of days to get data for.""",
)
if self.source == "cp":
@@ -688,7 +844,9 @@ Binance:
"1month": client.KLINE_INTERVAL_1MONTH,
}
_, quotes = show_available_pairs_for_given_symbol(self.current_coin)
_, quotes = binance_model.show_available_pairs_for_given_symbol(
self.current_coin
)
parser.add_argument(
"--vs",
@@ -718,13 +876,61 @@ Binance:
type=check_positive,
)
if self.source == "cb":
interval_map = {
"1min": 60,
"5min": 300,
"15min": 900,
"1hour": 3600,
"6hour": 21600,
"24hour": 86400,
"1day": 86400,
}
_, quotes = coinbase_model.show_available_pairs_for_given_symbol(
self.current_coin
)
if len(quotes) < 0:
print(
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
)
return
parser.add_argument(
"--vs",
help="Quote currency (what to view coin vs)",
dest="vs",
type=str,
default="USDT" if "USDT" in quotes else quotes[0],
choices=quotes,
)
parser.add_argument(
"-i",
"--interval",
help="Interval to get data",
choices=list(interval_map.keys()),
dest="interval",
default="1day",
type=str,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
default=100,
help="Number to get",
type=check_positive,
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if self.source == "bin":
if self.source in ["bin", "cb"]:
limit = ns_parser.limit
interval = ns_parser.interval
days = 0

View File

@@ -0,0 +1,35 @@
"""Coinbase model"""
__docformat__ = "numpy"
import pandas as pd
from gamestonk_terminal.cryptocurrency.coinbase_helpers import (
make_coinbase_request,
)
def get_trading_pairs() -> pd.DataFrame:
"""Get a list of available currency pairs for trading. [Source: Coinbase]
base_min_size - min order size
base_max_size - max order size
min_market_funds - min funds allowed in a market order.
max_market_funds - max funds allowed in a market order.
Returns
-------
pd.DataFrame
Available trading pairs on Coinbase
"""
columns = [
"id",
"display_name",
"base_currency",
"quote_currency",
"base_min_size",
"base_max_size",
"min_market_funds",
"max_market_funds",
]
pairs = make_coinbase_request("/products")
return pd.DataFrame(pairs)[columns]

View File

@@ -0,0 +1,60 @@
"""Coinbase view"""
__docformat__ = "numpy"
import os
from tabulate import tabulate
from gamestonk_terminal.helper_funcs import export_data
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.cryptocurrency.overview import coinbase_model
from gamestonk_terminal.helper_funcs import long_number_format
def display_trading_pairs(top: int, sortby: str, descend: bool, export: str) -> None:
"""Displays a list of available currency pairs for trading. [Source: Coinbase]
Parameters
----------
top: int
Top n of pairs
sortby: str
Key to sortby data
descend: bool
Sort descending flag
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_trading_pairs()
df_data = df.copy()
for col in [
"base_min_size",
"base_max_size",
"min_market_funds",
"max_market_funds",
]:
df[col] = df[col].apply(lambda x: long_number_format(x))
df = df.sort_values(by=sortby, ascending=descend).head(top)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".2f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"pairs",
df_data,
)

View File

@@ -20,6 +20,7 @@ from gamestonk_terminal.cryptocurrency.overview.coinpaprika_view import CURRENCI
from gamestonk_terminal.cryptocurrency.overview.coinpaprika_model import (
get_all_contract_platforms,
)
from gamestonk_terminal.cryptocurrency.overview import coinbase_view
class Controller:
@@ -52,6 +53,7 @@ class Controller:
"cpexchanges",
"cpplatforms",
"cpcontracts",
"cbpairs",
]
def __init__(self):
@@ -93,6 +95,8 @@ CoinPaprika:
cpexmarkets all available markets on given exchange
cpplatforms list blockchain platforms eg. ethereum, solana, kusama, terra
cpcontracts all smart contracts for given platform
Coinbase:
cbpairs info about available trading pairs on Coinbase
"""
print(help_text)
@@ -1524,6 +1528,75 @@ CoinPaprika:
except Exception as e:
print(e, "\n")
def call_cbpairs(self, other_args):
"""Process news command"""
parser = argparse.ArgumentParser(
prog="cbpairs",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Shows available trading pairs on Coinbase ",
)
parser.add_argument(
"-t",
"--top",
dest="top",
type=int,
help="top N number of news >=10",
default=15,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
type=str,
help="Sort by given column. Default: id",
default="id",
choices=[
"id",
"display_name",
"base_currency",
"quote_currency",
"base_min_size",
"base_max_size",
"min_market_funds",
"max_market_funds",
],
)
parser.add_argument(
"--descend",
action="store_false",
help="Flag to sort in descending order (lowest first)",
dest="descend",
default=True,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
coinbase_view.display_trading_pairs(
top=ns_parser.top,
export=ns_parser.export,
sortby=ns_parser.sortby,
descend=ns_parser.descend,
)
except Exception as e:
print(e, "\n")
def menu():
overview_controller = Controller()

View File

@@ -17,6 +17,7 @@ from gamestonk_terminal.portfolio.brokers import (
ally_api,
)
from gamestonk_terminal.portfolio.brokers.degiro import degiro_controller
from gamestonk_terminal.portfolio.brokers.coinbase import coinbase_controller
from gamestonk_terminal.portfolio.brokers.brokers_helpers import (
merge_brokers_holdings,
print_brokers_holdings,
@@ -40,6 +41,7 @@ class BrokersController:
"quit",
"rhhold",
"rhhist",
"cb",
]
BROKERS = [
@@ -66,6 +68,8 @@ class BrokersController:
" ally - Ally Invest\n"
" alp - Alpaca\n"
" rh - Robinhood\n"
"\nCrypto Exchanges Supported:\n"
" cb - Coinbase Pro\n"
"\nPortfolio:\n"
" cls clear screen\n"
" ?/help show this menu again\n"
@@ -87,6 +91,8 @@ class BrokersController:
" rhhist plot rh brokers holdings history\n"
"\nDegiro:\n"
"> degiro degiro standalone menu\n"
"\nCoinbase:\n"
"> cb coinbase standalone menu\n"
"\nMerge:\n"
" login login to your brokers\n"
" hold view net holdings across all logins\n"
@@ -201,6 +207,12 @@ class BrokersController:
return True
print("")
def call_cb(self, _):
"""Process degiro command."""
if coinbase_controller.menu():
return True
print("")
def call_hold(self, _):
"""Process hold command."""
holdings = pd.DataFrame(

View File

@@ -0,0 +1,368 @@
"""Coinbase Controller"""
__docformat__ = "numpy"
# pylint: disable=R0904, C0302, W0622
import argparse
import os
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.menu import session
from gamestonk_terminal.portfolio.brokers.coinbase import (
coinbase_view,
)
from gamestonk_terminal.helper_funcs import (
get_flair,
parse_known_args_and_warn,
check_positive,
)
class CoinbaseController:
CHOICES = [
"?",
"cls",
"help",
"q",
"quit",
]
CB_CHOICES = ["account", "history", "orders", "deposits"]
def __init__(self):
"""CONSTRUCTOR"""
self._cb_parser = argparse.ArgumentParser(add_help=False, prog="cb")
self.CHOICES.extend(self.CB_CHOICES)
self._cb_parser.add_argument("cmd", choices=self.CHOICES)
def print_help(self):
"""Print help"""
help_text = """
Coinbase:
cls clear screen
?/help show this menu again
q quit this menu, and shows back to main menu
quit quit to abandon the program
account show balance of your account
history show history of your account
deposits show all your deposits or internal transfers
orders show all your orders
"""
print(help_text)
def switch(self, an_input: str):
"""Process and dispatch input
Returns
-------
True, False or None
False - quit the menu
True - quit the program
None - continue in the menu
"""
# Empty command
if not an_input:
print("")
return None
(known_args, other_args) = self._cb_parser.parse_known_args(an_input.split())
# Help menu again
if known_args.cmd == "?":
self.print_help()
return None
# Clear screen
if known_args.cmd == "cls":
os.system("cls||clear")
return None
return getattr(
self, "call_" + known_args.cmd, lambda: "Command not recognized!"
)(other_args)
def call_help(self, _):
"""Process Help command"""
self.print_help()
def call_q(self, _):
"""Process Q command - quit the menu."""
return False
def call_quit(self, _):
"""Process Quit command - quit the program."""
return True
def call_account(self, other_args):
"""Process account command"""
parser = argparse.ArgumentParser(
prog="account",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Display info about your trading accounts on Coinbase",
)
parser.add_argument(
"--all",
action="store_true",
help="Flag to display all your account",
dest="all",
default=False,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
if other_args:
if not other_args[0][0] == "-":
other_args.insert(0, "--acc")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
coinbase_view.display_account(ns_parser.all, ns_parser.export)
except Exception as e:
print(e, "\n")
def call_history(self, other_args):
"""Process account command"""
parser = argparse.ArgumentParser(
prog="history",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Display account history",
)
parser.add_argument(
"-a",
"--acc",
dest="account",
help="Symbol of coin of account or id",
type=str,
default="BTC",
required=False,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
help="Limit parameter.",
default=20,
type=check_positive,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
if other_args:
if not other_args[0][0] == "-":
other_args.insert(0, "--acc")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
coinbase_view.display_history(
ns_parser.account, ns_parser.export, ns_parser.limit
)
except Exception as e:
print(e, "\n")
def call_orders(self, other_args):
"""Process orders command"""
parser = argparse.ArgumentParser(
prog="orders",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="List your current open orders",
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
help="Limit parameter.",
default=20,
type=check_positive,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
type=str,
help="Sort by given column. Default: created_at",
default="created_at",
choices=[
"product_id",
"side",
"price",
"size",
"type",
"created_at",
"status",
],
)
parser.add_argument(
"--descend",
action="store_false",
help="Flag to sort in descending order (lowest first)",
dest="descend",
default=False,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
if other_args:
if not other_args[0][0] == "-":
other_args.insert(0, "--acc")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
coinbase_view.display_orders(
ns_parser.limit, ns_parser.sortby, ns_parser.descend, ns_parser.export
)
except Exception as e:
print(e, "\n")
def call_deposits(self, other_args):
"""Process deposits command"""
parser = argparse.ArgumentParser(
prog="deposits",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Display a list of deposits for your account.",
)
parser.add_argument(
"-t",
"--type",
dest="type",
type=str,
help="Deposit type. Either: internal_deposits (transfer between portfolios) or deposit",
default="deposit",
choices=["internal_deposit", "deposit"],
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
help="Limit parameter.",
default=20,
type=check_positive,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
type=str,
help="Sort by given column. Default: created_at",
default="created_at",
choices=[
"created_at",
"amount",
],
)
parser.add_argument(
"--descend",
action="store_false",
help="Flag to sort in descending order (lowest first)",
dest="descend",
default=False,
)
parser.add_argument(
"--export",
choices=["csv", "json", "xlsx"],
default="",
type=str,
dest="export",
help="Export dataframe data to csv,json,xlsx file",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
coinbase_view.display_deposits(
ns_parser.limit,
ns_parser.sortby,
ns_parser.type,
ns_parser.descend,
ns_parser.export,
)
except Exception as e:
print(e, "\n")
def menu():
cb_controller = CoinbaseController()
cb_controller.print_help()
while True:
# Get input command from user
if session and gtff.USE_PROMPT_TOOLKIT:
completer = NestedCompleter.from_nested_dict(
{c: None for c in cb_controller.CHOICES}
)
an_input = session.prompt(
f"{get_flair()} (bro)>(cb)> ",
completer=completer,
)
else:
an_input = input(f"{get_flair()} (bro)>(cb)> ")
try:
process_input = cb_controller.switch(an_input)
if process_input is not None:
return process_input
except SystemExit:
print("The command selected doesn't exist\n")
continue

View File

@@ -0,0 +1,177 @@
"""Coinbase model"""
__docformat__ = "numpy"
import pandas as pd
import gamestonk_terminal.config_terminal as cfg
from gamestonk_terminal.cryptocurrency.coinbase_helpers import (
CoinbaseProAuth,
_check_account_validity,
make_coinbase_request,
)
def get_accounts() -> pd.DataFrame:
"""Get list of all your trading accounts. [Source: Coinbase]
Single account information:
{
"id": "71452118-efc7-4cc4-8780-a5e22d4baa53",
"currency": "BTC",
"balance": "0.0000000000000000",
"available": "0.0000000000000000",
"hold": "0.0000000000000000",
"profile_id": "75da88c5-05bf-4f54-bc85-5c775bd68254"
}
Returns
-------
pd.DataFrame
DataFrame with all your trading accounts.
"""
auth = CoinbaseProAuth(
cfg.API_COINBASE_KEY, cfg.API_COINBASE_SECRET, cfg.API_COINBASE_PASS_PHRASE
)
resp = make_coinbase_request("/accounts", auth=auth)
if not resp:
return pd.DataFrame()
df = pd.DataFrame(resp)
return df[["id", "currency", "balance", "available", "hold"]]
def get_account_history(account: str) -> pd.DataFrame:
"""Get your account history. Account activity either increases or decreases your account balance. [Source: Coinbase]
Example api response:
{
"id": "100",
"created_at": "2014-11-07T08:19:27.028459Z",
"amount": "0.001",
"balance": "239.669",
"type": "fee",
"details": {
"order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
"trade_id": "74",
"product_id": "BTC-USD"
}
}
Parameters
----------
account: str
id ("71452118-efc7-4cc4-8780-a5e22d4baa53") or currency (BTC)
Returns
-------
pd.DataFrame
DataFrame with account history.
"""
auth = CoinbaseProAuth(
cfg.API_COINBASE_KEY, cfg.API_COINBASE_SECRET, cfg.API_COINBASE_PASS_PHRASE
)
account = _check_account_validity(account)
if not account:
return pd.DataFrame()
resp = make_coinbase_request(f"/accounts/{account}/holds", auth=auth)
if not resp:
return pd.DataFrame()
df = pd.json_normalize(resp)
try:
df.columns = [
col.replace("details.", "") if "details" in col else col
for col in df.columns
]
except Exception as e:
print(e)
return df
def get_orders() -> pd.DataFrame:
"""List your current open orders. Only open or un-settled orders are returned. [Source: Coinbase]
Example response from API:
{
"id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2",
"price": "0.10000000",
"size": "0.01000000",
"product_id": "BTC-USD",
"side": "buy",
"stp": "dc",
"type": "limit",
"time_in_force": "GTC",
"post_only": false,
"created_at": "2016-12-08T20:02:28.53864Z",
"fill_fees": "0.0000000000000000",
"filled_size": "0.00000000",
"executed_value": "0.0000000000000000",
"status": "open",
"settled": false
}
Returns
-------
pd.DataFrame
Open orders in your account
"""
auth = CoinbaseProAuth(
cfg.API_COINBASE_KEY, cfg.API_COINBASE_SECRET, cfg.API_COINBASE_PASS_PHRASE
)
resp = make_coinbase_request("/orders", auth=auth)
if not resp:
df = pd.DataFrame(
columns=[
"product_id",
"side",
"price",
"size",
"type",
"created_at",
"status",
]
)
return df
return pd.DataFrame(resp)[
["product_id", "side", "price", "size", "type", "created_at", "status"]
]
def get_deposits(deposit_type: str = "deposit") -> pd.DataFrame:
"""Get a list of deposits for your account. [Source: Coinbase]
Parameters
----------
deposit_type: str
internal_deposits (transfer between portfolios) or deposit
Returns
-------
pd.DataFrame
List of deposits
"""
auth = CoinbaseProAuth(
cfg.API_COINBASE_KEY, cfg.API_COINBASE_SECRET, cfg.API_COINBASE_PASS_PHRASE
)
params = {"type": deposit_type}
if deposit_type not in ["internal_deposit", "deposit"]:
params["type"] = "deposit"
resp = make_coinbase_request("/transfers", auth=auth, params=params)
if not resp:
return pd.DataFrame()
if isinstance(resp, tuple):
resp = resp[0]
if deposit_type == "deposit":
df = pd.json_normalize(resp)
else:
df = pd.DataFrame(resp)[["type", "created_at", "amount", "currency"]]
return df

View File

@@ -0,0 +1,198 @@
"""Coinbase view"""
__docformat__ = "numpy"
import os
from tabulate import tabulate
from gamestonk_terminal.helper_funcs import export_data
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.portfolio.brokers.coinbase import coinbase_model
def display_account(show_all: bool = True, export: str = "") -> None:
"""Display list of all your trading accounts. [Source: Coinbase]
Parameters
----------
show_all: bool
Indicate if you want to show all your accounts or only one.
export : str
Export dataframe data to csv,json,xlsx file
"""
if show_all:
df = coinbase_model.get_accounts()
else:
df = coinbase_model.get_accounts()
df.balance = df["balance"].astype(float)
df = df[df.balance > 0]
if df.empty:
print(
"No funds found on you account. To display all wallets (including 0 balance) use account --all\n"
)
return
df_data = df.copy()
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"account",
df_data,
)
def display_history(account: str, export: str = "", limit: int = 20) -> None:
"""Display account history. [Source: Coinbase]
Parameters
----------
account: str
Symbol or account id
limit: int
For all accounts display only top n
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_account_history(account)
df_data = df.copy()
if df.empty:
print(
f"Your account {account} doesn't have any funds or you provide wrong account name or id. "
f"To check all your accounts use command account --all\n"
)
return
if gtff.USE_TABULATE_DF:
print(
tabulate(
df.head(limit),
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.head(limit).to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"history",
df_data,
)
def display_orders(limit: int, sortby: str, descend: bool, export: str = "") -> None:
"""List your current open orders [Source: Coinbase]
Parameters
----------
limit: int
Last <limit> of trades. Maximum is 1000.
sortby: str
Key to sort by
descend: bool
Flag to sort descending
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_orders()
if df.empty:
print("No orders found for your account\n")
return
df_data = df.copy()
df = df.sort_values(by=sortby, ascending=descend).head(limit)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"orders",
df_data,
)
def display_deposits(
limit: int, sortby: str, deposite_type: str, descend: bool, export: str = ""
) -> None:
"""Display last N trades for chosen trading pair. [Source: Coinbase]
Parameters
----------
limit: int
Last <limit> of trades. Maximum is 1000.
sortby: str
Key to sort by
descend: bool
Flag to sort descending
deposite_type: str
internal_deposits (transfer between portfolios) or deposit
export : str
Export dataframe data to csv,json,xlsx file
"""
df = coinbase_model.get_deposits(deposit_type=deposite_type)
if df.empty:
print("No deposits found for your account\n")
return
df_data = df.copy()
df = df.sort_values(by=sortby, ascending=descend).head(limit)
if gtff.USE_TABULATE_DF:
print(
tabulate(
df,
headers=df.columns,
floatfmt=".3f",
showindex=False,
tablefmt="fancy_grid",
),
"\n",
)
else:
print(df.to_string, "\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"deposits",
df_data,
)

View File

@@ -0,0 +1,239 @@
from unittest import mock, TestCase
import vcr
from gamestonk_terminal.cryptocurrency.due_diligence import (
coinbase_view as dd_coinbase_view,
)
from gamestonk_terminal.portfolio.brokers.coinbase import (
coinbase_view as bro_coinbase_view,
)
from gamestonk_terminal.cryptocurrency.overview import coinbase_view as ov_coinbase_view
from tests.helpers import check_print
# pylint: disable=unused-import
class TestCoinbaseView(TestCase): # pragma: allowlist secret
@check_print(assert_in="Open")
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.check_validity_of_product"
)
def test_display_candles(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = [
[1631318400, 0.0715, 0.07314, 0.07155, 0.07245, 5957.08396321],
[1631232000, 0.07088, 0.07476, 0.07382, 0.07159, 13264.33844153],
[1631145600, 0.07369, 0.07669, 0.07599, 0.07378, 12462.35265359],
]
dd_coinbase_view.display_candles("ETH-BTC", "1day", "")
@check_print(assert_in="Value")
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.check_validity_of_product"
)
def test_display_stats(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = {
"open": "3245.61000000",
"high": "3392.11000000",
"low": "3150.00000000",
"volume": "26185.51325269",
"last": "3333.19000000",
"volume_30day": "1019451.11188405",
}
dd_coinbase_view.display_stats("ETH-USDT", "")
@check_print(assert_in="price")
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.cryptocurrency.due_diligence.coinbase_model.check_validity_of_product"
)
def test_display_trades(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = [
{
"time": "2014-11-07T22:19:28.578544Z",
"trade_id": 74,
"price": "10.00000000",
"size": "0.01000000",
"side": "buy",
},
{
"time": "2014-11-07T01:08:43.642366Z",
"trade_id": 73,
"price": "100.00000000",
"size": "0.01000000",
"side": "sell",
},
]
dd_coinbase_view.display_trades("ETH-USDT", limit=100, side=None, export="")
@check_print(assert_in="product_id")
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model._check_account_validity"
)
def test_display_orders(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = [
{
"id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2",
"price": "0.10000000",
"size": "0.01000000",
"product_id": "BTC-USD",
"side": "buy",
"stp": "dc",
"type": "limit",
"time_in_force": "GTC",
"post_only": False,
"created_at": "2016-12-08T20:02:28.53864Z",
"fill_fees": "0.0000000000000000",
"filled_size": "0.00000000",
"executed_value": "0.0000000000000000",
"status": "open",
"settled": False,
},
{
"id": "b227e691-365c-470f-a860-a9b4a37dd1d8",
"price": "1.00000000",
"size": "1.00000000",
"product_id": "BTC-USD",
"side": "buy",
"type": "limit",
"created_at": "2016-12-08T20:01:19.038644Z",
"fill_fees": "0.0000000000000000",
"filled_size": "0.00000000",
"executed_value": "0.0000000000000000",
"status": "pending",
"settled": False,
},
{
"id": "b24327e691-365c-470f-a860-a9b4a37dd1d8",
"price": "2.00000000",
"size": "1.00000000",
"product_id": "BTC-USD",
"side": "buy",
"type": "limit",
"created_at": "2016-12-08T20:01:19.038644Z",
"fill_fees": "0.0000000000000000",
"filled_size": "0.00000000",
"executed_value": "0.0000000000000000",
"status": "pending",
"settled": False,
},
]
bro_coinbase_view.display_orders(2, "created_at", descend=True, export="")
@check_print(assert_in="crypto_address")
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model._check_account_validity"
)
def test_display_deposits(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = (
[
{
"id": "6cca6a14-a5e3-4219-9542-86123fc9d6c3",
"type": "deposit",
"created_at": "2019-06-18 01:37:48.78953+00",
"completed_at": "2019-06-18 01:37:49.756147+00",
"canceled_at": None,
"processed_at": "2019-06-18 01:37:49.756147+00",
"account_id": "bf091906-ca7f-499e-95fa-5bc15e918b46",
"user_id": "5eeac63c90b913bf3cf7c92e",
"user_nonce": None,
"amount": "40.00000000",
"details": {
"crypto_address": "testaddress",
"destination_tag": "379156162",
"coinbase_account_id": "7f8803e2-1be5-4a29-bfd2-3bc6645f5a24",
"destination_tag_name": "XRP Tag",
"crypto_transaction_id": "5eeac64cc46b34f5332e5326",
"coinbase_transaction_id": "5eeac652be6cf8b17f7625bd",
"crypto_transaction_hash": "testhash",
},
},
{
"id": "543cca6a14-a5e3-4219-9542-86123fc9d6c3",
"type": "deposit",
"created_at": "2019-06-18 01:37:48.78953+00",
"completed_at": "2019-06-18 01:37:49.756147+00",
"canceled_at": None,
"processed_at": "2019-06-18 01:37:49.756147+00",
"account_id": "bf091906-ca7f-499e-95fa-5bc15e918b46",
"user_id": "5eeac63c90b913bf3cf7c92e",
"user_nonce": None,
"amount": "50.00000000",
"details": {
"crypto_address": "12g",
"destination_tag": "379156162",
"coinbase_account_id": "7f8803e2-1be5-4a29-bfd2-3bc6645f5a24",
"destination_tag_name": "XRP Tag",
"crypto_transaction_id": "5eeac64cc46b34f5332e5326",
"coinbase_transaction_id": "5eeac652be6cf8b17f7625bd",
"crypto_transaction_hash": "324",
},
},
],
)
bro_coinbase_view.display_deposits(
2, "created_at", "deposit", descend=True, export=""
)
@check_print(assert_in="balance")
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model.make_coinbase_request"
)
@mock.patch(
"gamestonk_terminal.portfolio.brokers.coinbase.coinbase_model._check_account_validity"
)
def test_display_history(self, mock_validity, mock_request):
mock_validity.return_value = "ETH"
mock_request.return_value = [
{
"id": "100",
"created_at": "2014-11-07T08:19:27.028459Z",
"amount": "0.001",
"balance": "239.669",
"type": "fee",
"details": {
"order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
"trade_id": "74",
"product_id": "ETH-USD",
},
},
{
"id": "111",
"created_at": "2014-11-07T08:19:27.028459Z",
"amount": "0.001",
"balance": "239.669",
"type": "fee",
"details": {
"order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
"trade_id": "75",
"product_id": "ETH-USD",
},
},
]
bro_coinbase_view.display_history("ETH", "", 2)
@check_print(assert_in="base_currency")
@vcr.use_cassette(
"tests/cassettes/test_cryptocurrency/test_coinbase/test_trading_pairs.yaml",
record_mode="new_episodes",
)
def test_display_trading_pairs(self):
ov_coinbase_view.display_trading_pairs(10, "base_currency", False, "")