├── .gitignore ├── Binance_Detect_Moonings.py ├── Dockerfile ├── FAQ.md ├── LICENSE ├── README.md ├── TODO ├── bot ├── dynamics.py ├── grab.py ├── report.py ├── session.py ├── settings.py ├── tickers_list.py └── trade.py ├── config.example.yml ├── creds.example.yml ├── docker-compose.yml ├── docs └── proposed definitions for session_struct.py ├── helpers ├── __pycache__ │ ├── handle_creds.cpython-36.pyc │ ├── handle_creds.cpython-37.pyc │ ├── parameters.cpython-36.pyc │ └── parameters.cpython-37.pyc ├── handle_creds.py └── parameters.py ├── ignorelist.txt ├── modules ├── custsignalmod.py ├── custsignalmod_speed.py ├── pausebotmod.py ├── rsi_signalmod_nigec.py ├── rsi_stoch_signalmod_djcommie.py ├── signalsamplemod.py └── ta_indicator_signalmod_firewatch.py ├── requirements.txt ├── signals └── readme.md ├── signalsample.py ├── signalsample.txt ├── tickers ├── temp.txt ├── tickers.txt ├── tickers_BNB.txt ├── tickers_BTC.old ├── tickers_BTC.txt ├── tickers_BUSD.txt ├── tickers_ETH.old ├── tickers_ETH.txt ├── tickers_ETH_MCAP.txt ├── tickers_USDT.old └── tickers_USDT.txt └── utilities └── sell-remaining-coins.py /.gitignore: -------------------------------------------------------------------------------- 1 | creds.yml 2 | session_info.json 3 | test_coins_bought.json 4 | coins_bought.json 5 | trades.txt 6 | signals/* 7 | tickers/* 8 | .venv 9 | __pycache__ 10 | config.yml 11 | dockerfile 12 | docker-compose.yml 13 | tickers.* 14 | session_struct.py 15 | log.csv 16 | /env 17 | /.vscode -------------------------------------------------------------------------------- /Binance_Detect_Moonings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Disclaimer 3 | 4 | All investment strategies and investments involve risk of loss. 5 | Nothing contained in this program, scripts, code or repositoy should be 6 | construed as investment advice.Any reference to an investment's past or 7 | potential performance is not, and should not be construed as, a recommendation 8 | or as a guarantee of any specific outcome or profit. 9 | 10 | By using this program you accept all liabilities, 11 | and that no claims can be made against the developers, 12 | or others connected with the program. 13 | """ 14 | 15 | 16 | # use for environment variables 17 | from genericpath import exists 18 | import os 19 | from modules.rsi_signalmod_nigec import FULL_LOG 20 | 21 | # use if needed to pass args to external modules 22 | import sys 23 | 24 | # used to create threads & dynamic loading of modules 25 | import threading 26 | import importlib 27 | 28 | # used for directory handling 29 | import glob 30 | 31 | #gogo MOD telegram needs import request 32 | import requests 33 | 34 | # Needed for colorful console output Install with: python3 -m pip install colorama (Mac/Linux) or pip install colorama (PC) 35 | from colorama import init 36 | init() 37 | 38 | # needed for the binance API / websockets / Exception handling 39 | from binance.client import Client 40 | from binance.exceptions import BinanceAPIException 41 | from requests.exceptions import ReadTimeout, ConnectionError 42 | 43 | # used for dates 44 | from datetime import date, datetime, timedelta 45 | import time 46 | 47 | # used to repeatedly execute the code 48 | from itertools import count 49 | 50 | # used to store trades and sell assets 51 | import json 52 | 53 | # Load helper modules 54 | from helpers.parameters import ( 55 | parse_args, load_config 56 | ) 57 | 58 | # Load creds modules 59 | from helpers.handle_creds import ( 60 | load_correct_creds, test_api_key, 61 | load_telegram_creds 62 | ) 63 | 64 | #import bot extension functions including main function for trading 65 | from bot.settings import * 66 | from bot.dynamics import * 67 | from bot.report import * 68 | from bot.session import * 69 | from bot.tickers_list import * 70 | from bot.grab import * 71 | from bot.trade import * 72 | 73 | # print with timestamps 74 | old_out = sys.stdout 75 | class St_ampe_dOut: 76 | """Stamped stdout.""" 77 | nl = True 78 | def write(self, x: str) -> None: 79 | """Write function overloaded.""" 80 | if x == '\n': 81 | old_out.write(x) 82 | self.nl = True 83 | elif self.nl: 84 | old_out.write(f'{txcolors.DIM}[{str(datetime.now().replace(microsecond=0))}]{txcolors.DEFAULT} {x}') 85 | self.nl = False 86 | else: 87 | old_out.write(x) 88 | 89 | def flush(self) -> None: 90 | pass 91 | 92 | sys.stdout = St_ampe_dOut() 93 | 94 | 95 | def pause_bot() -> None: 96 | '''Pause the script when external indicators detect a bearish trend in the market''' 97 | global bot_paused, hsp_head, settings_struct 98 | global LIST_AUTOCREATE 99 | # start counting for how long the bot has been paused 100 | start_time = time.perf_counter() 101 | 102 | while os.path.isfile("signals/paused.exc"): 103 | 104 | if bot_paused == False: 105 | print(f"{txcolors.WARNING}Buying paused due to negative market conditions, stop loss and take profit will continue to work...{txcolors.DEFAULT}") 106 | # sell all bought coins if bot is bot_paused 107 | if STOP_LOSS_ON_PAUSE == True: 108 | session_struct['sell_all_coins'] = True 109 | bot_paused = True 110 | 111 | # sell all bought coins if bot is bot_paused 112 | if STOP_LOSS_ON_PAUSE == True: 113 | session_struct['sell_all_coins'] = True 114 | 115 | # Sell function needs to work even while paused 116 | coins_sold = sell_coins() 117 | remove_from_portfolio(coins_sold) 118 | get_price(True) 119 | 120 | # pausing here 121 | 122 | #gogo MOD todo more verbose having all the report things in it!!!!! 123 | report_update() 124 | 125 | time.sleep(settings_struct['RECHECK_INTERVAL']) 126 | 127 | else: 128 | # stop counting the pause time 129 | stop_time = time.perf_counter() 130 | time_elapsed = timedelta(seconds=int(stop_time-start_time)) 131 | 132 | # resume the bot and set pause_bot to False 133 | if bot_paused == True: 134 | print(f"{txcolors.WARNING}Resuming buying due to positive market conditions, total sleep time: {time_elapsed}{txcolors.DEFAULT}") 135 | tickers_list() 136 | session_struct['dynamic'] = 'reset' 137 | session_struct['sell_all_coins'] = False 138 | bot_paused = False 139 | 140 | return 141 | 142 | 143 | if __name__ == '__main__': 144 | 145 | mymodule = {} 146 | 147 | print('Press Ctrl-Q to stop the script') 148 | 149 | if not TEST_MODE: 150 | if not args.notimeout: # if notimeout skip this (fast for dev tests) 151 | print('WARNING: test mode is disabled in the configuration, you are using live funds.') 152 | print('WARNING: Waiting 10 seconds before live trading as a security measure!') 153 | time.sleep(10) 154 | 155 | signals = glob.glob("signals/*.exs") 156 | for filename in signals: 157 | for line in open(filename): 158 | try: 159 | os.remove(filename) 160 | except: 161 | if DEBUG: print(f'{txcolors.WARNING}Could not remove external signalling file {filename}{txcolors.DEFAULT}') 162 | 163 | if os.path.isfile("signals/paused.exc"): 164 | try: 165 | os.remove("signals/paused.exc") 166 | except: 167 | if DEBUG: print(f'{txcolors.WARNING}Could not remove external signalling file {filename}{txcolors.DEFAULT}') 168 | 169 | # load signalling modules 170 | try: 171 | if SIGNALLING_MODULES != None and len(SIGNALLING_MODULES) > 0: 172 | for module in SIGNALLING_MODULES: 173 | print(f'Starting {module}') 174 | mymodule[module] = importlib.import_module(module) 175 | t = threading.Thread(target=mymodule[module].do_work, args=()) 176 | t.daemon = True 177 | t.start() 178 | time.sleep(2) 179 | else: 180 | print(f'No modules to load {SIGNALLING_MODULES}') 181 | except Exception as e: 182 | print(e) 183 | 184 | 185 | # get decimal places for each coin as used by Binance 186 | get_symbol_info() 187 | 188 | # load historical price for PAIR_WITH 189 | get_historical_price() 190 | 191 | # seed initial prices 192 | get_price() 193 | 194 | #load previous session parameters 195 | session('load') 196 | 197 | #report that bot is started to defined communication channels 198 | report('message', 'Bot initiated') 199 | 200 | # start logging to CSV 201 | c = threading.Thread(target=csv_log, args=(60,)) 202 | c.daemon = True 203 | c.start() 204 | 205 | while True: 206 | 207 | ts = time.time() 208 | 209 | pause_bot() 210 | 211 | #main trading function 212 | trade_crypto() 213 | 214 | #recreate tickers list and reload it 215 | reload_tickers() 216 | 217 | #use dynamic settings to adjust change in price and take profit based on market support and resistance 218 | dynamic_settings('mrs_settings', TIME_DIFFERENCE, RECHECK_INTERVAL) 219 | 220 | #gogos MOD to adjust dynamically stoploss trailingstop loss and take profit based on wins 221 | dynamic_settings(type, TIME_DIFFERENCE, RECHECK_INTERVAL) 222 | 223 | #session calculations like unrealised potential etc 224 | session('calc') 225 | 226 | #save session data to session_info file 227 | session('save') 228 | 229 | #write report to console 230 | report_update() 231 | 232 | #sleep for RECHECK_INTERVAL time 233 | ts_sleep = settings_struct['RECHECK_INTERVAL'] - ( time.time() - ts ) 234 | if (ts_sleep > 0 ) : 235 | time.sleep(ts_sleep) 236 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY requirements.txt ./ 6 | RUN pip install --no-cache-dir -r requirements.txt -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Question 2 | 3 | ## 4 | 5 | ## Errors and solutions 6 | 7 | 8 | | Error | Solution | 9 | |----------|-------------| 10 | | `APIError(code=-1021): Timestamp for this request was 1000ms ahead of the server's time` | Update your computer/servers ntp server to `time.nist.gov` | 11 | |`'NoneType' object has no attribute 'encode'`| Your API key is not correct. Ensure the environment (mainnet/testnet) matches the API keys used | 12 | |`Insufficient Funds` Error| Make sure you have the available USDT. Make sure you dont have lots of it on order. Ensure you QUANTITY is at least 15. | 13 | | Other Binance API Errors| Go take a look at the [Binance API documentation Exceptions](https://github.com/binance/binance-spot-api-docs/blob/master/errors.md) page | 14 | 15 | ## Questions and Answers 16 | 17 | | Question | Answer | 18 | |----------|-------------| 19 | |Why am I getting weird / unreliable values using testnet| Testnet isn't for testing strategies. It's for testing functionality. We've talked about adding functionality to the main script to run against mainnet but with bogus buys. nothing yet. | 20 | | What type of funds are required for this to work in PROD | Ensure you account has the following: | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CyberPunkMetalHead 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 | # Dynamic Binance Trading Bot 2 | 3 | ## Description 4 | This Binance trading bot analyses the changes in price across all coins on Binance it constructs so called "market resistance" and "market support" based on those prices and buys coins that pass support lines and sells coins that pass resistance line. 5 | 6 | 7 | 8 | ## Internal Operation 9 | 10 | - Bot automatically creates list from binance coins and sorts it based on volume or price changes 11 | - Bot then scans price changes based on given time interval and creates market resistance and support 12 | - Bot puts coins that passed support line into trailing buy and waits for price to stop falling and then places a buy 13 | - Bot then monitors bought coins and pools for prices every recheck interval and if price is over market resistance 14 | activates trailing stoploss 15 | - Bot sells coin and logs if its win or loss for internal calculations (dynamic settings adjustment, stoploss etc) 16 | - Bot adjuststs all settings based on previous win/loss ratio and based on accumulated profit detects 17 | - Bot recreates list and restarts cycle 18 | 19 | ## Features 20 | 21 | - Automatic coins list creation polling binance coins list 22 | - Automatic list sorting based on VOLUME or PRICECHANGE 23 | - Automatic stoploss modification based on closed trades percent and win/loss ratio 24 | - Automatic buy treshold based on market support and TAKE PROFIT based on market resistance 25 | - Automatic buy trailing based on price dropping/rising 26 | - Automatic session restart after bot stopped with all current strategies 27 | - Reporting to discord channel and telegram optional 28 | - Automatic changing of settings from config file on the fly based on wins / losses 29 | 30 | ## WIKI 31 | https://github.com/goranjovic55/Binance-Trading-Bot/wiki 32 | 33 | ## READ BEFORE USE 34 | 1. If you use the `TEST_MODE: False` in your config, you will be using REAL money. 35 | 2. To ensure you do not do this, ALWAYS check the `TEST_MODE` configuration item in the config.yml file.. 36 | 37 | ## DISCORD CHANNEL 38 | https://discord.gg/AjJYXy3C 39 | 40 | ## 💥 Disclaimer 41 | 42 | All investment strategies and investments involve risk of loss. 43 | **Nothing contained in this program, scripts, code or repositoy should be construed as investment advice.** 44 | Any reference to an investment's past or potential performance is not, 45 | and should not be construed as, a recommendation or as a guarantee of 46 | any specific outcome or profit. 47 | By using this program you accept all liabilities, and that no claims can be made against the developers or others connected with the program. 48 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | add marketstate analysis so we know overall market tendency 2 | implement trailing take profit modulation based on some performance settings 3 | implement modulation of trailing buy treshold from dynmaic up and down 4 | -------------------------------------------------------------------------------- /bot/dynamics.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | 9 | from helpers.parameters import ( 10 | parse_args, load_config 11 | ) 12 | 13 | # Load creds modules 14 | from helpers.handle_creds import ( 15 | load_correct_creds, test_api_key, 16 | load_telegram_creds 17 | ) 18 | 19 | from bot.settings import * 20 | 21 | def dynamic_settings(type: str, TIME_DIFFERENCE: float, RECHECK_INTERVAL: float) -> None: 22 | global session_struct, settings_struct, trading_struct 23 | 24 | DYNAMIC_STOP_LOSS = settings_struct['STOP_LOSS'] 25 | 26 | if (session_struct['win_trade_count'] > 0) and (session_struct['loss_trade_count'] > 0): 27 | WIN_LOSS_PERCENT = round((session_struct['win_trade_count'] / (session_struct['win_trade_count'] + session_struct['loss_trade_count'])) * 100, 2) 28 | else: 29 | WIN_LOSS_PERCENT = 100 30 | 31 | if DYNAMIC_SETTINGS: 32 | 33 | # modifying of STOPLOSS based on closedtrades/tradeslots * win/loss percent and trailing stoploss based on profit to trade ratio 34 | # so we can not loose more than we can afford to 35 | 36 | if WIN_LOSS_PERCENT > 0 and session_struct['trade_slots'] > 0 and trading_struct['stop_loss_adjust'] == True: 37 | 38 | if session_struct['closed_trades_percent'] > (STOP_LOSS * TRADE_SLOTS) : 39 | DYNAMIC_STOP_LOSS = session_struct['closed_trades_percent'] / TRADE_SLOTS * Decimal(str(WIN_LOSS_PERCENT)) / Decimal('100') 40 | settings_struct['STOP_LOSS'] = (settings_struct['STOP_LOSS'] + DYNAMIC_STOP_LOSS) / Decimal('2') 41 | settings_struct['TRAILING_STOP_LOSS'] = settings_struct['TRAILING_STOP_LOSS'] + session_struct['profit_to_trade_ratio'] / Decimal('2') 42 | 43 | if session_struct['closed_trades_percent'] < (STOP_LOSS * TRADE_SLOTS): 44 | DYNAMIC_STOP_LOSS = session_struct['closed_trades_percent'] / TRADE_SLOTS * Decimal(str(WIN_LOSS_PERCENT)) / Decimal('100') 45 | settings_struct['STOP_LOSS'] = settings_struct['STOP_LOSS'] + DYNAMIC_STOP_LOSS 46 | settings_struct['TRAILING_STOP_LOSS'] = settings_struct['TRAILING_STOP_LOSS'] + (TRAILING_STOP_LOSS / DYNAMIC_MIN_MAX) 47 | 48 | trading_struct['stop_loss_adjust'] = False 49 | 50 | # here we risk more untill we go above with our stoploss over closed trades later we apply limited loss strategy 51 | if session_struct['closed_trades_percent'] < settings_struct['STOP_LOSS'] * DYNAMIC_MIN_MAX: 52 | settings_struct['STOP_LOSS'] = STOP_LOSS * DYNAMIC_MIN_MAX 53 | settings_struct['TRAILING_STOP_LOSS'] = TRAILING_STOP_LOSS / DYNAMIC_MIN_MAX 54 | 55 | #limiting STOP_LOSS TIME_DIFFERENCE and TRAILING_STOP_LOSS to dynamic min and max values 56 | if settings_struct['STOP_LOSS'] < STOP_LOSS / DYNAMIC_MIN_MAX: 57 | settings_struct['STOP_LOSS'] = STOP_LOSS / DYNAMIC_MIN_MAX 58 | 59 | if settings_struct['TRAILING_STOP_LOSS'] < TRAILING_STOP_LOSS / DYNAMIC_MIN_MAX: 60 | settings_struct['TRAILING_STOP_LOSS'] = TRAILING_STOP_LOSS /DYNAMIC_MIN_MAX 61 | 62 | if settings_struct['TIME_DIFFERENCE'] < TIME_DIFFERENCE / DYNAMIC_MIN_MAX: 63 | settings_struct['TIME_DIFFERENCE'] = TIME_DIFFERENCE / DYNAMIC_MIN_MAX 64 | 65 | #if settings_struct['STOP_LOSS'] > STOP_LOSS * DYNAMIC_MIN_MAX: 66 | #settings_struct['STOP_LOSS'] = STOP_LOSS * DYNAMIC_MIN_MAX 67 | if settings_struct['TIME_DIFFERENCE'] > TIME_DIFFERENCE * DYNAMIC_MIN_MAX: 68 | settings_struct['TIME_DIFFERENCE'] = TIME_DIFFERENCE * DYNAMIC_MIN_MAX 69 | if settings_struct['TRAILING_STOP_LOSS'] > STOP_LOSS * DYNAMIC_MIN_MAX: 70 | settings_struct['TRAILING_STOP_LOSS'] = TRAILING_STOP_LOSS * DYNAMIC_MIN_MAX 71 | 72 | if settings_struct['HOLDING_PRICE_THRESHOLD'] < HOLDING_PRICE_THRESHOLD: 73 | settings_struct['HOLDING_PRICE_THRESHOLD'] = HOLDING_PRICE_THRESHOLD 74 | 75 | # this part checks to see if last trade was a win if it was it checks to see what was previous dynamics state and if it was up 76 | # it will go up with TIMEDIFFERENCE by % percent and if it was down it will go down with it, also it will TRIGGER 77 | # all other settings adding % on every win, also timedifference % applied is lowered by TIMEDIFFERNCE % on every consecutive win/loss trigger 78 | 79 | if session_struct['last_trade_won'] == True: 80 | if session_struct['dynamics_state'] == 'up': 81 | settings_struct['TIME_DIFFERENCE'] = settings_struct['TIME_DIFFERENCE'] + (settings_struct['TIME_DIFFERENCE'] * settings_struct['DYNAMIC_WIN_LOSS_UP']) /100 82 | session_struct['dynamics_state'] = 'up' 83 | 84 | if session_struct['dynamics_state'] == 'down': 85 | settings_struct['TIME_DIFFERENCE'] = settings_struct['TIME_DIFFERENCE'] - (settings_struct['TIME_DIFFERENCE'] * settings_struct['DYNAMIC_WIN_LOSS_DOWN']) /100 86 | session_struct['dynamics_state'] = 'down' 87 | 88 | session_struct['last_trade_won'] = 'none' 89 | type = 'performance_adjust_up' 90 | 91 | # this code will change "direction" for timedifference change aka if it was up it will go down and vice versa on next win 92 | # to prevent accumulating losses on same timedifference and to sync with market better, also it will subtract all other 93 | # dynamic settings by corresponding numberes to protect from consecutive losses 94 | 95 | if session_struct['last_trade_won'] == False: 96 | if session_struct['dynamics_state'] == 'up': 97 | session_struct['dynamics_state'] = 'down' 98 | 99 | if session_struct['dynamics_state'] == 'down': 100 | session_struct['dynamics_state'] = 'up' 101 | 102 | session_struct['last_trade_won'] = 'none' 103 | type = 'performance_adjust_down' 104 | 105 | 106 | # this part of code jumps to different part of timedifference scale this is to protect from consecutive losses 107 | # and to change context so bot goes from 5 minute range to 50 minute range for example if those were corresponding 108 | # scale values, it jumps on TRADE_SLOTS / DYNAMIC_MIN_MAX consecutive losses 109 | # also it counts consecutive wins and future implementation of modifiyng dynamic winloss up and down wil be based on this 110 | # idea is to lower dynamic winloss up for example when we are winning so we can cut our losses upon sudden loss aftere win streak 111 | # if market turns upside down on us we will protect atuomatically from loosing streak that way 112 | 113 | if trading_struct['consecutive_loss'] > (TRADE_SLOTS / DYNAMIC_MIN_MAX): 114 | if settings_struct['TIME_DIFFERENCE'] > TIME_DIFFERENCE: 115 | settings_struct['TIME_DIFFERENCE'] = TIME_DIFFERENCE - (settings_struct['TIME_DIFFERENCE'] / TIME_DIFFERENCE * TIME_DIFFERENCE/DYNAMIC_MIN_MAX) 116 | print(f"TIMEFRAME JUMP TRIGGERED! TIME_DIFFERENCE: {settings_struct['TIME_DIFFERENCE']}") 117 | 118 | if settings_struct['TIME_DIFFERENCE'] < TIME_DIFFERENCE: 119 | settings_struct['TIME_DIFFERENCE'] = (TIME_DIFFERENCE * DYNAMIC_MIN_MAX) - (settings_struct['TIME_DIFFERENCE']/TIME_DIFFERENCE * TIME_DIFFERENCE * DYNAMIC_MIN_MAX) 120 | print(f"TIMEFRAME JUMP TRIGGERED! TIME_DIFFERENCE: {settings_struct['TIME_DIFFERENCE']}") 121 | 122 | settings_struct['DYNAMIC_WIN_LOSS_DOWN'] = settings_struct['DYNAMIC_WIN_LOSS_DOWN'] + (settings_struct['DYNAMIC_WIN_LOSS_DOWN'] * DYNAMIC_WIN_LOSS_DOWN) / 100 123 | settings_struct['DYNAMIC_WIN_LOSS_UP'] = settings_struct['DYNAMIC_WIN_LOSS_UP'] - (settings_struct['DYNAMIC_WIN_LOSS_UP'] * DYNAMIC_WIN_LOSS_UP) / 100 124 | 125 | # this code limits DYNAMICS WINLOSS UP and down to multiply of dynamic min max and divide so it has lower and upper limits 126 | 127 | if settings_struct['DYNAMIC_WIN_LOSS_DOWN'] < DYNAMIC_WIN_LOSS_DOWN / DYNAMIC_MIN_MAX: settings_struct['DYNAMIC_WIN_LOSS_DOWN'] = DYNAMIC_WIN_LOSS_DOWN / DYNAMIC_MIN_MAX 128 | if settings_struct['DYNAMIC_WIN_LOSS_UP'] > DYNAMIC_WIN_LOSS_UP * DYNAMIC_MIN_MAX: settings_struct['DYNAMIC_WIN_LOSS_UP'] = DYNAMIC_WIN_LOSS_UP * DYNAMIC_MIN_MAX 129 | 130 | trading_struct['consecutive_loss'] = 0 131 | 132 | # when we have consecutive wins we lower our dynamic winloss up multiplier and we get our down multiplier higher so we react with more gain once market turns 133 | # in different direction aka we won 10 trades and 4 of them are in a row so our dynamics will get our stoploss higher and all other settings 134 | # but if we loose in this condition losses can be havey if market turned downtrend on us so we up the gain on losses on every win and once we loose we 135 | # respond with more force and we cut our losses more forcefully, also if we are on a loosing streak and we win we will up our dynamics multiplier more forcefully etc 136 | # this is a bit experimental and needs testing 137 | 138 | if trading_struct['consecutive_win'] > (TRADE_SLOTS / DYNAMIC_MIN_MAX): 139 | 140 | settings_struct['DYNAMIC_WIN_LOSS_DOWN'] = settings_struct['DYNAMIC_WIN_LOSS_DOWN'] - (settings_struct['DYNAMIC_WIN_LOSS_DOWN'] * DYNAMIC_WIN_LOSS_DOWN) / 100 141 | settings_struct['DYNAMIC_WIN_LOSS_UP'] = settings_struct['DYNAMIC_WIN_LOSS_UP'] + (settings_struct['DYNAMIC_WIN_LOSS_UP'] * DYNAMIC_WIN_LOSS_UP) / 100 142 | 143 | # this code limits DYNAMICS WINLOSS UP and down to multiply of dynamic min max and divide so it has lower and upper limits 144 | 145 | if settings_struct['DYNAMIC_WIN_LOSS_DOWN'] > DYNAMIC_WIN_LOSS_DOWN * DYNAMIC_MIN_MAX: settings_struct['DYNAMIC_WIN_LOSS_DOWN'] = DYNAMIC_WIN_LOSS_DOWN * DYNAMIC_MIN_MAX 146 | if settings_struct['DYNAMIC_WIN_LOSS_UP'] < DYNAMIC_WIN_LOSS_UP / DYNAMIC_MIN_MAX: settings_struct['DYNAMIC_WIN_LOSS_UP'] = DYNAMIC_WIN_LOSS_UP / DYNAMIC_MIN_MAX 147 | 148 | trading_struct['consecutive_win'] = 0 149 | 150 | #print(f'{txcolors.NOTICE}>> TRADE_WON: {session_struct['last_trade_won']} and DYNAMICS_STATE: {session_struct['dynamics_state']} <<<{txcolors.DEFAULT}') 151 | 152 | # this part of code alteres trading settings for next trade based on win/loss so if we win all our settings get more 153 | # if we loose they get less so we protect from consecutive losses and we are more "brave" on consecutive wins 154 | 155 | if type == 'performance_adjust_up': 156 | settings_struct['STOP_LOSS'] = settings_struct['STOP_LOSS'] + (settings_struct['STOP_LOSS'] * DYNAMIC_WIN_LOSS_UP) / Decimal('100') 157 | settings_struct['TAKE_PROFIT'] = settings_struct['TAKE_PROFIT'] + (settings_struct['TAKE_PROFIT'] * DYNAMIC_WIN_LOSS_UP) / Decimal('100') 158 | settings_struct['TRAILING_STOP_LOSS'] = settings_struct['TRAILING_STOP_LOSS'] + (settings_struct['TRAILING_STOP_LOSS'] * DYNAMIC_WIN_LOSS_UP) / Decimal('100') 159 | settings_struct['CHANGE_IN_PRICE_MAX'] = settings_struct['CHANGE_IN_PRICE_MAX'] + (settings_struct['CHANGE_IN_PRICE_MAX'] * DYNAMIC_WIN_LOSS_UP) /100 160 | settings_struct['CHANGE_IN_PRICE_MIN'] = settings_struct['CHANGE_IN_PRICE_MIN'] + (settings_struct['CHANGE_IN_PRICE_MIN'] * DYNAMIC_WIN_LOSS_UP) /100 161 | settings_struct['DYNAMIC_CHANGE_IN_PRICE'] = settings_struct['DYNAMIC_CHANGE_IN_PRICE'] + (settings_struct['DYNAMIC_CHANGE_IN_PRICE'] * DYNAMIC_WIN_LOSS_UP) / Decimal('100') 162 | 163 | settings_struct['HOLDING_PRICE_THRESHOLD'] = settings_struct['HOLDING_PRICE_THRESHOLD'] + (settings_struct['HOLDING_PRICE_THRESHOLD'] * DYNAMIC_WIN_LOSS_UP) / Decimal('100') 164 | session_struct['dynamic'] = 'none' 165 | 166 | print(f"{txcolors.NOTICE}>> DYNAMICS_UP Changing STOP_LOSS: {settings_struct['STOP_LOSS']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_UP']:.2f} - TAKE_PROFIT: {settings_struct['TAKE_PROFIT']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_UP']:.2f} - TRAILING_STOP_LOSS: {settings_struct['TRAILING_STOP_LOSS']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_UP']:.2f} CIP:{settings_struct['CHANGE_IN_PRICE_MIN']:.4f}/{settings_struct['CHANGE_IN_PRICE_MAX']:.4f}/{settings_struct['DYNAMIC_WIN_LOSS_UP']:.2f} HTL: {settings_struct['HOLDING_TIME_LIMIT']:.2f} TD: {settings_struct['TIME_DIFFERENCE']} RI: {settings_struct['RECHECK_INTERVAL']} <<{txcolors.DEFAULT}") 167 | 168 | if type == 'performance_adjust_down': 169 | settings_struct['STOP_LOSS'] = settings_struct['STOP_LOSS'] - (settings_struct['STOP_LOSS'] * DYNAMIC_WIN_LOSS_DOWN) / Decimal('100') 170 | settings_struct['TAKE_PROFIT'] = settings_struct['TAKE_PROFIT'] - (settings_struct['TAKE_PROFIT'] * DYNAMIC_WIN_LOSS_DOWN) / Decimal('100') 171 | settings_struct['TRAILING_STOP_LOSS'] = settings_struct['TRAILING_STOP_LOSS'] - (settings_struct['TRAILING_STOP_LOSS'] * DYNAMIC_WIN_LOSS_DOWN) / Decimal('100') 172 | settings_struct['CHANGE_IN_PRICE_MAX'] = settings_struct['CHANGE_IN_PRICE_MAX'] - (settings_struct['CHANGE_IN_PRICE_MAX'] * DYNAMIC_WIN_LOSS_DOWN) /100 173 | settings_struct['CHANGE_IN_PRICE_MIN'] = settings_struct['CHANGE_IN_PRICE_MIN'] - (settings_struct['CHANGE_IN_PRICE_MIN'] * DYNAMIC_WIN_LOSS_DOWN) /100 174 | settings_struct['DYNAMIC_CHANGE_IN_PRICE'] = settings_struct['DYNAMIC_CHANGE_IN_PRICE'] - (settings_struct['DYNAMIC_CHANGE_IN_PRICE'] * DYNAMIC_WIN_LOSS_DOWN) / Decimal('100') 175 | 176 | settings_struct['HOLDING_PRICE_THRESHOLD'] = settings_struct['HOLDING_PRICE_THRESHOLD'] - (settings_struct['HOLDING_PRICE_THRESHOLD'] * DYNAMIC_WIN_LOSS_DOWN) / Decimal('100') 177 | session_struct['dynamic'] = 'none' 178 | 179 | print(f"{txcolors.NOTICE}>> DYNAMICS_DOWN Changing STOP_LOSS: {settings_struct['STOP_LOSS']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_DOWN']:.2f} - TAKE_PROFIT: {settings_struct['TAKE_PROFIT']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_DOWN']:.2f} - TRAILING_STOP_LOSS: {settings_struct['TRAILING_STOP_LOSS']:.2f}/{settings_struct['DYNAMIC_WIN_LOSS_DOWN']:.2f} CIP:{settings_struct['CHANGE_IN_PRICE_MIN']:.4f}/{settings_struct['CHANGE_IN_PRICE_MAX']:.4f}/{settings_struct['DYNAMIC_WIN_LOSS_DOWN']:.2f} HTL: {settings_struct['HOLDING_TIME_LIMIT']:.2f} TD: {settings_struct['TIME_DIFFERENCE']} RI: {settings_struct['RECHECK_INTERVAL']} <<{txcolors.DEFAULT}") 180 | 181 | # this code makes our market resistance and support levels triggres for buys and also applyies our dynamics based on wins/losses 182 | 183 | if type == 'mrs_settings': 184 | if session_struct['prices_grabbed'] == True: 185 | settings_struct['CHANGE_IN_PRICE_MIN'] = session_struct['market_support'] + (session_struct['market_support'] * settings_struct['DYNAMIC_CHANGE_IN_PRICE']) / Decimal('100') 186 | settings_struct['CHANGE_IN_PRICE_MAX'] = session_struct['market_support'] - (session_struct['market_support'] * settings_struct['DYNAMIC_CHANGE_IN_PRICE']) / Decimal('100') 187 | settings_struct['TAKE_PROFIT'] = session_struct['market_resistance'] + (session_struct['market_resistance'] * settings_struct['DYNAMIC_CHANGE_IN_PRICE']) / Decimal('100') 188 | 189 | if session_struct['loss_trade_count'] > 1: 190 | trading_struct['trade_support'] = trading_struct['sum_lost_trades'] / session_struct['loss_trade_count'] 191 | 192 | if session_struct['win_trade_count'] > 1: 193 | trading_struct['trade_resistance'] = trading_struct['sum_won_trades'] / session_struct['win_trade_count'] 194 | settings_struct['TRAILING_STOP_LOSS'] = trading_struct['trade_resistance'] 195 | # this part of code changes time if we use TEST or REAL mode based on timing in each, aka realmode uses miliseconds so we 196 | # multiply for HOLDING TIME LIMIT 197 | 198 | settings_struct['HOLDING_TIME_LIMIT'] = (settings_struct['TIME_DIFFERENCE'] * 60 * 1000) * HOLDING_INTERVAL_LIMIT 199 | -------------------------------------------------------------------------------- /bot/grab.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | from typing import Tuple, Dict 9 | 10 | #gogo MOD telegram needs import request 11 | import requests 12 | 13 | # needed for the binance API / websockets / Exception handling 14 | from binance.client import Client 15 | from binance.exceptions import BinanceAPIException 16 | from requests.exceptions import ReadTimeout, ConnectionError 17 | 18 | # used to store trades and sell assets 19 | import simplejson 20 | 21 | from helpers.parameters import ( 22 | parse_args, load_config 23 | ) 24 | 25 | # used for dates 26 | from datetime import date, datetime, timedelta 27 | import time 28 | 29 | # Load creds modules 30 | from helpers.handle_creds import ( 31 | load_correct_creds, test_api_key, 32 | load_telegram_creds 33 | ) 34 | 35 | from bot.settings import * 36 | 37 | # rolling window of prices; cyclical queue 38 | historical_prices = [None] * 2 39 | hsp_head = -1 40 | 41 | def get_symbol_info(url: str = 'https://api.binance.com/api/v3/exchangeInfo') -> None: 42 | global session_struct 43 | response = requests.get(url) 44 | json_message = simplejson.loads(response.content, use_decimal=True) 45 | 46 | for symbol_info in json_message['symbols']: 47 | session_struct['symbol_info'][symbol_info['symbol']] = symbol_info 48 | 49 | 50 | def get_historical_price() -> None: 51 | global session_struct 52 | if is_fiat(): 53 | session_struct['market_price'] = 1 54 | session_struct['exchange_symbol'] = PAIR_WITH 55 | else: 56 | session_struct['exchange_symbol'] = PAIR_WITH + 'USDT' 57 | market_historic = client.get_historical_trades(symbol=session_struct['exchange_symbol']) 58 | session_struct['market_price'] = market_historic[0].get('price') 59 | 60 | def external_signals() -> Dict[str, str]: 61 | external_list = {} 62 | signals = {} 63 | 64 | # check directory and load pairs from files into external_list 65 | signals = glob.glob("signals/*.exs") 66 | for filename in signals: 67 | for line in open(filename): 68 | symbol = line.strip() 69 | external_list[symbol] = symbol 70 | print(f'>>> SIGNAL DETECTED ON: {symbol} - SIGNALMOD: {filename} <<<<') 71 | try: 72 | os.remove(filename) 73 | except: 74 | if DEBUG: print(f"{txcolors.WARNING}Could not remove external signalling file{txcolors.DEFAULT}") 75 | 76 | return external_list 77 | 78 | 79 | def get_price(add_to_historical: bool = True) -> Dict: 80 | '''Return the current price for all coins on binance''' 81 | 82 | global historical_prices, hsp_head, session_struct 83 | 84 | initial_price = {} 85 | 86 | # get all info on tickers from binance 87 | # with retry on error reading 88 | while True: 89 | try: 90 | prices = client.get_all_tickers() 91 | except: 92 | print(f"{txcolors.WARNING}Binance Problem Get All Tickers{txcolors.DEFAULT}") 93 | time.sleep(0.2) 94 | continue 95 | break 96 | 97 | # current price BNB pair BNB ;-) 1 = 1 98 | if PAIR_WITH == 'BNB': 99 | session_struct['bnb_current_price'] = Decimal('1') 100 | 101 | for coin in prices: 102 | # Get Current Bnb Price to fee calculation 103 | if coin['symbol'] == 'BNB' + PAIR_WITH: 104 | session_struct['bnb_current_price'] = Decimal(coin['price']) 105 | 106 | if any(item + PAIR_WITH == coin['symbol'] for item in session_struct['tickers']) and all(item not in coin['symbol'] for item in EXCLUDED_PAIRS): 107 | initial_price[coin['symbol']] = { 'price': Decimal(coin['price']), 'time': datetime.now()} 108 | 109 | if add_to_historical: 110 | hsp_head += 1 111 | 112 | if hsp_head == 2: 113 | hsp_head = 0 114 | 115 | historical_prices[hsp_head] = initial_price 116 | return initial_price 117 | 118 | def wait_for_price(type: str) -> Tuple[Dict, int, Dict]: 119 | '''calls the initial price and ensures the correct amount of time has passed 120 | before reading the current price again''' 121 | 122 | global historical_prices, hsp_head, volatility_cooloff, session_struct, settings_struct 123 | 124 | volatile_coins = {} 125 | externals = {} 126 | 127 | current_time_minutes = Decimal(round(time.time()))/60 128 | 129 | #first time we just skip untill we find a way for historic fata to be grabbed here 130 | if session_struct['price_timedelta'] == 0: session_struct['price_timedelta'] = current_time_minutes 131 | #we give local variable value of time that we use for checking to grab prices again 132 | price_timedelta_value = session_struct['price_timedelta'] 133 | 134 | #if historical_prices[hsp_head]['BNB' + PAIR_WITH]['time'] > datetime.now() - timedelta(minutes=Decimal(TIME_DIFFERENCE / RECHECK_INTERVAL)): 135 | 136 | # sleep for exactly the amount of time required 137 | #time.sleep((timedelta(minutes=Decimal(TIME_DIFFERENCE / RECHECK_INTERVAL)) - (datetime.now() - historical_prices[hsp_head]['BNB' + PAIR_WITH]['time'])).total_seconds()) 138 | #print(f'PRICE_TIMEDELTA: {price_timedelta_value} - CURRENT_TIME: {current_time_minutes} - TIME_DIFFERENCE: {TIME_DIFFERENCE}') 139 | 140 | session_struct['prices_grabbed'] = False 141 | 142 | if session_struct['price_timedelta'] < current_time_minutes - round(settings_struct['TIME_DIFFERENCE']): 143 | 144 | #print(f'GET PRICE TRIGGERED !!!!! PRICE_TIMEDELTA: {price_timedelta_value} - TIME_DIFFERENCE: {TIME_DIFFERENCE}') 145 | # retrieve latest prices 146 | get_price(add_to_historical=True) 147 | externals = external_signals() 148 | session_struct['price_timedelta'] = current_time_minutes 149 | session_struct['market_resistance'] = 0 150 | session_struct['market_support'] = 0 151 | coins_up = 0 152 | coins_down = 0 153 | session_struct['prices_grabbed'] = True 154 | 155 | 156 | if session_struct['prices_grabbed'] == True: 157 | # calculate the difference in prices 158 | 159 | for coin in historical_prices[hsp_head]: 160 | # Verify if coin doesn't appear 161 | try: 162 | for x in historical_prices: 163 | if coin not in x: 164 | raise 165 | except: 166 | continue 167 | 168 | # minimum and maximum prices over time period 169 | min_price = min(historical_prices, key = lambda x: Decimal("inf") if x is None else x[coin]['price']) 170 | max_price = max(historical_prices, key = lambda x: Decimal('-1') if x is None else x[coin]['price']) 171 | 172 | threshold_check = (Decimal('-1.0') if min_price[coin]['time'] > max_price[coin]['time'] else Decimal('1.0')) * (max_price[coin]['price'] - min_price[coin]['price']) / min_price[coin]['price'] * Decimal('100') 173 | 174 | if threshold_check > 0: 175 | session_struct['market_resistance'] = session_struct['market_resistance'] + threshold_check 176 | coins_up = coins_up +1 177 | 178 | if threshold_check < 0: 179 | session_struct['market_support'] = session_struct['market_support'] - threshold_check 180 | coins_down = coins_down +1 181 | 182 | if coins_up != 0: session_struct['market_resistance'] = session_struct['market_resistance'] / coins_up 183 | if coins_down != 0: session_struct['market_support'] = -session_struct['market_support'] / coins_down 184 | 185 | # calculate the difference in prices 186 | for coin in historical_prices[hsp_head]: 187 | 188 | # Verify if coin doesn't appear 189 | try: 190 | for x in historical_prices: 191 | if coin not in x: 192 | raise 193 | except: 194 | continue 195 | 196 | # minimum and maximum prices over time period 197 | min_price = min(historical_prices, key = lambda x: Decimal("inf") if x is None else x[coin]['price']) 198 | max_price = max(historical_prices, key = lambda x: Decimal('-1') if x is None else x[coin]['price']) 199 | 200 | threshold_check = (Decimal('-1.0') if min_price[coin]['time'] > max_price[coin]['time'] else Decimal('1.0')) * (max_price[coin]['price'] - min_price[coin]['price']) / min_price[coin]['price'] * Decimal('100') 201 | 202 | if type == 'percent_mix_signal': 203 | 204 | # each coin with higher gains than our CHANGE_IN_PRICE is added to the volatile_coins dict if less than TRADE_SLOTS is not reached. 205 | if threshold_check > settings_struct['CHANGE_IN_PRICE_MIN'] and threshold_check < settings_struct['CHANGE_IN_PRICE_MAX']: 206 | 207 | #if os.path.exists('signals/nigec_custsignalmod.exs') or os.path.exists('signals/djcommie_custsignalmod.exs') or os.path.exists('signals/firewatch_signalsample.exs'): 208 | #signals = glob.glob("signals/*.exs") 209 | 210 | for excoin in externals: 211 | #print(f'EXCOIN: {excoin}') 212 | if excoin == coin: 213 | # print(f'EXCOIN: {excoin} == COIN: {coin}') 214 | if coin not in volatility_cooloff: 215 | volatility_cooloff[coin] = datetime.now() - timedelta(minutes=round(settings_struct['TIME_DIFFERENCE'])) 216 | # only include coin as volatile if it hasn't been picked up in the last TIME_DIFFERENCE minutes already 217 | if datetime.now() >= volatility_cooloff[coin] + timedelta(minutes=round(settings_struct['TIME_DIFFERENCE'])): 218 | volatility_cooloff[coin] = datetime.now() 219 | if session_struct['trade_slots'] + len(volatile_coins) < TRADE_SLOTS or TRADE_SLOTS == 0: 220 | volatile_coins[coin] = round(threshold_check, 3) 221 | print(f"{coin} has gained {volatile_coins[coin]}% within the last {settings_struct['TIME_DIFFERENCE']} minutes, and coin {excoin} recived a signal... calculating {QUANTITY} {PAIR_WITH} value of {coin} for purchase!") 222 | #else: 223 | #print(f"{txcolors.WARNING}{coin} has gained {round(threshold_check, 3)}% within the last {TIME_DIFFERENCE} minutes, , and coin {excoin} recived a signal... but you are using all available trade slots!{txcolors.DEFAULT}") 224 | 225 | 226 | if type == 'percent_and_signal': 227 | 228 | # each coin with higher gains than our CHANGE_IN_PRICE is added to the volatile_coins dict if less than TRADE_SLOTS is not reached. 229 | if threshold_check > settings_struct['CHANGE_IN_PRICE_MIN'] and threshold_check < settings_struct['CHANGE_IN_PRICE_MAX']: 230 | 231 | if coin not in volatility_cooloff: 232 | volatility_cooloff[coin] = datetime.now() - timedelta(minutes=round(settings_struct['TIME_DIFFERENCE'])) 233 | 234 | # only include coin as volatile if it hasn't been picked up in the last TIME_DIFFERENCE minutes already 235 | if datetime.now() >= volatility_cooloff[coin] + timedelta(minutes=round(settings_struct['TIME_DIFFERENCE'])): 236 | volatility_cooloff[coin] = datetime.now() 237 | 238 | if session_struct['trade_slots'] + len(volatile_coins) < TRADE_SLOTS or TRADE_SLOTS == 0: 239 | volatile_coins[coin] = round(threshold_check, 3) 240 | print(f"{coin} has gained {volatile_coins[coin]}% within the last {settings_struct['TIME_DIFFERENCE']} minutes {QUANTITY} {PAIR_WITH} value of {coin} for purchase!") 241 | 242 | #else: 243 | #print(f"{txcolors.WARNING}{coin} has gained {round(threshold_check, 3)}% within the last {TIME_DIFFERENCE} minutes but you are using all available trade slots!{txcolors.DEFAULT}") 244 | 245 | externals = external_signals() 246 | exnumber = 0 247 | 248 | for excoin in externals: 249 | if excoin not in volatile_coins and excoin not in coins_bought and (len(coins_bought) + exnumber) < TRADE_SLOTS: 250 | volatile_coins[excoin] = 1 251 | exnumber +=1 252 | print(f"External signal received on {excoin}, calculating {QUANTITY} {PAIR_WITH} value of {excoin} for purchase!") 253 | 254 | return volatile_coins, len(volatile_coins), historical_prices[hsp_head] 255 | -------------------------------------------------------------------------------- /bot/report.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | 9 | from helpers.parameters import ( 10 | parse_args, load_config 11 | ) 12 | 13 | # used for dates 14 | from datetime import date, datetime, timedelta 15 | import time 16 | 17 | #gogo MOD telegram needs import request 18 | import requests 19 | 20 | # Load creds modules 21 | from helpers.handle_creds import ( 22 | load_correct_creds, test_api_key, 23 | load_telegram_creds 24 | ) 25 | 26 | from bot.settings import * 27 | 28 | 29 | def txcolor(input: float) -> str: 30 | if input > 0: 31 | return txcolors.SELL_PROFIT 32 | elif input < 0: 33 | return txcolors.SELL_LOSS 34 | else: 35 | return txcolors.DEFAULT 36 | 37 | # code for showing discord avatar based on crypto coin type 38 | 39 | def discord_avatar() -> str: 40 | # Custom coin avatar dependant on PAIR_WITH 41 | # Fallback image is a nice Binance logo, yay! 42 | DISCORD_AVATAR = "https://i.imgur.com/w1vS5Oc.png" 43 | if PAIR_WITH == 'ETH': 44 | DISCORD_AVATAR = "https://i.imgur.com/L9Txc9F.jpeg" 45 | if PAIR_WITH == 'BTC': 46 | DISCORD_AVATAR = "https://i.imgur.com/oIeAiEo.jpeg" 47 | if PAIR_WITH == 'USDT': 48 | DISCORD_AVATAR = "https://i.imgur.com/VyOdlRS.jpeg" 49 | return DISCORD_AVATAR 50 | 51 | # code for fixing errors with multiple coins buy not writing to trades.txt 52 | 53 | def report_add(line: str, message: bool = False) -> None: 54 | if report_struct['report'] != "": 55 | report_struct['report'] += "\n" 56 | report_struct['report'] += line 57 | report_struct['log'] = True 58 | if message: 59 | report_struct['message'] = True 60 | 61 | # code for generating reports that will print current bot state upon sell to telegram or discord 62 | # to console and to logfile 63 | 64 | def report(type: str, reportline: str) -> None: 65 | 66 | global session_struct, settings_struct, trading_struct 67 | 68 | try: # does it exist? 69 | session_struct['investment_value_gain'] 70 | except NameError: # if not, set to 0 71 | session_struct['investment_value_gain'] = 0 72 | 73 | WON = session_struct['win_trade_count'] 74 | LOST = session_struct['loss_trade_count'] 75 | DECIMALS = int(decimals()) 76 | INVESTMENT_TOTAL = round((QUANTITY * TRADE_SLOTS), DECIMALS) 77 | CURRENT_EXPOSURE = round(session_struct['CURRENT_EXPOSURE'], DECIMALS) 78 | session_struct['TOTAL_GAINS'] = round(session_struct['TOTAL_GAINS'], DECIMALS) 79 | INVESTMENT_VALUE_GAIN = round(session_struct['investment_value_gain'], 2) 80 | 81 | # testing: 82 | NEW_BALANCE_TRIM = "%g" % round(session_struct['NEW_BALANCE'], DECIMALS) 83 | INVESTMENT_VALUE_TRIM = "%g" % round(session_struct['investment_value'], 2) 84 | INVESTMENT_VALUE_GAIN_TRIM = "%g" % round(session_struct['investment_value_gain'], 2) 85 | CURRENT_EXPOSURE_TRIM = "%g" % session_struct['CURRENT_EXPOSURE'] 86 | INVESTMENT_TOTAL_TRIM = "%g" % INVESTMENT_TOTAL 87 | CLOSED_TRADES_PERCENT_TRIM = "%g" % round(session_struct['closed_trades_percent'], 2) 88 | SESSION_PROFIT_TRIM = format(session_struct['session_profit'], '.8f') 89 | 90 | # SESSION_PROFIT_TRIM = "%g" % round(session_profit, DECIMALS) 91 | 92 | SETTINGS_STRING = 'Time: '+str(round(settings_struct['TIME_DIFFERENCE'], 2))+' | Interval: '\ 93 | +str(round(settings_struct['RECHECK_INTERVAL'], 2))+' | Price change min/max: '\ 94 | +str(round(settings_struct['CHANGE_IN_PRICE_MIN'], 2))+'/'\ 95 | +str(round(settings_struct['CHANGE_IN_PRICE_MAX'], 2))+'% | Stop loss: '\ 96 | +str(round(settings_struct['STOP_LOSS'], 2))+' | Take profit: '\ 97 | +str(round(settings_struct['TAKE_PROFIT'], 2))+' | Trailing stop loss: '\ 98 | +str(round(settings_struct['TRAILING_STOP_LOSS'], 2))+' | Trailing take profit: '\ 99 | +str(round(settings_struct['TRAILING_TAKE_PROFIT'], 2))+ ' | Holding time limit: ' \ 100 | +str(round(settings_struct['HOLDING_TIME_LIMIT'], 2)) 101 | 102 | UNREALISED_PERCENT = session_struct['unrealised_percent'] 103 | 104 | if (session_struct['win_trade_count'] > 0) and (session_struct['loss_trade_count'] > 0): 105 | WIN_LOSS_PERCENT = round((session_struct['win_trade_count'] / (session_struct['win_trade_count'] + session_struct['loss_trade_count'])) * 100, 2) 106 | else: 107 | WIN_LOSS_PERCENT = 100 108 | 109 | # adding all the stats together: 110 | report_string= 'Trade slots: '+str(session_struct['trade_slots'])+'/'\ 111 | +str(TRADE_SLOTS)+' ('+str(CURRENT_EXPOSURE_TRIM)+'/'\ 112 | +str(INVESTMENT_TOTAL_TRIM)+' '+PAIR_WITH+') | Session: '\ 113 | +str(SESSION_PROFIT_TRIM)+' '+PAIR_WITH+' ('+str(CLOSED_TRADES_PERCENT_TRIM)+'%) | Win/Loss: '\ 114 | +str(WON)+'/'+str(LOST)+' ('+str(WIN_LOSS_PERCENT)+'%) | Gains: '\ 115 | +str(round(session_struct['INVESTMENT_GAIN'], 4))+'%'+' | Balance: '\ 116 | +str(NEW_BALANCE_TRIM)+' | Value: '+str(INVESTMENT_VALUE_TRIM)+' USD | Value gain: '\ 117 | +str(INVESTMENT_VALUE_GAIN_TRIM)+' | Uptime: '\ 118 | +str(timedelta(seconds=(int(session_struct['session_uptime']/1000)))) 119 | 120 | 121 | #gogo MOD todo more verbose having all the report things in it!!!!! 122 | if type == 'console': 123 | # print(f"{txcolors.NOTICE}>> Using {len(coins_bought)}/{TRADE_SLOTS} trade slots. OT:{UNREALISED_PERCENT:.2f}%> SP:{session_profit:.2f}%> Est:{TOTAL_GAINS:.{decimals()}f} {PAIR_WITH}> W:{win_trade_count}> L:{loss_trade_count}> IT:{INVESTMENT:.{decimals()}f} {PAIR_WITH}> CE:{CURRENT_EXPOSURE:.{decimals()}f} {PAIR_WITH}> NB:{NEW_BALANCE:.{decimals()}f} {PAIR_WITH}> IV:{investment_value:.2f} {exchange_symbol}> IG:{INVESTMENT_GAIN:.2f}%> IVG:{investment_value_gain:.{decimals()}f} {exchange_symbol}> {reportline} <<{txcolors.DEFAULT}") 124 | print(f"{report_string}") 125 | 126 | #More detailed/verbose report style 127 | if type == 'detailed': 128 | print( 129 | f"{txcolors.NOTICE}>> Using {session_struct['trade_slots']}/{TRADE_SLOTS} trade slots. << \n" 130 | , f"Profit on unsold coins: {txcolor(UNREALISED_PERCENT)}" 131 | f"{UNREALISED_PERCENT:.2f}%\n" 132 | , f"Closed trades: {txcolor(session_struct['closed_trades_percent'])}" 133 | f"{str(CLOSED_TRADES_PERCENT_TRIM)}%\n" 134 | , f"Session profit: {txcolor(session_struct['session_profit'])}" 135 | f"{str(SESSION_PROFIT_TRIM)} {PAIR_WITH}\n" 136 | , f"Trades won/lost: {txcolor(session_struct['win_trade_count']-session_struct['loss_trade_count'])}" 137 | f"{session_struct['win_trade_count']} / {session_struct['loss_trade_count']}\n" 138 | , f"Profit To Trade: {txcolors.DEFAULT}" 139 | f"{session_struct['profit_to_trade_ratio']}\n" 140 | , f"Investment: {txcolors.DEFAULT}" 141 | f"{INVESTMENT_TOTAL:g} {PAIR_WITH}\n" 142 | , f"Current exposure: {txcolors.DEFAULT}" 143 | f"{session_struct['CURRENT_EXPOSURE']:g} {PAIR_WITH}\n" 144 | , f"New balance: {txcolor(session_struct['NEW_BALANCE']-INVESTMENT_TOTAL)}" 145 | f"{session_struct['NEW_BALANCE']:g} {PAIR_WITH}\n" 146 | , f"Initial investment: {txcolors.DEFAULT}" 147 | f"{session_struct['investment_value']:.2f} USD\n" 148 | , f"Investment gain: {txcolor(session_struct['INVESTMENT_GAIN'])}" 149 | f"{session_struct['INVESTMENT_GAIN']:.2f}%\n" 150 | , f"Investment value vain: {txcolor(session_struct['investment_value_gain'])}" 151 | f"{str(INVESTMENT_VALUE_GAIN)} USD\n" 152 | , f"Market Resistance: {txcolors.DEFAULT}" 153 | f"{session_struct['market_resistance']:.2f}\n" 154 | , f"Market Support: {txcolors.DEFAULT}" 155 | f"{session_struct['market_support']:.2f}\n" 156 | , f"Change in Price: {txcolors.DEFAULT}" 157 | f"{settings_struct['CHANGE_IN_PRICE_MIN']:.2f} - {settings_struct['CHANGE_IN_PRICE_MAX']:.2f}\n" 158 | , f"Stop Loss: {txcolors.DEFAULT}" 159 | f"{settings_struct['STOP_LOSS']:.2f}\n" 160 | , f"Take Profit: {txcolors.DEFAULT}" 161 | f"{settings_struct['TAKE_PROFIT']:.2f}\n" 162 | , f"Trailing Stop Loss: {txcolors.DEFAULT}" 163 | f"{settings_struct['TRAILING_STOP_LOSS']:.2f}\n" 164 | , f"Trailing Take Profit: {txcolors.DEFAULT}" 165 | f"{settings_struct['TRAILING_TAKE_PROFIT']:.2f}\n" 166 | , f"Holding Price Threshold:{txcolors.DEFAULT}" 167 | f"{settings_struct['HOLDING_PRICE_THRESHOLD']:.2f}\n" 168 | , f"Time Difference: {txcolors.DEFAULT}" 169 | f"{settings_struct['TIME_DIFFERENCE']:.2f}\n" 170 | , f"Recheck Interval: {txcolors.DEFAULT}" 171 | f"{settings_struct['RECHECK_INTERVAL']:.2f}\n" 172 | , f"Holding Time Limit: {txcolors.DEFAULT}" 173 | f"{settings_struct['HOLDING_TIME_LIMIT']:.2f}\n" 174 | , f"Session uptime: {txcolors.DEFAULT}" 175 | f"{str(timedelta(seconds=(int(session_struct['session_uptime']/1000))))}" 176 | ) 177 | 178 | # sending messages report to telegram and or discord channel 179 | 180 | if type == 'message' or report_struct['message']: 181 | TELEGRAM_BOT_TOKEN, TELEGRAM_BOT_ID, TEST_DISCORD_WEBHOOK, LIVE_DISCORD_WEBHOOK = load_telegram_creds(parsed_creds) 182 | bot_message = SETTINGS_STRING + '\n' + reportline + '\n' + report_string + '\n' 183 | 184 | if TEST_MODE: MODE = 'TEST' 185 | if not TEST_MODE: MODE = 'MAIN' 186 | 187 | BOT_SETTINGS_ID = BOT_ID + str(get_git_commit_number()) + '_' + MODE + '_' + PAIR_WITH + '_' + str(TIME_DIFFERENCE) + 'M' 188 | 189 | if BOT_MESSAGE_REPORTS and TELEGRAM_BOT_TOKEN: 190 | bot_token = TELEGRAM_BOT_TOKEN 191 | bot_chatID = TELEGRAM_BOT_ID 192 | send_text = 'https://api.telegram.org/bot' + bot_token + '/sendMessage?chat_id=' + bot_chatID + '&parse_mode=Markdown&text=' + BOT_SETTINGS_ID + bot_message 193 | response = requests.get(send_text) 194 | 195 | if BOT_MESSAGE_REPORTS and (TEST_DISCORD_WEBHOOK or LIVE_DISCORD_WEBHOOK): 196 | if not TEST_MODE: 197 | mUrl = "https://discordapp.com/api/webhooks/"+LIVE_DISCORD_WEBHOOK 198 | if TEST_MODE: 199 | mUrl = "https://discordapp.com/api/webhooks/"+TEST_DISCORD_WEBHOOK 200 | 201 | data = {"username" : BOT_SETTINGS_ID , "avatar_url": discord_avatar(), "content": bot_message} 202 | response = requests.post(mUrl, json=data) 203 | # print(response.content) 204 | 205 | report_struct['message'] = False 206 | 207 | if type == 'log' or report_struct['log']: 208 | timestamp = datetime.now().strftime("%d/%m %H:%M:%S") 209 | # print(f'LOG_FILE: {LOG_FILE}') 210 | with open(LOG_FILE,'a+') as f: 211 | for line in reportline.splitlines(): 212 | f.write(timestamp + ' ' + line + '\n') 213 | report_struct['log'] = False 214 | 215 | session_struct['last_report_time'] = time.time() 216 | 217 | def report_update() -> None : 218 | if time.time() - session_struct['last_report_time'] > REPORT_FREQUENCY: 219 | report(SESSION_REPORT_STYLE,report_struct['report']) 220 | report_struct['report'] = "" 221 | 222 | # code that logs settings to scv file for creating graphs 223 | 224 | def csv_log(interval: float = 60) -> None: 225 | global session_struct, trading_struct, settings_struct 226 | # Intended for use in separate thread 227 | log_file = 'log.csv' 228 | if not os.path.isfile(log_file): 229 | with open(log_file,'w+') as l: 230 | l.write("Timestamp;" 231 | + "Time Difference;" 232 | + "Recheck Interval;" 233 | + "Change in price (min);" 234 | + "Change in price (max);" 235 | + "Stop Loss;" 236 | + "Take Profit;" 237 | + "Trailing Stop Loss;" 238 | + "Trailing Take Profit;" 239 | + "Holding Time Limit;" 240 | + "Dynamic Change in Price;" 241 | + "Holding timeout dynamic;" 242 | + "Holding timeout sell;" 243 | + "Session profit;" 244 | + "Unrealised percent;" 245 | + "Market Price;" 246 | + "Investment value;" 247 | + "Investment value gain;" 248 | + "Session uptime;" 249 | + "Session start time;" 250 | + "Closed trades percent;" 251 | + "Win trade count;" 252 | + "Loss trade count;" 253 | + "Market support;" 254 | + "Market resistance;" 255 | + "Dynamic;" 256 | + "Sell all coins;" 257 | + "Ticker list changed;" 258 | + "Exchange symbol;" 259 | + "Price list counter;" 260 | + "Current exposure;" 261 | + "Total gains;" 262 | + "New balance;" 263 | + "Investment gain;" 264 | + "List Auto-create;" 265 | + "Price timedelta;" 266 | + "Trade slots;" 267 | + "Dynamics state;" 268 | + "Last trade won;" 269 | + "Profit to trade ratio;" 270 | + "Percent trades lost;" 271 | + "Percent trades won;" 272 | + "Trade support;" 273 | + "Trade resistance;" 274 | + "Sum Won trades;" 275 | + "Sum Lost trades;" 276 | + "Max holding price;" 277 | + "Min holding price;" 278 | + "\n" 279 | ) 280 | 281 | while True: 282 | with open(log_file, 'a+') as l: 283 | l.write(f"{time.time()};" 284 | + f"{settings_struct['TIME_DIFFERENCE']};" 285 | + f"{settings_struct['RECHECK_INTERVAL']};" 286 | + f"{settings_struct['CHANGE_IN_PRICE_MIN']};" 287 | + f"{settings_struct['CHANGE_IN_PRICE_MAX']};" 288 | + f"{settings_struct['STOP_LOSS'] * -1};" 289 | + f"{settings_struct['TAKE_PROFIT']};" 290 | + f"{settings_struct['TRAILING_STOP_LOSS'] * -1};" 291 | + f"{settings_struct['TRAILING_TAKE_PROFIT']};" 292 | + f"{settings_struct['HOLDING_TIME_LIMIT']};" 293 | + f"{settings_struct['DYNAMIC_CHANGE_IN_PRICE']};" 294 | + f"{trading_struct['holding_timeout_dynamic']};" 295 | + f"{trading_struct['holding_timeout_sell']};" 296 | + f"{session_struct['session_profit']};" 297 | + f"{session_struct['unrealised_percent']};" 298 | + f"{session_struct['market_price']};" 299 | + f"{session_struct['investment_value']};" 300 | + f"{session_struct['investment_value_gain']};" 301 | + f"{session_struct['session_uptime']};" 302 | + f"{session_struct['session_start_time']};" 303 | + f"{session_struct['closed_trades_percent']};" 304 | + f"{session_struct['win_trade_count']};" 305 | + f"{session_struct['loss_trade_count']};" 306 | + f"{session_struct['market_support']};" 307 | + f"{session_struct['market_resistance']};" 308 | + f"{session_struct['dynamic']};" 309 | + f"{session_struct['sell_all_coins']};" 310 | + f"{session_struct['tickers_list_changed']};" 311 | + f"{session_struct['exchange_symbol']};" 312 | + f"{session_struct['price_list_counter']};" 313 | + f"{session_struct['CURRENT_EXPOSURE']};" 314 | + f"{session_struct['TOTAL_GAINS']};" 315 | + f"{session_struct['NEW_BALANCE']};" 316 | + f"{session_struct['INVESTMENT_GAIN']};" 317 | + f"{session_struct['LIST_AUTOCREATE']};" 318 | + f"{session_struct['price_timedelta']};" 319 | + f"{ session_struct['trade_slots']};" 320 | + f"{session_struct['dynamics_state']};" 321 | + f"{session_struct['last_trade_won']};" 322 | + f"{session_struct['profit_to_trade_ratio']};" 323 | + f"{trading_struct['lost_trade_percent']};" 324 | + f"{trading_struct['won_trade_percent']};" 325 | + f"{trading_struct['trade_support']};" 326 | + f"{trading_struct['trade_resistance']};" 327 | + f"{trading_struct['sum_won_trades']};" 328 | + f"{trading_struct['sum_lost_trades']};" 329 | + f"{trading_struct['max_holding_price']};" 330 | + f"{trading_struct['min_holding_price']};" 331 | + "\n") 332 | 333 | time.sleep(interval) 334 | -------------------------------------------------------------------------------- /bot/session.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | 9 | from helpers.parameters import ( 10 | parse_args, load_config 11 | ) 12 | 13 | # Load creds modules 14 | from helpers.handle_creds import ( 15 | load_correct_creds, test_api_key, 16 | load_telegram_creds 17 | ) 18 | 19 | # used for dates 20 | from datetime import date, datetime, timedelta 21 | import time 22 | 23 | # used to store trades and sell assets 24 | import simplejson 25 | 26 | from bot.settings import * 27 | 28 | # code that saves settings to session file and loads it from it so we can transfer session from one instance of bot to another 29 | 30 | def session(type: str) -> None: 31 | #various session calculations like uptime 24H gain profit risk to reward ratio unrealised profit etc 32 | 33 | global session_struct, settings_struct 34 | 35 | if type == 'calc': 36 | session_struct['TOTAL_GAINS'] = session_struct['session_profit'] 37 | session_struct['NEW_BALANCE'] = (INVESTMENT + session_struct['TOTAL_GAINS']) 38 | session_struct['INVESTMENT_GAIN'] = (session_struct['TOTAL_GAINS'] / INVESTMENT) * Decimal('100') 39 | session_struct['CURRENT_EXPOSURE'] = (QUANTITY * session_struct['trade_slots']) 40 | 41 | # this number is your actual ETH or other coin value in correspondence to USDT aka your market investment_value 42 | # it is important cuz your exchange aha ETH or BTC can vary and if you pause bot during that time you gain profit 43 | session_struct['investment_value'] = Decimal(session_struct['market_price']) * session_struct['NEW_BALANCE'] 44 | session_struct['investment_value_gain'] = Decimal(session_struct['market_price']) * (session_struct['NEW_BALANCE'] - INVESTMENT) 45 | 46 | current_time = round(time.time() * 1000) 47 | if session_struct['session_start_time'] == 0: session_struct['session_start_time'] = current_time 48 | session_struct['session_uptime'] = current_time - session_struct['session_start_time'] 49 | 50 | if session_struct['win_trade_count'] > 0 or session_struct['loss_trade_count'] > 0: 51 | session_struct['profit_to_trade_ratio'] = Decimal(session_struct['closed_trades_percent'] / (session_struct['win_trade_count']+session_struct['loss_trade_count'])) 52 | 53 | else: 54 | session_struct['profit_to_trade_ratio'] = Decimal('0') 55 | 56 | if session_struct['win_trade_count'] > 0 and session_struct['loss_trade_count'] > 0: 57 | trading_struct['trade_support'] = trading_struct['sum_min_holding_price'] / session_struct['loss_trade_count'] 58 | trading_struct['trade_resistance'] = trading_struct['sum_max_holding_price'] / session_struct['win_trade_count'] 59 | 60 | # saving session info to file during work 61 | 62 | if type == 'save': 63 | 64 | session_info = {} 65 | session_info_file_path = 'session_info.json' 66 | session_info = { 67 | 'session_profit': session_struct['session_profit'], 68 | 'win_trade_count': session_struct['win_trade_count'], 69 | 'loss_trade_count': session_struct['loss_trade_count'], 70 | # 'investment_value': investment_value, 71 | 'new_balance': session_struct['NEW_BALANCE'], 72 | 'session_start_time': session_struct['session_start_time'], 73 | 'session_uptime': session_struct['session_uptime'], 74 | 'closed_trades_percent': session_struct['closed_trades_percent'], 75 | 'last_trade_won': session_struct['last_trade_won'], 76 | 'TIME_DIFFERENCE': settings_struct['TIME_DIFFERENCE'], 77 | 'RECHECK_INTERVAL': settings_struct['RECHECK_INTERVAL'], 78 | 'CHANGE_IN_PRICE_MIN': settings_struct['CHANGE_IN_PRICE_MIN'], 79 | 'CHANGE_IN_PRICE_MAX': settings_struct['CHANGE_IN_PRICE_MAX'], 80 | 'STOP_LOSS': settings_struct['STOP_LOSS'], 81 | 'TAKE_PROFIT': settings_struct['TAKE_PROFIT'], 82 | 'TRAILING_STOP_LOSS': settings_struct['TRAILING_STOP_LOSS'], 83 | 'TRAILING_TAKE_PROFIT': settings_struct['TRAILING_TAKE_PROFIT'], 84 | 'HOLDING_TIME_LIMIT': settings_struct['HOLDING_TIME_LIMIT'], 85 | 'market_resistance': session_struct['market_resistance'], 86 | 'market_support': session_struct['market_support'], 87 | 'trade_slots': session_struct['trade_slots'], 88 | 89 | 'trade_support': trading_struct['trade_support'], 90 | 'trade_resistance': trading_struct['trade_resistance'], 91 | 'sum_won_trades': trading_struct['sum_won_trades'], 92 | 'sum_lost_trades': trading_struct['sum_lost_trades'], 93 | 'min_holding_price': trading_struct['min_holding_price'], 94 | 'max_holding_price': trading_struct['max_holding_price'], 95 | 'trade_resistance': trading_struct['trade_resistance'], 96 | 'trade_support': trading_struct['trade_support'], 97 | 'HOLDING_PRICE_THRESHOLD': settings_struct['HOLDING_PRICE_THRESHOLD'] 98 | } 99 | 100 | # save the coins in a json file in the same directory 101 | with open(session_info_file_path, 'w') as file: 102 | simplejson.dump(session_info, file, indent=4, use_decimal=True) 103 | 104 | if type == 'load': 105 | 106 | session_info = {} 107 | 108 | #gogo MOD path to session info file and loading variables from previous sessions 109 | #sofar only used for session profit TODO implement to use other things too 110 | #session_profit is calculated in % wich is innacurate if QUANTITY is not the same!!!!! 111 | 112 | session_info_file_path = 'session_info.json' 113 | 114 | if os.path.isfile(session_info_file_path) and os.stat(session_info_file_path).st_size!= 0: 115 | json_file=open(session_info_file_path) 116 | session_info=simplejson.load(json_file, use_decimal=True) 117 | json_file.close() 118 | 119 | session_struct['session_profit'] = session_info['session_profit'] 120 | session_struct['win_trade_count'] = session_info['win_trade_count'] 121 | session_struct['loss_trade_count'] = session_info['loss_trade_count'] 122 | # investment_value = session['investment_value'] 123 | session_struct['NEW_BALANCE'] = session_info['new_balance'] 124 | session_struct['session_start_time'] = session_info['session_start_time'] 125 | session_struct['closed_trades_percent'] = session_info['closed_trades_percent'] 126 | session_struct['session_uptime'] = session_info['session_uptime'] 127 | session_struct['last_trade_won'] = session_info['last_trade_won'] 128 | session_struct['market_resistance'] = session_info['market_resistance'] 129 | session_struct['market_support'] = session_info['market_support'] 130 | session_struct['trade_slots'] = session_info['trade_slots'] 131 | 132 | settings_struct['TIME_DIFFERENCE'] = session_info['TIME_DIFFERENCE'] 133 | settings_struct['RECHECK_INTERVAL'] = session_info['RECHECK_INTERVAL'] 134 | settings_struct['CHANGE_IN_PRICE_MIN'] = session_info['CHANGE_IN_PRICE_MIN'] 135 | settings_struct['CHANGE_IN_PRICE_MAX'] = session_info['CHANGE_IN_PRICE_MAX'] 136 | settings_struct['STOP_LOSS'] = session_info['STOP_LOSS'] 137 | settings_struct['TAKE_PROFIT'] = session_info['TAKE_PROFIT'] 138 | settings_struct['TRAILING_STOP_LOSS'] = session_info['TRAILING_STOP_LOSS'] 139 | settings_struct['TRAILING_TAKE_PROFIT'] = session_info['TRAILING_TAKE_PROFIT'] 140 | settings_struct['HOLDING_TIME_LIMIT'] = session_info['HOLDING_TIME_LIMIT'] 141 | 142 | trading_struct['trade_support'] = session_info['trade_support'] 143 | trading_struct['trade_resistance'] = session_info['trade_resistance'] 144 | trading_struct['sum_won_trades'] = session_info['sum_won_trades'] 145 | trading_struct['sum_lost_trades'] = session_info['sum_lost_trades'] 146 | trading_struct['min_holding_price'] = session_info['max_holding_price'] 147 | trading_struct['max_holding_price'] = session_info['max_holding_price'] 148 | trading_struct['trade_resistance'] = session_info['trade_resistance'] 149 | trading_struct['trade_support'] = session_info['trade_support'] 150 | settings_struct['HOLDING_PRICE_THRESHOLD'] = session_info['HOLDING_PRICE_THRESHOLD'] 151 | 152 | session_struct['TOTAL_GAINS'] = (session_struct['session_profit']) 153 | session_struct['NEW_BALANCE'] = (INVESTMENT + session_struct['TOTAL_GAINS']) 154 | session_struct['INVESTMENT_GAIN'] = (session_struct['TOTAL_GAINS'] / INVESTMENT) * Decimal('100') 155 | -------------------------------------------------------------------------------- /bot/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | import subprocess 9 | 10 | from helpers.parameters import ( 11 | parse_args, load_config 12 | ) 13 | 14 | # used to store trades and sell assets 15 | import simplejson 16 | 17 | # Load creds modules 18 | from helpers.handle_creds import ( 19 | load_correct_creds, test_api_key, 20 | load_telegram_creds 21 | ) 22 | 23 | # needed for the binance API / websockets / Exception handling 24 | from binance.client import Client 25 | from binance.exceptions import BinanceAPIException 26 | from requests.exceptions import ReadTimeout, ConnectionError 27 | 28 | # Decimal better precision 29 | from decimal import * 30 | # Truncate down always 31 | getcontext().rounding = ROUND_DOWN 32 | # Precision like Binance 33 | DECIMAL_PRECISION = Decimal('.00000001') 34 | 35 | # for colourful logging to the console 36 | class txcolors: 37 | BUY = '\033[92m' 38 | WARNING = '\033[93m' 39 | SELL_LOSS = '\033[91m' 40 | SELL_PROFIT = '\033[32m' 41 | DIM = '\033[2m\033[35m' 42 | DEFAULT = '\033[39m' 43 | NOTICE = '\033[96m' 44 | 45 | global historical_prices 46 | global hsp_head 47 | global session_struct 48 | global settings_struct 49 | global trading_struct 50 | global trail_buy_historical 51 | global trail_buy_coins 52 | 53 | trail_buy_coins = {} 54 | trail_buy_historical = {} 55 | 56 | # structure used for session variable for saving and loading 57 | session_struct = { 58 | 'session_profit': Decimal('0'), 59 | 'unrealised_percent': Decimal('0'), 60 | 'market_price': Decimal('0'), 61 | 'investment_value': Decimal('0'), 62 | 'investment_value_gain': Decimal('0'), 63 | 'session_uptime': 0, 64 | 'session_start_time': 0, 65 | 'closed_trades_percent': Decimal('0'), 66 | 'win_trade_count': int(0), 67 | 'loss_trade_count': int(0), 68 | 'market_support': Decimal('0'), 69 | 'market_resistance': Decimal('0'), 70 | 'dynamic': 'none', 71 | 'sell_all_coins': False, 72 | 'tickers_list_changed': False, 73 | 'exchange_symbol': 'USDT', 74 | 'price_list_counter': int(0), 75 | 'CURRENT_EXPOSURE': Decimal('0'), 76 | 'TOTAL_GAINS': Decimal('0'), 77 | 'NEW_BALANCE': Decimal('0'), 78 | 'INVESTMENT_GAIN': Decimal('0'), 79 | 'STARTUP': True, 80 | 'LIST_AUTOCREATE': False, 81 | 'symbol_info': {}, 82 | 'price_timedelta': Decimal('0'), 83 | 'trade_slots': int(0), 84 | 'dynamics_state': 'down', 85 | 'last_trade_won': 2, 86 | 'last_report_time': 0, 87 | 'session_start': False, 88 | 'prices_grabbed': False, 89 | 'reload_tickers_list': True, 90 | 'profit_to_trade_ratio': Decimal('0'), 91 | 'bnb_current_price': Decimal('0'), 92 | 'tickers': [] 93 | } 94 | 95 | report_struct = { 96 | 'report': '', 97 | 'message': False, 98 | 'log': False 99 | } 100 | 101 | # creating git commit number so we can use it in reports to see wich bot version is running 102 | def get_git_commit_number() -> str: 103 | 104 | try: 105 | git_commit_count = str(subprocess.check_output(['git', 'rev-list', '--count', 'HEAD']))[:-3][2:] 106 | 107 | except: 108 | git_commit_count = "NONE" 109 | 110 | return git_commit_count 111 | 112 | def decimals() -> int: 113 | # set number of decimals for reporting fractions 114 | if is_fiat(): 115 | return 2 116 | else: 117 | return 8 118 | 119 | def is_fiat() -> bool: 120 | # check if we are using a fiat as a base currency 121 | global hsp_head 122 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 123 | #list below is in the order that Binance displays them, apologies for not using ASC order but this is easier to update later 124 | fiats = ['USDT', 'BUSD', 'AUD', 'BRL', 'EUR', 'GBP', 'RUB', 'TRY', 'TUSD', \ 125 | 'USDC', 'PAX', 'BIDR', 'DAI', 'IDRT', 'UAH', 'NGN', 'VAI', 'BVND'] 126 | 127 | if PAIR_WITH in fiats: 128 | return True 129 | else: 130 | return False 131 | 132 | 133 | args = parse_args() 134 | 135 | DEFAULT_CONFIG_FILE = 'config.yml' 136 | DEFAULT_CREDS_FILE = 'creds.yml' 137 | 138 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 139 | creds_file = args.creds if args.creds else DEFAULT_CREDS_FILE 140 | parsed_config = load_config(config_file) 141 | parsed_creds = load_config(creds_file) 142 | 143 | # Load system vars 144 | TEST_MODE = parsed_config['script_options']['TEST_MODE'] 145 | # LOG_TRADES = parsed_config['script_options'].get('LOG_TRADES') 146 | LOG_FILE = parsed_config['script_options'].get('LOG_FILE') 147 | SESSION_REPORT_STYLE = parsed_config['script_options']['SESSION_REPORT_STYLE'] 148 | DEBUG_SETTING = parsed_config['script_options'].get('VERBOSE_MODE') 149 | AMERICAN_USER = parsed_config['script_options'].get('AMERICAN_USER') 150 | BOT_MESSAGE_REPORTS = parsed_config['script_options'].get('BOT_MESSAGE_REPORTS') 151 | BOT_ID = parsed_config['script_options'].get('BOT_ID') 152 | 153 | # Load trading vars 154 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 155 | INVESTMENT = Decimal(str(parsed_config['trading_options']['INVESTMENT'])) 156 | TRADE_SLOTS = parsed_config['trading_options']['TRADE_SLOTS'] 157 | UNIQUE_BUYS = parsed_config['trading_options'].get('UNIQUE_BUYS') 158 | EXCLUDED_PAIRS = parsed_config['trading_options']['EXCLUDED_PAIRS'] 159 | TIME_DIFFERENCE = parsed_config['trading_options']['TIME_DIFFERENCE'] 160 | RECHECK_INTERVAL = parsed_config['trading_options']['RECHECK_INTERVAL'] 161 | CHANGE_IN_PRICE_MIN = Decimal(str(parsed_config['trading_options']['CHANGE_IN_PRICE_MIN'])) 162 | CHANGE_IN_PRICE_MAX = Decimal(str(parsed_config['trading_options']['CHANGE_IN_PRICE_MAX'])) 163 | STOP_LOSS = Decimal(str(parsed_config['trading_options']['STOP_LOSS'])) 164 | TAKE_PROFIT = Decimal(str(parsed_config['trading_options']['TAKE_PROFIT'])) 165 | CUSTOM_LIST = parsed_config['trading_options']['CUSTOM_LIST'] 166 | TICKERS_LIST = parsed_config['trading_options']['TICKERS_LIST'] 167 | USE_TRAILING_STOP_LOSS = parsed_config['trading_options']['USE_TRAILING_STOP_LOSS'] 168 | TRAILING_STOP_LOSS = Decimal(str(parsed_config['trading_options']['TRAILING_STOP_LOSS'])) 169 | TRAILING_TAKE_PROFIT = Decimal(str(parsed_config['trading_options']['TRAILING_TAKE_PROFIT'])) 170 | TRADING_FEE = Decimal(str(parsed_config['trading_options']['TRADING_FEE'])) 171 | TRADING_FEE_BNB = parsed_config['trading_options']['TRADING_FEE_BNB'] 172 | SIGNALLING_MODULES = parsed_config['trading_options']['SIGNALLING_MODULES'] 173 | DYNAMIC_WIN_LOSS_UP = Decimal(str(parsed_config['trading_options']['DYNAMIC_WIN_LOSS_UP'])) 174 | DYNAMIC_WIN_LOSS_DOWN = Decimal(str(parsed_config['trading_options']['DYNAMIC_WIN_LOSS_DOWN'])) 175 | DYNAMIC_CHANGE_IN_PRICE = Decimal(str(parsed_config['trading_options']['DYNAMIC_CHANGE_IN_PRICE'])) 176 | DYNAMIC_SETTINGS = parsed_config['trading_options']['DYNAMIC_SETTINGS'] 177 | DYNAMIC_MIN_MAX = Decimal(str(parsed_config['trading_options']['DYNAMIC_MIN_MAX'])) 178 | HOLDING_PRICE_THRESHOLD = Decimal(str(parsed_config['trading_options']['HOLDING_PRICE_THRESHOLD'])) 179 | TRAILING_BUY_THRESHOLD = Decimal(str(parsed_config['trading_options']['TRAILING_BUY_THRESHOLD'])) 180 | STOP_LOSS_ON_PAUSE = parsed_config['trading_options']['STOP_LOSS_ON_PAUSE'] 181 | PERCENT_SIGNAL_BUY = parsed_config['trading_options']['PERCENT_SIGNAL_BUY'] 182 | SORT_LIST_TYPE = parsed_config['trading_options']['SORT_LIST_TYPE'] 183 | LIST_AUTOCREATE = parsed_config['trading_options']['LIST_AUTOCREATE'] 184 | LIST_CREATE_TYPE = parsed_config['trading_options']['LIST_CREATE_TYPE'] 185 | LIST_CREATE_TYPE_OPTION = parsed_config['trading_options']['LIST_CREATE_TYPE_OPTION'] 186 | IGNORE_LIST = parsed_config['trading_options']['IGNORE_LIST'] 187 | 188 | REPORT_FREQUENCY = parsed_config['script_options']['REPORT_FREQUENCY'] 189 | HOLDING_INTERVAL_LIMIT = parsed_config['trading_options']['HOLDING_INTERVAL_LIMIT'] 190 | QUANTITY = Decimal(INVESTMENT/TRADE_SLOTS) 191 | 192 | HOLDING_TIME_LIMIT = (TIME_DIFFERENCE * 60 * 1000) * HOLDING_INTERVAL_LIMIT 193 | 194 | # structure used for settings variables and feed for bot settings 195 | settings_struct = { 196 | 'TIME_DIFFERENCE': TIME_DIFFERENCE, 197 | 'RECHECK_INTERVAL': RECHECK_INTERVAL, 198 | 'CHANGE_IN_PRICE_MIN': CHANGE_IN_PRICE_MIN, 199 | 'CHANGE_IN_PRICE_MAX': CHANGE_IN_PRICE_MAX, 200 | 'STOP_LOSS': STOP_LOSS, 201 | 'TAKE_PROFIT': TAKE_PROFIT, 202 | 'TRAILING_STOP_LOSS': TRAILING_STOP_LOSS, 203 | 'TRAILING_TAKE_PROFIT': TRAILING_TAKE_PROFIT, 204 | 'HOLDING_TIME_LIMIT': HOLDING_TIME_LIMIT, 205 | 'DYNAMIC_CHANGE_IN_PRICE': DYNAMIC_CHANGE_IN_PRICE, 206 | 'SESSION_REPORT_STYLE': SESSION_REPORT_STYLE, 207 | 'HOLDING_PRICE_THRESHOLD': HOLDING_PRICE_THRESHOLD, 208 | 'TRAILING_BUY_THRESHOLD': TRAILING_BUY_THRESHOLD, 209 | 'DYNAMIC_WIN_LOSS_UP': DYNAMIC_WIN_LOSS_UP, 210 | 'DYNAMIC_WIN_LOSS_DOWN': DYNAMIC_WIN_LOSS_DOWN 211 | } 212 | 213 | # structure used for trading variables during runtime 214 | trading_struct = { 215 | 'holding_timeout_dynamic': 'up', 216 | 'holding_timeout_sell': 'none', 217 | 'lost_trade_percent': Decimal('0'), 218 | 'won_trade_percent': Decimal('0'), 219 | 'trade_support': Decimal('0'), 220 | 'trade_resistance': Decimal('0'), 221 | 'sum_won_trades': settings_struct['TRAILING_STOP_LOSS'], 222 | 'sum_lost_trades': -settings_struct['STOP_LOSS'], 223 | 'max_holding_price': Decimal('0'), 224 | 'min_holding_price': Decimal('0'), 225 | 'sum_min_holding_price': Decimal('0'), 226 | 'sum_max_holding_price': Decimal('0'), 227 | 'consecutive_loss': Decimal('0'), 228 | 'consecutive_win': Decimal('0'), 229 | 'stop_loss_adjust': False 230 | } 231 | 232 | # Default no debugging 233 | DEBUG = False 234 | 235 | if DEBUG_SETTING or args.debug: 236 | DEBUG = True 237 | 238 | # Load creds for correct environment 239 | access_key, secret_key = load_correct_creds(parsed_creds) 240 | 241 | # Telegram_Bot enabled? # **added by*Coding60plus 242 | if BOT_MESSAGE_REPORTS: 243 | TELEGRAM_BOT_TOKEN, TELEGRAM_BOT_ID, TEST_DISCORD_WEBHOOK, LIVE_DISCORD_WEBHOOK = load_telegram_creds(parsed_creds) 244 | 245 | # set to false at Start 246 | global bot_paused 247 | bot_paused = False 248 | 249 | # Authenticate with the client, Ensure API key is good before continuing 250 | if AMERICAN_USER: 251 | client = Client(access_key, secret_key, tld='us') 252 | else: 253 | client = Client(access_key, secret_key) 254 | 255 | # If the users has a bad / incorrect API key. 256 | # this will stop the script from starting, and display a helpful error. 257 | api_ready, msg = test_api_key(client, BinanceAPIException) 258 | if api_ready is not True: 259 | exit(f'{txcolors.SELL_LOSS}{msg}{txcolors.DEFAULT}') 260 | 261 | # Telegram_Bot enabled? # **added by*Coding60plus 262 | if DEBUG: 263 | print(f'Loaded config below\n{simplejson.dumps(parsed_config, indent=4, use_decimal=True)}') 264 | print(f'Your credentials have been loaded from {creds_file}') 265 | 266 | # prevent including a coin in volatile_coins if it has already appeared there less than TIME_DIFFERENCE minutes ago 267 | volatility_cooloff = {} 268 | 269 | # try to load all the coins bought by the bot if the file exists and is not empty 270 | coins_bought = {} 271 | 272 | # path to the saved coins_bought file 273 | coins_bought_file_path = 'coins_bought.json' 274 | 275 | # use separate files for testing and live trading 276 | if TEST_MODE: 277 | coins_bought_file_path = 'test_' + coins_bought_file_path 278 | 279 | # if saved coins_bought json file exists and it's not empty then load it 280 | if os.path.isfile(coins_bought_file_path) and os.stat(coins_bought_file_path).st_size!= 0: 281 | with open(coins_bought_file_path) as file: 282 | coins_bought = simplejson.load(file, use_decimal=True) 283 | 284 | # Initiate the conneciton error counters 285 | READ_TIMEOUT_COUNT=0 286 | CONNECTION_ERROR_COUNT = 0 287 | -------------------------------------------------------------------------------- /bot/tickers_list.py: -------------------------------------------------------------------------------- 1 | import os 2 | # use if needed to pass args to external modules 3 | import sys 4 | # used for directory handling 5 | import glob 6 | import time 7 | import threading 8 | 9 | #gogo MOD telegram needs import request 10 | import requests 11 | import re 12 | 13 | # needed for the binance API / websockets / Exception handling 14 | from binance.client import Client 15 | from binance.exceptions import BinanceAPIException 16 | from requests.exceptions import ReadTimeout, ConnectionError 17 | 18 | # used to store trades and sell assets 19 | import json 20 | 21 | from helpers.parameters import ( 22 | parse_args, load_config 23 | ) 24 | 25 | # Load creds modules 26 | from helpers.handle_creds import ( 27 | load_correct_creds, test_api_key, 28 | load_telegram_creds 29 | ) 30 | 31 | from bot.settings import * 32 | 33 | def tickers_list() -> None: 34 | 35 | global LIST_AUTOCREATE, LIST_CREATE_TYPE, SORT_LIST_TYPE, session_struct 36 | 37 | # Load coins to be ignored from file 38 | ignorelist=[line.strip() for line in open(IGNORE_LIST)] 39 | 40 | # Use CUSTOM_LIST symbols if CUSTOM_LIST is set to True 41 | if CUSTOM_LIST: 42 | session_struct['tickers']=[line.strip() for line in open(TICKERS_LIST)] 43 | 44 | tickers_sort = {} 45 | 46 | # get all info on tickers from binance 47 | # with retry on error reading 48 | while True: 49 | try: 50 | tickers_binance = client.get_ticker() 51 | except: 52 | print(f"{txcolors.WARNING}Binance Problem Get Tickers{txcolors.DEFAULT}") 53 | time.sleep(1) 54 | continue 55 | break 56 | 57 | if LIST_AUTOCREATE: 58 | # pull coins from trading view and create a list 59 | if LIST_CREATE_TYPE == 'tradingview': 60 | response = requests.get('https://scanner.tradingview.com/crypto/scan') 61 | ta_data = response.json() 62 | signals_file = open(TICKERS_LIST,'w') 63 | with open (TICKERS_LIST, 'w') as f: 64 | for i in ta_data['data']: 65 | if i['s'][:7]=='BINANCE' and i['s'][-len(PAIR_WITH):] == PAIR_WITH and i['s'][8:-len(PAIR_WITH)] not in ignorelist: 66 | f.writelines(str(i['s'][8:-len(PAIR_WITH)])+'\n') 67 | session_struct['tickers_list_changed'] = True 68 | print(f'>> Tickers CREATED from TradingView tickers!!!{TICKERS_LIST} <<') 69 | 70 | if LIST_CREATE_TYPE == 'edgesforledges': 71 | url = 'http://edgesforledges.com/watchlists/download/binance/' + LIST_CREATE_TYPE_OPTION 72 | response = requests.get(url) 73 | signals_file = open(TICKERS_LIST,'w') 74 | with open (TICKERS_LIST, 'w') as f: 75 | for line in response.text.splitlines(): 76 | if line.endswith(PAIR_WITH): 77 | coin = re.sub(r'BINANCE:(.*)'+PAIR_WITH,r'\1',line) 78 | if coin not in ignorelist: 79 | f.writelines(coin+'\n') 80 | session_struct['tickers_list_changed'] = True 81 | print(f'>> Tickers CREATED from {url} tickers!!!{TICKERS_LIST} <<') 82 | 83 | # pull coins from binance and create list 84 | if LIST_CREATE_TYPE == 'binance': 85 | with open (TICKERS_LIST, 'w') as f: 86 | for ticker in tickers_binance: 87 | if ticker['symbol'].endswith(PAIR_WITH): 88 | coin = ticker['symbol'].replace(PAIR_WITH,"") 89 | if coin not in ignorelist: 90 | f.writelines(coin+'\n') 91 | session_struct['tickers_list_changed'] = True 92 | print(f'>> Tickers CREATED from Binance tickers!!!{TICKERS_LIST} <<') 93 | 94 | # Reload Tickers 95 | session_struct['tickers']=[line.strip() for line in open(TICKERS_LIST)] 96 | 97 | if SORT_LIST_TYPE == 'volume' or SORT_LIST_TYPE == 'price_change': 98 | # create list with volume and change in price on our pairs 99 | for coin in tickers_binance: 100 | if any(item + PAIR_WITH == coin['symbol'] for item in session_struct['tickers']) and all(item not in coin['symbol'] for item in EXCLUDED_PAIRS): 101 | if SORT_LIST_TYPE == 'volume': 102 | tickers_sort[coin['symbol']] = { 'volume': Decimal(coin['volume'])} 103 | if SORT_LIST_TYPE == 'price_change': 104 | tickers_sort[coin['symbol']] = { 'priceChangePercent': Decimal(coin['priceChangePercent'])} 105 | 106 | # sort tickers by descending order volume and price 107 | if SORT_LIST_TYPE == 'volume': 108 | tickers_sort = list(sorted( tickers_sort.items(), key=lambda x: x[1]['volume'], reverse=True)) 109 | if SORT_LIST_TYPE == 'price_change': 110 | tickers_sort = list(sorted( tickers_sort.items(), key=lambda x: x[1]['priceChangePercent'], reverse=True)) 111 | 112 | # write sorted lists to files 113 | with open (TICKERS_LIST, 'w') as f: 114 | for sublist in tickers_sort: 115 | f.writelines(str(sublist[0].replace(PAIR_WITH,''))+'\n') 116 | session_struct['tickers_list_changed'] = True 117 | 118 | print(f'>> Tickers sort List {TICKERS_LIST} by {SORT_LIST_TYPE}<<') 119 | session_struct['tickers']=[line.strip() for line in open(TICKERS_LIST)] 120 | 121 | session_struct['tickers_list_changed'] = False 122 | session_struct['reload_tickers_list'] = False 123 | 124 | def reload_tickers() -> None: 125 | if session_struct['reload_tickers_list'] == True: 126 | tickers_list() 127 | 128 | tickers_list() -------------------------------------------------------------------------------- /bot/trade.py: -------------------------------------------------------------------------------- 1 | from bot.report import report 2 | import os 3 | # use if needed to pass args to external modules 4 | import sys 5 | # used for directory handling 6 | import glob 7 | import time 8 | import threading 9 | import math 10 | from typing import Tuple, Dict 11 | 12 | #gogo MOD telegram needs import request 13 | import requests 14 | 15 | # needed for the binance API / websockets / Exception handling 16 | from binance.client import Client 17 | from binance.enums import * 18 | from binance.exceptions import BinanceAPIException 19 | from requests.exceptions import ReadTimeout, ConnectionError 20 | 21 | # used to store trades and sell assets 22 | import json 23 | 24 | from helpers.parameters import ( 25 | parse_args, load_config 26 | ) 27 | 28 | # Load creds modules 29 | from helpers.handle_creds import ( 30 | load_correct_creds, test_api_key, 31 | load_telegram_creds 32 | ) 33 | 34 | from bot.settings import * 35 | from bot.grab import * 36 | from bot.report import * 37 | 38 | # this function puts coins that trigger a price change buy to trailing buy list then it follows and when coins trigger 39 | # a buy signal aka when they price passes TRAILING_BUY_THRESHOLD they are sent to buy list wich is passed to rest of buy procedures 40 | 41 | def trailing_buy(volatile_coins: Dict[str, Decimal]) -> Dict[str, Decimal]: 42 | 43 | global trail_buy_historical 44 | global trail_buy_coins 45 | 46 | buy_volatile_coins = {} 47 | 48 | trail_buy_last_price = get_price(False) 49 | 50 | for coin in volatile_coins: 51 | trail_buy_coins[coin] = volatile_coins[coin] 52 | 53 | for coin in trail_buy_coins: 54 | if trail_buy_historical[coin]['price'] > trail_buy_last_price[coin]['price']: 55 | 56 | trail_buy_coins[coin] = trail_buy_coins[coin] + (Decimal('-1.0') *(trail_buy_historical[coin]['price'] - trail_buy_last_price[coin]['price']) / trail_buy_historical[coin]['price'] * Decimal('100')) 57 | print(f"COIN: {coin} has DROPPED from {trail_buy_historical[coin]['price']} to {trail_buy_last_price[coin]['price']}") 58 | print(f"COIN: {coin} has DROPPED for {Decimal('-1.0') *(trail_buy_historical[coin]['price'] - trail_buy_last_price[coin]['price']) / trail_buy_historical[coin]['price'] * Decimal('100')}%") 59 | 60 | if trail_buy_historical[coin]['price'] < trail_buy_last_price[coin]['price']: 61 | print(f"COIN: {coin} has GONE UP!!!! from {trail_buy_historical[coin]['price']} to {trail_buy_last_price[coin]['price']}") 62 | print(f"COIN: {coin} has GONE UP!!!! for {Decimal('-1.0') *(trail_buy_historical[coin]['price'] - trail_buy_last_price[coin]['price']) / trail_buy_historical[coin]['price'] * Decimal('100')}%") 63 | 64 | if Decimal(Decimal('-1.0') *(trail_buy_historical[coin]['price'] - trail_buy_last_price[coin]['price']) / trail_buy_historical[coin]['price'] * Decimal('100')) > settings_struct['TRAILING_BUY_THRESHOLD']: 65 | 66 | buy_volatile_coins[coin] = trail_buy_coins[coin] 67 | 68 | if buy_volatile_coins: 69 | for coin in buy_volatile_coins: 70 | del trail_buy_coins[coin] 71 | 72 | trail_buy_historical = trail_buy_last_price 73 | 74 | print(f"TRAIL_BUY_COINS: {trail_buy_coins}") 75 | print(f"BUY_VOLATILE_COINS: {buy_volatile_coins}") 76 | 77 | return buy_volatile_coins 78 | 79 | # this functions makes various trade calculations and writes them to global structures wich get passed to other funtions 80 | 81 | def trade_calculations(type: str, priceChange: Decimal) -> None: 82 | 83 | if type == 'holding': 84 | 85 | if trading_struct['max_holding_price'] < priceChange : 86 | trading_struct['max_holding_price'] = priceChange 87 | 88 | if trading_struct['min_holding_price'] > priceChange : 89 | trading_struct['min_holding_price'] = priceChange 90 | 91 | session_struct['unrealised_percent'] = session_struct['unrealised_percent'] + priceChange 92 | 93 | if type == 'sell': 94 | 95 | if priceChange > 0: 96 | 97 | if session_struct['last_trade_won'] == True: 98 | trading_struct['consecutive_win'] += 1 99 | 100 | session_struct['win_trade_count'] = session_struct['win_trade_count'] + 1 101 | 102 | session_struct['last_trade_won'] = True 103 | trading_struct['consecutive_loss'] = 0 104 | 105 | trading_struct['won_trade_percent'] = priceChange 106 | trading_struct['sum_won_trades'] = trading_struct['sum_won_trades'] + trading_struct['won_trade_percent'] 107 | 108 | else: 109 | 110 | if session_struct['last_trade_won'] == False: 111 | trading_struct['consecutive_loss'] += 1 112 | 113 | session_struct['loss_trade_count'] = session_struct['loss_trade_count'] + 1 114 | 115 | session_struct['last_trade_won'] = False 116 | trading_struct['consecutive_win'] = 0 117 | 118 | trading_struct['lost_trade_percent'] = priceChange 119 | trading_struct['sum_lost_trades'] = trading_struct['sum_lost_trades'] + trading_struct['lost_trade_percent'] 120 | 121 | if DYNAMIC_SETTINGS: settings_struct['STOP_LOSS'] = (settings_struct['STOP_LOSS'] + session_struct['profit_to_trade_ratio']) / Decimal('2') 122 | 123 | trading_struct['sum_max_holding_price'] = trading_struct['sum_max_holding_price'] + trading_struct['max_holding_price'] 124 | trading_struct['max_holding_price'] = 0 125 | trading_struct['sum_min_holding_price'] = trading_struct['sum_min_holding_price'] + trading_struct['min_holding_price'] 126 | trading_struct['min_holding_price'] = 0 127 | session_struct['closed_trades_percent'] = session_struct['closed_trades_percent'] + priceChange 128 | session_struct['reload_tickers_list'] = True 129 | 130 | trading_struct['stop_loss_adjust'] = True 131 | 132 | def convert_volume() -> Tuple[Dict, Dict]: 133 | global session_struct 134 | 135 | '''Converts the volume given in QUANTITY from USDT to the each coin's volume''' 136 | 137 | #added feature to buy only if percent and signal triggers uses PERCENT_SIGNAL_BUY true or false from config 138 | if PERCENT_SIGNAL_BUY == True: 139 | volatile_coins, number_of_coins, last_price = wait_for_price('percent_mix_signal') 140 | else: 141 | volatile_coins, number_of_coins, last_price = wait_for_price('percent_and_signal') 142 | 143 | buy_volatile_coins = {} 144 | lot_size = {} 145 | volume = {} 146 | 147 | buy_volatile_coins = trailing_buy(volatile_coins) 148 | 149 | for coin in buy_volatile_coins: 150 | 151 | if session_struct['trade_slots'] + len(volume) < TRADE_SLOTS or TRADE_SLOTS == 0: 152 | 153 | # calculate the volume in coin from QUANTITY in USDT (default) 154 | try: 155 | volume[coin] = coin_volume_precision(coin,QUANTITY / last_price[coin]['price'],last_price[coin]['price']) 156 | except Exception as e: 157 | print(f"{txcolors.SELL_LOSS}ERROR BUY volume " + coin + " : " +str(e)) 158 | 159 | return volume, last_price 160 | 161 | def coin_volume_precision(coin : str, volume: Decimal, price: Decimal) -> Decimal: 162 | stepSize = 0 163 | minQty = 0 164 | minNotional = 0 165 | 166 | # Find the correct step size for each coin 167 | # max accuracy for BTC for example is 6 decimal points 168 | # while XRP is only 1 169 | try: 170 | coin_info = session_struct['symbol_info'][coin] 171 | except KeyError: 172 | # not retrieved at startup, try again 173 | try: 174 | session_struct['symbol_info'][coin] = client.get_symbol_info(coin) 175 | coin_info = session_struct['symbol_info'][coin] 176 | except Exception as e: 177 | raise Exception("Volume unable to get_symbol_info " + str(e)) 178 | 179 | for coin_info_fiter in coin_info['filters']: 180 | if coin_info_fiter['filterType'] == 'LOT_SIZE': 181 | stepSize = Decimal(coin_info_fiter['stepSize'].rstrip('0')) 182 | minQty = Decimal(coin_info_fiter['minQty']) 183 | if coin_info_fiter['filterType'] == 'MIN_NOTIONAL': 184 | minNotional = Decimal(coin_info_fiter['minNotional']) 185 | 186 | volume = volume.quantize(stepSize) 187 | 188 | if volume < minQty: 189 | raise Exception("Volume too lower/not enought (minQty)") 190 | 191 | if price * volume < minNotional: 192 | raise Exception("Volume too lower/not enought (minNotional)") 193 | 194 | return volume 195 | 196 | def test_order_id() -> int: 197 | import random 198 | """returns a fake order id by hashing the current time""" 199 | test_order_id_number = random.randint(100000000,999999999) 200 | return test_order_id_number 201 | 202 | def buy() -> Tuple[Dict, Dict, Dict]: 203 | '''Place Buy market orders for each volatile coin found''' 204 | global UNIQUE_BUYS, session_struct 205 | volume, last_price = convert_volume() 206 | orders = {} 207 | 208 | for coin in volume: 209 | BUYABLE = True 210 | if UNIQUE_BUYS and (coin in coins_bought): 211 | BUYABLE = False 212 | 213 | # only buy if the there are no active trades on the coin 214 | if BUYABLE: 215 | print(f"{txcolors.BUY}Preparing to buy {volume[coin]} {coin}{txcolors.DEFAULT}") 216 | 217 | REPORT = str(f"Buy : {volume[coin]} {coin} - {last_price[coin]['price']}") 218 | 219 | try: 220 | orders[coin] = order_coin(coin,SIDE_BUY,last_price[coin]['price'],volume[coin]) 221 | except Exception as e: 222 | print(f"{txcolors.SELL_LOSS}ERROR "+ SIDE_BUY + " " + coin + " " +str(e)) 223 | continue 224 | 225 | # Log, announce, and report trade 226 | print('Order returned, saving order to file') 227 | 228 | REPORT = str(f"BUY: bought {orders[coin]['volume']} {coin} - average price: {orders[coin]['avgPrice']} {PAIR_WITH}") 229 | 230 | report_add(REPORT) 231 | 232 | else: 233 | print(f'Signal detected, but there is already an active trade on {coin}') 234 | 235 | return orders, last_price, volume 236 | 237 | def sell_coins() -> Dict: 238 | '''sell coins that have reached the STOP LOSS or TAKE PROFIT threshold''' 239 | global session_struct, settings_struct, trading_struct 240 | 241 | global hsp_head 242 | global FULL_LOG 243 | last_price = get_price(False) # don't populate rolling window 244 | #last_price = get_price(add_to_historical=True) # don't populate rolling window 245 | coins_sold = {} 246 | holding_timeout_sell_trigger = False 247 | session_struct['unrealised_percent'] = 0 248 | 249 | for coin in list(coins_bought): 250 | 251 | BUY_PRICE = Decimal(coins_bought[coin]['bought_at']) 252 | # coinTakeProfit is the price at which to 'take profit' based on config % markup 253 | coinTakeProfit = BUY_PRICE + ((BUY_PRICE * coins_bought[coin]['take_profit']) / Decimal('100')) 254 | # coinStopLoss is the price at which to 'stop losses' based on config % markdown 255 | coinStopLoss = BUY_PRICE + ((BUY_PRICE * coins_bought[coin]['stop_loss']) / Decimal('100')) 256 | # coinHoldingTimeLimit is the time limit for holding onto a coin 257 | coinHoldingTimeLimit = Decimal(coins_bought[coin]['timestamp']) + settings_struct['HOLDING_TIME_LIMIT'] 258 | lastPrice = last_price[coin]['price'] 259 | LAST_PRICE = "{:.8f}".format(lastPrice) 260 | buyPrice = Decimal(coins_bought[coin]['bought_at']) 261 | BUY_PRICE = "{:.8f}". format(buyPrice) 262 | 263 | # Note: priceChange and priceChangeWithFee are percentages! 264 | priceChange = Decimal((lastPrice - buyPrice) / buyPrice * Decimal('100')) 265 | 266 | profit_estimate = (QUANTITY*(priceChange))/100 267 | 268 | # check that the price is above the take profit and readjust coinStopLoss and coinTakeProfit accordingly if trialing stop loss used 269 | if lastPrice > coinTakeProfit and USE_TRAILING_STOP_LOSS: 270 | # increasing coinTakeProfit by TRAILING_TAKE_PROFIT (essentially next time to readjust coinStopLoss) 271 | coins_bought[coin]['take_profit'] = priceChange + settings_struct['TRAILING_TAKE_PROFIT'] 272 | coins_bought[coin]['stop_loss'] = coins_bought[coin]['take_profit'] - settings_struct['TRAILING_STOP_LOSS'] 273 | if DEBUG: print(f"{coin} TP reached, adjusting TP {coins_bought[coin]['take_profit']:.{decimals()}f} and SL {coins_bought[coin]['stop_loss']:.{decimals()}f} accordingly to lock-in profit") 274 | continue 275 | 276 | current_time = round(time.time() * 1000) 277 | # print(f'TL:{coinHoldingTimeLimit}, time: {current_time} HOLDING_TIME_LIMIT: {HOLDING_TIME_LIMIT}, TimeLeft: {(coinHoldingTimeLimit - current_time)/1000/60} ') 278 | 279 | trade_calculations('holding', priceChange) 280 | 281 | if coinHoldingTimeLimit < current_time and priceChange > settings_struct['HOLDING_PRICE_THRESHOLD']: 282 | holding_timeout_sell_trigger = True 283 | 284 | # check that the price is below the stop loss or above take profit (if trailing stop loss not used) and sell if this is the case 285 | ORDER = "" 286 | if session_struct['sell_all_coins']: 287 | ORDER = "PAUSE_SELL" 288 | if lastPrice < coinStopLoss: 289 | ORDER = "STOP_LOSS" 290 | if lastPrice > coinTakeProfit and not USE_TRAILING_STOP_LOSS: 291 | ORDER = "TAKE_PROFIT" 292 | if holding_timeout_sell_trigger: 293 | ORDER = "HOLDING_TIMEOUT" 294 | 295 | if ORDER != "": 296 | print(f"{txcolors.SELL_PROFIT if priceChange >= 0. else txcolors.SELL_LOSS}TP or SL reached, selling {coins_bought[coin]['volume']} {coin}. Bought at: {BUY_PRICE} (Price now: {LAST_PRICE}) - {priceChange:.2f}% - Est: {(QUANTITY * priceChange) / Decimal('100'):.{decimals()}f} {PAIR_WITH}{txcolors.DEFAULT}") 297 | 298 | try: 299 | volume = coin_volume_precision(coin,coins_bought[coin]['volume'],lastPrice) 300 | coins_sold[coin] = order_coin(coin,SIDE_SELL,lastPrice,volume) 301 | except Exception as e: 302 | print(f"{txcolors.WARNING} "+ SIDE_SELL + " " + coin + " " +str(e)) 303 | continue 304 | 305 | lastPrice = coins_sold[coin]['avgPrice'] 306 | coins_sold[coin]['orderId'] = coins_bought[coin]['orderId'] 307 | priceChange = Decimal((lastPrice - buyPrice) / buyPrice * Decimal(100)) 308 | 309 | # prevent system from buying this coin for the next TIME_DIFFERENCE minutes 310 | volatility_cooloff[coin] = datetime.now() 311 | 312 | # Log trade 313 | trade_profit = coins_sold[coin]['tradeWithFee'] - coins_bought[coin]['tradeWithFee'] 314 | 315 | trade_calculations('sell', priceChange) 316 | 317 | #gogo MOD to trigger trade lost or won and to count lost or won trades 318 | 319 | session_struct['session_profit'] = session_struct['session_profit'] + trade_profit 320 | 321 | holding_timeout_sell_trigger = False 322 | 323 | report_add(f"{ORDER} - SELL: {coins_sold[coin]['volume']} {coin} - Bought at {buyPrice:.{decimals()}f}, sold at {lastPrice:.{decimals()}f} - Profit: {trade_profit:.{decimals()}f} {PAIR_WITH} ({priceChange:.2f}%)",True) 324 | 325 | continue 326 | 327 | if len(coins_bought) > 0: 328 | print(f"TP:{coinTakeProfit:.{decimals()}f}:{coins_bought[coin]['take_profit']:.2f} or SL:{coinStopLoss:.{decimals()}f}:{coins_bought[coin]['stop_loss']:.2f} not yet reached, not selling {coin} for now >> Bought at: {BUY_PRICE} - Now: {LAST_PRICE} : {txcolors.SELL_PROFIT if priceChange >= 0. else txcolors.SELL_LOSS}{priceChange:.2f}% Est: {profit_estimate:.{decimals()}f} {PAIR_WITH} - CIP: {settings_struct['CHANGE_IN_PRICE_MIN']:.2f}/{settings_struct['CHANGE_IN_PRICE_MAX']:.2f} - TAKE_PROFIT: {settings_struct['TAKE_PROFIT']:.2f}{txcolors.DEFAULT}") 329 | 330 | return coins_sold 331 | 332 | def order_coin(coin: str, order: str, lastPrice: Decimal, volume: Decimal) -> Dict: 333 | global TRADING_FEE, STOP_LOSS, TAKE_PROFIT, session_struct 334 | 335 | if TEST_MODE: 336 | # Simulate request... check Input 337 | order_details = client.create_test_order( 338 | symbol = coin, 339 | side = order, 340 | type = ORDER_TYPE_MARKET, 341 | quantity = volume 342 | ) 343 | # Simulate request... wait 100 ms ( bad condition ) 344 | time.sleep(0.1) 345 | # Simulate response 346 | if TRADING_FEE_BNB: 347 | commissionAsset = 'BNB' 348 | commission = lastPrice * volume * TRADING_FEE / Decimal('100') / session_struct['bnb_current_price'] 349 | else: 350 | if order == SIDE_BUY: 351 | commissionAsset = coin[:len(coin) - len(PAIR_WITH)] 352 | commission = volume * TRADING_FEE / Decimal('100') 353 | else: 354 | commissionAsset = PAIR_WITH 355 | commission = lastPrice * volume * TRADING_FEE / Decimal('100') 356 | # Prepare Order Coin 357 | order_details = { 358 | 'symbol': coin, 359 | 'orderId': test_order_id(), 360 | 'transactTime': datetime.now().timestamp() * 1000, 361 | 'side': order, 362 | 'price': lastPrice, 363 | "fills": [ 364 | { 365 | 'price': lastPrice, 366 | 'qty': volume, 367 | 'commission':commission.quantize(DECIMAL_PRECISION), 368 | 'commissionAsset':commissionAsset 369 | }] 370 | } 371 | else: 372 | # try to create a real order 373 | order_details = client.create_order( 374 | symbol = coin, 375 | side = order, 376 | type = ORDER_TYPE_MARKET, 377 | quantity = volume, 378 | newOrderRespType = "FULL" 379 | ) 380 | 381 | transactionInfo = {} 382 | # adding order fill extractions here 383 | # 384 | # just to explain what I am doing here: 385 | # Market orders are not always filled at one price, we need to find the averages of all 'parts' (fills) of this order. 386 | # 387 | # reset other variables to 0 before use 388 | FILLS_TOTAL = Decimal('0') 389 | FILLS_QTY = Decimal('0') 390 | FILLS_QTY_FEE = Decimal('0') 391 | BNB_WARNING = 0 392 | tradeWithFee = Decimal('0') 393 | tradeWithoutFee = Decimal('0') 394 | # loop through each 'fill': 395 | for fills in order_details['fills']: 396 | FILL_PRICE = Decimal(fills['price']) 397 | FILL_QTY = Decimal(fills['qty']) 398 | FILL_FEE = Decimal(fills['commission']) 399 | 400 | # check if the fee was in BNB. If not, log a nice warning: 401 | if (fills['commissionAsset'] != 'BNB') and (TRADING_FEE_BNB) and (BNB_WARNING == 0): 402 | print(f"{txcolors.WARNING}BNB not used for trading fee, please...{txcolors.DEFAULT}") 403 | BNB_WARNING += 1 404 | # Sell or Buy with BNB 405 | if fills['commissionAsset'] == 'BNB': 406 | tradeWithoutFee += FILL_PRICE * FILL_QTY 407 | if order_details['side'] == SIDE_BUY: 408 | tradeWithFee += FILL_PRICE * FILL_QTY + FILL_FEE * session_struct['bnb_current_price'] 409 | else: 410 | tradeWithFee += FILL_PRICE * FILL_QTY - FILL_FEE * session_struct['bnb_current_price'] 411 | else: 412 | # Sell without BNB ? 413 | if fills['commissionAsset'] == PAIR_WITH: 414 | tradeWithFee += FILL_PRICE * FILL_QTY - FILL_FEE 415 | tradeWithoutFee += FILL_PRICE * FILL_QTY 416 | # Buy without BNB 417 | else: 418 | tradeWithFee += FILL_PRICE * FILL_QTY 419 | tradeWithoutFee += FILL_PRICE * FILL_QTY - FILL_FEE * FILL_PRICE 420 | # Quantity Fee... ! 421 | FILLS_QTY_FEE += FILL_FEE 422 | 423 | # quantity of fills * price 424 | FILLS_TOTAL += (FILL_PRICE * FILL_QTY) 425 | # add to running total of fills quantity 426 | FILLS_QTY += FILL_QTY 427 | 428 | # calculate average fill price: 429 | FILL_AVG = Decimal(FILLS_TOTAL / FILLS_QTY).quantize(DECIMAL_PRECISION) 430 | 431 | # Real Volume without Fee when don't use BNB... sometime you loose more than 0.1 due to precision of volume coin 432 | # Example a coin can be only be in integer mode ... you can 1 off coin ... 433 | FILLS_QTY = Decimal(FILLS_QTY - FILLS_QTY_FEE).quantize(DECIMAL_PRECISION) 434 | 435 | # create object with received data from Binance 436 | transactionInfo = { 437 | 'symbol': order_details['symbol'], 438 | 'orderId': order_details['orderId'], 439 | 'timestamp': order_details['transactTime'], 440 | 'avgPrice': FILL_AVG, 441 | 'volume': FILLS_QTY, 442 | 'tradeWithFee': tradeWithFee.quantize(DECIMAL_PRECISION), 443 | 'tradeWithoutFee': tradeWithoutFee.quantize(DECIMAL_PRECISION) 444 | } 445 | 446 | return transactionInfo 447 | 448 | 449 | def update_portfolio(orders: Dict, last_price: Dict, volume: Dict) -> Dict: 450 | 451 | global session_struct 452 | 453 | '''add every coin bought to our portfolio for tracking/selling later''' 454 | if DEBUG: print(orders) 455 | for coin in orders: 456 | # Prepare Coin Bought 457 | coin_bought = { 458 | 'symbol': orders[coin]['symbol'], 459 | 'orderId': orders[coin]['orderId'], 460 | 'timestamp': orders[coin]['timestamp'], 461 | 'bought_at': orders[coin]['avgPrice'], 462 | 'volume': orders[coin]['volume'], 463 | 'tradeWithFee': orders[coin]['tradeWithFee'], 464 | 'tradeWithoutFee': orders[coin]['tradeWithoutFee'], 465 | 'stop_loss': -settings_struct['STOP_LOSS'], 466 | 'take_profit': settings_struct['TAKE_PROFIT'], 467 | } 468 | # Multi Buy Same Coin ? 469 | if coin in coins_bought: 470 | coin_bought['volume'] += coins_bought[coin]['volume'] 471 | coin_bought['bought_at'] = ( orders[coin]['avgPrice'] * orders[coin]['volume'] + coins_bought[coin]['bought_at'] * coins_bought[coin]['volume'] ) / coin_bought['volume'] 472 | coin_bought['tradeWithFee'] += coins_bought[coin]['tradeWithFee'] 473 | coin_bought['tradeWithoutFee'] += coins_bought[coin]['tradeWithoutFee'] 474 | 475 | coins_bought[coin] = coin_bought 476 | 477 | print(f'Order for {orders[coin]["symbol"]} with ID {orders[coin]["orderId"]} placed and saved to file.') 478 | 479 | if len(orders) > 0: 480 | # save the coins in a json file in the same directory 481 | with open(coins_bought_file_path, 'w') as file: 482 | simplejson.dump(coins_bought, file, indent=4, use_decimal=True) 483 | 484 | update_trade_slot() 485 | 486 | def update_trade_slot() -> None: 487 | totalTrade = 0 488 | for coin in coins_bought: 489 | totalTrade += coins_bought[coin]['tradeWithoutFee'] 490 | 491 | if totalTrade > 0: 492 | session_struct['trade_slots'] = int(totalTrade / QUANTITY) + 1 493 | else: 494 | session_struct['trade_slots'] = 0 495 | 496 | 497 | def remove_from_portfolio(coins_sold: Dict) -> None: 498 | 499 | global session_struct 500 | 501 | '''Remove coins sold due to SL or TP from portfolio''' 502 | for coin,data in coins_sold.items(): 503 | symbol = coin 504 | order_id = data['orderId'] 505 | # code below created by getsec <3 506 | for bought_coin, bought_coin_data in coins_bought.items(): 507 | if bought_coin_data['orderId'] == order_id: 508 | print(f"Sold {bought_coin}, removed order ID {order_id} from history.") 509 | coins_bought.pop(bought_coin) 510 | with open(coins_bought_file_path, 'w') as file: 511 | simplejson.dump(coins_bought, file, indent=4, use_decimal=True) 512 | break 513 | update_trade_slot() 514 | session_struct['reload_tickers_list'] = True 515 | 516 | 517 | def trade_crypto() -> None: 518 | global CONNECTION_ERROR_COUNT, READ_TIMEOUT_COUNT 519 | try: 520 | 521 | orders, last_price, volume = buy() 522 | update_portfolio(orders, last_price, volume) 523 | coins_sold = sell_coins() 524 | remove_from_portfolio(coins_sold) 525 | except ReadTimeout as rt: 526 | READ_TIMEOUT_COUNT += 1 527 | print(f'We got a timeout error from from binance. Going to re-loop. Current Count: {READ_TIMEOUT_COUNT}') 528 | except ConnectionError as ce: 529 | CONNECTION_ERROR_COUNT +=1 530 | print(f'{txcolors.WARNING}We got a timeout error from from binance. Going to re-loop. Current Count: {CONNECTION_ERROR_COUNT}\n{ce}{txcolors.DEFAULT}') 531 | -------------------------------------------------------------------------------- /config.example.yml: -------------------------------------------------------------------------------- 1 | # These options apply to how the script will operate. 2 | script_options: 3 | 4 | # Setting TEST_MODE to False will use REAL funds, use at your own risk! 5 | TEST_MODE: True 6 | 7 | # Set this to true if you are accessing binance from within the United States of America. 8 | AMERICAN_USER: False 9 | 10 | # Save trades to a log file. 11 | LOG_TRADES: True 12 | 13 | # Specify the location of log file (default: trades.txt). 14 | LOG_FILE: 'trades.txt' 15 | 16 | # gogos MOD Telegram bot integration 17 | BOT_MESSAGE_REPORTS: True 18 | 19 | # Bot id for BOT_MESSAGE_REPORTS on discord 20 | # Give your bot a recognizable name so everyone understands exactly what going on 21 | BOT_ID: '@>>' 22 | 23 | # Should session status be reported detailed or simple? Options: 'detailed' 'console' 24 | SESSION_REPORT_STYLE: 'detailed' 25 | 26 | # How often the bot reports session status to console, in seconds. 27 | REPORT_FREQUENCY: 5 28 | 29 | # show/hide additional data on bot screen. False = silent, True = spam (debug mode). 30 | VERBOSE_MODE: False 31 | 32 | 33 | # These options apply to the trading methods the script executes 34 | trading_options: 35 | 36 | # select your base currency to use for trading pairs (for example USDT, ETH, BTC) 37 | PAIR_WITH: ETH 38 | 39 | # INVESTMENT = total amount you're trading across all TRADE_SLOTS 40 | # Binance uses specifies a min value per trade, add a bit extra above their 'min trade value' 41 | # ..to enable selling if the price drops. 42 | # 43 | # TIPS (per TRADE_SLOT) 44 | # - Using USDT? Recommended 13+ per TRADE_SLOT (min trade value: 10) 45 | # - Using ETH? Recommended 0.0065+ per TRADE_SLOT (Min trade value: 0.005) 46 | # - Using BTC? Recommended 0.000175+ per TRADE_SLOT (min trade value: 0.0001) 47 | INVESTMENT: 0.0065 48 | 49 | # TRADE_SLOTS = Maximum number of tradable 'slots' at any time. 50 | # (your PAIR_WITH balance must be at least TRADE_SLOTS * QUANTITY) 51 | TRADE_SLOTS: 3 52 | 53 | # Use more than one TRADE_SLOTS of the same coin? Set to False 54 | # allows buying the same coin again if a future buy signal is received 55 | UNIQUE_BUYS: True 56 | 57 | # List of trading pairs to exclude 58 | # by default we're excluding the most popular fiat pairs 59 | EXCLUDED_PAIRS: 60 | - EURUSDT 61 | - GBPUSDT 62 | - JPYUSDT 63 | - USDUSDT 64 | 65 | # Trading fee in % per trade. ( Only in Test Mode / Real mode get information from Binance Transaction ) 66 | # If using 0.75% (using BNB for fees) you must have BNB in your account to cover trading fees. 67 | # If using BNB for fees, it MUST be enabled in your Binance 'Dashboard' page (checkbox). 68 | TRADING_FEE: .075 69 | TRADING_FEE_BNB: True 70 | 71 | # Specify in minutes the frequency to check for trading signals from TradingView 72 | SIGNALS_FREQUENCY: 1 73 | 74 | # TICKERS: Use custom 'tickers.txt' list for filtering pairs? 75 | CUSTOM_LIST: True 76 | TICKERS_LIST: 'tickers/tickers_ETH.txt' 77 | 78 | # Name of list that cointains currencies to be ignored 79 | IGNORE_LIST: 'ignorelist.txt' 80 | 81 | # the amount of time in MINUTES to calculate the difference from the current price (minimum: 1). 82 | # it will also affect minimum and maximum time difference multiplied or divided by DYNAMIC_MIN_MAX 83 | # example: TIME_DIFFERENCE 15 with DYNAMIC_MIN_MAX 10 will let bot work from 1.5 to 150 minutes 84 | TIME_DIFFERENCE: 15 85 | 86 | # Number of times to check for TP/SL during each TIME_DIFFERENCE (minimum: 1). 87 | # Don't spam the Binance API, you will be banned (max 1200 requests per minute per IP). 88 | RECHECK_INTERVAL: 5 89 | 90 | # the difference in % between the first and second checks for the price. 91 | # CHANGE_IN_PRICE is a range because we want more precise pickings 92 | CHANGE_IN_PRICE_MIN: -3 93 | CHANGE_IN_PRICE_MAX: 3 94 | 95 | # define in % when to sell a coin that's not making a profit. 96 | STOP_LOSS: 3 97 | 98 | # define in % when to take profit on a profitable coin. 99 | TAKE_PROFIT: 0.3 100 | 101 | # whether to use trailing stop loss or not; default is True 102 | USE_TRAILING_STOP_LOSS: True 103 | 104 | # when hit TAKE_PROFIT, move STOP_LOSS to TRAILING_STOP_LOSS percentage points below TAKE_PROFIT hence locking in profit 105 | # when hit TAKE_PROFIT, move TAKE_PROFIT up by TRAILING_TAKE_PROFIT percentage points 106 | # when hit TRAILING_BUY_THRESHOLD buy coin that was in trailing buy list 107 | 108 | TRAILING_STOP_LOSS: 0.6 109 | TRAILING_TAKE_PROFIT: .01 110 | TRAILING_BUY_THRESHOLD: 0.01 111 | 112 | # gogo MOD Dynamic perecent applied to SL and TP upon WIN or LOSS trade 113 | # this enables all dynamic setting if its true 114 | DYNAMIC_SETTINGS: True 115 | 116 | # this is % by wich we add up to all settings like stoploss takeprofit trailing take profit etc if we win 117 | DYNAMIC_WIN_LOSS_UP: 10 118 | 119 | # this is % by wich we subtract all settings like stoploss takeprofit trailing take profit etc if we loose 120 | DYNAMIC_WIN_LOSS_DOWN : 10 121 | 122 | # this is a fixed % for dynamic change in price calculation 123 | DYNAMIC_CHANGE_IN_PRICE: 10 124 | 125 | # this is a coeficient we use to determine max an min TIME_DIFFERENCE in wich we let bot work 126 | # including hard caps on STOPLOSS, TRAILING_STOP_LOSS, TIME_DIFFERENCE and HOLDING_PRICE_THRESHOLD 127 | # example: MIN=STOPLOSS/DYNAMIC_MIN_MAX, MAX=STOPLOSS * DYNAMIC_MIN_MAX 128 | # this sets range in wich our dynamics work!!! 129 | DYNAMIC_MIN_MAX: 3 130 | 131 | # this is minimum price % we want to sell above if max holding time is up (this was added to cut loses if coins are stale) 132 | HOLDING_PRICE_THRESHOLD: 0.3 133 | 134 | # activate all stop losses on pausebot mod trigger 135 | STOP_LOSS_ON_PAUSE: False 136 | 137 | # mix % and signals for Buying aka buy only if both are TRUE 138 | PERCENT_SIGNAL_BUY: False 139 | 140 | # MODS SETTINGS_STRING 141 | 142 | # pausebotmod 143 | # X of 15 MA's indicating sell 144 | PAUSEBOTMOD_THRESHOLD: 14 145 | PAUSEBOTMOD_SYMBOL: ETHUSDT 146 | 147 | # custsignalmod 148 | # How many of the 26 indicators to indicate a buy 149 | SIGNALSAMPLEMOD_THRESHOLD : 18 150 | 151 | 152 | # multiplier for HOLDING_TIME_LIMIT wich gives actual TIME_DIFFERENCE * HOLDING_TIME_LIMIT_MULTIPLIER when to start lowering STOP_LOSS 153 | # holding take profit is % at wich we sell if holding time limit is passed defualt is 1 and after one timeperiod we will sell on holding take profit 154 | HOLDING_INTERVAL_LIMIT: 3 155 | 156 | # sort list by volume or price_change 157 | # this enables autocreation of list and will recreate current list and overwrite it 158 | LIST_AUTOCREATE: True 159 | 160 | # type of list creation : 161 | # - binance / binance 162 | # - tradingview / tradingview 163 | # - edgesforledges : http://edgesforledges.com/watchlists/binance / create_edge 164 | LIST_CREATE_TYPE: binance 165 | 166 | # option by type of list creation : 167 | # - edgesforledges : ebd of url ex : innovation => fiat/usdt/innovation-zone 168 | LIST_CREATE_TYPE_OPTION : fiat/usdt/innovation-zone 169 | 170 | # list sorting by 'volume' or by 'price_change' 171 | SORT_LIST_TYPE: volume 172 | 173 | SIGNALLING_MODULES: 174 | #- modules.pausebotmod 175 | #- modules.custsignalmod 176 | #- modules.custsignalmod_speed 177 | #- modules.signalsamplemod 178 | #- modules.rsi_signalmod_nigec 179 | #- modules.rsi_stoch_signalmod_djcommie 180 | #- modules.ta_indicator_signalmod_firewatch -------------------------------------------------------------------------------- /creds.example.yml: -------------------------------------------------------------------------------- 1 | # MAIN NET 2 | prod: 3 | access_key: replace_me 4 | secret_key: replace_me 5 | 6 | # TEST NET 7 | test: 8 | access_key: replace_me 9 | secret_key: replace_me 10 | 11 | #creds for telegrambot 12 | telegram: 13 | TELEGRAM_BOT_TOKEN: '1234567890:ABECDEFG2HIJKLMNOPROSTUWVXYZQABCD-ABDEFG' 14 | TELEGRAM_BOT_ID: '1234567890' 15 | 16 | 17 | #this is used to give bot performance to discord channel 18 | discord: 19 | TEST_DISCORD_WEBHOOK: '/852094287763472385/GbKS-0n-EvNUcl6iekXIp-XJI5g0wRee1zrMg_94Jkacx9r6cuDE6L2D0BT-2Tu554MF' 20 | LIVE_DISCORD_WEBHOOK: '/854046894041202739/BV8-la1zj7kauMnNL25ZpGPF7kSRzJNliqXg0f8YbzQxBXeBNfO8kCCn6QJDqxLDLA6-' 21 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | g0g0: 5 | build: ./ 6 | tty: yes 7 | volumes: 8 | - "./:/usr/src/app:Z" 9 | command: python3 "Binance_Detect_Moonings.py" 10 | -------------------------------------------------------------------------------- /docs/proposed definitions for session_struct.py: -------------------------------------------------------------------------------- 1 | """ Current session_struct items and their definitions""" 2 | 3 | session_struct = { 4 | # Profits, gains and fees 5 | 'session_profit': 0, # Profit in PAIR_WITH, including fees 6 | 'market_price': 0, # Market price in USDT for PAIR_WITH 7 | 'investment_value': 0, # Value in USDT of the crypto bot started with 8 | 'investment_value_gain': 0, # Change in value of the investment (USDT) 9 | 'TOTAL_GAINS': 0, # ? duplicate of investment_value_gain? 10 | 'NEW_BALANCE': 0, # ? investment_value + investment_value_gain? 11 | 'INVESTMENT_GAIN': 0, # Percentage gain of investment? 12 | 'win_trade_count': 0, # Trades ended with profit (including fees) 13 | 'loss_trade_count': 0, # Trades ended with loss (including fees) 14 | 'closed_trades_percent': 0, # Percentage won or lost on completed trades 15 | 'unrealised_percent': 0, # Percentage won or lost by selling bought coins at current price 16 | 'CURRENT_EXPOSURE': 0, # How much crypto is currently in another coin than PAIR_WITH ? 17 | # Market info 18 | 'market_support': 0, # ? 19 | 'market_resistance': 0, # ? 20 | 'exchange_symbol': 'USDT', # Symbol used to track investment value 21 | 'price_list_counter': 0, # ? Seems only used in settings.py 22 | 'symbol_info': {}, # Decimal places used on Binance for each coinpair 23 | # Settings 24 | 'trade_slots': 0, # Amount of slots bot can use 25 | # Flags / constants 26 | 'STARTUP': True, # Did bot just start? 27 | 'LIST_AUTOCREATE': False, # Should bot make ticker files automatically? 28 | 'tickers_list_changed': False, # Was the ticker list changed? 29 | 'sell_all_coins': False, # Does bot need to sell all coins now? 30 | 'dynamic': 'none', # Current state of the dynamic adjustments 31 | # Variables & counters 32 | 'price_timedelta': 0, # Time in minutes when prices were last compared 33 | 'session_start_time': 0, # When did bot start? 34 | 'session_uptime': 0, # How long is bot running (time since session_start_time) 35 | 'last_report_time': 0 36 | } 37 | 38 | """ Proposed new session_struct terms and definitions""" 39 | session_struct = { 40 | # Profits, gains and fees 41 | 'session_profit': 0, # Profit in PAIR_WITH, excluding fees 42 | 'session_fees': 0, # Fees paid this session, in BNB 43 | 44 | 'investment': 0, # Amount of crypto bot started with (in PAIR_WITH) 45 | 'balance': 0, # Amount of crypto after adding gains/losses (in PAIR_WITH) 46 | 47 | 'bnb_investment': 0, # BNB starting balance 48 | 'bnb_balance': 0, # BNB current balance (after payment of fees) 49 | 50 | 'investment_value': 0, # Value of the crypto bot started with (in exchange_symbol) 51 | 'investment_value_gain': 0, # Change in value of the investment (in exchange_symbol) 52 | 'investment_percent_gain': 0, # Percentage gain of investment (%) 53 | 54 | 'net_investment_value_gain': 0, # Change in value of the investment after fees (in exchange_symbol) 55 | 'net_investment_percent_gain': 0, # Percentage gain of investment after fees (%) 56 | 57 | 'win_trade_count': 0, # Trades ended with profit (including fees) 58 | 'loss_trade_count': 0, # Trades ended with loss (including fees) 59 | 'closed_trades_percent': 0, # Percentage won or lost on completed trades (excluding fees) 60 | 'unrealised_percent': 0, # Percentage won or lost by selling bought coins at current price (excluding fees) 61 | 'CURRENT_EXPOSURE': 0, # How much crypto is currently in another coin than PAIR_WITH ? 62 | 63 | # Market info 64 | 'market_support': 0, # ? 65 | 'market_resistance': 0, # ? 66 | 'exchange_symbol': 'USDT', # Symbol used to track investment value 67 | 'price_list_counter': 0, # ? Seems only used in settings.py 68 | 'symbol_info': {}, # Decimal places used on Binance for each coinpair 69 | 'market_price': 0, # Market price in USDT for PAIR_WITH 70 | # Settings 71 | 'trade_slots': 0, # Amount of slots bot can use 72 | # Flags / constants 73 | 'STARTUP': True, # Did bot just start? 74 | 'LIST_AUTOCREATE': False, # Should bot make ticker files automatically? 75 | 'tickers_list_changed': False, # Was the ticker list changed? 76 | 'sell_all_coins': False, # Does bot need to sell all coins now? 77 | 'dynamic': 'none', # Current state of the dynamic adjustments 78 | # Variables & counters 79 | 'price_timedelta': 0, # Time in minutes when prices were last compared 80 | 'session_start_time': 0, # When did bot start? 81 | 'session_uptime': 0, # How long is bot running (time since session_start_time) 82 | 'last_report_time': 0 83 | } 84 | -------------------------------------------------------------------------------- /helpers/__pycache__/handle_creds.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goranjovic55/Binance-Trading-Bot/5be177266f1200d7c6b779a0383b2dcb94ef692d/helpers/__pycache__/handle_creds.cpython-36.pyc -------------------------------------------------------------------------------- /helpers/__pycache__/handle_creds.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goranjovic55/Binance-Trading-Bot/5be177266f1200d7c6b779a0383b2dcb94ef692d/helpers/__pycache__/handle_creds.cpython-37.pyc -------------------------------------------------------------------------------- /helpers/__pycache__/parameters.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goranjovic55/Binance-Trading-Bot/5be177266f1200d7c6b779a0383b2dcb94ef692d/helpers/__pycache__/parameters.cpython-36.pyc -------------------------------------------------------------------------------- /helpers/__pycache__/parameters.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goranjovic55/Binance-Trading-Bot/5be177266f1200d7c6b779a0383b2dcb94ef692d/helpers/__pycache__/parameters.cpython-37.pyc -------------------------------------------------------------------------------- /helpers/handle_creds.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple, Dict 2 | 3 | def load_correct_creds(creds: Dict) -> Tuple[str, str]: 4 | """Returns the binance API key details from the config 5 | 6 | Args: 7 | creds (dict): the dict containing the details from the config 8 | 9 | Returns: 10 | tuple[str, str]: the binance access_key followed by the secret_key) 11 | """ 12 | return creds['prod']['access_key'], creds['prod']['secret_key'] 13 | 14 | def load_telegram_creds(creds: Dict) -> Tuple[str, str, str, str]: 15 | return creds['telegram']['TELEGRAM_BOT_TOKEN'], creds['telegram']['TELEGRAM_BOT_ID'], creds['discord']['TEST_DISCORD_WEBHOOK'], creds['discord']['LIVE_DISCORD_WEBHOOK'] 16 | 17 | 18 | def test_api_key(client, BinanceAPIException) -> Tuple[bool, str]: 19 | """Checks to see if API keys supplied returns errors 20 | 21 | Args: 22 | client (class): binance client class 23 | BinanceAPIException (clas): binance exeptions class 24 | 25 | Returns: 26 | bool | msg: true/false depending on success, and message 27 | """ 28 | try: 29 | client.get_account() 30 | return True, "API key validated succesfully" 31 | 32 | except BinanceAPIException as e: 33 | 34 | 35 | if e.code in [-2015,-2014]: 36 | bad_key = "Your API key is not formatted correctly..." 37 | america = "If you are in america, you will have to update the config to set AMERICAN_USER: True" 38 | ip_b = "If you set an IP block on your keys make sure this IP address is allowed. check ipinfo.io/ip" 39 | 40 | msg = f"Your API key is either incorrect, IP blocked, or incorrect tld/permissons...\n most likely: {bad_key}\n {america}\n {ip_b}" 41 | 42 | elif e.code in [-2021,-1021]: 43 | issue = "https://github.com/CyberPunkMetalHead/Binance-volatility-trading-bot/issues/28" 44 | desc = "Ensure your OS is time synced with a timeserver. See issue." 45 | msg = f"Timestamp for this request was 1000ms ahead of the server's time.\n {issue}\n {desc}" 46 | 47 | else: 48 | msg = "Encountered an API Error code that was not caught nicely, please open issue...\n" 49 | 50 | 51 | return False, msg 52 | 53 | except Exception as e: 54 | return False, f"Fallback exception occured:\n{e}" 55 | -------------------------------------------------------------------------------- /helpers/parameters.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import argparse 3 | 4 | 5 | def load_config(file: str): 6 | try: 7 | 8 | with open(file) as file: 9 | return yaml.load(file, Loader=yaml.FullLoader) 10 | except FileNotFoundError as fe: 11 | exit(f'Could not find {file}') 12 | 13 | except Exception as e: 14 | exit(f'Encountered exception...\n {e}') 15 | 16 | 17 | def parse_args(): 18 | x = argparse.ArgumentParser() 19 | x.add_argument('--debug', '-d', help="extra logging", action='store_true') 20 | x.add_argument('--config', '-c', help="Path to config.yml") 21 | x.add_argument('--creds', '-u', help="Path to creds file") 22 | x.add_argument('--notimeout', help="Dont use timeout in prod", action="store_true") 23 | return x.parse_args() -------------------------------------------------------------------------------- /ignorelist.txt: -------------------------------------------------------------------------------- 1 | EUR 2 | USD 3 | GBP 4 | BUSD 5 | USDT 6 | TUSD 7 | AUD 8 | BRL 9 | RUB 10 | TRY 11 | USDC 12 | PAX 13 | BIDR 14 | DAI 15 | IDRT 16 | UAH 17 | NGN 18 | VAI 19 | BVND 20 | JPY 21 | NCASH 22 | UPUSDT 23 | DOWNUSDT 24 | USDTUP 25 | USDTDOWN 26 | BNBDOWN 27 | EOSDOWN 28 | XLMDOWN 29 | UNIDOWN 30 | LINKDOWN 31 | FILDOWN 32 | TRXDOWN 33 | XRPDOWN 34 | AAVEDOWN 35 | SUSHIDOWN 36 | XTZDOWN 37 | ADADOWN 38 | 1INCHDOWN 39 | BTCDOWN 40 | SXPDOWN 41 | DOTDOWN 42 | ETHDOWN 43 | BCHDOWN 44 | LTCDOWN 45 | YFIDOWN 46 | XTZUP 47 | DOTUP 48 | ADAUP 49 | SUPER 50 | AAVEUP 51 | BTCUP 52 | BNBUP 53 | SXPUP 54 | YFIUP 55 | XLMUP 56 | UNIUP 57 | FILUP 58 | 1INCHUP 59 | LINKUP 60 | LTCUP 61 | ETHUP 62 | SUSHIUP 63 | EOSUP 64 | XRPUP 65 | TRXUP 66 | BCHUP 67 | RENBTC 68 | BULL 69 | BEAR 70 | ETHBULL 71 | ETHBEAR 72 | EOSBULL 73 | EOSBEAR 74 | XRPBULL 75 | XRPBEAR 76 | BNBBULL 77 | BNBBEAR -------------------------------------------------------------------------------- /modules/custsignalmod.py: -------------------------------------------------------------------------------- 1 | from helpers.parameters import ( 2 | parse_args, load_config 3 | ) 4 | # Load arguments then parse settings 5 | args = parse_args() 6 | #get config file 7 | DEFAULT_CONFIG_FILE = 'config.yml' 8 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 9 | parsed_config = load_config(config_file) 10 | 11 | # Available indicators here: https://python-tradingview-ta.readthedocs.io/en/latest/usage.html#retrieving-the-analysis 12 | 13 | from tradingview_ta import TA_Handler, Interval, Exchange 14 | # use for environment variables 15 | import os 16 | # use if needed to pass args to external modules 17 | import sys 18 | # used for directory handling 19 | import glob 20 | import time 21 | import threading 22 | 23 | OSC_INDICATORS = ['MACD', 'Stoch.RSI', 'Mom', 'BBP', 'AO', 'RSI'] # Indicators to use in Oscillator analysis 24 | OSC_THRESHOLD = 3 # Must be less or equal to number of items in OSC_INDICATORS 25 | MA_INDICATORS = ['VWMA', 'HullMA', 'Ichimoku'] # Indicators to use in Moving averages analysis 26 | MA_THRESHOLD = 2 # Must be less or equal to number of items in MA_INDICATORS 27 | INTERVAL = Interval.INTERVAL_1_MINUTE #Timeframe for analysis 28 | 29 | EXCHANGE = 'BINANCE' 30 | SCREENER = 'CRYPTO' 31 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 32 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 33 | TIME_TO_WAIT = parsed_config['trading_options']['TIME_DIFFERENCE'] # Minutes to wait between analysis 34 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List analysis result to console 35 | 36 | def analyze(pairs): 37 | signal_coins = {} 38 | analysis = {} 39 | handler = {} 40 | 41 | if os.path.exists('signals/custsignalmod.exs'): 42 | os.remove('signals/custsignalmod.exs') 43 | 44 | for pair in pairs: 45 | handler[pair] = TA_Handler( 46 | symbol=pair, 47 | exchange=EXCHANGE, 48 | screener=SCREENER, 49 | interval=INTERVAL, 50 | timeout= 10) 51 | 52 | for pair in pairs: 53 | try: 54 | analysis = handler[pair].get_analysis() 55 | except Exception as e: 56 | # print("Signalsample:") 57 | # print("Exception:") 58 | # print(e) 59 | # print (f'Coin: {pair}') 60 | # print (f'handler: {handler[pair]}') 61 | # print('') 62 | dont_print_on_exception = True 63 | 64 | oscCheck=0 65 | maCheck=0 66 | for indicator in OSC_INDICATORS: 67 | if analysis.oscillators ['COMPUTE'][indicator] == 'BUY': oscCheck +=1 68 | 69 | for indicator in MA_INDICATORS: 70 | if analysis.moving_averages ['COMPUTE'][indicator] == 'BUY': maCheck +=1 71 | 72 | if FULL_LOG: 73 | print(f'Custsignalmod:{pair} Oscillators:{oscCheck}/{len(OSC_INDICATORS)} Moving averages:{maCheck}/{len(MA_INDICATORS)}') 74 | 75 | if oscCheck >= OSC_THRESHOLD and maCheck >= MA_THRESHOLD: 76 | signal_coins[pair] = pair 77 | if FULL_LOG: 78 | print(f'Custsignalmod: Signal detected on {pair} at {oscCheck}/{len(OSC_INDICATORS)} oscillators and {maCheck}/{len(MA_INDICATORS)} moving averages.') 79 | with open('signals/custsignalmod.exs','a+') as f: 80 | f.write(pair + '\n') 81 | 82 | return signal_coins 83 | 84 | def do_work(): 85 | signal_coins = {} 86 | pairs = {} 87 | 88 | pairs=[line.strip() for line in open(TICKERS)] 89 | for line in open(TICKERS): 90 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 91 | 92 | while True: 93 | if not threading.main_thread().is_alive(): exit() 94 | signal_coins = analyze(pairs) 95 | if FULL_LOG: 96 | print(f'Custsignalmod: Analyzing {len(pairs)} coins') 97 | print(f'Custsignalmod: {len(signal_coins)} coins above {OSC_THRESHOLD}/{len(OSC_INDICATORS)} oscillators and {MA_THRESHOLD}/{len(MA_INDICATORS)} moving averages Waiting {TIME_TO_WAIT} minutes for next analysis.') 98 | time.sleep((TIME_TO_WAIT*60)) 99 | -------------------------------------------------------------------------------- /modules/custsignalmod_speed.py: -------------------------------------------------------------------------------- 1 | # Available indicators here: https://python-tradingview-ta.readthedocs.io/en/latest/usage.html#retrieving-the-analysis 2 | 3 | from tradingview_ta import TA_Handler, Interval, Exchange, get_multiple_analysis 4 | # use for environment variables 5 | import os 6 | # use if needed to pass args to external modules 7 | import sys 8 | # used for directory handling 9 | import glob 10 | import time 11 | import threading 12 | 13 | from helpers.parameters import load_config, parse_args 14 | 15 | 16 | 17 | args = parse_args() 18 | config_file = args.config if args.config else 'config.yml' 19 | parsed_config = load_config(config_file) 20 | 21 | OSC_INDICATORS = ['MACD', 'Stoch.RSI', 'Mom', 'BBP', 'AO', 'RSI'] # Indicators to use in Oscillator analysis 22 | OSC_THRESHOLD = 3 # Must be less or equal to number of items in OSC_INDICATORS 23 | MA_INDICATORS = ['EMA10', 'EMA20', 'Ichimoku','VWMA'] # Indicators to use in Moving averages analysis 24 | MA_THRESHOLD = 2 # Must be less or equal to number of items in MA_INDICATORS 25 | INTERVAL = Interval.INTERVAL_5_MINUTES #Timeframe for analysis 26 | 27 | EXCHANGE = 'BINANCE' 28 | SCREENER = 'CRYPTO' 29 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 30 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 31 | SIGNAL_OUTPUT_PATH = 'signals' 32 | TIME_TO_WAIT = parsed_config['trading_options']['SIGNALS_FREQUENCY'] # Minutes to wait between analysis 33 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List analysis result to console 34 | 35 | 36 | def analyze(total_pairs): 37 | signal_coins = {} 38 | analysis = {} 39 | handler = {} 40 | # print(f'Module Path: {os.path.dirname(os.path.realpath(__file__))}') 41 | if os.path.exists(f'{SIGNAL_OUTPUT_PATH}/custsignalmod.exs'): 42 | os.remove(f'{SIGNAL_OUTPUT_PATH}/custsignalmod.exs') 43 | 44 | # Add exchange to pair list... 45 | exchange_and_pair_list = [f'{EXCHANGE}:{pair}' for pair in total_pairs] 46 | 47 | signals_to_buy = [] 48 | 49 | # chicks dig it 50 | multiple_anal = get_multiple_analysis( 51 | screener=SCREENER, 52 | interval=INTERVAL, 53 | symbols=exchange_and_pair_list, 54 | timeout=20) 55 | 56 | 57 | for pair_name, analysis in multiple_anal.items(): 58 | pair_without_exchange = pair_name.split(':')[1] 59 | oscilator_check = 0 60 | moving_average_check= 0 61 | 62 | for indicator in OSC_INDICATORS: 63 | if analysis.oscillators ['COMPUTE'][indicator] == 'BUY': 64 | oscilator_check +=1 65 | for indicator in MA_INDICATORS: 66 | if analysis.moving_averages ['COMPUTE'][indicator] == 'BUY': 67 | moving_average_check +=1 68 | 69 | if FULL_LOG: 70 | print(f'Custsignalmod:{pair_without_exchange} Oscillators:{oscilator_check}/{len(OSC_INDICATORS)} Moving averages:{moving_average_check}/{len(MA_INDICATORS)}') 71 | 72 | if oscilator_check >= OSC_THRESHOLD and moving_average_check >= MA_THRESHOLD: 73 | signal_coins[pair_without_exchange] = pair_without_exchange 74 | # print(f'Custsignalmod: Signal detected on {pair_without_exchange} at {oscilator_check}/{len(OSC_INDICATORS)} oscillators and {moving_average_check}/{len(MA_INDICATORS)} moving averages.') 75 | signals_to_buy.append(pair_without_exchange) 76 | 77 | if FULL_LOG: 78 | print(f'Custsignalmod: Identified {len(signals_to_buy)}/{len(total_pairs)} coins to execute on') 79 | 80 | # write all pairs instead of opening the file handler for each one... 81 | with open(f'{SIGNAL_OUTPUT_PATH}/custsignalmod.exs','a+') as f: 82 | for item in signals_to_buy: 83 | f.write(f"{item}\n") 84 | 85 | # print(signal_coins) 86 | return signal_coins 87 | 88 | def do_work(): 89 | signal_coins = {} 90 | pairs = {} 91 | 92 | pairs=[line.strip() for line in open(TICKERS)] 93 | for line in open(TICKERS): 94 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 95 | 96 | while True: 97 | if not threading.main_thread().is_alive(): exit() 98 | signal_coins = analyze(pairs) 99 | if FULL_LOG: 100 | print(f'Custsignalmod: Analyzing {len(pairs)} coins') 101 | print(f'Custsignalmod: {len(signal_coins)} coins above {OSC_THRESHOLD}/{len(OSC_INDICATORS)} oscillators and {MA_THRESHOLD}/{len(MA_INDICATORS)} moving averages Waiting {TIME_TO_WAIT} minutes for next analysis.') 102 | time.sleep((TIME_TO_WAIT*60)) 103 | -------------------------------------------------------------------------------- /modules/pausebotmod.py: -------------------------------------------------------------------------------- 1 | from helpers.parameters import ( 2 | parse_args, load_config 3 | ) 4 | # Load arguments then parse settings 5 | args = parse_args() 6 | #get config file 7 | DEFAULT_CONFIG_FILE = 'config.yml' 8 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 9 | parsed_config = load_config(config_file) 10 | 11 | from tradingview_ta import TA_Handler, Interval, Exchange 12 | import os 13 | import time 14 | import threading 15 | 16 | # for colourful logging to the console 17 | class txcolors: 18 | WARNING = '\033[93m' 19 | NEGATIVE = '\033[91m' 20 | POSITIVE = '\033[32m' 21 | 22 | global market_resistance 23 | 24 | INTERVAL = Interval.INTERVAL_1_MINUTE #Timeframe for analysis 25 | 26 | EXCHANGE = 'BINANCE' 27 | SCREENER = 'CRYPTO' 28 | SYMBOL = parsed_config['trading_options']['PAUSEBOTMOD_SYMBOL'] 29 | TYPE = 'SELL' 30 | THRESHOLD = parsed_config['trading_options']['PAUSEBOTMOD_THRESHOLD'] # 7 of 15 MA's indicating sell 31 | TIME_TO_WAIT = parsed_config['trading_options']['TIME_DIFFERENCE'] # Minutes to wait between analysis 32 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List analysis result to console 33 | 34 | def analyze(): 35 | analysis = {} 36 | handler = {} 37 | 38 | handler = TA_Handler( 39 | symbol = SYMBOL, 40 | exchange = EXCHANGE, 41 | screener = SCREENER, 42 | interval = INTERVAL, 43 | timeout = 10 44 | ) 45 | 46 | try: 47 | analysis = handler.get_analysis() 48 | except Exception as e: 49 | print("pausebotmod:") 50 | print("Exception:") 51 | print(e) 52 | 53 | ma_analysis = analysis.moving_averages[TYPE] 54 | if ma_analysis >= THRESHOLD: 55 | paused = True 56 | print(f'pausebotmod: {txcolors.WARNING}{SYMBOL} {txcolors.NEGATIVE}Market not looking too good, bot paused from buying {txcolors.WARNING}{ma_analysis}/{THRESHOLD} Waiting {TIME_TO_WAIT} minutes for next market checkup') 57 | else: 58 | print(f'pausebotmod: {txcolors.WARNING}{SYMBOL} {txcolors.POSITIVE}Market looks ok, bot is running {txcolors.WARNING}{ma_analysis}/{THRESHOLD} Waiting {TIME_TO_WAIT} minutes for next market checkup ') 59 | paused = False 60 | 61 | return paused 62 | 63 | #if __name__ == '__main__': 64 | def do_work(): 65 | 66 | while True: 67 | if not threading.main_thread().is_alive(): exit() 68 | # print(f'pausebotmod: Fetching market state') 69 | paused = analyze() 70 | if paused: 71 | with open('signals/paused.exc','a+') as f: 72 | f.write('yes') 73 | else: 74 | if os.path.isfile("signals/paused.exc"): 75 | os.remove('signals/paused.exc') 76 | 77 | # print(f'pausebotmod: Waiting {TIME_TO_WAIT} minutes for next market checkup') 78 | time.sleep((TIME_TO_WAIT*60)) 79 | -------------------------------------------------------------------------------- /modules/rsi_signalmod_nigec.py: -------------------------------------------------------------------------------- 1 | # Available indicators here: https://python-tradingview-ta.readthedocs.io/en/latest/usage.html#retrieving-the-analysis 2 | 3 | # NigeC v1.01 - June 9 2021 - Credit to @DJcommie and @Firewatch for the inspiration and initial code 4 | # No future support offered, use this script at own risk - test before using real funds 5 | # If you lose money using this MOD (and you will at some point) you've only got yourself to blame! 6 | # FILENAME: rsi-mod.py 7 | 8 | from tradingview_ta import TA_Handler, Interval, Exchange 9 | # use for environment variables 10 | import os 11 | # use if needed to pass args to external modules 12 | import sys 13 | # used for directory handling 14 | import glob 15 | import time 16 | import threading 17 | 18 | ######################################################## 19 | # These are the TradingView Oscillator signals available 20 | ######################################################## 21 | 22 | #{'Recommend.Other': 0.09090909, 'Recommend.All': 0.17878788, 'Recommend.MA': 0.26666667, 'RSI': 51.35657473, 'RSI[1]': 56.0809039, 'Stoch.K': 40.83410422, 'Stoch.D': 36.71946441, 'Stoch.K[1]': 31.67255276, 'Stoch.D[1]': 39.57313164, 'CCI20': -52.17234223, 'CCI20[1]': 4.5072255, 'ADX': 35.60476973, 'ADX+DI': 28.49583595, 'ADX-DI': 25.60684839, 'ADX+DI[1]': 29.85479333, 'ADX-DI[1]': 26.11840839, 'AO': 8.26394676, 'AO[1]': 12.62397794, 'Mom': -15.22, 'Mom[1]': -2.67, 'MACD.macd': 7.00976885, 'MACD.signal': 10.30480624, 'Rec.Stoch.RSI': 0, 'Stoch.RSI.K': 9.72185595, 'Rec.WR': 0, 'W.R': -62.00277521, 'Rec.BBPower': 1, 'BBPower': -6.09964786, 'Rec.UO': 0, 'UO': 50.27359668} 23 | 24 | ############################################################# 25 | # Settings - edit below to change analysis buy & sell signals 26 | # Default settings in brackets at end of comments 27 | ############################################################# 28 | from helpers.parameters import ( 29 | parse_args, load_config 30 | ) 31 | # Load arguments then parse settings 32 | args = parse_args() 33 | #get config file 34 | DEFAULT_CONFIG_FILE = 'config.yml' 35 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 36 | parsed_config = load_config(config_file) 37 | 38 | INTERVAL = Interval.INTERVAL_15_MINUTES # Main Timeframe for analysis on Oscillators and Moving Averages (15 mins) 39 | INTERVAL2 = Interval.INTERVAL_5_MINUTES # Secondary Timeframe for analysis on BUY signals for next lowest timescale | Check Entry Point (5) 40 | 41 | OSC_INDICATORS = ['RSI', 'Stoch.RSI', 'Mom', 'MACD', 'UO', 'BBP'] # Indicators to use in Oscillator analysis 42 | OSC_THRESHOLD = 5 # Must be less or equal to number of items in OSC_INDICATORS (5) 43 | MA_INDICATORS = ['EMA10', 'EMA20', 'SMA10', 'SMA20'] # Indicators to use in Moving Averages analysis 44 | MA_THRESHOLD = 3 # Must be less or equal to number of items in MA_INDICATORS (3) 45 | MA_SUMMARY = 13 # Buy indicators out of 26 to use in Moving Averages INTERVAL analysis (13) 46 | MA_SUMMARY2 = 13 # Buy indicators out of 26 to use in Moving Averages INTERVAL2 analysis (13) 47 | OSC_SUMMARY = 2 # Sell indicators out of 11 to use in Oscillators analysis (2) 48 | 49 | RSI_MIN = 12 # Min RSI Level for Buy Signal - Under 25 considered oversold (12) 50 | RSI_MAX = 55 # Max RSI Level for Buy Signal - Over 80 considered overbought (55) 51 | STOCH_MIN = 12 # Min Stoch %K Level for Buy Signal - Under 15 considered bearish until it crosses %D line (12) 52 | STOCH_MAX = 99 # Max Stoch %K Level for Buy Signal - Over 80 ok as long as %D line doesn't cross %K (99) 53 | 54 | RSI_BUY = 0.3 # Difference in RSI levels over last 2 timescales for a Buy Signal (-0.3) 55 | STOCH_BUY = 10 # Difference between the Stoch K&D levels for a Buy Signal (10) 56 | 57 | SELL_COINS = True # Set to true if you want the module to sell coins immediately upon bearish signals (False) 58 | RSI_SELL = -5 # Difference in RSI levels over last 2 timescales for a Sell Signal (-5) 59 | STOCH_SELL = -10 # Difference between the Stoch D&K levels for a Sell Signal (-10) 60 | SIGNALS_SELL = 7 # Max number of buy signals on both INTERVALs to add coin to sell list (7) 61 | 62 | EXCHANGE = 'BINANCE' 63 | SCREENER = 'CRYPTO' 64 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 65 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 66 | TIME_TO_WAIT = parsed_config['trading_options']['SIGNALS_FREQUENCY'] # Minutes to wait between analysis 67 | FULL_LOG = False # List analysis result to console 68 | 69 | ######################################## 70 | # Do NOT edit settings below these lines 71 | ######################################## 72 | 73 | def analyze(pairs): 74 | 75 | signal_coins = {} 76 | analysis = {} 77 | handler = {} 78 | analysis2 = {} 79 | handler2 = {} 80 | 81 | if os.path.exists('signals/nigec_custsignalmod.exs'): 82 | os.remove('signals/nigec_custsignalmod.exs') 83 | 84 | if os.path.exists('signals/nigec_custsignalmod.sell'): 85 | os.remove('signals/nigec_custsignalmod.sell') 86 | 87 | for pair in pairs: 88 | handler[pair] = TA_Handler( 89 | symbol=pair, 90 | exchange=EXCHANGE, 91 | screener=SCREENER, 92 | interval=INTERVAL, 93 | timeout= 10) 94 | 95 | handler2[pair] = TA_Handler( 96 | symbol=pair, 97 | exchange=EXCHANGE, 98 | screener=SCREENER, 99 | interval=INTERVAL2, 100 | timeout= 10) 101 | 102 | for pair in pairs: 103 | try: 104 | analysis = handler[pair].get_analysis() 105 | analysis2 = handler2[pair].get_analysis() 106 | except Exception as e: 107 | print("Signalsample:") 108 | print("Exception:") 109 | print(e) 110 | print (f'Coin: {pair}') 111 | print (f'handler: {handler[pair]}') 112 | print (f'handler2: {handler2[pair]}') 113 | 114 | oscCheck=0 115 | maCheck=0 116 | 117 | for indicator in OSC_INDICATORS: 118 | oscResult = analysis.oscillators ['COMPUTE'][indicator] 119 | #print(f'{pair} - Indicator for {indicator} is {oscResult}') 120 | if analysis.oscillators ['COMPUTE'][indicator] != 'SELL': oscCheck +=1 121 | 122 | for indicator in MA_INDICATORS: 123 | if analysis.moving_averages ['COMPUTE'][indicator] == 'BUY': maCheck +=1 124 | 125 | # Stoch.RSI (19 - 99), RSI (19 - 69) 126 | RSI = round(analysis.indicators['RSI'],2) 127 | RSI1 = round(analysis.indicators['RSI[1]'],2) 128 | STOCH_K = round(analysis.indicators['Stoch.K'],2) 129 | STOCH_D = round(analysis.indicators['Stoch.D'],2) 130 | STOCH_K1 = round(analysis.indicators['Stoch.K[1]'],2) 131 | STOCH_D1 = round(analysis.indicators['Stoch.D[1]'],2) 132 | EMA10 = round(analysis.indicators['EMA10'],2) 133 | EMA20 = round(analysis.indicators['EMA20'],2) 134 | EMA30 = round(analysis.indicators['EMA30'],2) 135 | SMA10 = round(analysis.indicators['SMA10'],2) 136 | SMA20 = round(analysis.indicators['SMA20'],2) 137 | SMA30 = round(analysis.indicators['SMA30'],2) 138 | BUY_SIGS = round(analysis.summary['BUY'],0) 139 | BUY_SIGS2 = round(analysis2.summary['BUY'],0) 140 | STOCH_DIFF = round(STOCH_K - STOCH_D,2) 141 | RSI_DIFF = round(RSI - RSI1,2) 142 | 143 | if FULL_LOG: 144 | if (RSI < 80) and (BUY_SIGS >= 10) and (STOCH_DIFF >= 0.01) and (RSI_DIFF >= 0.01): 145 | print(f'Signals OSC: {pair} = RSI:{RSI}/{RSI1} DIFF: {RSI_DIFF} | STOCH_K/D:{STOCH_K}/{STOCH_D} DIFF: {STOCH_DIFF} | BUYS: {BUY_SIGS}_{BUY_SIGS2}/26 | {oscCheck}-{maCheck}') 146 | #print(f'{STOCH_K1}/{STOCH_D1}') 147 | 148 | if (RSI >= RSI_MIN and RSI <= RSI_MAX) and (RSI_DIFF >= RSI_BUY): 149 | if (STOCH_DIFF >= STOCH_BUY) and (STOCH_K >= STOCH_MIN and STOCH_K <= STOCH_MAX) and (STOCH_D >= STOCH_MIN and STOCH_D <= STOCH_MAX): 150 | if (BUY_SIGS >= MA_SUMMARY) and (BUY_SIGS2 >= MA_SUMMARY2) and (STOCH_K > STOCH_K1): 151 | if (oscCheck >= OSC_THRESHOLD and maCheck >= MA_THRESHOLD): 152 | signal_coins[pair] = pair 153 | # print(f'\033[92mSignals RSI: {pair} - Buy Signal Detected | {BUY_SIGS}_{BUY_SIGS2}/26') 154 | with open('signals/nigec_custsignalmod.exs','a+') as f: 155 | f.write(pair + '\n') 156 | # else: 157 | # print(f'Signals RSI: {pair} - Stoch/RSI ok, not enough buy signals | {BUY_SIGS}_{BUY_SIGS2}/26 | {STOCH_DIFF}/{RSI_DIFF} | {STOCH_K}') 158 | 159 | if SELL_COINS: 160 | if (BUY_SIGS < SIGNALS_SELL) and (BUY_SIGS2 < SIGNALS_SELL) and (STOCH_DIFF < STOCH_SELL) and (RSI_DIFF < RSI_SELL) and (STOCH_K < STOCH_K1): 161 | #signal_coins[pair] = pair 162 | # print(f'\033[33mSignals RSI: {pair} - Sell Signal Detected | {BUY_SIGS}_{BUY_SIGS2}/26') 163 | with open('signals/_nigec_custsignalmod.sell','a+') as f: 164 | f.write(pair + '\n') 165 | #else: 166 | # print(f'Signal: {pair} - Not selling!') 167 | 168 | return signal_coins 169 | 170 | def do_work(): 171 | signal_coins = {} 172 | pairs = {} 173 | 174 | pairs=[line.strip() for line in open(TICKERS)] 175 | for line in open(TICKERS): 176 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 177 | 178 | while True: 179 | if not threading.main_thread().is_alive(): exit() 180 | # print(f'Signals RSI: Analyzing {len(pairs)} coins') 181 | signal_coins = analyze(pairs) 182 | # print(f'Signals RSI: {len(signal_coins)} coins with Buy Signals. Waiting {TIME_TO_WAIT} minutes for next analysis.') 183 | time.sleep((TIME_TO_WAIT*60)) 184 | -------------------------------------------------------------------------------- /modules/rsi_stoch_signalmod_djcommie.py: -------------------------------------------------------------------------------- 1 | # Available indicators here: https://python-tradingview-ta.readthedocs.io/en/latest/usage.html#retrieving-the-analysis 2 | 3 | from tradingview_ta import TA_Handler, Interval, Exchange 4 | # use for environment variables 5 | import os 6 | # use if needed to pass args to external modules 7 | import sys 8 | # used for directory handling 9 | import glob 10 | import time 11 | import threading 12 | 13 | from helpers.parameters import ( 14 | parse_args, load_config 15 | ) 16 | # Load arguments then parse settings 17 | args = parse_args() 18 | #get config file 19 | DEFAULT_CONFIG_FILE = 'config.yml' 20 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 21 | parsed_config = load_config(config_file) 22 | 23 | 24 | OSC_INDICATORS = ['RSI', 'Stoch.RSI'] # Indicators to use in Oscillator analysis 25 | OSC_THRESHOLD = 2 # Must be less or equal to number of items in OSC_INDICATORS 26 | MA_INDICATORS = ['EMA10', 'EMA20'] # Indicators to use in Moving averages analysis 27 | MA_THRESHOLD = 2 # Must be less or equal to number of items in MA_INDICATORS 28 | INTERVAL = Interval.INTERVAL_5_MINUTES #Timeframe for analysis 29 | EXCHANGE = 'BINANCE' 30 | SCREENER = 'CRYPTO' 31 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 32 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 33 | SIGNAL_OUTPUT_PATH = 'signals' 34 | TIME_TO_WAIT = parsed_config['trading_options']['SIGNALS_FREQUENCY'] # Minutes to wait between analysis 35 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List analysis result to console 36 | 37 | # TODO: check every 1 minute on 5 minute timeframes by keeping a circular buffer array 38 | global last_RSI 39 | last_RSI = {} 40 | 41 | def analyze(pairs): 42 | global last_RSI 43 | 44 | signal_coins = {} 45 | analysis = {} 46 | handler = {} 47 | 48 | if os.path.exists(f'{SIGNAL_OUTPUT_PATH}/djcommie_custsignalmod.exs'): 49 | os.remove(f'{SIGNAL_OUTPUT_PATH}/djcommie_custsignalmod.exs') 50 | 51 | for pair in pairs: 52 | handler[pair] = TA_Handler( 53 | symbol=pair, 54 | exchange=EXCHANGE, 55 | screener=SCREENER, 56 | interval=INTERVAL, 57 | timeout= 10) 58 | 59 | for pair in pairs: 60 | try: 61 | analysis = handler[pair].get_analysis() 62 | except Exception as e: 63 | print("Signalsample:") 64 | print("Exception:") 65 | print(e) 66 | print (f'Coin: {pair}') 67 | print (f'handler: {handler[pair]}') 68 | 69 | oscCheck=0 70 | maCheck=0 71 | for indicator in OSC_INDICATORS: 72 | oscResult = analysis.oscillators ['COMPUTE'][indicator] 73 | #print(f'Indicator for {indicator} is {oscResult}') 74 | if analysis.oscillators ['COMPUTE'][indicator] != 'SELL': oscCheck +=1 75 | 76 | for indicator in MA_INDICATORS: 77 | if analysis.moving_averages ['COMPUTE'][indicator] == 'BUY': maCheck +=1 78 | 79 | # TODO: Use same type of analysis for sell indicators 80 | 81 | # Stoch.RSI (25 - 52) & Stoch.RSI.K > Stoch.RSI.D, RSI (49-67), EMA10 > EMA20 > EMA100, Stoch.RSI = BUY, RSI = BUY, EMA10 = EMA20 = BUY 82 | RSI = float(analysis.indicators['RSI']) 83 | STOCH_RSI_K = float(analysis.indicators['Stoch.RSI.K']) 84 | # STOCH_RSI_D = float(analysis.indicators['Stoch.D']) 85 | EMA10 = float(analysis.indicators['EMA10']) 86 | EMA20 = float(analysis.indicators['EMA20']) 87 | EMA100 = float(analysis.indicators['EMA100']) 88 | STOCH_K = float(analysis.indicators['Stoch.K']) 89 | STOCH_D = float(analysis.indicators['Stoch.D']) 90 | 91 | #print(f'Custsignalmod: {pair} stats = RSI:{RSI}, STOCH_RSI_K:{STOCH_RSI_K}, STOCH_K:{STOCH_K}, STOCH_D:{STOCH_D} EMA10:{EMA10}, EMA20:{EMA20}, EMA100:{EMA100}') 92 | if pair in last_RSI and (RSI - last_RSI[pair] >= 2.5) and (RSI >= 49 and RSI <= 67) and (STOCH_RSI_K >= 25 and STOCH_RSI_K <= 58) and \ 93 | '''(EMA10 > EMA20 and EMA20 > EMA100)''' and (STOCH_K - STOCH_D >= 4.5): 94 | 95 | if oscCheck >= OSC_THRESHOLD and maCheck >= MA_THRESHOLD: 96 | signal_coins[pair] = pair 97 | 98 | if FULL_LOG: 99 | print(f'\033[92mCustsignalmod: Signal detected on {pair} at {oscCheck}/{len(OSC_INDICATORS)} oscillators and {maCheck}/{len(MA_INDICATORS)} moving averages.') 100 | with open('signals/djcommie_custsignalmod.exs','a+') as f: 101 | f.write(pair + '\n') 102 | 103 | last_RSI[pair] = RSI 104 | #print(f'Custsignalmod:{pair} Oscillators:{oscCheck}/{len(OSC_INDICATORS)} Moving averages:{maCheck}/{len(MA_INDICATORS)}') 105 | 106 | return signal_coins 107 | 108 | def do_work(): 109 | signal_coins = {} 110 | pairs = {} 111 | 112 | pairs=[line.strip() for line in open(TICKERS)] 113 | for line in open(TICKERS): 114 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 115 | 116 | while True: 117 | if not threading.main_thread().is_alive(): exit() 118 | signal_coins = analyze(pairs) 119 | if FULL_LOG: 120 | print(f'Custsignalmod: Analyzing {len(pairs)} coins') 121 | print(f'Custsignalmod: {len(signal_coins)} coins above {OSC_THRESHOLD}/{len(OSC_INDICATORS)} oscillators and {MA_THRESHOLD}/{len(MA_INDICATORS)} moving averages Waiting {TIME_TO_WAIT} minutes for next analysis.') 122 | time.sleep((TIME_TO_WAIT*60)) 123 | -------------------------------------------------------------------------------- /modules/signalsamplemod.py: -------------------------------------------------------------------------------- 1 | from helpers.parameters import ( 2 | parse_args, load_config 3 | ) 4 | # Load arguments then parse settings 5 | args = parse_args() 6 | #get config file 7 | DEFAULT_CONFIG_FILE = 'config.yml' 8 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 9 | parsed_config = load_config(config_file) 10 | 11 | from tradingview_ta import TA_Handler, Interval, Exchange 12 | # use for environment variables 13 | import os 14 | # use if needed to pass args to external modules 15 | import sys 16 | # used for directory handling 17 | import glob 18 | 19 | import time 20 | 21 | MY_EXCHANGE = 'BINANCE' 22 | MY_SCREENER = 'CRYPTO' 23 | MY_FIRST_INTERVAL = Interval.INTERVAL_1_MINUTE 24 | MY_SECOND_INTERVAL = Interval.INTERVAL_5_MINUTES 25 | TA_BUY_THRESHOLD = parsed_config['trading_options']['SIGNALSAMPLEMOD_THRESHOLD'] # How many of the 26 indicators to indicate a buy 26 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 27 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 28 | TIME_TO_WAIT = parsed_config['trading_options']['TIME_DIFFERENCE'] # Minutes to wait between analysis 29 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List anylysis result to console 30 | 31 | def analyze(pairs): 32 | taMax = 0 33 | taMaxCoin = 'none' 34 | signal_coins = {} 35 | first_analysis = {} 36 | second_analysis = {} 37 | first_handler = {} 38 | second_handler = {} 39 | if os.path.exists('signals/signalsample.exs'): 40 | os.remove('signals/signalsample.exs') 41 | 42 | for pair in pairs: 43 | first_handler[pair] = TA_Handler( 44 | symbol=pair, 45 | exchange=MY_EXCHANGE, 46 | screener=MY_SCREENER, 47 | interval=MY_FIRST_INTERVAL, 48 | timeout= 10 49 | ) 50 | second_handler[pair] = TA_Handler( 51 | symbol=pair, 52 | exchange=MY_EXCHANGE, 53 | screener=MY_SCREENER, 54 | interval=MY_SECOND_INTERVAL, 55 | timeout= 10 56 | ) 57 | 58 | for pair in pairs: 59 | 60 | try: 61 | first_analysis = first_handler[pair].get_analysis() 62 | second_analysis = second_handler[pair].get_analysis() 63 | except Exception as e: 64 | # print("Signalsample:") 65 | # print("Exception:") 66 | # print(e) 67 | # print (f'Coin: {pair}') 68 | # print (f'First handler: {first_handler[pair]}') 69 | # print (f'Second handler: {second_handler[pair]}') 70 | # print('') 71 | dont_print_on_exception = True 72 | tacheckS = 0 73 | 74 | first_tacheck = first_analysis.summary['BUY'] 75 | second_tacheck = second_analysis.summary['BUY'] 76 | if FULL_LOG: 77 | print(f'Signalsample:{pair} First {first_tacheck} Second {second_tacheck}') 78 | #else: 79 | #print(".", end = '') 80 | 81 | if first_tacheck > taMax: 82 | taMax = first_tacheck 83 | taMaxCoin = pair 84 | if first_tacheck >= TA_BUY_THRESHOLD and second_tacheck >= TA_BUY_THRESHOLD: 85 | signal_coins[pair] = pair 86 | print(f'Signalsample: Signal detected on {pair}') 87 | with open('signals/signalsample.exs','a+') as f: 88 | f.write(pair + '\n') 89 | print(f'Signalsample: Max signal by {taMaxCoin} at {taMax} on shortest timeframe') 90 | 91 | return signal_coins 92 | 93 | def do_work(): 94 | signal_coins = {} 95 | pairs = {} 96 | 97 | pairs=[line.strip() for line in open(TICKERS)] 98 | for line in open(TICKERS): 99 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 100 | 101 | while True: 102 | print(f'Signalsample: Analyzing {len(pairs)} coins') 103 | signal_coins = analyze(pairs) 104 | if len(signal_coins) == 0: 105 | print(f'Signalsample: No coins above {TA_BUY_THRESHOLD} threshold on both timeframes. Waiting {TIME_TO_WAIT} minutes for next analysis') 106 | else: 107 | print(f'Signalsample: {len(signal_coins)} coins above {TA_BUY_THRESHOLD} treshold on both timeframes. Waiting {TIME_TO_WAIT} minutes for next analysis') 108 | 109 | time.sleep((TIME_TO_WAIT*60)) 110 | -------------------------------------------------------------------------------- /modules/ta_indicator_signalmod_firewatch.py: -------------------------------------------------------------------------------- 1 | from tradingview_ta import TA_Handler, Interval, Exchange 2 | # use for environment variables 3 | import os 4 | # use if needed to pass args to external modules 5 | import sys 6 | # used for directory handling 7 | import glob 8 | 9 | import time 10 | 11 | from helpers.parameters import ( 12 | parse_args, load_config 13 | ) 14 | # Load arguments then parse settings 15 | args = parse_args() 16 | #get config file 17 | DEFAULT_CONFIG_FILE = 'config.yml' 18 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 19 | parsed_config = load_config(config_file) 20 | 21 | MY_EXCHANGE = 'BINANCE' 22 | MY_SCREENER = 'CRYPTO' 23 | MY_FIRST_INTERVAL = Interval.INTERVAL_1_MINUTE 24 | MY_SECOND_INTERVAL = Interval.INTERVAL_5_MINUTES 25 | MY_THIRD_INTERVAL = Interval.INTERVAL_15_MINUTES 26 | TA_BUY_THRESHOLD = 13 # How many of the 26 indicators to indicate a buy 27 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 28 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 29 | SIGNAL_OUTPUT_PATH = 'signals' 30 | TIME_TO_WAIT = parsed_config['trading_options']['SIGNALS_FREQUENCY'] # Minutes to wait between analysis 31 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List analysis result to console 32 | 33 | def analyze(pairs): 34 | taMax = 0 35 | taMaxCoin = 'none' 36 | signal_coins = {} 37 | first_analysis = {} 38 | second_analysis = {} 39 | third_analysis = {} 40 | first_handler = {} 41 | second_handler = {} 42 | third_handler = {} 43 | 44 | if os.path.exists(f'{SIGNAL_OUTPUT_PATH}/firewatch_signalsample.exs'): 45 | os.remove(f'{SIGNAL_OUTPUT_PATH}/firewatch_signalsample.exs') 46 | 47 | if os.path.exists(f'{SIGNAL_OUTPUT_PATH}/firewatch_signalsample.sell'): 48 | os.remove(f'{SIGNAL_OUTPUT_PATH}/firewatch_signalsample.sell') 49 | 50 | for pair in pairs: 51 | first_handler[pair] = TA_Handler( 52 | symbol=pair, 53 | exchange=MY_EXCHANGE, 54 | screener=MY_SCREENER, 55 | interval=MY_FIRST_INTERVAL, 56 | timeout= 10 57 | ) 58 | second_handler[pair] = TA_Handler( 59 | symbol=pair, 60 | exchange=MY_EXCHANGE, 61 | screener=MY_SCREENER, 62 | interval=MY_SECOND_INTERVAL, 63 | timeout= 10 64 | ) 65 | third_handler[pair] = TA_Handler( 66 | symbol=pair, 67 | exchange=MY_EXCHANGE, 68 | screener=MY_SCREENER, 69 | interval=MY_THIRD_INTERVAL, 70 | timeout= 10 71 | ) 72 | 73 | for pair in pairs: 74 | 75 | try: 76 | first_analysis = first_handler[pair].get_analysis() 77 | second_analysis = second_handler[pair].get_analysis() 78 | third_analysis = third_handler[pair].get_analysis() 79 | except Exception as e: 80 | print("buysellcustsignal:") 81 | print("Exception:") 82 | print(e) 83 | print (f'Coin: {pair}') 84 | print (f'First handler: {first_handler[pair]}') 85 | print (f'Second handler: {second_handler[pair]}') 86 | print (f'Second handler: {third_handler[pair]}') 87 | tacheckS = 0 88 | 89 | first_tacheck = first_analysis.summary['BUY'] 90 | first_recommendation = first_analysis.summary['RECOMMENDATION'] 91 | first_RSI = float(first_analysis.indicators['RSI']) 92 | 93 | second_tacheck = second_analysis.summary['BUY'] 94 | second_recommendation = second_analysis.summary['RECOMMENDATION'] 95 | second_RSI = float(second_analysis.indicators['RSI']) 96 | 97 | third_tacheck = third_analysis.summary['BUY'] 98 | third_recommendation = third_analysis.summary['RECOMMENDATION'] 99 | third_RSI = float(third_analysis.indicators['RSI']) 100 | 101 | if FULL_LOG: 102 | print(f'buysellcustsignal:{pair} First {first_tacheck} Second {second_tacheck} Third {third_tacheck}') 103 | print(f'buysellcustsignal:{pair} First {first_recommendation} Second {second_recommendation} Third {third_recommendation}') 104 | #else: 105 | #print(".", end = '') 106 | 107 | if first_tacheck > taMax: 108 | taMax = first_tacheck 109 | taMaxCoin = pair 110 | 111 | if (first_recommendation == "BUY" or first_recommendation == "STRONG_BUY") and (second_recommendation == "BUY" or second_recommendation == "STRONG_BUY") and \ 112 | (third_recommendation == "BUY" or third_recommendation == "STRONG_BUY"): 113 | if first_RSI <= 67 and second_RSI <= 67 and third_RSI <= 67: 114 | signal_coins[pair] = pair 115 | 116 | if FULL_LOG: 117 | print(f'buysellcustsignal: Buy Signal detected on {pair}') 118 | with open('signals/firewatch_signalsample.exs','a+') as f: 119 | f.write(pair + '\n') 120 | 121 | if (first_recommendation == "SELL" or first_recommendation == "STRONG_SELL") and (second_recommendation == "SELL" or second_recommendation == "STRONG_SELL") and \ 122 | (third_recommendation == "SELL" or third_recommendation == "STRONG_SELL"): 123 | #signal_coins[pair] = pair 124 | if FULL_LOG: 125 | print(f'buysellcustsignal: Sell Signal detected on {pair}') 126 | with open('signals/firewatch_signalsample.sell','a+') as f: 127 | f.write(pair + '\n') 128 | 129 | #print(f'buysellcustsignal: Max signal by {taMaxCoin} at {taMax} on shortest timeframe') 130 | 131 | return signal_coins 132 | 133 | #if __name__ == '__main__': 134 | def do_work(): 135 | signal_coins = {} 136 | pairs = {} 137 | 138 | pairs=[line.strip() for line in open(TICKERS)] 139 | for line in open(TICKERS): 140 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 141 | 142 | while True: 143 | if FULL_LOG: 144 | print(f'buysellcustsignal: Analyzing {len(pairs)} coins') 145 | signal_coins = analyze(pairs) 146 | if len(signal_coins) == 0: 147 | print(f'buysellcustsignal: No coins above {TA_BUY_THRESHOLD} threshold on three timeframes. Waiting {TIME_TO_WAIT} minutes for next analysis') 148 | else: 149 | print(f'buysellcustsignal: {len(signal_coins)} coins above {TA_BUY_THRESHOLD} treshold on three timeframes. Waiting {TIME_TO_WAIT} minutes for next analysis') 150 | 151 | time.sleep((TIME_TO_WAIT*60)) 152 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-binance==1.0.12 2 | colorama==0.4.4 3 | PyYAML==5.4.1 4 | tradingview-ta==3.2.7 5 | simplejson==3.17.3 -------------------------------------------------------------------------------- /signals/readme.md: -------------------------------------------------------------------------------- 1 | In this folder goes filenames with extention .exs 2 | exs files are lists of pairs to be traded. 3 | ex module1.exs: 4 | ETHUSDT 5 | BTCUSDT -------------------------------------------------------------------------------- /signalsample.py: -------------------------------------------------------------------------------- 1 | from helpers.parameters import ( 2 | parse_args, load_config 3 | ) 4 | # Load arguments then parse settings 5 | args = parse_args() 6 | #get config file 7 | DEFAULT_CONFIG_FILE = 'config.yml' 8 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 9 | parsed_config = load_config(config_file) 10 | 11 | 12 | from tradingview_ta import TA_Handler, Interval, Exchange 13 | # use for environment variables 14 | import os 15 | # use if needed to pass args to external modules 16 | import sys 17 | # used for directory handling 18 | import glob 19 | import time 20 | 21 | MY_EXCHANGE = 'BINANCE' 22 | MY_SCREENER = 'CRYPTO' 23 | MY_FIRST_INTERVAL = Interval.INTERVAL_1_MINUTE 24 | MY_SECOND_INTERVAL = Interval.INTERVAL_5_MINUTES 25 | TA_BUY_THRESHOLD = 18 # How many of the 26 indicators to indicate a buy 26 | PAIR_WITH = parsed_config['trading_options']['PAIR_WITH'] 27 | TICKERS = parsed_config['trading_options']['TICKERS_LIST'] 28 | TIME_TO_WAIT = parsed_config['trading_options']['SIGNALS_FREQUENCY'] # Minutes to wait between analysis 29 | FULL_LOG = parsed_config['script_options']['VERBOSE_MODE'] # List anylysis result to console 30 | 31 | def analyze(pairs): 32 | taMax = 0 33 | taMaxCoin = 'none' 34 | signal_coins = {} 35 | first_analysis = {} 36 | second_analysis = {} 37 | first_handler = {} 38 | second_handler = {} 39 | if os.path.exists('signals/signalsample.exs'): 40 | os.remove('signals/signalsample.exs') 41 | 42 | for pair in pairs: 43 | first_handler[pair] = TA_Handler( 44 | symbol=pair, 45 | exchange=MY_EXCHANGE, 46 | screener=MY_SCREENER, 47 | interval=MY_FIRST_INTERVAL, 48 | timeout= 10 49 | ) 50 | second_handler[pair] = TA_Handler( 51 | symbol=pair, 52 | exchange=MY_EXCHANGE, 53 | screener=MY_SCREENER, 54 | interval=MY_SECOND_INTERVAL, 55 | timeout= 10 56 | ) 57 | 58 | for pair in pairs: 59 | 60 | try: 61 | first_analysis = first_handler[pair].get_analysis() 62 | second_analysis = second_handler[pair].get_analysis() 63 | except Exception as e: 64 | print("Exeption:") 65 | print(e) 66 | print (f'Coin: {pair}') 67 | print (f'First handler: {first_handler[pair]}') 68 | print (f'Second handler: {second_handler[pair]}') 69 | tacheckS = 0 70 | 71 | first_tacheck = first_analysis.summary['BUY'] 72 | second_tacheck = second_analysis.summary['BUY'] 73 | if FULL_LOG: 74 | print(f'{pair} First {first_tacheck} Second {second_tacheck}') 75 | else: 76 | print(".", end = '') 77 | 78 | if first_tacheck > taMax: 79 | taMax = first_tacheck 80 | taMaxCoin = pair 81 | if first_tacheck >= TA_BUY_THRESHOLD and second_tacheck >= TA_BUY_THRESHOLD: 82 | signal_coins[pair] = pair 83 | print("") 84 | print(f'Signal detected on {pair}') 85 | with open('signals/signalsample.exs','a+') as f: 86 | f.write(pair + '\n') 87 | print("") 88 | print(f'Max signal by {taMaxCoin} at {taMax} on shortest timeframe') 89 | 90 | return signal_coins 91 | 92 | if __name__ == '__main__': 93 | signal_coins = {} 94 | pairs = {} 95 | 96 | pairs=[line.strip() for line in open(TICKERS)] 97 | for line in open(TICKERS): 98 | pairs=[line.strip() + PAIR_WITH for line in open(TICKERS)] 99 | 100 | while True: 101 | print(f'Analyzing {len(pairs)} coins') 102 | signal_coins = analyze(pairs) 103 | if len(signal_coins) == 0: 104 | print(f'No coins above {TA_BUY_THRESHOLD} threshold') 105 | else: 106 | print(f'{len(signal_coins)} coins above {TA_BUY_THRESHOLD} treshold on both timeframes') 107 | print(f'Waiting {TIME_TO_WAIT} minutes for next analysis') 108 | time.sleep((TIME_TO_WAIT*60)) 109 | -------------------------------------------------------------------------------- /signalsample.txt: -------------------------------------------------------------------------------- 1 | BNB 2 | ADA 3 | DEXE 4 | VET 5 | TRX 6 | XVG 7 | LINK 8 | XRP 9 | THETA 10 | LTC 11 | BETH 12 | NEO 13 | XLM 14 | LTC 15 | AAVE 16 | WBTC 17 | ETC 18 | SC 19 | XMR 20 | EOS 21 | ENJ 22 | SLP 23 | QTUM 24 | ZIL 25 | DENT 26 | GRT 27 | ZIL 28 | NANO 29 | STMX 30 | GXS 31 | HOT 32 | DASH 33 | WAVES 34 | IOST 35 | RLC 36 | UFT 37 | OMG 38 | PROS 39 | IOTA 40 | QLC 41 | PUNDIX 42 | FIRO 43 | IOTX 44 | ELF 45 | MFT 46 | SCRT 47 | KEY 48 | AION 49 | ONT 50 | LRC 51 | NAS 52 | SNT 53 | KNC 54 | FUN 55 | VIB 56 | GLM 57 | ICX 58 | CDT 59 | LOOM 60 | ZEN 61 | STEEM 62 | XEM 63 | ADX 64 | BQX 65 | FRONT 66 | BAT 67 | MANA 68 | ZRX 69 | QSP 70 | EZ 71 | BNT 72 | WAN 73 | ZEC 74 | QKC 75 | LSK 76 | KMD 77 | COVER 78 | CVP 79 | POWR 80 | DATA 81 | GHST 82 | REP 83 | MTL 84 | NCASH 85 | HEGIC 86 | PIVX 87 | CVC 88 | NAV 89 | DF 90 | OST 91 | NEBL 92 | OST 93 | STRAX 94 | BRD 95 | BLZ 96 | RENBTC 97 | -------------------------------------------------------------------------------- /tickers/temp.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tickers/tickers.txt: -------------------------------------------------------------------------------- 1 | C98 2 | ONG 3 | MASK 4 | AVAX 5 | TRIBE 6 | CTK 7 | BOND 8 | QNT 9 | RSR 10 | FUN 11 | FOR 12 | WTC 13 | NULS 14 | MDX 15 | LUNA 16 | GTO 17 | PSG 18 | GXS 19 | BEL 20 | CTSI 21 | SXP 22 | HBAR 23 | PUNDIX 24 | SC 25 | KEEP 26 | TWT 27 | ATM 28 | MFT 29 | BCC 30 | VEN 31 | BCHABC 32 | BCHSV 33 | USDS 34 | USDSB 35 | ERD 36 | NPXS 37 | STORM 38 | HC 39 | MCO 40 | STRAT 41 | XZC 42 | LEND 43 | BKRW 44 | PAXG 45 | VITE 46 | MDT 47 | BAR 48 | TOMO 49 | BEAM 50 | DENT 51 | RAMP 52 | TKO 53 | STRAX 54 | OG 55 | STORJ 56 | ARDR 57 | MIR 58 | STPT 59 | RAY 60 | ALPHA 61 | ZEN 62 | PHA 63 | SOL 64 | FLM 65 | XMR 66 | JUV 67 | MITH 68 | ASR 69 | BNT 70 | RIF 71 | BTC 72 | STX 73 | ONT 74 | DEXE 75 | NMR 76 | AION 77 | MLN 78 | INJ 79 | MBL 80 | ACM 81 | DCR 82 | AVA 83 | KAVA 84 | HIVE 85 | COS 86 | HOT 87 | BAT 88 | FORTH 89 | COCOS 90 | GHST 91 | AR 92 | CELO 93 | TORN 94 | HARD 95 | FARM 96 | DUSK 97 | ETH 98 | TFUEL 99 | ANKR 100 | XLM 101 | XEM 102 | WING 103 | VET 104 | TRX 105 | ZEC 106 | MANA 107 | ADA 108 | JST 109 | WRX 110 | BCH 111 | QUICK 112 | KNC 113 | XTZ 114 | MKR 115 | ICX 116 | ORN 117 | BLZ 118 | ANT 119 | BTG 120 | BADGER 121 | VTHO 122 | ZIL 123 | CAKE 124 | POND 125 | REQ 126 | WNXM 127 | XVG 128 | KMD 129 | ROSE 130 | OXT 131 | ETC 132 | DNT 133 | UTK 134 | FIRO 135 | TCT 136 | IOTA 137 | IOST 138 | NEO 139 | DOCK 140 | LTC 141 | LTO 142 | BNB 143 | BTCST 144 | CVC 145 | STMX 146 | KEY 147 | QTUM 148 | WIN 149 | FTT 150 | YFI 151 | BTT 152 | LRC 153 | NBS 154 | DATA 155 | OMG 156 | ZRX 157 | ONE 158 | KLAY 159 | XRP 160 | RVN 161 | COTI 162 | EGLD 163 | REP 164 | OM 165 | PNT 166 | TROY 167 | ALGO 168 | DGB 169 | NEAR 170 | DASH 171 | DREP 172 | MINA 173 | IOTX 174 | ATA 175 | WAVES 176 | CLV 177 | CTXC 178 | UNI 179 | FIL 180 | BAND 181 | REEF 182 | MATIC 183 | XVS 184 | DIA 185 | CRV 186 | AAVE 187 | NANO 188 | SHIB 189 | MTL 190 | ALPACA 191 | PERL 192 | DOGE 193 | ARPA 194 | NU 195 | TLM 196 | NKN 197 | FIO 198 | WAN 199 | OCEAN 200 | 1INCH 201 | AXS 202 | FIS 203 | BTS 204 | ICP 205 | LPT 206 | EOS 207 | DEGO 208 | SUN 209 | THETA 210 | SAND 211 | UMA 212 | IRIS 213 | LINK 214 | CHZ 215 | PERP 216 | ENJ 217 | MBOX 218 | ATOM 219 | OGN 220 | AKRO 221 | GTC 222 | DOT 223 | BAL 224 | FLOW 225 | SFP 226 | POLS 227 | CFX 228 | LINA 229 | BURGER 230 | SRM 231 | LIT 232 | BAKE 233 | SNX 234 | REN 235 | RLC 236 | FET 237 | AUDIO 238 | KSM 239 | TRB 240 | COMP 241 | LSK 242 | SKL 243 | CELR 244 | GRT 245 | TRU 246 | RUNE 247 | ALICE 248 | SLP 249 | UNFI 250 | EPS 251 | BZRX 252 | SUSHI 253 | CHR 254 | DODO 255 | CKB 256 | YFII 257 | ERN 258 | FTM 259 | HNT 260 | AUTO 261 | TVK 262 | WAXP 263 | -------------------------------------------------------------------------------- /tickers/tickers_BNB.txt: -------------------------------------------------------------------------------- 1 | ICX 2 | XMR 3 | THETA 4 | REN 5 | ZEC 6 | OMG 7 | REP 8 | AION 9 | QSP 10 | DASH 11 | BAT 12 | TRX 13 | NAV 14 | BLZ 15 | NAS 16 | OST 17 | QLC 18 | NEO 19 | IOST 20 | XRP 21 | WAN 22 | ZRX 23 | QTUM 24 | ZIL 25 | WAVES 26 | IOTA 27 | ADA 28 | LOOM 29 | HOT 30 | NCASH 31 | STMX 32 | AAVE 33 | VET 34 | STEEM 35 | MFT 36 | EOS 37 | NEBL 38 | XEM 39 | XLM 40 | POWR 41 | PIVX 42 | ZEN 43 | ENJ 44 | ETC 45 | LTC 46 | ONT 47 | RLC 48 | NANO 49 | SC 50 | BRD 51 | CVC 52 | ADX 53 | LSK 54 | -------------------------------------------------------------------------------- /tickers/tickers_BTC.old: -------------------------------------------------------------------------------- 1 | SNT 2 | DLT 3 | NKN 4 | OMG 5 | AMB 6 | MITH 7 | EZ 8 | BAL 9 | SUSD 10 | TRX 11 | GVT 12 | KNC 13 | DNT 14 | PNT 15 | TRB 16 | MTH 17 | SUSHI 18 | BEAM 19 | BCD 20 | ASR 21 | CFX 22 | XTZ 23 | IDEX 24 | HIVE 25 | CDT 26 | FRONT 27 | ST 28 | REQ 29 | APPC 30 | MDA 31 | BNT 32 | NULS 33 | WTC 34 | CELR 35 | XVS 36 | AUCTION 37 | NANO 38 | QSP 39 | KLAY 40 | BQX 41 | SOL 42 | DREP 43 | HARD 44 | RSR 45 | IRIS 46 | NAS 47 | YFII 48 | PSG 49 | BAKE 50 | REP 51 | VIDT 52 | NBS 53 | LUNA 54 | HNT 55 | ICP 56 | KEEP 57 | UMA 58 | SKY 59 | CHR 60 | YOYO 61 | ORN 62 | AERGO 63 | DEGO 64 | POND 65 | CKB 66 | TVK 67 | XRP 68 | CVC 69 | LOOM 70 | OG 71 | OGN 72 | ARDR 73 | AION 74 | NEAR 75 | OM 76 | BEL 77 | GRT 78 | FIRO 79 | POWR 80 | KMD 81 | SNX 82 | ONE 83 | AKRO 84 | PHA 85 | ZIL 86 | AR 87 | TWT 88 | PERP 89 | WAN 90 | RLC 91 | AAVE 92 | STRAX 93 | ATA 94 | LPT 95 | AUDIO 96 | ICX 97 | FOR 98 | RVN 99 | LRC 100 | ANT 101 | ACM 102 | NU 103 | OAX 104 | ALGO 105 | ONT 106 | PHB 107 | KSM 108 | NAV 109 | TORN 110 | FET 111 | UNI 112 | VITE 113 | POLY 114 | MKR 115 | AST 116 | BAT 117 | SCRT 118 | XMR 119 | GTC 120 | W 121 | KAVA 122 | BADGER 123 | REN 124 | LSK 125 | QLC 126 | STX 127 | POLS 128 | ATOM 129 | ADA 130 | QKC 131 | FIO 132 | RDN 133 | TKO 134 | RCN 135 | SAND 136 | FTT 137 | AVA 138 | WING 139 | SRM 140 | TOMO 141 | WABI 142 | TFUEL 143 | VIA 144 | NXS 145 | JST 146 | CHZ 147 | DGB 148 | CAKE 149 | NMR 150 | ETC 151 | GLM 152 | ZEC 153 | QTUM 154 | IOTA 155 | COMP 156 | DODO 157 | MANA 158 | IOTX 159 | EGLD 160 | GAS 161 | REEF 162 | ENJ 163 | FORTH 164 | UTK 165 | NEBL 166 | ZRX 167 | POA 168 | RIF 169 | ETH 170 | RAMP 171 | DUSK 172 | CELO 173 | STMX 174 | WAVES 175 | SC 176 | BTG 177 | DASH 178 | COS 179 | BTS 180 | BRD 181 | ARK 182 | FLM 183 | STEEM 184 | MIR 185 | BNB 186 | BCH 187 | BAND 188 | AGIX 189 | SXP 190 | ALICE 191 | LINA 192 | GXS 193 | LIT 194 | ZEN 195 | XLM 196 | CTXC 197 | PAXG 198 | SNM 199 | GTO 200 | PIVX 201 | WNXM 202 | FUN 203 | MDT 204 | PERL 205 | FTM 206 | VIB 207 | DIA 208 | PPT 209 | CRV 210 | ALPHA 211 | TLM 212 | AXS 213 | EOS 214 | DCR 215 | AVAX 216 | 1INCH 217 | WRX 218 | CTSI 219 | XVG 220 | INJ 221 | MATIC 222 | ADX 223 | NEO 224 | DOT 225 | IOST 226 | ARPA 227 | STPT 228 | EVX 229 | TCT 230 | DOGE 231 | SKL 232 | OCEAN 233 | ANKR 234 | CTK 235 | STORJ 236 | LTC 237 | BLZ 238 | MDX 239 | VET 240 | ATM 241 | WPR 242 | FIL 243 | RUNE 244 | DATA 245 | HBAR 246 | TROY 247 | FXS 248 | YFI 249 | OXT 250 | ELF 251 | AUTO 252 | LTO 253 | SYS 254 | XEM 255 | ONG 256 | JUV 257 | TRU 258 | GRS 259 | CND 260 | BAR 261 | SNGLS 262 | EPS 263 | ROSE 264 | LINK 265 | THETA 266 | BZRX 267 | COTI 268 | GO 269 | FIS 270 | SFP 271 | DOCK 272 | UNFI 273 | MTL 274 | -------------------------------------------------------------------------------- /tickers/tickers_BTC.txt: -------------------------------------------------------------------------------- 1 | LTO 2 | ALICE 3 | IOST 4 | UMA 5 | TVK 6 | POLS 7 | EVX 8 | TRX 9 | EZ 10 | POLY 11 | STPT 12 | NKN 13 | AAVE 14 | NXS 15 | KNC 16 | BEL 17 | DATA 18 | UNI 19 | BEAM 20 | NEAR 21 | BLZ 22 | RSR 23 | ARK 24 | ANT 25 | CHR 26 | SKL 27 | XTZ 28 | ICP 29 | GRS 30 | GO 31 | GRT 32 | DCR 33 | HNT 34 | AST 35 | REN 36 | PSG 37 | NAV 38 | SNM 39 | FXS 40 | ATA 41 | SRM 42 | DOGE 43 | AGIX 44 | ROSE 45 | NEO 46 | ONE 47 | AKRO 48 | RIF 49 | WTC 50 | PNT 51 | MANA 52 | WNXM 53 | SNT 54 | VIDT 55 | OMG 56 | YFII 57 | GTC 58 | LIT 59 | CDT 60 | MTL 61 | CVC 62 | RLC 63 | WAN 64 | FOR 65 | QSP 66 | LRC 67 | OM 68 | CAKE 69 | FRONT 70 | SKY 71 | OG 72 | ELF 73 | STMX 74 | PHB 75 | BQX 76 | KLAY 77 | GLM 78 | RAMP 79 | POA 80 | FIL 81 | DUSK 82 | VITE 83 | NANO 84 | ATOM 85 | DNT 86 | ZIL 87 | BADGER 88 | BAT 89 | SUSD 90 | AVAX 91 | IRIS 92 | KAVA 93 | AVA 94 | TRB 95 | MTH 96 | PIVX 97 | UTK 98 | DREP 99 | ACM 100 | CTSI 101 | OAX 102 | SNGLS 103 | EGLD 104 | SOL 105 | PHA 106 | SYS 107 | ADX 108 | VIA 109 | CELO 110 | ONT 111 | FLM 112 | FTT 113 | BAR 114 | YFI 115 | WPR 116 | EPS 117 | ARDR 118 | ALGO 119 | JST 120 | POWR 121 | FIS 122 | COMP 123 | REP 124 | SXP 125 | HARD 126 | AERGO 127 | 1INCH 128 | TFUEL 129 | XRP 130 | NAS 131 | AION 132 | GXS 133 | OGN 134 | KMD 135 | ICX 136 | SC 137 | PERP 138 | SCRT 139 | ASR 140 | XMR 141 | NMR 142 | MDX 143 | LSK 144 | FET 145 | NU 146 | TLM 147 | TWT 148 | LOOM 149 | TKO 150 | AR 151 | UNFI 152 | AUTO 153 | ZEC 154 | DOCK 155 | IOTA 156 | REEF 157 | ENJ 158 | FUN 159 | BTS 160 | STORJ 161 | SUSHI 162 | CRV 163 | DGB 164 | BAND 165 | PPT 166 | FIO 167 | BNT 168 | WRX 169 | BAL 170 | ZRX 171 | MDA 172 | BTG 173 | TCT 174 | WING 175 | CTXC 176 | PERL 177 | OCEAN 178 | QTUM 179 | BRD 180 | COTI 181 | MITH 182 | TROY 183 | WAVES 184 | IOTX 185 | XLM 186 | BNB 187 | MIR 188 | TORN 189 | RVN 190 | ADA 191 | DODO 192 | NEBL 193 | ARPA 194 | QKC 195 | INJ 196 | AUDIO 197 | CKB 198 | LPT 199 | GAS 200 | OXT 201 | COS 202 | HBAR 203 | ONG 204 | MATIC 205 | FORTH 206 | QLC 207 | GVT 208 | STRAX 209 | VIB 210 | SAND 211 | EOS 212 | DIA 213 | CND 214 | CHZ 215 | ETC 216 | WABI 217 | RUNE 218 | SNX 219 | BAKE 220 | DEGO 221 | YOYO 222 | RDN 223 | KSM 224 | IDEX 225 | AXS 226 | REQ 227 | ZEN 228 | LINK 229 | LUNA 230 | CTK 231 | DOT 232 | ALPHA 233 | LINA 234 | NBS 235 | TOMO 236 | CELR 237 | XVG 238 | HIVE 239 | AUCTION 240 | MDT 241 | APPC 242 | AMB 243 | BZRX 244 | GTO 245 | FIRO 246 | ETH 247 | PAXG 248 | VET 249 | SFP 250 | CFX 251 | JUV 252 | POND 253 | DLT 254 | DASH 255 | ANKR 256 | FTM 257 | MKR 258 | BCH 259 | LTC 260 | STEEM 261 | XEM 262 | ORN 263 | THETA 264 | RCN 265 | NULS 266 | ATM 267 | KEEP 268 | XVS 269 | -------------------------------------------------------------------------------- /tickers/tickers_BUSD.txt: -------------------------------------------------------------------------------- 1 | AERGO 2 | ICX 3 | XTZ 4 | FTM 5 | TUSD 6 | IOST 7 | CKB 8 | ACM 9 | WRX 10 | FIO 11 | BIFI 12 | ZIL 13 | DODO 14 | COMP 15 | SAND 16 | BEL 17 | KNC 18 | ETH 19 | BADGER 20 | DEXE 21 | DF 22 | BTG 23 | CHZ 24 | NEO 25 | LINA 26 | NU 27 | WING 28 | DGB 29 | AR 30 | CVP 31 | ATOM 32 | CRV 33 | ONT 34 | PAX 35 | ALICE 36 | SNX 37 | QTUM 38 | HBAR 39 | XVG 40 | KP3R 41 | CELR 42 | GRT 43 | TKO 44 | CFX 45 | RSR 46 | DG 47 | CAKE 48 | BURGER 49 | GHST 50 | NEAR 51 | PSG 52 | SLP 53 | RAMP 54 | NANO 55 | ATA 56 | UNI 57 | PERP 58 | TR 59 | ALPHA 60 | LUNA 61 | STRAX 62 | SHIB 63 | EOS 64 | GBP 65 | AUTO 66 | KAVA 67 | LINK 68 | TOMO 69 | WIN 70 | POLS 71 | XRP 72 | VET 73 | EGLD 74 | SXP 75 | HEGIC 76 | FIL 77 | BAR 78 | TRU 79 | BNT 80 | ICP 81 | BTT 82 | FLM 83 | DATA 84 | AUD 85 | BTCST 86 | ATM 87 | OMG 88 | LTC 89 | INJ 90 | TLM 91 | DEGO 92 | BCHA 93 | TWT 94 | UNFI 95 | DNT 96 | SUPER 97 | FIS 98 | AVAX 99 | DIA 100 | LIT 101 | XMR 102 | FORTH 103 | SUSHI 104 | ZRX 105 | ETC 106 | XVS 107 | SOL 108 | MDX 109 | SRM 110 | SYS 111 | BAKE 112 | AM 113 | SWRV 114 | PHA 115 | YFII 116 | LRC 117 | BAT 118 | YFI 119 | SKL 120 | PROM 121 | BAND 122 | EUR 123 | CTK 124 | PH 125 | ONE 126 | KSM 127 | 1INCH 128 | LPT 129 | POND 130 | RLC 131 | IOTA 132 | CTSI 133 | TRB 134 | OCEAN 135 | ALGO 136 | REEF 137 | THETA 138 | AAVE 139 | UFT 140 | MANA 141 | MKR 142 | VI 143 | BTC 144 | CREAM 145 | MATIC 146 | ENJ 147 | DOGE 148 | SFP 149 | TVK 150 | BAL 151 | ANT 152 | FOR 153 | BZRX 154 | FRONT 155 | AUCTION 156 | MIR 157 | AVA 158 | USDC 159 | XEM 160 | EPS 161 | JUV 162 | VIDT 163 | AUDIO 164 | HOT 165 | BN 166 | DASH 167 | ZEN 168 | RUNE 169 | HARD 170 | DOT 171 | MASK 172 | COVER 173 | RVN 174 | NMR 175 | FXS 176 | JST 177 | CK 178 | AXS 179 | IDEX 180 | ZEC 181 | ADA 182 | ROSE 183 | BCH 184 | WAVES 185 | IQ 186 | XLM 187 | OM 188 | BNB 189 | TRX 190 | -------------------------------------------------------------------------------- /tickers/tickers_ETH.old: -------------------------------------------------------------------------------- 1 | STRAX 2 | XMR 3 | STEEM 4 | OMG 5 | WBTC 6 | EZ 7 | NAV 8 | NCASH 9 | XLM 10 | STMX 11 | ZIL 12 | PUNDIX 13 | BRD 14 | CDT 15 | LOOM 16 | XRP 17 | DF 18 | HEGIC 19 | CVC 20 | NEO 21 | VIB 22 | IOTX 23 | IOTA 24 | EOS 25 | GXS 26 | NEBL 27 | ZEN 28 | DENT 29 | FUN 30 | WAVES 31 | DATA 32 | CVP 33 | ETC 34 | LSK 35 | ZRX 36 | KEY 37 | QSP 38 | GLM 39 | MFT 40 | ICX 41 | BAT 42 | THETA 43 | LINK 44 | MTL 45 | GRT 46 | ADX 47 | AAVE 48 | SC 49 | NAS 50 | SNT 51 | LTC 52 | ZEC 53 | PROS 54 | SCRT 55 | QLC 56 | ONT 57 | COVER 58 | UFT 59 | IOST 60 | WAN 61 | AION 62 | GHST 63 | OST 64 | ELF 65 | ENJ 66 | PIVX 67 | POWR 68 | REP 69 | BLZ 70 | DEXE 71 | BQX 72 | NANO 73 | QKC 74 | HOT 75 | SLP 76 | XEM 77 | XVG 78 | FIRO 79 | TRX 80 | RLC 81 | MANA 82 | KNC 83 | QTUM 84 | FRONT 85 | VET 86 | DASH 87 | LRC 88 | ADA 89 | BNT 90 | BNB 91 | KMD 92 | RENBTC 93 | -------------------------------------------------------------------------------- /tickers/tickers_ETH.txt: -------------------------------------------------------------------------------- 1 | DENT 2 | HOT 3 | SC 4 | VET 5 | KEY 6 | STMX 7 | FUN 8 | XVG 9 | IOTX 10 | SLP 11 | MFT 12 | XRP 13 | TRX 14 | QKC 15 | ADA 16 | IOST 17 | VIB 18 | CDT 19 | ZIL 20 | DATA 21 | ADX 22 | XLM 23 | QSP 24 | HEGIC 25 | QLC 26 | AION 27 | BRD 28 | NAS 29 | SNT 30 | XEM 31 | GRT 32 | LRC 33 | LOOM 34 | STEEM 35 | PROS 36 | BLZ 37 | ENJ 38 | GXS 39 | UFT 40 | GHST 41 | PIVX 42 | POWR 43 | ELF 44 | ONT 45 | GLM 46 | IOTA 47 | THETA 48 | KMD 49 | SCRT 50 | BAT 51 | EOS 52 | LSK 53 | LINK 54 | MTL 55 | BQX 56 | WAN 57 | ZRX 58 | ICX 59 | PUNDIX 60 | CVP 61 | MANA 62 | RLC 63 | BNT 64 | NEBL 65 | CVC 66 | QTUM 67 | NANO 68 | EZ 69 | STRAX 70 | OMG 71 | NAV 72 | BNB 73 | KNC 74 | WAVES 75 | NEO 76 | FIRO 77 | ETC 78 | LTC 79 | DEXE 80 | REP 81 | AAVE 82 | XMR 83 | DASH 84 | ZEN 85 | COVER 86 | ZEC 87 | WBTC 88 | OST 89 | DF 90 | -------------------------------------------------------------------------------- /tickers/tickers_ETH_MCAP.txt: -------------------------------------------------------------------------------- 1 | BNB 2 | ADA 3 | DEXE 4 | VET 5 | TRX 6 | XVG 7 | LINK 8 | XRP 9 | THETA 10 | LTC 11 | BETH 12 | NEO 13 | XLM 14 | LTC 15 | AAVE 16 | WBTC 17 | ETC 18 | SC 19 | XMR 20 | EOS 21 | ENJ 22 | SLP 23 | QTUM 24 | ZIL 25 | DENT 26 | GRT 27 | ZIL 28 | NANO 29 | STMX 30 | GXS 31 | HOT 32 | DASH 33 | WAVES 34 | IOST 35 | RLC 36 | UFT 37 | OMG 38 | PROS 39 | IOTA 40 | QLC 41 | PUNDIX 42 | FIRO 43 | IOTX 44 | ELF 45 | MFT 46 | SCRT 47 | KEY 48 | AION 49 | ONT 50 | LRC 51 | NAS 52 | SNT 53 | KNC 54 | FUN 55 | VIB 56 | GLM 57 | ICX 58 | CDT 59 | LOOM 60 | ZEN 61 | STEEM 62 | XEM 63 | ADX 64 | BQX 65 | FRONT 66 | BAT 67 | MANA 68 | ZRX 69 | QSP 70 | EZ 71 | BNT 72 | WAN 73 | ZEC 74 | QKC 75 | LSK 76 | KMD 77 | COVER 78 | CVP 79 | POWR 80 | DATA 81 | GHST 82 | REP 83 | MTL 84 | NCASH 85 | HEGIC 86 | PIVX 87 | CVC 88 | NAV 89 | DF 90 | OST 91 | NEBL 92 | OST 93 | STRAX 94 | BRD 95 | BLZ 96 | RENBTC 97 | -------------------------------------------------------------------------------- /tickers/tickers_USDT.old: -------------------------------------------------------------------------------- 1 | 1INCH 2 | AAVE 3 | ADA 4 | ALGO 5 | ALPHA 6 | AR 7 | ARRR 8 | ATOM 9 | AVAX 10 | BAKE 11 | BAT 12 | BCD 13 | BCH 14 | BCHA 15 | BNB 16 | BNT 17 | BSV 18 | BTC 19 | BTCB 20 | BTCST 21 | BTG 22 | CAKE 23 | CCXX 24 | CEL 25 | CELO 26 | CHSB 27 | COMP 28 | CRV 29 | CTC 30 | DAI 31 | DASH 32 | DCR 33 | DFI 34 | DOT 35 | EGLD 36 | ENJ 37 | EOS 38 | ETC 39 | ETH 40 | FIL 41 | FLOW 42 | FTT 43 | GRT 44 | GT 45 | HNT 46 | HT 47 | ICP 48 | ICX 49 | KCS 50 | KLAY 51 | KNC 52 | KSM 53 | LEO 54 | LINK 55 | LPT 56 | LSK 57 | LTC 58 | LUNA 59 | LUSD 60 | MANA 61 | MDX 62 | MIOTA 63 | MIR 64 | MKR 65 | NANO 66 | NEAR 67 | NEO 68 | NEXO 69 | OCEAN 70 | OKB 71 | OMG 72 | ONT 73 | ORC 74 | PROM 75 | PUNDIX 76 | QNT 77 | QTUM 78 | RAY 79 | RENBTC 80 | RLC 81 | RUNE 82 | SNX 83 | SOL 84 | STORJ 85 | STX 86 | SUSHI 87 | THETA 88 | TTT 89 | UMA 90 | UNI 91 | UST 92 | VGX 93 | WAVES 94 | WBNB 95 | WBTC 96 | WRX 97 | XMR 98 | XRP 99 | XTZ 100 | XVS 101 | XWC 102 | YFI 103 | ZEC 104 | ZEN 105 | ZRX -------------------------------------------------------------------------------- /tickers/tickers_USDT.txt: -------------------------------------------------------------------------------- 1 | MIR 2 | HIVE 3 | MANA 4 | SC 5 | WTC 6 | CRV 7 | SRM 8 | KSM 9 | ONT 10 | PAXG 11 | RUNE 12 | FORTH 13 | XVG 14 | DOT 15 | HBAR 16 | DUSK 17 | MBL 18 | BCH 19 | EGLD 20 | LTC 21 | PERL 22 | BAL 23 | YFI 24 | TKO 25 | DOGE 26 | USDC 27 | TRX 28 | TROY 29 | MATIC 30 | FIRO 31 | OCEAN 32 | DNT 33 | DENT 34 | NANO 35 | VET 36 | POLS 37 | STORJ 38 | MTL 39 | SUSHI 40 | RSR 41 | CELO 42 | ATM 43 | ETC 44 | GXS 45 | WNXM 46 | ORN 47 | STMX 48 | REP 49 | AVAX 50 | OG 51 | MDX 52 | TRU 53 | WAVES 54 | GRT 55 | NEAR 56 | XTZ 57 | OMG 58 | AR 59 | SAND 60 | REN 61 | POND 62 | BTCST 63 | WING 64 | SOL 65 | TWT 66 | QTUM 67 | LTO 68 | PERP 69 | CAKE 70 | DGB 71 | FUN 72 | UTK 73 | HNT 74 | BTG 75 | CVC 76 | BZRX 77 | SFP 78 | LPT 79 | MFT 80 | NKN 81 | BADGER 82 | BAR 83 | EUR 84 | KNC 85 | AKRO 86 | FTT 87 | DASH 88 | CFX 89 | NEO 90 | BAT 91 | MITH 92 | ZEN 93 | STRAX 94 | ETH 95 | OM 96 | PSG 97 | ALGO 98 | BNT 99 | BEAM 100 | COMP 101 | VITE 102 | SUSD 103 | SHIB 104 | ATOM 105 | AUDIO 106 | EPS 107 | LINA 108 | OXT 109 | IOTX 110 | FET 111 | FTM 112 | ZRX 113 | YFII 114 | TUSD 115 | COTI 116 | ROSE 117 | BTC 118 | EOS 119 | ARPA 120 | UNI 121 | TOMO 122 | RAMP 123 | MKR 124 | UMA 125 | JUV 126 | HOT 127 | JST 128 | AUTO 129 | BTT 130 | CTSI 131 | CTXC 132 | NBS 133 | LRC 134 | INJ 135 | SUN 136 | ANKR 137 | ICX 138 | UNFI 139 | CHZ 140 | STPT 141 | FIO 142 | TFUEL 143 | ZIL 144 | KAVA 145 | ATA 146 | COCOS 147 | ARDR 148 | RVN 149 | KMD 150 | TRB 151 | AUD 152 | GTO 153 | ALICE 154 | ONG 155 | DIA 156 | DOCK 157 | IOTA 158 | XVS 159 | PUNDIX 160 | LIT 161 | ONE 162 | FIL 163 | NU 164 | BAKE 165 | LUNA 166 | DCR 167 | NMR 168 | BLZ 169 | ADA 170 | ASR 171 | BURGER 172 | FLM 173 | LSK 174 | STX 175 | GBP 176 | THETA 177 | SUPER 178 | ANT 179 | MDT 180 | SKL 181 | DEGO 182 | CTK 183 | PAX 184 | KEY 185 | BAND 186 | DATA 187 | XMR 188 | BNB 189 | ALPHA 190 | IOST 191 | WRX 192 | REEF 193 | BEL 194 | XEM 195 | IRIS 196 | TLM 197 | COS 198 | ENJ 199 | BTS 200 | OGN 201 | AVA 202 | AAVE 203 | 1INCH 204 | WIN 205 | BUSD 206 | AXS 207 | SLP 208 | AION 209 | RIF 210 | CHR 211 | NULS 212 | MASK 213 | CELR 214 | SXP 215 | FIS 216 | XLM 217 | PNT 218 | VTHO 219 | WAN 220 | XRP 221 | ACM 222 | ZEC 223 | RLC 224 | TCT 225 | ICP 226 | HARD 227 | DODO 228 | LINK 229 | SNX 230 | CKB 231 | DREP 232 | -------------------------------------------------------------------------------- /utilities/sell-remaining-coins.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('..') 3 | 4 | import json 5 | import os 6 | from binance.client import Client 7 | from datetime import datetime 8 | 9 | # Load helper modules 10 | from helpers.parameters import ( 11 | parse_args, load_config 12 | ) 13 | 14 | # Load creds modules 15 | from helpers.handle_creds import ( 16 | load_correct_creds 17 | ) 18 | 19 | args = parse_args() 20 | 21 | DEFAULT_CONFIG_FILE = '../config.yml' 22 | DEFAULT_CREDS_FILE = '../creds.yml' 23 | 24 | config_file = args.config if args.config else DEFAULT_CONFIG_FILE 25 | creds_file = args.creds if args.creds else DEFAULT_CREDS_FILE 26 | parsed_creds = load_config(creds_file) 27 | parsed_config = load_config(config_file) 28 | 29 | LOG_TRADES = parsed_config['script_options'].get('LOG_TRADES') 30 | LOG_FILE = parsed_config['script_options'].get('LOG_FILE') 31 | LOG_FILE_PATH = '../' + LOG_FILE 32 | 33 | access_key, secret_key = load_correct_creds(parsed_creds) 34 | 35 | client = Client(access_key, secret_key) 36 | 37 | def write_log(logline: str) -> None: 38 | timestamp = datetime.now().strftime("%d/%m %H:%M:%S") 39 | with open(LOG_FILE_PATH,'a+') as f: 40 | f.write(timestamp + ' ' + logline + '\n') 41 | 42 | with open('../coins_bought.json', 'r') as f: 43 | coins = json.load(f) 44 | 45 | for coin in list(coins): 46 | sell_coin = client.create_order( 47 | symbol = coin, 48 | side = 'SELL', 49 | type = 'MARKET', 50 | quantity = coins[coin]['volume'] 51 | ) 52 | 53 | buyPrice = float(coins[coin]['bought_at']) 54 | lastPrice = float(sell_coin['fills'][0]['price']) 55 | profit = (lastPrice - buyPrice) * coins[coin]['volume'] 56 | priceChange = float((lastPrice - buyPrice) / buyPrice * 100) 57 | 58 | if LOG_TRADES: 59 | timestamp = datetime.now().strftime("%d/%m %H:%M:%S") 60 | write_log(f"Sell: {coins[coin]['volume']} {coin} - {buyPrice} - {lastPrice} Profit: {profit:.2f} {priceChange:.2f}%") 61 | 62 | os.remove('../coins_bought.json') --------------------------------------------------------------------------------