├── .gitignore ├── .idea ├── .gitignore ├── vcs.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── binance-chart-plugin-telegram-bot.iml ├── graph.example.png ├── graph2.example.png ├── requirements.txt ├── custom_scripts.json ├── config_example ├── LICENSE ├── README.md ├── __main__.py └── db_chart.py /.gitignore: -------------------------------------------------------------------------------- 1 | graph.png 2 | config 3 | __pycache__ -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /graph.example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozetaa/binance-chart-plugin-telegram-bot/HEAD/graph.example.png -------------------------------------------------------------------------------- /graph2.example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozetaa/binance-chart-plugin-telegram-bot/HEAD/graph2.example.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-binance==1.0.10 2 | matplotlib==3.1.2 3 | matplotlib-inline==0.1.2 4 | PyYAML==5.3.1 5 | requests==2.22.0 6 | requests-unixsocket==0.2.0 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /custom_scripts.json: -------------------------------------------------------------------------------- 1 | { 2 | "Crypto chart from Binance": "bash -c 'cd .. && python3 binance-chart-plugin-telegram-bot -bn'", 3 | "Crypto chart from databae": "bash -c 'cd .. && python3 binance-chart-plugin-telegram-bot -db'", 4 | "Update crypto chart": "bash -c 'cd ../binance-chart-plugin-telegram-bot && git pull'" 5 | } -------------------------------------------------------------------------------- /config_example: -------------------------------------------------------------------------------- 1 | [config] 2 | bot_path=/home/ubuntu/Binance2/binance-trade-bot 3 | # Datetime format: YYYY-MM-DD 4 | min_datetime = 0 5 | # display coin value for each coin 6 | enable_coin_value = 1 7 | # display Fiat evolution graph 8 | enable_fiat_evolution = 1 9 | # only display active coins 10 | show_active_coin_only = 1 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/binance-chart-plugin-telegram-bot.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Marco Zanghieri 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coin Progress Chart and Database Backup plugin for Binance Trade Bot 2 | 3 | This is a program tool based on [Binance Trade Bot]. 4 | 5 | The intent of this bot is to retrieve the local database trade infomation, e.g. the orders since last database reset, and store it for several types of plottings and text outputs. If you have also a Telegram Bot account, it will automaticaly connect to it send the results. 6 | To operate the bot creates a backup of the database to not interfere with the main bot 7 | 8 | ## Install 9 | 10 | Create the file `config` based on `config.example`. 11 | 12 | ``` 13 | [config] 14 | bot_path=your/path/to/binance-trade-bot 15 | # Datetime format: YYYY-MM-DD 16 | min_datetime = 2021-01-01 17 | # display coin value for each coin 18 | enable_coin_value = 1 19 | # display Fiat evolution graph 20 | enable_fiat_evolution = 1 21 | # only display active coins 22 | show_active_coin_only = 1 23 | ``` 24 | 25 | Then run 26 | ``` 27 | pip3 install -r requirements.txt 28 | ``` 29 | 30 | ## Usage 31 | 32 | Tt is possible to use data retrieved from bot database, it generates three plots: all the coin amount progress, all the coin valure progress, the overall value of current coin. 33 | The last two graphs are printed only if `enable_coin_value` and `enable_fiat_evolution` are set to 1, 0 otherwise: 34 | 35 | ```bash 36 | python3 -m binance-chart-plugin-telegram-bot -db 37 | ``` 38 | 39 | As alternative tt is possible to generate chart with data retrieved from Binance [WIP - shows only all coin progress of coin list]: 40 | 41 | ```bash 42 | python3 -m binance-chart-plugin-telegram-bot -bn 43 | ``` 44 | 45 | ## Telegram 46 | 47 | The output can be sent to an external service. It detectes the APPRISE file in the bot folder and connect automatically to it. 48 | 49 | ## Output example 50 | 51 | 52 |

53 | 54 |

55 | 56 |

57 | 58 |

