├── .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:
- BNB ~$5 USD (used for fees / transactions)
- Atleast X USDT matching your QUANTITYxTRADE_SLOTS (>$15 USD but check config for 'QUANTITY')
|
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')
--------------------------------------------------------------------------------