├── db └── .gitignore ├── .gitignore ├── requirements.txt ├── brb ├── conf.py ├── io.py ├── __init__.py ├── text.py ├── graph.py ├── report.py └── __main__.py ├── LICENSE.md ├── conf_default.py └── README.md /db/.gitignore: -------------------------------------------------------------------------------- 1 | *.* 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | venv/ 3 | *.pyc 4 | conf_user.py 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy >= 1.20.3 2 | requests >= 2.25.1 3 | matplotlib >= 3.4.2 4 | python-binance >= 1.0.10 5 | click>=7.1.2 6 | rich>=10.2.2 7 | datetime >= 4.3 8 | apprise >= 0.9.3 9 | -------------------------------------------------------------------------------- /brb/conf.py: -------------------------------------------------------------------------------- 1 | from conf_default import * 2 | try: 3 | from conf_user import * 4 | except ModuleNotFoundError: 5 | try: 6 | from conf import * 7 | print("WARNING : please rename conf.py to conf_user.py") 8 | except ModuleNotFoundError: 9 | raise ModuleNotFoundError("File conf_user.py missing. Please check the installation section in the README.") 10 | 11 | #Conf check 12 | assert BINANCE_API_KEY != "" 13 | assert BINANCE_API_SECRET != "" 14 | assert TLD in ("com", 'us') 15 | assert type(APPRISE_URLS) == list 16 | for url in APPRISE_URLS: 17 | assert type(url) == str 18 | 19 | if CURRENCY not in ("EUR", "USD"): 20 | assert OER_APP_ID != "" 21 | -------------------------------------------------------------------------------- /brb/io.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import brb.conf as conf 3 | import os 4 | import matplotlib.pyplot as plt 5 | import logging 6 | import brb 7 | 8 | if len(conf.APPRISE_URLS) > 0: 9 | import apprise 10 | if conf.RICH_PRINTING: 11 | import rich.console, rich.markdown 12 | 13 | 14 | def output(msg, img, quiet): 15 | if not quiet: 16 | if len(msg) > 0: 17 | if conf.RICH_PRINTING: 18 | rich.console.Console().print(rich.markdown.Markdown(msg)) 19 | else: 20 | print(msg) 21 | if img is not None: 22 | plt.close() 23 | fig = plt.figure() 24 | ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0]) 25 | ax.set_axis_off() 26 | fig.add_axes(ax) 27 | plt.imshow(plt.imread(img)) 28 | plt.show() 29 | 30 | if brb.notifier is not None: 31 | brb.notifier.notify( 32 | body=msg, body_format=apprise.NotifyFormat.MARKDOWN, attach=img 33 | ) 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 titulebolide 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /brb/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import datetime as dt 3 | import apprise 4 | import brb.conf as conf 5 | import os 6 | 7 | log_level = logging.DEBUG if os.environ.get("BRB_DEBUG") is not None else logging.WARNING 8 | 9 | logging.basicConfig( 10 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=log_level 11 | ) 12 | logger = logging.getLogger(__name__) 13 | 14 | notifier = None 15 | if len(conf.APPRISE_URLS) > 0: 16 | notifier = apprise.Apprise() 17 | for url in conf.APPRISE_URLS: 18 | notifier.add(url) 19 | 20 | class ErrorAppriseNotifier(logging.Handler): 21 | def __init__(self): 22 | """ 23 | Logging Handler that sends an apprise notification when an error is raised 24 | """ 25 | super().__init__() 26 | 27 | def emit(self, record): 28 | if record.levelno >= 30: # warning, critical or error 29 | date = str(dt.datetime.fromtimestamp(int(record.created))) 30 | notifier.notify( 31 | body=f"```{date} - {record.name} - {record.levelname} - {record.msg}```", 32 | ) 33 | 34 | logger.addHandler(ErrorAppriseNotifier()) 35 | -------------------------------------------------------------------------------- /conf_default.py: -------------------------------------------------------------------------------- 1 | # Your binance API keys 2 | # ATTENTION : CENSOR THESE KEYS IF ASKED TO PASTE THIS FILE 3 | BINANCE_API_KEY = "" 4 | BINANCE_API_SECRET = "" 5 | 6 | # The list of coins that you would like to follow 7 | COINS = ["BTC","ADA","ATOM","BAT","BTT","CAKE","DASH","EOS","ETC","ICX","IOTA","NEO","OMG","ONT","QTUM","ROSE","TRX","VET","WIN","XLM"] 8 | 9 | # The currency with which the reports will be printed 10 | CURRENCY = "EUR" #Or USD 11 | 12 | # The symbol of your currency 13 | CURRENCY_SYMBOL = "€" 14 | 15 | # Set here the different diffs you want to see in the text report 16 | # E.g. if you want tp see the yearly, monthly, weekly or daily diffs 17 | DIFFS_POSITIONS = [ 18 | {"ts_delta" : 60*60*24, "text" : "day"}, 19 | {"ts_delta" : 60*60*24*7, "text" : "week"}, 20 | {"ts_delta" : 60*60*24*30, "text" : "month"}, 21 | #{"ts_delta" : 60*60*24*365, "text" : "year"}, 22 | ] 23 | 24 | # Set this to false if your terminal does not support rich printing 25 | RICH_PRINTING = True 26 | 27 | # Leave empty if you don't want to use apprise notifications. 28 | # Use an apprise service that supports attachment (e.g. Discord, Telegram or Emails) 29 | # Examples: 30 | # APPRISE_URLS = ["discord://something"] if you want to use only discord 31 | # APPRISE_URLS = ["discord://something", "telegram://something"] if you want to use both telegram and discord, don't miss the comma "," 32 | APPRISE_URLS = [] 33 | 34 | # openexchangerates.org api key. Mandatory if CURRENCY is not EUR or USD 35 | # Get yours here : https://openexchangerates.org/signup/free 36 | OER_APP_ID = "" 37 | 38 | # Set this to "us" if you are using binance.us 39 | TLD = "com" 40 | -------------------------------------------------------------------------------- /brb/text.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import brb 3 | import brb.conf as conf 4 | 5 | 6 | def text_report(reports): 7 | msg = "**Current market**:" 8 | 9 | currency_change = 1 / reports[-1]["tickers"][conf.CURRENCY] 10 | for symbol, qty in reports[-1]["balances"].items(): 11 | if symbol not in reports[-1]["tickers"]: 12 | brb.logger.debug(f"{symbol} has not price in the very last saved report") 13 | continue 14 | ticker = reports[-1]["tickers"][symbol] 15 | value_usd = qty * ticker 16 | if value_usd < 0.1: 17 | continue 18 | value = round(value_usd * currency_change, 2) 19 | msg += f"\n- **{symbol}** *(@ ${ticker})* : {value} {conf.CURRENCY_SYMBOL}" 20 | 21 | total = reports[-1]["total_usdt"] * currency_change 22 | msg += f"\n\n**Total** : {round(total,2)} {conf.CURRENCY_SYMBOL}" 23 | 24 | all_ts = np.array([report["time"] for report in reports]) 25 | current_ts = reports[-1]["time"] 26 | for pos in conf.DIFFS_POSITIONS: 27 | older_than_pos = np.where(all_ts < current_ts - pos["ts_delta"])[0] 28 | if len(older_than_pos) == 0: 29 | # if there is no record old enough 30 | continue 31 | # taking the report at the earlier timestamp that is 32 | # older than current_ts - pos['ts_delta'] 33 | report = reports[older_than_pos[-1]] 34 | if not conf.CURRENCY in report["tickers"]: 35 | nb_report = older_than_pos[-1] 36 | brb.logger.warning(f"The report #{nb_report} has no price for the chosen currency in your configuration ({conf.CURRENCY})") 37 | continue 38 | pos_report_total = report["total_usdt"] / report["tickers"][conf.CURRENCY] 39 | diff = total - pos_report_total 40 | diff_sign = "+" if diff > 0 else "" 41 | text = pos["text"] 42 | msg += f"\n- Since last {text} : {diff_sign}{round(diff,2)} {conf.CURRENCY_SYMBOL} ({diff_sign}{round(diff/pos_report_total*100,2)}%)" 43 | 44 | return msg 45 | -------------------------------------------------------------------------------- /brb/graph.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import matplotlib.dates as mdates 3 | import datetime as dt 4 | import numpy as np 5 | import time 6 | 7 | 8 | def graph_report(reports, symbols, relative, days, graph_type, ref_currency): 9 | plt.clf() 10 | plt.close() 11 | if len(symbols) < 10: 12 | plt.figure() 13 | else: 14 | plt.figure(figsize=(10, 6)) 15 | 16 | min_timestamp = 0 17 | if days != 0: 18 | min_timestamp = time.time() - days * 24 * 60 * 60 19 | 20 | nb_plot = 0 21 | for symbol in symbols: 22 | X, Y = [], [] 23 | for report in reports: 24 | if report["time"] < min_timestamp: 25 | continue # skip if too recent 26 | if symbol not in report["tickers"]: 27 | ts = report["time"] 28 | brb.logger.debug(f"{symbol} has no price in the report with timestamp {ts}") 29 | continue 30 | ticker = report["tickers"][symbol] 31 | if ticker == 0: 32 | ts = report["time"] 33 | brb.logger.debug(f"{symbol} has an invalid price in the report with timestamp {ts}") 34 | continue 35 | 36 | y = None 37 | if graph_type == "amount": 38 | y = report["total_usdt"] / ticker 39 | elif graph_type == "price": 40 | ref_currency_ticker = 1 41 | if ref_currency not in ("USD", "USDT"): 42 | if ref_currency not in report["tickers"]: 43 | continue 44 | ref_currency_ticker = report["tickers"][ref_currency] 45 | if ref_currency_ticker == 0: 46 | continue 47 | y = ticker / ref_currency_ticker 48 | if y is None: 49 | continue 50 | 51 | Y.append(y) 52 | X.append(dt.datetime.fromtimestamp(report["time"])) 53 | nb_plot += 1 54 | 55 | if relative: 56 | Y = np.array(Y) 57 | Y = (Y / Y[0] - 1) * 100 58 | plt.plot(X, Y, label=symbol) 59 | 60 | plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%d/%m %H:%M")) 61 | plt.setp(plt.xticks()[1], rotation=15) 62 | if graph_type == "amount": 63 | if relative: 64 | plt.ylabel("Relative evolution of amount (%)") 65 | plt.legend(bbox_to_anchor=(1, 1), loc="upper left") 66 | else: 67 | label = "Amount" 68 | label += f" ({symbols[0]})" if len(symbols) == 1 else "" 69 | plt.ylabel(label) 70 | elif graph_type == "price": 71 | if relative: 72 | plt.ylabel(f"Relative evolution of price in {ref_currency} (%)") 73 | else: 74 | plt.ylabel(f"Price in {ref_currency}") 75 | plt.grid() 76 | figname = f"db/quantity_{symbol}.png" 77 | plt.savefig(figname) 78 | return figname, nb_plot 79 | -------------------------------------------------------------------------------- /brb/report.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import requests 4 | import binance 5 | import numpy as np 6 | import brb 7 | import brb.conf as conf 8 | 9 | 10 | def build_ticker(all_symbols, tickers_raw): 11 | backup_coins = ["BTC", "ETH", "BNB"] 12 | tickers = {"USDT": 1, "USD": 1} 13 | tickers_raw = {t["symbol"]: float(t["price"]) for t in tickers_raw} 14 | failed_coins = [] 15 | 16 | for symbol in set(backup_coins + all_symbols): 17 | success = False 18 | for stable in ("USD", "USDT", "BUSD", "USDC", "DAI"): 19 | pair = symbol + stable 20 | if pair in tickers_raw: 21 | tickers[symbol] = tickers_raw[pair] 22 | success = True 23 | break 24 | if not success: 25 | failed_coins.append(symbol) 26 | 27 | for symbol in failed_coins: 28 | success = False 29 | for b_coin in backup_coins: 30 | pair = symbol + b_coin 31 | if pair in tickers_raw: 32 | tickers[symbol] = tickers_raw[pair] * tickers[b_coin] 33 | success = True 34 | break 35 | if not success: 36 | brb.logger.debug(f'Could not retreive USD price for {symbol}, skipping') 37 | 38 | return tickers 39 | 40 | 41 | def get_report(): 42 | api = binance.Client( 43 | conf.BINANCE_API_KEY, 44 | conf.BINANCE_API_SECRET, 45 | tld = conf.TLD 46 | ) 47 | 48 | account = api.get_account() 49 | account_symbols = [] 50 | balances = {} 51 | for balance in account["balances"]: 52 | symbol = balance["asset"] 53 | 54 | if symbol.startswith("LD"): 55 | # skip the coins in binance saving 56 | # (see https://github.com/titulebolide/binance-report-bot/issues/5) 57 | continue 58 | 59 | qty = float(balance["free"]) + float(balance["locked"]) 60 | if qty != 0: 61 | account_symbols.append(symbol) 62 | balances[symbol] = qty 63 | 64 | all_symbols = list(set(conf.COINS + account_symbols)) 65 | if conf.CURRENCY == "EUR": 66 | all_symbols.append("EUR") 67 | tickers_raw = api.get_symbol_ticker() 68 | tickers = build_ticker(all_symbols, tickers_raw) 69 | if conf.CURRENCY not in ("USD", "EUR"): 70 | ticker = ( 71 | 1 72 | / requests.get( 73 | "https://openexchangerates.org/api/latest.json?app_id=" 74 | + conf.OER_APP_ID 75 | ).json()["rates"][conf.CURRENCY] 76 | ) 77 | tickers[conf.CURRENCY] = ticker 78 | 79 | brb.logger.debug(f"Prices after filtering : {tickers}") 80 | 81 | total_usdt = 0 82 | for symbol in account_symbols: 83 | if symbol not in tickers: 84 | brb.logger.debug(f"{symbol} has no price, skipping") 85 | continue 86 | total_usdt += balances[symbol] * tickers[symbol] 87 | 88 | report = {} 89 | report["total_usdt"] = total_usdt 90 | report["balances"] = balances 91 | report["tickers"] = tickers 92 | return report 93 | 94 | 95 | def get_previous_reports(): 96 | if os.path.exists("db/crypto.npy"): 97 | reports = np.load("db/crypto.npy", allow_pickle=True).tolist() 98 | return reports 99 | else: 100 | return [] 101 | 102 | 103 | def save_report(report, old_reports): 104 | report["time"] = int(time.time()) 105 | old_reports.append(report) 106 | np.save("db/crypto.npy", old_reports, allow_pickle=True) 107 | return old_reports 108 | -------------------------------------------------------------------------------- /brb/__main__.py: -------------------------------------------------------------------------------- 1 | import brb 2 | import brb.report 3 | import brb.text 4 | import brb.graph 5 | import brb.io 6 | import brb.conf as conf 7 | import logging 8 | import click 9 | import sys 10 | import traceback 11 | 12 | 13 | @click.group() 14 | @click.option("--debug/--no-debug", default=False, help="Prints debug data") 15 | def cli(debug): 16 | """ 17 | Binance Report Bot 18 | 19 | Take a snapshot of your binance wallet, e.g. the current balances and store it for further plotting. 20 | """ 21 | if debug: 22 | brb.logger.setLevel(logging.DEBUG) 23 | 24 | 25 | @cli.command( 26 | "snapshot", 27 | short_help="Take a snapshot of your wallet", 28 | help="Take a snapshot of the binance wallet and save it for further plotting", 29 | ) 30 | def snapshot(): 31 | crypto_report = brb.report.get_report() 32 | crypto_reports = brb.report.save_report( 33 | crypto_report, brb.report.get_previous_reports() 34 | ) 35 | brb.logger.info("Snapshot saved") 36 | 37 | 38 | @cli.command( 39 | "output", 40 | short_help="Output the previously stored data", 41 | help="Output the previously stored data with 'snapshot'", 42 | ) 43 | @click.option( 44 | "--quiet/--no-quiet", 45 | default=False, 46 | help="Set to true if you don't want to print in the console or display an image", 47 | ) 48 | @click.option( 49 | "--text/--no-text", 50 | default=True, 51 | help="Can be used to prevent the generation of the text report", 52 | ) 53 | @click.option( 54 | "--graph/--no-graph", 55 | default=True, 56 | help="Can be used to prevent the generation of the graph report", 57 | ) 58 | @click.option( 59 | "-r", 60 | "--relative/--no-relative", 61 | default=False, 62 | help="If the graph should be plotted relative to its initial value", 63 | ) 64 | @click.option( 65 | "-s", 66 | "--symbol", 67 | default=conf.CURRENCY, 68 | help="""The currency the graph will be plotted on. 69 | To plot several symbols on the same graph, separate them by a coma. 70 | If plotting several symbols, the --relative option is enabled. 71 | To plot all symbols, use '*'. 72 | Default : FIAT""", 73 | ) 74 | @click.option( 75 | "-d", 76 | "--days", 77 | default=7, 78 | help="""The number of days over which the graph will be plotted. 79 | If set to 0, the graph will plot all the records. 80 | Default : 7 days""", 81 | ) 82 | @click.option( 83 | "-t", 84 | "--graph-type", 85 | type=click.Choice(["amount", "price"], case_sensitive=False), 86 | default="amount", 87 | help="Graph type. Amount : shows the equivalent amount that you are holding on your wallet. Price : shows price over time", 88 | ) 89 | @click.option( 90 | "-p", 91 | "--price-in", 92 | "ref_currency", 93 | default="USD", 94 | help="Currency in which to express the prices. Default : USD", 95 | ) 96 | def output(quiet, text, graph, relative, symbol, days, graph_type, ref_currency): 97 | if symbol == "*": 98 | symbol = conf.COINS 99 | else: 100 | symbol = symbol.split(",") 101 | for s in symbol: 102 | assert s in conf.COINS + [conf.CURRENCY] 103 | if len(symbol) > 1: 104 | relative = True 105 | reports = brb.report.get_previous_reports() 106 | 107 | msg = "" 108 | figname = None 109 | 110 | if len(reports) == 0: 111 | brb.logger.warning( 112 | "No snapshot in database. Run at least once main.py snapshot" 113 | ) 114 | else: 115 | if graph: 116 | figname, nb_plot = brb.graph.graph_report( 117 | reports, symbol, relative, days, graph_type, ref_currency 118 | ) 119 | if nb_plot <= 1: 120 | brb.logger.warning( 121 | "Less than one report has been used to generate the plot. As a result, no line will be visible on the graph. Please check that snapshots are actually made." 122 | ) 123 | if text: 124 | msg += brb.text.text_report(reports) 125 | brb.io.output(msg, figname, quiet) 126 | 127 | 128 | if __name__ == "__main__": 129 | try: 130 | cli() 131 | except Exception as e: 132 | brb.logger.error("".join(traceback.format_exception(*sys.exc_info()))) 133 | raise 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Binance Report Bot 2 | 3 | The intent of this bot is to take a snapshot of your binance wallet, e.g. the current balances and store it for further plotting. 4 | 5 | ## Install 6 | 7 | Create the file `conf_user.py` based on `conf_default.py`. If a field is missing in `conf_user.py`, it will defaults to the one in `conf_default.py`. 8 | 9 | Then run 10 | ``` 11 | pip3 install -r requirements.txt 12 | ``` 13 | 14 | ## Basic Usage 15 | To save a snapshot of the binance account run: 16 | ```bash 17 | python3 -m brb snapshot 18 | ``` 19 | To show the previously saved snapshots 20 | ```bash 21 | python3 -m brb output # --help for options 22 | ``` 23 | 24 | ## Deployment 25 | One can use crontab to use this code: 26 | ```cron 27 | 0 * * * * cd [FOLDER] ; python3 -m brb snapshot 28 | 2 19 * * * cd [FOLDER] ; python3 -m brb output --quiet 29 | ``` 30 | To have a snaphsot made every hour and a report made every day at 19:02. 31 | 32 | The output can be sent to an external service, that can be configured with the APPRISE_URL parameter. See [here](https://github.com/caronc/apprise/wiki) to choose your external service and to create your APPRISE_URL. Please use a service that supports attachment, in order to send images. Recommended services : [Discord](https://github.com/caronc/apprise/wiki/Notify_discord), [Telegram](https://github.com/caronc/apprise/wiki/Notify_telegram) or [Email](https://github.com/caronc/apprise/wiki/Notify_email). 33 | 34 | ## Output example 35 | Plot `EOS` *equivalent* holdings: 36 | ```bash 37 | python3 -m brb output --symbol EOS 38 | ``` 39 | 40 | Plot `ICX` relative equivalent holdings: 41 | ```bash 42 | python3 -m brb output --symbol ICX --relative 43 | ``` 44 | 45 | Plot `ICX` and `EOS` equivalent holdings since three days ago: 46 | ```bash 47 | python3 -m brb output --symbol ICX,EOS --days 3 48 | ``` 49 | 50 | Plot the equivalent holdings of all soins registered in the conf file: 51 | ```bash 52 | python3 -m brb output --symbol * # or '*' if using zsh 53 | ``` 54 | 55 | Plot the price of EOS and BTC over the last 10 days expressed in WIN: 56 | ```bash 57 | python3 -m brb output --symbol EOS,BTC --graph-type price --price-in WIN 58 | ``` 59 | 60 | **Note** : The *equivalent holding* is your portfolio's value in a certain currency. It represents what you would be holding if all your portfolio was under this single currency. 61 | 62 | ## CLI specification 63 | ```bash 64 | $ python3 -m brb --help 65 | Usage: python -m brb [OPTIONS] COMMAND [ARGS]... 66 | 67 | Binance Report Bot 68 | 69 | Take a snapshot of your binance wallet, e.g. the current balances and store 70 | it for further plotting. 71 | 72 | Options: 73 | --debug / --no-debug Prints debug data 74 | --help Show this message and exit. 75 | 76 | Commands: 77 | output Output the previously stored data 78 | snapshot Take a snapshot of your wallet 79 | ``` 80 | 81 | ```bash 82 | $ python3 -m brb snapshot --help 83 | Usage: main.py snapshot [OPTIONS] 84 | 85 | Take a snapshot of the binance wallet and save it for further plotting 86 | 87 | Options: 88 | --help Show this message and exit. 89 | ``` 90 | 91 | ```bash 92 | $ python3 -m brb output --help 93 | Usage: python -m brb output [OPTIONS 94 | 95 | Output the previously stored data with 'snapshot' 96 | 97 | Options: 98 | --quiet / --no-quiet Set to true if you don't want to print in 99 | the console or display an image 100 | --text / --no-text Can be used to prevent the generation of the 101 | text report 102 | --graph / --no-graph Can be used to prevent the generation of the 103 | graph report 104 | -r, --relative / --no-relative If the graph should be plotted relative to 105 | 106 | its initial value 107 | -s, --symbol TEXT The currency the graph will be plotted on. 108 | To plot several symbols on the same graph, 109 | separate them by a coma. If plotting several 110 | symbols, the --relative option is enabled. 111 | To plot all symbols, use '*'. Default : FIAT 112 | -d, --days INTEGER The number of days over which the graph will 113 | be plotted. If set to 0, the graph will plot 114 | all the records. Default : 7 days 115 | -t, --graph-type [amount|price] 116 | Graph type. Amount : shows the equivalent 117 | amount that you are holding on your wallet. 118 | Price : shows price over time 119 | -p, --price-in TEXT Currency in which to express the prices. 120 | Default : USD 121 | --help Show this message and exit. 122 | ``` 123 | --------------------------------------------------------------------------------