59 | 60 | Summary sent as message: 61 | ``` 62 | Coin amount 63 | ATOM: +33.807% 64 | ADA: +68.088% 65 | BAT: +110.986% 66 | BTT: +84.621% 67 | CAKE: +9.986% 68 | DASH: +27.212% 69 | SOL: +54.906% 70 | EOS: +94.184% 71 | ETC: +72.531% 72 | ICX: +42.828% 73 | IOTA: +75.491% 74 | NEO: +16.123% 75 | OMG: +49.507% 76 | ONT: -29.614% 77 | QTUM: +27.982% 78 | ROSE: +8.613% 79 | TRX: +51.156% 80 | VET: +71.316% 81 | XMR: +34.778% 82 | 83 | Coin value 84 | ATOM: -10.524% 85 | ADA: +49.92% 86 | BAT: +130.296% 87 | BTT: +6.053% 88 | CAKE: +19.776% 89 | DASH: -10.105% 90 | SOL: +10.68% 91 | EOS: +128.894% 92 | ETC: +120.774% 93 | ICX: +16.264% 94 | IOTA: +3.663% 95 | NEO: +18.429% 96 | OMG: +7.387% 97 | ONT: -27.752% 98 | QTUM: -19.113% 99 | ROSE: +1.673% 100 | TRX: +122.067% 101 | VET: +3.275% 102 | XMR: +62.788% 103 | 104 | FIAT evolution: +133.192% 105 | ``` 106 | 107 | **Note** : This program is a tool for [Binance Trade Bot] aimed to analize the bot behaviours since the last reset. The binance coin progress evaluation feature is still work in progress. 108 | 109 | 110 | [binance trade bot]: https://github.com/edeng23/binance-trade-bot 111 | -------------------------------------------------------------------------------- /__main__.py: -------------------------------------------------------------------------------- 1 | from db_chart import * 2 | 3 | import shutil 4 | import datetime 5 | import configparser 6 | import sqlite3 7 | import yaml 8 | import requests 9 | import os 10 | import sys 11 | import inspect, os.path 12 | import matplotlib.pyplot as plt 13 | from binance.client import Client 14 | 15 | if len(sys.argv) != 2 or not (sys.argv[1] == "-bn" or sys.argv[1] == "-db"): 16 | print("You need to specify one of these modality:\n -bn Generate chart with Binance history orders\n -db Generate chart with database bot history orders") 17 | exit(1) 18 | 19 | # Path files 20 | filename = inspect.getframeinfo(inspect.currentframe()).filename 21 | path = os.path.dirname(os.path.abspath(filename)) 22 | 23 | CONFIG_PATH_FILE = path + "/config" 24 | 25 | CFG_SECTION = "config" 26 | config = configparser.ConfigParser() 27 | if not os.path.exists(CONFIG_PATH_FILE): 28 | print("No configuration file (config) found!") 29 | exit(1) 30 | else: 31 | config.read(CONFIG_PATH_FILE) 32 | 33 | BOT_PATH = os.environ.get("bot_path") or config.get(CFG_SECTION, "bot_path") 34 | ORIGINAL_DB_PATH = BOT_PATH + "/data/crypto_trading.db" 35 | DB_PATH = BOT_PATH + "/data/crypto_trading.db.backup" 36 | CFG_FL_NAME = BOT_PATH + "/user.cfg" 37 | COINLIST_PATH_FILE = BOT_PATH + "/supported_coin_list" 38 | APPRISE_PATH_FILE = BOT_PATH + "/config/apprise.yml" 39 | 40 | # Retrieve data from config file 41 | min_datetime = str(config.get(CFG_SECTION, "min_datetime")) 42 | try: 43 | assert datetime.datetime.strptime(min_datetime, "%Y-%m-%d") 44 | except: 45 | min_datetime = "" 46 | print("Wrong date format (expecting YYYY-MM-DD); Display everything.") 47 | 48 | try: 49 | enable_fiat_evolution = config.get(CFG_SECTION, "enable_fiat_evolution") == "1" 50 | except: 51 | enable_fiat_evolution = 0 52 | 53 | try: 54 | enable_coin_value = config.get(CFG_SECTION, "enable_coin_value") == "1" 55 | except: 56 | enable_coin_value = 0 57 | 58 | try: 59 | show_active_coin_only = config.get(CFG_SECTION, "show_active_coin_only") == "1" 60 | except: 61 | show_active_coin_only = 0 62 | 63 | original = ORIGINAL_DB_PATH 64 | target = DB_PATH 65 | 66 | if not os.path.exists(DB_PATH): 67 | print("No backup database (crypto_trading.db.backup) found, creating one...") 68 | f = open(DB_PATH,'w') 69 | 70 | shutil.copyfile(original, target) 71 | 72 | # Config matplotlib 73 | plt.rcParams["figure.figsize"] = (15,8) 74 | colors = plt.rcParams["axes.prop_cycle"]() 75 | 76 | # Load Telegram bot info 77 | apprise_conf = {} 78 | with open(APPRISE_PATH_FILE, 'r') as file: 79 | apprise_conf = yaml.safe_load(file) 80 | 81 | url_info = [] 82 | for url in apprise_conf['urls']: 83 | if url.split('/')[0] == 'tgram:': 84 | url_info = url.split('/') 85 | 86 | if len(url_info) == 0: 87 | exit(0) 88 | 89 | TOKEN = url_info[2] 90 | CHAT_ID = url_info[3] 91 | 92 | if sys.argv[1] == "-bn": 93 | exchange_crypto = [] 94 | exchange_crypto = get_coin_list_file(COINLIST_PATH_FILE) 95 | # Load user.cfg 96 | USER_CFG_SECTION = "binance_user_config" 97 | user_config = configparser.ConfigParser() 98 | if not os.path.exists(CFG_FL_NAME): 99 | print("No configuration file (user.cfg) found!") 100 | exit(1) 101 | else: 102 | user_config.read(CFG_FL_NAME) 103 | 104 | BINANCE_API_KEY = os.environ.get("API_KEY") or user_config.get(USER_CFG_SECTION, "api_key") 105 | BINANCE_API_SECRET_KEY = os.environ.get("API_SECRET_KEY") or user_config.get(USER_CFG_SECTION, "api_secret_key") 106 | 107 | BRIDGE_BINANCE = os.environ.get("BRIDGE_BINANCE") or config.get(CFG_SECTION, "bridge_binance") or "USDT" 108 | 109 | client = Client(BINANCE_API_KEY, BINANCE_API_SECRET_KEY) 110 | binance_coin_amount(client,exchange_crypto,min_datetime,colors,BRIDGE_BINANCE) 111 | sendImage("graph.png",TOKEN,CHAT_ID) 112 | 113 | elif sys.argv[1] == "-db": 114 | # create con object to connect 115 | # the database geeks_db.db 116 | con = sqlite3.connect(DB_PATH) 117 | 118 | # create the cursor object 119 | cur = con.cursor() 120 | exchange_crypto = [] 121 | if show_active_coin_only: 122 | exchange_crypto = get_coin_list_db(min_datetime,cur) 123 | else: 124 | exchange_crypto = get_coin_list_file(COINLIST_PATH_FILE) 125 | process_coin_amount(exchange_crypto,min_datetime,cur,colors) 126 | sendImage("graph.png",TOKEN,CHAT_ID) 127 | if enable_coin_value: 128 | process_coin_value(exchange_crypto,min_datetime,cur,colors) 129 | sendImage("graph.png",TOKEN,CHAT_ID) 130 | if enable_fiat_evolution: 131 | process_fiat_evolution(exchange_crypto,min_datetime,cur,colors) 132 | sendImage("graph2.png",TOKEN,CHAT_ID) 133 | 134 | cur.close() 135 | 136 | 137 | -------------------------------------------------------------------------------- /db_chart.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import configparser 3 | import sqlite3 4 | import subprocess 5 | import yaml 6 | import requests 7 | import io 8 | import os 9 | import inspect, os.path 10 | import matplotlib.pyplot as plt 11 | import datetime 12 | import math 13 | 14 | ### METHODS ### 15 | # Define functions 16 | def sendImage(photoname,token,chat_id): 17 | url = "https://api.telegram.org/bot"+token+"/sendPhoto"; 18 | files = {'photo': open(photoname, 'rb')} 19 | data = {'chat_id' : chat_id} 20 | r= requests.post(url, files=files, data=data) 21 | 22 | def draw_grow(xs, ys, grows, labels, title, colors): 23 | ncols = 3 24 | if len(xs) <3: 25 | ncols = len(xs) 26 | fig, axes = plt.subplots(nrows=int(math.ceil(len(ys)/3)), ncols=3, sharex=True) 27 | plt.subplots_adjust(left=None, bottom=None, right=None, top=3, wspace=None, hspace=None) 28 | 29 | for ax, x, y, grow, label in zip(axes.flat, xs, ys, grows, labels): 30 | # Get the next color from the cycler 31 | c = next(colors)["color"] 32 | 33 | ax.title.set_text(label+' ('+grow+'%)') 34 | ax.plot(x, y, label=label, color=c) 35 | ax.scatter(x, y, color=c) # dots 36 | ax.set_xticks(x) 37 | ax.grid(False) 38 | fig.legend(loc="upper left") 39 | 40 | # define y position of suptitle to be ~20% of a row above the top row 41 | y_title_pos = axes[0][0].get_position().get_points()[1][1]+(1/int(len(ys)/3))*0.5 42 | fig.suptitle(title, y=y_title_pos, fontsize=14) 43 | 44 | def draw_sum(x,y,grow,label, title, colors): 45 | fig, axes = plt.subplots(nrows=1, ncols=1, sharex=True) 46 | plt.subplots_adjust(left=None, bottom=None, right=None, top=3, wspace=None, hspace=None) 47 | 48 | c = next(colors)["color"] 49 | axes.title.set_text(title+' ('+grow+'%)') 50 | axes.plot(x, y, label=label, color=c) 51 | axes.scatter(x, y, color=c) # dots 52 | axes.set_xticks(x) 53 | axes.grid(False) 54 | fig.legend(loc="upper left") 55 | 56 | 57 | ### BINANCE ### 58 | def filledOrder(order): 59 | MIN_TIMESTAMP_BINANCE = int("1518308894652") 60 | return order["status"] == 'FILLED' and order["side"] == 'BUY' and order["time"] >= MIN_TIMESTAMP_BINANCE 61 | 62 | def mapOrder(order): 63 | return float(order["executedQty"]) 64 | 65 | def binance_coin_amount(client, exchange_crypto, min_datetime, colors, bridge_binance): 66 | 67 | coin_grow = [] 68 | coin_perc = [] 69 | trades_number = [] 70 | grow_text= "" 71 | 72 | for crypto in range(0, len(exchange_crypto)): 73 | orders = client.get_all_orders(symbol=exchange_crypto[crypto]+bridge_binance) 74 | 75 | orders = list(filter(filledOrder, orders)) 76 | orders = list(map(mapOrder, orders)) 77 | 78 | perc = round(((orders[-1] - orders[0])/orders[0])*100, 3) if len(orders) > 0 else 0.0 79 | perc_str = "+"+str(perc) if perc > 0 else str(perc) 80 | 81 | coin_grow.append(orders) 82 | trades_number.append(list(range(1,len(orders)+1))) 83 | coin_perc.append(perc_str) 84 | grow_text = grow_text + exchange_crypto[crypto]+": "+(str(orders[0])+" -> "+str(orders[-1])+" ("+perc_str+"%)" if len(orders) >= 2 else "N.A.")+"\n" 85 | 86 | draw_grow(trades_number, coin_grow, coin_perc, exchange_crypto,"Coin amount evolution",colors) 87 | # Send result 88 | plt.savefig("graph.png", bbox_inches='tight') 89 | print(grow_text) 90 | 91 | def process_coin_amount(exchange_crypto, min_datetime, cur, colors): 92 | coin_grow = [] 93 | coin_perc = [] 94 | trades_number = [] 95 | grow_text= "" 96 | 97 | for crypto in range(0,len(exchange_crypto)): 98 | order_list = [] 99 | 100 | # retrieving coin list of orders 101 | sqlite_select_query = "select alt_trade_amount from trade_history where alt_coin_id=? and state='COMPLETE' and selling=0 and DATE(datetime) > '" + str(min_datetime) +"'" 102 | 103 | orders = cur.execute(sqlite_select_query,[exchange_crypto[crypto]]) 104 | 105 | for order in orders.fetchall(): 106 | order_list.append(order[0]) 107 | 108 | perc = round(((order_list[-1] - order_list[0])/order_list[0])*100, 3) if len(order_list) > 0 else 0.0 109 | perc_str = "+"+str(perc) if perc > 0 else str(perc) 110 | 111 | coin_grow.append(order_list) 112 | trades_number.append(list(range(1,len(order_list)+1))) 113 | coin_perc.append(perc_str) 114 | grow_text = grow_text + exchange_crypto[crypto]+": "+perc_str+"% \n" 115 | 116 | draw_grow(trades_number, coin_grow, coin_perc, exchange_crypto, "Coin amount evolution",colors) 117 | 118 | plt.savefig("graph.png", bbox_inches='tight') 119 | print("Coin amount") 120 | print(grow_text) 121 | 122 | 123 | def process_coin_value(exchange_crypto, min_datetime, cur, colors): 124 | coin_grow = [] 125 | coin_perc = [] 126 | trades_number = [] 127 | grow_text= "" 128 | 129 | for crypto in range(0,len(exchange_crypto)): 130 | order_list = [] 131 | 132 | # retrieving coin list of orders 133 | sqlite_select_query = "select crypto_trade_amount from trade_history where alt_coin_id=? and state='COMPLETE' and selling=0 and DATE(datetime) > '" + str(min_datetime) +"'" 134 | orders = cur.execute(sqlite_select_query,[exchange_crypto[crypto]]) 135 | 136 | for order in orders.fetchall(): 137 | order_list.append(order[0]) 138 | 139 | perc = round(((order_list[-1] - order_list[0])/order_list[0])*100, 3) if len(order_list) > 0 else 0.0 140 | perc_str = "+"+str(perc) if perc > 0 else str(perc) 141 | 142 | coin_grow.append(order_list) 143 | trades_number.append(list(range(1,len(order_list)+1))) 144 | coin_perc.append(perc_str) 145 | grow_text = grow_text + exchange_crypto[crypto]+": "+perc_str+"% \n" 146 | 147 | draw_grow(trades_number, coin_grow, coin_perc, exchange_crypto, "Coin value evolution",colors) 148 | 149 | plt.savefig("graph.png", bbox_inches='tight') 150 | print("Coin value") 151 | print(grow_text) 152 | 153 | 154 | def process_fiat_evolution(exchange_crypto, min_datetime, cur, colors): 155 | # retrieve FIAT evolution 156 | coin_grow = [] 157 | coin_perc = [] 158 | trades_number = [] 159 | order_list = [] 160 | grow_text= "" 161 | sqlite_select_query = "select crypto_trade_amount from trade_history where state='COMPLETE' and selling=0 and alt_coin_id <> 'BNB' and DATE(datetime) > '" + str(min_datetime) +"'" 162 | orders = cur.execute(sqlite_select_query) 163 | for order in orders.fetchall(): 164 | order_list.append(order[0]) 165 | 166 | perc = round(((order_list[-1] - order_list[0])/order_list[0])*100, 3) if len(order_list) > 0 else 0.0 167 | perc_str = "+"+str(perc) if perc > 0 else str(perc) 168 | 169 | coin_grow.append(order_list) 170 | trades_number.append(list(range(1,len(order_list)+1))) 171 | coin_perc.append(perc_str) 172 | grow_text = grow_text +"FIAT evolution: "+perc_str+"% \n" 173 | exchange_crypto.clear() 174 | exchange_crypto.append("SUM") 175 | draw_sum(trades_number[0], coin_grow[0], perc_str, "SUM", "Overall value evolution [USDT] ",colors) 176 | 177 | plt.savefig("graph2.png", bbox_inches='tight') 178 | print(grow_text) 179 | 180 | # Load coin list from the file 181 | def get_coin_list_file(coinlist_file): 182 | exchange_crypto = [] 183 | if os.path.exists(coinlist_file): 184 | with open(coinlist_file) as rfh: 185 | for line in rfh: 186 | line = line.strip() 187 | if not line or line.startswith("#") or line in exchange_crypto: 188 | continue 189 | exchange_crypto.append(line) 190 | 191 | return exchange_crypto 192 | 193 | # Load active coin list from the db 194 | def get_coin_list_db(min_datetime, cur): 195 | exchange_crypto = [] 196 | 197 | sqlite_select_query = "select distinct alt_coin_id from trade_history where state='COMPLETE' and selling=0 and DATE(datetime) > '" + str(min_datetime) +"'" 198 | active_coins = cur.execute(sqlite_select_query) 199 | 200 | for coin in active_coins: 201 | exchange_crypto.append(coin[0]) 202 | 203 | return exchange_crypto 204 | --------------------------------------------------------------------------------