├── .gitignore ├── README.md ├── amplitudes.py ├── backtest_amplitudes.py ├── backtest_strategy.py ├── bina ├── __init__.py └── bina.py ├── call_bina.py ├── data └── ADAUSDT2022-06-01 21.xlsx ├── ignore ├── .gitignore ├── config.yml └── telconfig.yml ├── license.txt ├── messenger_d.py ├── messenger_h_rsi.py └── tools ├── __init__.py └── tools.py /.gitignore: -------------------------------------------------------------------------------- 1 | src/signalFinder/* 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # pdm 106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 107 | #pdm.lock 108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 109 | # in version control. 110 | # https://pdm.fming.dev/#use-with-ide 111 | .pdm.toml 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # amplitudes.py 3 | Fit a crypto trade predictor model with a determined sequence of candels from different Cryptocurrency. [(read jupyter_analisis also, for a better interpretation)](https://github.com/elbernaderen/jupyter_analysis#btcusdt_adausdt_xmrusdt_ethusdt_bnbusdt_30m_hour_dayipynb) 4 | 5 | With this script, we run through each Crypto-currency Historical Data like BTCUSDT or ETHUSDT and generate numerous rows with a determinated number of candels. For each candel add technical indicators like: 6 | * macd 7 | * macd histogram 8 | * macd signal 9 | * RSI 10 | 11 | It also include indicators that express: 12 | * amplitude of high and low values 13 | * open and closure values 14 | * normalized volume . 15 | 16 | With all those indicators, we try to find a pattern in prices movement that allow us to know if the prices are going to rise or fall. Then, with a determinated number of candels that come next of the last one of the generated row, we know if the price have risen or fallen. 17 | 18 | If the price increase its value in an determinated percent, we assign this row or sequence as a forecast or prevision of value rise with the determinated "increase", and for any other case we consider that is not allowing us to predict anything and assign it as "0". 19 | 20 | Once we have ran over all the Cryptocurrency Historical Data we determined and generated the mentionated rows with its assigned values, it's time to train the Random Tree Forest model. 21 | 22 | Then, with the model we can predict if the prices will rise and send a signal with the telegram bot, and so we'll know if have to buy or not an Cryptocurrency determined. 23 | 24 | Before we create the rows, first we filter them taking in count the slope of the candels (rising or falling market, this is adjusted to falling market but it can be easily changed), the rsi value and the volume of the last candels, and just if the candel sequence fit the requirements the row mentionated is created and considered for the predictor. 25 | 26 | ## Must install 27 | [pandas](https://pandas.pydata.org/), [numpy](https://numpy.org/install/) and [scipy](https://scipy.org/install/) libraries are used to work with data frames and lists. 28 | 29 | [sklearn](https://scikit-learn.org/stable/install.html) is a library used to create and train the machine learning model. 30 | 31 | [pickle](https://docs.python.org/3/library/pickle.html#:~:text=%E2%80%9CPickling%E2%80%9D%20is%20the%20process%20whereby,back%20into%20an%20object%20hierarchy.) is needed to save the model in a .sav file, so we can use it in a easy way with the backtester [backtest_amplitudes.py](https://github.com/elbernaderen/machine-learning-signal-finder#backtest_strategypy) or the signal sender [messenger_d.py](https://github.com/elbernaderen/machine-learning-signal-finder#messenger_dpy). 32 | 33 | 34 | ## Usage 35 | Once we have downloaded the Cryptocurrency Historical Data (one or more) with the same interval in the same directory of the program, we call the program with the Crypto-currency as command line arguments in capital letters as: 36 | 37 | ```bash 38 | py amplitudes_rsi_vol_rsi.py BTCUSDT ETHUSDT ADAUSDT 39 | ``` 40 | Then, the program will ask the next variables: 41 | ```bash 42 | Enter the percentage that have to rise the price to consider it as a success: 43 | ``` 44 | This is the increment that we look for predict, ex: 0.01 45 | ```bash 46 | Enter the number of candels (Y) to consider in the model for the prediction: 47 | ``` 48 | The increment before mentionated has to be between the Y candels 49 | ```bash 50 | Enter the number of candels (X) considered in the model for the prediction: 51 | ``` 52 | These will be the candels we use to predict 53 | ```bash 54 | Enter the amount of periods for rsi calculation (14 recomended): 55 | ``` 56 | A period for rsi calculation can be better for a candle interval analysis, and not for other one, so, it can be modificated if want it 57 | ```bash 58 | Enter the rsi value to consider (30 recomended): 59 | ``` 60 | The RSI value is a indicator for some strategies in crypto-trading, so it also can be modificated as a superior limit (the script can be easily changeable) 61 | ```bash 62 | Enter how much to increase the mean volume value: 63 | ``` 64 | This is a filter to consider just the candles with a bigger volume than the mean volume of a determined amount of candles 65 | ```bash 66 | Enter the slope to take in reference, (0 recomended): 67 | ``` 68 | The slope of the close value of the candels indicates if the market (in this sequence) is bullish or bearish. 69 | ```bash 70 | Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m 71 | ``` 72 | The interval of the Crypto-currency Historical Data to consider. 73 | ```bash 74 | Enter how many candels consider to calculate the volume mean: 75 | ``` 76 | To calculate the mean volume,so it can know if the volume has a increment or in other words if there are big participants, ex: 300. 77 | Once the program have finished, a classification report will be printed in console, with the accuracy, precission, etc of the model, and a .sav file will be created with the model ready for be used. 78 | 79 | 80 | ![tempsnip](https://user-images.githubusercontent.com/65098903/178709836-f52c5451-a3dc-410a-9404-ed0b931e587e.png) 81 | 82 | 83 | 84 | # backtest_strategy.py 85 | This program, as it's name says is a backtest for a strategy with a determinated Crypto-currency Historical Data. 86 | ## Description 87 | With a Crypto-currency Historical Data, that has been download with **call_bina.py**, this program creates an .xlsx spreadsheet where is the data and the decision of the stratrategy, buy or do nothing. 88 | Taking in count the slope of the candels (rising or falling market, this is adjusted to falling market but it can be easily changed), the rsi value and the volume of the last candels, if the strategy set fits with the sequence, the column "vale" will be the value of the increment predicted. In all the other cases the value will be 0. Further, a determined number of candles that come next to the sequence analyzed will be added, with the highest and lowest values, so we can know if the strategy is acerted, and with this information we can improve it. 89 | ## Must install 90 | [pandas](https://pandas.pydata.org/), [numpy](https://numpy.org/install/) and [scipy](https://scipy.org/install/) libraries are used to work with data frames and lists. 91 | ## Usage 92 | Once we have downloaded the Crypto-currency Historical Data in the same directory, we call the program: 93 | 94 | ```bash 95 | py backtest_strategy.py 96 | ``` 97 | Then, the program will ask the next variables: 98 | ```bash 99 | Enter the number of candels (Y) that come after the prediction: 100 | ``` 101 | The increment before mentionated has to be between the Y candels 102 | ```bash 103 | Enter the number of candels (X) considered for the technical analysis: 104 | ``` 105 | ```bash 106 | Enter the amount of periods for rsi calculation (14 recomended): 107 | ``` 108 | A period for rsi calculation can be better for a candle interval analysis, and not for other one, so, it can be modificated if want it 109 | ```bash 110 | Enter how much to increase the mean volume value: 111 | ``` 112 | This is a filter to consider just the candles with a bigger volume than the mean volume of a determined amount of candles 113 | ```bash 114 | Enter the slope to take in reference, (0 recomended): 115 | ``` 116 | The slope of the close value of the candels indicates if the market (in this sequence) is bullish or bearish. 117 | ```bash 118 | Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m 119 | ``` 120 | The interval of the Crypto-currency Historical Data to consider. 121 | Once the program has finished, a .xlsx spreadsheet will be created with a column with the found signals and the consecutive variations of the high and low candels value respect the close value of the X's last candel, so we can verify if prices had rises or fell and modificate the strategy to improve the prediction. 122 | 123 | 124 | 125 | 126 | # backtest_amplitudes.py 127 | With this program,the user can make a backtest for one or more models created with [amplitudes](https://github.com/elbernaderen/machine-learning-signal-finder/blob/main/README.md#amplitudespy) with a determinated Crypto-currency Historical Data. 128 | ## Description 129 | With a Crypto-currency Historical Data, that has been download with **call_bina.py**, this program creates an .xlsx spreadsheet where is the data and the decision (buy or do nothing) of one or more models created with [amplitudes](https://github.com/elbernaderen/machine-learning-signal-finder/blob/main/README.md#amplitudespy), where these models could be created with different criteria, like the percentage that have to rise the price to consider it as a success RSI, volume, etc. 130 | Using the .sav model/s files, the program predict if the price will increase it's value to achieve the increment predict, and this will be added in a column with the model name. 131 | Taking in count the slope of the candels (rising or falling market, this is adjusted to falling market but it can be easily changed), the rsi value and the volume of the last candels, if the variables set fits with the sequence, the column "vale" will be 1 and otherwise will be 0, so it can be used as a filter.the value of the increment predicted. In all the other cases the value will be 0. Further, a determined number of candles that come next to the sequence analyzed will be added, with the highest and lowest values, so we can know if the strategy is acerted, and with this information we can improve it. 132 | ## Must install 133 | [pandas](https://pandas.pydata.org/), [numpy](https://numpy.org/install/) and [scipy](https://scipy.org/install/) libraries are used to work with data frames and lists. 134 | [Binance](https://pypi.org/project/python-binance/) is needed to download the crypto-currency historical data. 135 | with [sklearn](https://scikit-learn.org/stable/install.html) we can use the model with the predictor. 136 | 137 | [pickle](https://docs.python.org/3/library/pickle.html#:~:text=%E2%80%9CPickling%E2%80%9D%20is%20the%20process%20whereby,back%20into%20an%20object%20hierarchy.) is needed to open the .sav file with the predictor model. 138 | ## Usage 139 | Once we have downloaded the Crypto-currency Historical Data in the same directory, we call the program: 140 | 141 | ```bash 142 | py backtest_amplitudes.py 143 | ``` 144 | Then, the program will ask the next variables: 145 | ```bash 146 | Enter the number of candels (Y) that come after the prediction: 147 | ``` 148 | The increment before mentionated has to be between the Y candels 149 | ```bash 150 | Enter the number of candels (X) considered for the technical analysis: 151 | ``` 152 | ```bash 153 | Enter the amount of periods for rsi calculation (14 recomended): 154 | ``` 155 | A period for rsi calculation can be better for a candle interval analysis, and not for other one, so, it can be modificated if want it 156 | 157 | ```bash 158 | Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m 159 | ``` 160 | The interval of the Crypto-currency Historical Data to consider. 161 | ```bash 162 | Enter how many candels consider to calculate the volume mean: 163 | ``` 164 | To calculate the mean volume,so it can know if the volume has a increment or in other words if there are big participants, ex: 300. 165 | Once the program have finished, a classification report will be printed in console, with the accuracy, precission, etc of the model, and a .sav file will be created with the model ready for be used. 166 | 167 | Once the program has finished, a .xlsx spreadsheet will be created with a column with the found signals and the consecutive variations of the high and low candels value respect the close value of the X's last candel, so we can verify if prices had rises or fell and modificate the strategy to improve the prediction. 168 | 169 | 170 | 171 | 172 | # messenger_d.py 173 | ## Description 174 | Here we have a signal bot trading, that using [telegram](https://python-telegram-bot.readthedocs.io/en/stable/) sends signals found with a [predictor](https://github.com/elbernaderen/machine-learning-signal-finder/blob/main/README.md#amplitudespy) made with Machine Learning, trained with a some crypto-currency historical database, obtained 175 | with the [Binance](https://resilient-quant-trader.medium.com/scraping-crypto-currency-historical-data-from-binance-using-python-9c0e77c04df7) library. 176 | ## Must install 177 | [pandas](https://pandas.pydata.org/), [numpy](https://numpy.org/install/) and [scipy](https://scipy.org/install/) libraries are used to work with data frames and lists. 178 | With [sklearn](https://scikit-learn.org/stable/install.html) we can use the model with the predictor. 179 | 180 | Will use [bina.py](https://github.com/elbernaderen/machine-learning-signal-finder#binapy) to download the actualized data to make the prediction. 181 | 182 | Also need [yaml](https://pypi.org/project/PyYAML/) to save and read the api data in a yml file. 183 | [Binance](https://pypi.org/project/python-binance/) is needed to download the crypto-currency historical data. 184 | ## Usage: 185 | As this bot use Telegram to send messagess, we'll need the user id of the receiver and the API key from the count of telegram that we'll use, and this data will be set in the yml file **telconfig** that is in the ignore folder. 186 | 187 | Also, before calling the program, we must have the sav file of the predictor [model](https://github.com/elbernaderen/machine-learning-signal-finder#amplitudespy) created, because it has to be entered as an argumet in console. 188 | 189 | With the predictor model created, we must be call the program in console as continue: 190 | 191 | ```bash 192 | py messenger_d.py model_p_15_perio_14_in_6_0.05_2022-05-24_17_44 193 | ``` 194 | Where **model_p_15_perio_14_in_6_0.05_2022-05-24_17_44** is the name of the model created. 195 | 196 | Then the program will ask to enter the next variables: 197 | ```bash 198 | Enter the number of candels (X) considered in the model for the prediction: 199 | ``` 200 | The number of candels that will be used to make the prediction, must be equal to the number used in the [model](https://github.com/elbernaderen/machine-learning-signal-finder#amplitudespy) 201 | ```bash 202 | Enter the amount of periods for rsi calculation (14 recomended): 203 | ``` 204 | A period for rsi calculation can be better for a candle interval analysis, and not for other one, so, it can be modificated if want it 205 | 206 | ```bash 207 | Enter how much to increase the mean volume value: 208 | ``` 209 | This is a filter to consider just the candles with a bigger volume than the mean volume of a determined amount of candles 210 | ```bash 211 | Enter the rsi value to consider (30 recomended): 212 | ``` 213 | The RSI value is a indicator for some strategies in crypto-trading, so it also can be modificated as a superior limit (the script can be easily changeable) 214 | 215 | ```bash 216 | Enter the name of the symbol, ex BTCUSDT: 217 | ``` 218 | The symbol of the crypto-currency from which we want the signals. 219 | ```bash 220 | Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m 221 | ``` 222 | This is the interval of time between each analysis of the bot. For example, if we choose 1h, the bot will download an historical data to predict if to buy or not, every hour. 223 | ```bash 224 | Enter the slope to take in reference, (0 recomended): 225 | ``` 226 | The slope of the close value of the candels indicates if the market (in this sequence) is bullish or bearish. 227 | 228 | 229 | 230 | 231 | # messenger_h_rsi.py 232 | ## Description 233 | Here we have a signal bot trading, that using [telegram](https://python-telegram-bot.readthedocs.io/en/stable/) sends signals obtained 234 | from some technical analysis, taking in count the slope of the candels (rising or falling market, this is adjusted to falling market but it can be easily changed), the rsi value and the volume of the last candels. If the strategy set fits with the sequence obtained with the [Binance](https://resilient-quant-trader.medium.com/scraping-crypto-currency-historical-data-from-binance-using-python-9c0e77c04df7) library. 235 | ## Must install 236 | [Pandas](https://pandas.pydata.org/), [numpy](https://numpy.org/install/) and [scipy](https://scipy.org/install/) libraries are used to work with data frames and lists. 237 | 238 | Will use [bina.py](https://github.com/elbernaderen/machine-learning-signal-finder#binapy) to download the actualized data to make the prediction. 239 | Also need [yaml](https://pypi.org/project/PyYAML/) to save and read the api data in a yml file. 240 | ## Usage: 241 | As this bot use Telegram to send messagess, we'll need the user id of the receiver and the API key from the count of telegram that we'll use, and this data will be set in the yml file **telconfig** that is in the ignore folder. 242 | We must be call the program in console as continue: 243 | 244 | ```bash 245 | py messenger_h_rsi.py 246 | ``` 247 | Then the program will ask to enter the next variables: 248 | ```bash 249 | Enter the number of candels (X) considered in the model for the prediction: 250 | ``` 251 | The number of candels that will be used to make the prediction, must be equal to the number used in the [model](https://github.com/elbernaderen/machine-learning-signal-finder#amplitudespy) 252 | ```bash 253 | Enter the amount of periods for rsi calculation (14 recomended): 254 | ``` 255 | A period for rsi calculation can be better for a candle interval analysis, and not for other one, so, it can be modificated if want it 256 | 257 | ```bash 258 | Enter how much to increase the mean volume value: 259 | ``` 260 | This is a filter to consider just the candles with a bigger volume than the mean volume of a determined amount of candles 261 | ```bash 262 | Enter the rsi value to consider (30 recomended): 263 | ``` 264 | The RSI value is a indicator for some strategies in crypto-trading, so it also can be modificated as a superior limit (the script can be easily changeable) 265 | 266 | ```bash 267 | Enter the name of the symbol, ex BTCUSDT: 268 | ``` 269 | The symbol of the crypto-currency from which we want the signals. 270 | ```bash 271 | Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m 272 | ``` 273 | This is the interval of time between each analysis of the bot. For example, if we choose 1h, the bot will download an historical data to predict if to buy or not, every hour. 274 | ```bash 275 | Enter the slope to take in reference, (0 recomended): 276 | ``` 277 | The slope of the close value of the candels indicates if the market (in this sequence) is bullish or bearish. 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | # bina.py 287 | ## Description 288 | This script contains **store_ohlcv** function, that is used to download the Crypto-currency Historical Data. 289 | ## Must install 290 | [Binance](https://resilient-quant-trader.medium.com/scraping-crypto-currency-historical-data-from-binance-using-python-9c0e77c04df7) library to download Crypto-currency Historical Data and [pandas](https://pandas.pydata.org/) to work with data frames. 291 | Also need [yaml](https://pypi.org/project/PyYAML/) to save and read the api data in a yml file. 292 | ## Usage: 293 | To use it, we need to have a Binance account. If you don't have one, can create a account following [this](https://www.binance.com/es/activity/referral-entry?fromActivityPage=true&ref=LIMIT_MYXYAGGF) and by doing that will be my refered and also colaborate with this project. Once that you have an account, you need to generate an API, as [follows](https://resilient-quant-trader.medium.com/scraping-crypto-currency-historical-data-from-binance-using-python-9c0e77c04df7). Then, have to set the API_key and the API_ secret in the config yml file located in the ignore folder. 294 | 295 | # call_bina.py 296 | Interface to download the Crypto-currency Historical Data to use them as base for [amplitudes.py](https://github.com/elbernaderen/machine-learning-signal-finder#amplitudespy) or to make a backtest with [backtest_amplitudes.py](https://github.com/elbernaderen/machine-learning-signal-finder#backtest_amplitudespy). 297 | ## Description 298 | This script calls the function **store_ohlcv** from [bina.py](https://github.com/elbernaderen/machine-learning-signal-finder#binapy), that is used to download the Crypto-currency Historical Data, setting the name of the Crypto-currency in capital letters, name of the file that will be created, year, month and day since when take in count. 299 | ## Usage: 300 | To download a Crypto-currency Historical Data for [amplitudes.py](https://github.com/elbernaderen/machine-learning-signal-finder#amplitudespy) and a Crypto-currency, for example ETHUSDT since a determinated date, must be called the program in console as continue: 301 | 302 | ```bash 303 | py call_bina.py ETHUSDT base 2019 1 1 304 | ``` 305 | To download a Crypto-currency Historical Data for [backtest_amplitudes.py](https://github.com/elbernaderen/machine-learning-signal-finder#backtest_amplitudespy) and a Crypto-currency, for example BTCUSDT since a determinated date, must be called the program in console as continue: 306 | ```bash 307 | py call_bina.py BTCUSDT backtest 2022 3 5 308 | ``` 309 | # References: 310 | * [Scraping Crypto-currency Historical Data from Binance using python](https://resilient-quant-trader.medium.com/scraping-crypto-currency-historical-data-from-binance-using-python-9c0e77c04df7) 311 | * [RSI value](https://programmerclick.com/article/34731200625/) 312 | * [macd with PANDAS](https://www.alpharithms.com/calculate-macd-python-272222/) 313 | # license: 314 | MIT [Bernardo Derendinger](https://github.com/elbernaderen) 315 | 316 | # Disclaimer: 317 | This project is for informational purposes only. You should not construe any such information or other material as legal, tax, investment, financial, or other advice. Nothing contained here constitutes a solicitation, recommendation, endorsement, or offer by me or any third party service provider to buy or sell any securities or other financial instruments in this or in any other jurisdiction in which such solicitation or offer would be unlawful under the securities laws of such jurisdiction. 318 | 319 | If you plan to use real money, USE AT YOUR OWN RISK. 320 | 321 | Under no circumstances will I be held responsible or liable in any way for any claims, damages, losses, expenses, costs, or liabilities whatsoever, including, without limitation, any direct or indirect damages for loss of profits. 322 | -------------------------------------------------------------------------------- /amplitudes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pandas as pd 3 | from tools.tools import name_col, RSI, macd, edit_df,print_results 4 | from sklearn.model_selection import train_test_split 5 | from sklearn.ensemble import RandomForestClassifier 6 | from sklearn.metrics import classification_report 7 | import pickle 8 | import datetime 9 | import numpy as np 10 | from scipy.stats import linregress 11 | 12 | 13 | ###################################################################################################### 14 | # PROGRAM DEFINITIONS 15 | 16 | # B is the rise percent of the candles next to the close of the last candle 17 | # that we use to decide if this sequence anticipate a rise 18 | 19 | B = float( 20 | input( 21 | "Enter the percentage that have to rise the price to consider it as a success: \n" 22 | ) 23 | ) 24 | 25 | # in_ is the number of candles we consider to know if the price rises or fall 26 | 27 | in_ = int( 28 | input( 29 | "Enter the number of candels (Y) to consider in the model for the prediction: \n" 30 | ) 31 | ) 32 | 33 | # Number of candels to consider for the prediction 34 | 35 | rows = int( 36 | input( 37 | "Enter the number of candels (X) consider in the model for the prediction: \n" 38 | ) 39 | ) 40 | 41 | # The relative strength index (RSI) is a momentum indicator 42 | # used in technical analysis that measures the magnitude 43 | # of recent price changes to evaluate overbought or oversold 44 | # conditions in the price of a stock or other asset 45 | # The number of the previous candlesticks (periods) is the main setting 46 | # of the indicator called a period. By default Period = 14; this is the value the author used. 47 | 48 | periods = int( 49 | input("Enter the amount of periods for rsi calculation (14 recomended): \n") 50 | ) 51 | 52 | p = rows + in_ 53 | 54 | # slope_ will be used to compare the candels slope (if it is negative is falling 55 | # and if it is positive is rising) 56 | 57 | # interval is the interval to consider, ex: 1d or 1h or 30m or 15m or 5m for each candlestick 58 | 59 | interval = input("Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m \n") 60 | 61 | # vol_p is the number that will be used to calculate the volume mean 62 | 63 | vol_p = int( 64 | input("Enter how many candels consider to calculate the volume mean: \n" 65 | ) 66 | ) 67 | 68 | # List that'll be filled with the different Crypto-currency used 69 | 70 | names = ["LISTA:"] 71 | 72 | 73 | 74 | 75 | 76 | ###################################################################################################### 77 | 78 | ###################################################################################################### 79 | # MAIN PROGRAM 80 | 81 | # This for will read each historical Crypto-currency data added as an argument 82 | def main(): 83 | 84 | 85 | 86 | count = 0 87 | 88 | # counter is used in the main program to count the number of historical Crypto-currency data 89 | # added as argument 90 | 91 | for nam in range(1, len(sys.argv)): 92 | 93 | # file is a DataFrame created since the csv file with the historical Crypto-currency data 94 | 95 | file = pd.read_csv(f"base/{str(sys.argv[nam])}_{interval}_base.csv") 96 | 97 | # First need to add macd and rsi to the DataFrame 98 | 99 | file = add_rsi_macd(file) 100 | 101 | 102 | # if it is the first historical Crypto-currency data, then: 103 | 104 | if count == 0: 105 | 106 | # The funtion that generates the rows with the sequence of candels 107 | # named verify is called. 108 | # verify requires the DataFrame with the historical Crypto-currency data 109 | # and returns a DataFrame where each row is a sequence of candels determinated (rows), its 110 | # technical indicators (rsi,macd, etc), other parameters like slope and finaly if the consecutives candels (in_) 111 | # have shown an increase or not (buy_decide) of its value in a the determinated percent (B) 112 | 113 | df = verify(file) 114 | 115 | count += 1 116 | 117 | # if it's not the first historical Crypto-currency data, then: 118 | 119 | else: 120 | 121 | df_ = verify(file) 122 | 123 | # After we call verify, we append the new DataFrame of the new historical Crypto-currency data 124 | # to the previusly generated. 125 | 126 | df = pd.concat([df, df_], ignore_index = True) 127 | 128 | # add the name of the historical Crypto-currency in the names list 129 | 130 | names.append( str( sys.argv[nam])) 131 | 132 | # Make a last edition to the DataFrame 133 | 134 | df,list_dummies = edit_df(df, rows) 135 | 136 | df = df.drop(columns = ["date"], axis = 1) 137 | 138 | # get_dummies convert categorical variable into dummy/indicator variables 139 | 140 | X = pd.get_dummies(df[list_dummies]) 141 | 142 | y = df.buy 143 | 144 | X_train, X_test, y_train, y_test = train_test_split( 145 | X, y, test_size=0.3, 146 | random_state=10 147 | ) 148 | 149 | rfc = RandomForestClassifier(n_estimators=100, 150 | max_depth = 90) 151 | 152 | rfc.fit(X_train, y_train) 153 | 154 | predictions = rfc.predict(X_test) 155 | 156 | # To assign a name of the predictor model file we use some variables and the date and hour when it was created 157 | st = str(datetime.datetime.now()) 158 | 159 | st = st.replace(" ", "_") 160 | 161 | st = st.replace(":", "_") 162 | 163 | filename_ = f"rows{rows}_periods_{periods}_in_{in_}_{B}_{st[0:16]}.sav" 164 | 165 | # pickle creates a .sav file with the rfc model so we can use it with the backtesting or the messenger, 166 | # and names it as filename_ 167 | 168 | pickle.dump(rfc, open(filename_, "wb")) 169 | 170 | # This function prints the results 171 | 172 | print_results(names,p,periods,in_,B,rows,vol_p,st,predictions, y_test) 173 | 174 | ###################################################################################################### 175 | 176 | ###################################################################################################### 177 | 178 | 179 | # FUNCTIONS 180 | 181 | def verify(file): 182 | # verify requires a DataFrame and use the values defined before 183 | 184 | # create the index of the df 185 | 186 | index_ = name_col(rows,1) 187 | 188 | dataframe = pd.DataFrame(columns = index_) 189 | 190 | # Generate the x-axis list to analize the slope with linear regression from cero to rows 191 | 192 | X = [x for x in range(0, rows)] 193 | X_long = [x for x in range(0, vol_p)] 194 | 195 | for i in range(p + vol_p, len(file.index)): 196 | 197 | Y_long = [file["close"][t] for t in range(i - vol_p - in_, i - in_)] 198 | Y = [file["close"][t] for t in range(i - p, i - in_)] 199 | 200 | slope_prev_short, intercept, r_value, p_value_2, std_err = linregress(X, Y) 201 | slope_prev, intercept, r_value, p_value_2, std_err = linregress(X_long, Y_long) 202 | 203 | close = np.mean([(file["close"][i - t] - file["close"][i - in_]) / file["close"][i - in_] for t in range(in_ )]) 204 | 205 | # Generate the y-axis list to analize the slope with linear regression from cero to rows candels close values 206 | 207 | 208 | # Here creates a list with the RSI of the candels involved to find the minimun one 209 | # and compare with the stablished before 210 | 211 | # The slope of the candels is calculated 212 | 213 | 214 | # Mean volume calculate 215 | 216 | vol = [file["volume"][i - x] for x in range(in_, in_ + 1 + vol_p)] 217 | vol_prom = np.mean(vol) 218 | 219 | mean_15 = np.mean([file["close"][t] for t in range(i - 15-in_, i-in_)]) 220 | mean_30 = np.mean([file["close"][t] for t in range(i - 30 - in_, i- in_)]) 221 | mean_60 = np.mean([file["close"][t] for t in range(i - 60 - in_, i- in_)]) 222 | mean_100 = np.mean([file["close"][t] for t in range(i - 100 - in_, i- in_)]) 223 | 224 | mean_rel_15_30 = mean_15 / mean_30 225 | mean_rel_30_60 = mean_30 / mean_60 226 | mean_rel_60_100 = mean_60 / mean_100 227 | 228 | # hi is the list with variaton of the candles we consider to know if the price rises or fall 229 | 230 | high_candel = [(file["high"][i - t] - file["close"][i - in_]) / file["close"][i - in_] for t in range(in_ )] 231 | 232 | # If the candles that come after to the first candles considered (rows) 233 | # are bigger than the B established value, buy decide will be B, otherwise it will be 0 234 | 235 | if max(high_candel) > B: 236 | 237 | buy_decide = f"{B}" 238 | 239 | else: 240 | 241 | buy_decide = "0" 242 | 243 | # either cero or B, if the row was considered, is will be added to the DataFrame to then execute the predictor 244 | 245 | row = list() 246 | 247 | for t in range(in_, p + 1): 248 | row = row + [ 249 | file["volume"][i - t] / float(vol_prom), 250 | (file["open"][i - t] - file["close"][i - t]) / file["low"][i - t], 251 | (file["close"][i - t] - file["open"][i - t]) / file["high"][i - t], 252 | (file["high"][i - t]) / (file["low"][i - t]), 253 | file["rsi"][i - t], 254 | file["macd"][i - t], 255 | file["macd_h"][i - t], 256 | file["macd_s"][i - t], 257 | ] 258 | row = row + [file["date"][i - in_], 259 | close, 260 | slope_prev, 261 | slope_prev_short, 262 | mean_rel_15_30, 263 | mean_rel_30_60, 264 | mean_rel_60_100, 265 | buy_decide] 266 | 267 | dataframe.loc[len(dataframe.index)] = row 268 | 269 | # The Nan values must be dropped 270 | dataframe = dataframe.dropna() 271 | return dataframe 272 | 273 | def add_rsi_macd(file_): 274 | # rsi is a list with each candel RSI value 275 | file = file_ 276 | 277 | rsi = RSI(file["close"], periods) 278 | 279 | # Here the RSI list is added to the dataframe file 280 | 281 | file["rsi"] = rsi 282 | 283 | # The macd function receives a DataFrame and add to it the 284 | # macd, macd_h and macd_s columns 285 | 286 | file = macd(file) 287 | 288 | # Here we delete the first 95 columns because the firts RSI values are 289 | # erroneous 290 | 291 | file.drop(index = file.index[:95], axis=0, inplace=True) 292 | 293 | # Reset the index after the first 95 rows are been deleted 294 | 295 | file = file.reset_index() 296 | 297 | return file 298 | 299 | main() 300 | ###################################################################################################### 301 | -------------------------------------------------------------------------------- /backtest_amplitudes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pandas as pd 3 | import datetime 4 | 5 | from tools.tools import name_col, RSI, macd, name_col_2,edit_df 6 | 7 | from sklearn.ensemble import RandomForestClassifier 8 | import pickle 9 | import numpy as np 10 | from scipy.stats import linregress 11 | 12 | ###################################################################################################### 13 | # PROGRAM DEFINITIONS 14 | 15 | # in_ is the number of candles we consider to know if the price rises or fall 16 | 17 | in_ = int( 18 | input( 19 | "Enter the number of candels (Y) considered in the model for the prediction: \n" 20 | ) 21 | ) 22 | # Number of candels to consider for the prediction 23 | 24 | rows = int( 25 | input( 26 | "Enter the number of candels (X) considered in the model for the prediction:: \n" 27 | ) 28 | ) 29 | 30 | # The relative strength index (RSI) is a momentum indicator 31 | # used in technical analysis that measures the magnitude 32 | # of recent price changes to evaluate overbought or oversold 33 | # conditions in the price of a stock or other asset 34 | # The number of the previous candlesticks (periods) is the main setting 35 | # of the indicator called a period. By default Period = 14; this is the value the author used. 36 | 37 | periods = int( 38 | input("Enter the amount of periods for rsi calculation (14 recomended): \n") 39 | ) 40 | 41 | # a will allow us know if the volume of the candels is "a" times bigger than the mean volume 42 | # of the previus candels 43 | 44 | # name of the Crypto-currency to analyze 45 | 46 | name = input("Enter the name of the symbol, ex BTCUSDT:\n") 47 | 48 | # interval is the interval to consider, ex: 1d or 1h or 30m or 15m or 5m for each candlestick 49 | 50 | interval = input("Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m \n") 51 | 52 | # vol_p is the number that will be used to calculate the volume mean 53 | 54 | vol_p = int(input("Enter how many candels to consider if the market is ascending or descending: \n")) 55 | 56 | # p represents all the candles involved 57 | 58 | p = rows + in_ 59 | 60 | 61 | ###################################################################################################### 62 | 63 | ###################################################################################################### 64 | # MAIN PROGRAM 65 | 66 | def main(): 67 | 68 | # file is a DataFrame created since the csv file with the historical Crypto-currency data 69 | 70 | file = pd.read_csv(f"backtest/{name}_30m_backtest.csv") 71 | 72 | # rsi is a list with each candel RSI value 73 | 74 | rsi = RSI(file["close"], periods) 75 | 76 | # Here the RSI list is added to the dataframe file 77 | 78 | file["rsi"] = rsi 79 | 80 | # The macd function receives a DataFrame and add to it the 81 | # macd, macd_h and macd_s columns 82 | 83 | file = macd(file) 84 | 85 | # Here we delete the first 95 columns because the firts RSI values are 86 | # erroneous 87 | 88 | file.drop(index=file.index[:95], axis=0, inplace=True) 89 | 90 | # Reset the index after the first 95 rows are been deleted 91 | 92 | file = file.reset_index() 93 | 94 | # Once we have the DataFrame with the technical indicators, we call backtest_prepare function 95 | # that generates 96 | 97 | df_actual = backtest_prepare(file) 98 | 99 | columns_down = ["date"] 100 | columns_down.extend(name_col_2(in_)) 101 | 102 | 103 | df_down = df_actual[columns_down] 104 | 105 | # if are there more than one model of predictor 106 | 107 | df_actual, list_dummies= edit_df(df_actual, rows, prop = 2) 108 | 109 | df_predict = pd.get_dummies(df_actual[list_dummies]) 110 | 111 | 112 | for na_sav in range(1, len(sys.argv)): 113 | 114 | print(str(sys.argv[na_sav])) 115 | 116 | exc = str(sys.argv[na_sav]) 117 | filename = f"{exc}.sav" 118 | rfc = pickle.load(open(filename, "rb")) 119 | 120 | df_down[f"{exc}"] = pd.to_numeric(rfc.predict(df_predict)) 121 | 122 | st = str(datetime.datetime.now()) 123 | 124 | df_down.to_excel(f"data/{name}_amp_{st[0:13]}.xlsx", sheet_name="NUMBERS") 125 | 126 | ###################################################################################################### 127 | 128 | ###################################################################################################### 129 | # FUNCTIONS 130 | 131 | def backtest_prepare(file): 132 | index_ = name_col(rows,4) 133 | 134 | index_.extend(name_col_2(in_)) 135 | 136 | df = pd.DataFrame(columns=index_) 137 | 138 | X = [x for x in range(0, rows)] 139 | X_long = [x for x in range(0, vol_p)] 140 | 141 | for i in range(p + vol_p, len(file)): 142 | 143 | # Generate the y-axis list to analize the slope with linear regression from cero to rows candels close values 144 | 145 | Y_long = [file["close"][t] for t in range(i - vol_p - in_, i - in_)] 146 | 147 | Y = [file["close"][t] for t in range(i - p, i - in_)] 148 | 149 | # The slope of the candels is calculated 150 | 151 | slope_prev_short, intercept, r_value, p_value_2, std_err = linregress(X, Y) 152 | slope_prev, intercept, r_value, p_value_2, std_err = linregress(X_long, Y_long) 153 | 154 | vol = [file["volume"][i - x] for x in range(in_, in_ + 1 + vol_p)] 155 | 156 | 157 | row = list() 158 | 159 | vol_prom = np.mean(vol) 160 | 161 | mean_15 = np.mean([file["close"][t] for t in range(i - 15-in_, i-in_)]) 162 | mean_30 = np.mean([file["close"][t] for t in range(i - 30 - in_, i- in_)]) 163 | mean_60 = np.mean([file["close"][t] for t in range(i - 60 - in_, i- in_)]) 164 | mean_100 = np.mean([file["close"][t] for t in range(i - 100 - in_, i- in_)]) 165 | 166 | mean_rel_15_30 = mean_15 / mean_30 167 | mean_rel_30_60 = mean_30 / mean_60 168 | mean_rel_60_100 = mean_60 / mean_100 169 | 170 | for t in range(in_, p + 1): 171 | row = row + [ 172 | file["volume"][i - t] / float(vol_prom), 173 | (file["open"][i - t] - file["close"][i - t]) / file["low"][i - t], 174 | (file["close"][i - t] - file["open"][i - t]) / file["high"][i - t], 175 | (file["high"][i - t]) / (file["low"][i - t]), 176 | file["rsi"][i - t], 177 | file["macd"][i - t], 178 | file["macd_h"][i - t], 179 | file["macd_s"][i - t] 180 | ] 181 | 182 | row = row + [file["date"][i - in_], 183 | slope_prev, 184 | slope_prev_short, 185 | mean_rel_15_30, 186 | mean_rel_30_60, 187 | mean_rel_60_100] 188 | 189 | for t in reversed(range(in_)): 190 | row = row + [ 191 | (file["high"][i - t] - file["close"][i - in_]) / file["close"][i - in_] 192 | ] 193 | for t in reversed(range(in_)): 194 | row = row + [ 195 | (file["low"][i - t] - file["close"][i - in_]) / file["close"][i - in_] 196 | ] 197 | df.loc[i] = row 198 | 199 | return df 200 | 201 | main() 202 | ###################################################################################################### -------------------------------------------------------------------------------- /backtest_strategy.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import datetime 3 | from tools.tools import RSI, name_col_2, macd 4 | import numpy as np 5 | from scipy.stats import linregress 6 | 7 | ###################################################################################################### 8 | # PROGRAM DEFINITIONS 9 | 10 | # in_ is the number of candles we consider to know if the price rises or fall 11 | 12 | in_ = int(input("Enter the number of candels (Y) that come after the prediction: \n")) 13 | 14 | # Number of candels to consider for the prediction 15 | 16 | rows = int( 17 | input("Enter the number of candels (X) considered for the technical analysis: \n") 18 | ) 19 | 20 | # The relative strength index (RSI) is a momentum indicator 21 | # used in technical analysis that measures the magnitude 22 | # of recent price changes to evaluate overbought or oversold 23 | # conditions in the price of a stock or other asset 24 | 25 | rsi_compare = int( 26 | input("Enter the rsi value to consider (30 recomended): \n" 27 | ) 28 | ) 29 | 30 | # The number of the previous candlesticks (periods) is the main setting 31 | # of the indicator called a period. By default Period = 14; this is the value the author used. 32 | 33 | periods = int( 34 | input("Enter the amount of periods for rsi calculation (14 recomended): \n") 35 | ) 36 | 37 | # a will allow us know if the volume of the candels is "a" times bigger than the mean volume 38 | # of the previus candels 39 | 40 | a = int( 41 | input("Enter how much to increase the mean volume value: \n" 42 | ) 43 | ) 44 | 45 | # name of the Crypto-currency to analyze 46 | 47 | name = input("Enter the name of the symbol, ex BTCUSDT:\n") 48 | 49 | # temp is the interval to consider, ex: 1d or 1h or 30m or 15m or 5m for each candlestick 50 | 51 | interval = input("Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m \n") 52 | 53 | # slope_ will be used to compare the candels slope (if it is negative is falling 54 | # and if it is positive is rising) 55 | 56 | slope_ = int( 57 | input("Enter the slope to take in reference, (0 recomended):\n" 58 | ) 59 | ) 60 | p = rows + in_ 61 | 62 | ###################################################################################################### 63 | 64 | ###################################################################################################### 65 | # MAIN PROGRAM 66 | 67 | # file is a DataFrame created since the csv file with the historical Crypto-currency data 68 | 69 | file = pd.read_csv(f"prueba/{name}_{interval}_backtest.csv") 70 | 71 | # rsi is a list with each candel RSI value 72 | 73 | rsi = RSI(file["close"], periods) 74 | 75 | # Here the RSI list is added to the dataframe file 76 | 77 | file["rsi"] = rsi 78 | 79 | # The macd function receives a DataFrame and add to it the 80 | # macd, macd_h and macd_s columns 81 | 82 | file = macd(file) 83 | 84 | # Here we delete the first 95 columns because the firts RSI values are 85 | # erroneous 86 | 87 | file.drop(index=file.index[:95], axis=0, inplace=True) 88 | 89 | # Reset the index after the first 95 rows are been deleted 90 | 91 | file = file.reset_index() 92 | 93 | # Once we have the DataFrame with the technical indicators, we call backtest function 94 | 95 | df_actual = backtest(file) 96 | 97 | # Here we generate the list with the columns of the DataFrame that will be download 98 | # name_col_2 is function that generates the names of the columns with the higher and lowest value 99 | # of each candle predicted 100 | 101 | index_ = name_col_2(in_) 102 | 103 | index_.extend(["date", "close", "rsi", "VOLUME", "vale"]) 104 | 105 | df_down = df_actual[index_] 106 | 107 | # To assign a name of the spread sheet file we use some variables and the date and hour when it was created 108 | 109 | st = str(datetime.datetime.now()) 110 | 111 | # A .xlsx file is download in the data folder 112 | 113 | df_down.to_excel(f"data/{name}_stg_{st[0:13]}_{rsi_compare}.xlsx", sheet_name="NUMBERS") 114 | 115 | ###################################################################################################### 116 | 117 | ###################################################################################################### 118 | # FUNCTIONS 119 | 120 | def backtest(file): 121 | # backtest requires a DataFrame and use the values defined before 122 | 123 | # Generate the x-axis list to analize the slope with linear regression from cero to rows 124 | X = [x for x in range(0, rows)] 125 | 126 | # Name the columns for each candle 127 | 128 | index_ = name_col_2(in_) 129 | 130 | # Add name the columns for each final row 131 | 132 | index_.extend(["date", "close", "rsi", "VOLUME", "macd_h", "vale"]) 133 | 134 | # The DataFrame that will be filled with the Backtest is created 135 | 136 | new = pd.DataFrame(columns=index_) 137 | 138 | for i in range(rows + in_, len(file)): 139 | # Generate the y-axis list to analize the slope with linear regression from cero to rows candels close values 140 | Y = [file["close"][t] for t in range(i - p, i - in_)] 141 | slope, intercept, r_value, p_value_2, std_err = linregress(X, Y) 142 | row = list() 143 | 144 | # satisfy is a variable that shows if the sequence pass the filter, 145 | # it will be 1 if the conditions are the required and 0 if they don't 146 | 147 | satisfy = 0 148 | 149 | vol = [file["volume"][i - x] for x in range(in_, rows + in_ + 1)] 150 | vol_prom = np.mean(vol) 151 | 152 | # Here the predicted consecutive candles are added, so we can know 153 | # if the price rises or fall 154 | 155 | for t in reversed(range(in_)): 156 | 157 | row.append( 158 | (file["high"][i - t] - file["close"][i - in_]) / file["close"][i - in_] 159 | ) 160 | 161 | for t in reversed(range(in_)): 162 | 163 | row.append( 164 | (file["low"][i - t] - file["close"][i - in_]) / file["close"][i - in_] 165 | ) 166 | 167 | row.append(file["date"][i - in_]) 168 | row.append(file["close"][i - in_]) 169 | row.append(file["rsi"][i - in_]) 170 | row.append(file["volume"][i - in_]) 171 | row.append(file["macd_h"][i - in_]) 172 | 173 | # Here is the filter to asign a value to satisfy 174 | 175 | if ( 176 | slope < slope_ 177 | and ( 178 | file["volume"][i - in_] > vol_prom * a 179 | or file["volume"][i - in_ - 1] > vol_prom * a 180 | ) 181 | and ( 182 | file["rsi"][i - in_] < rsi_compare 183 | or file["rsi"][i - in_ - 1] < rsi_compare 184 | or file["rsi"][i - in_ - 2] < rsi_compare 185 | ) 186 | ): 187 | satisfy = 1 188 | row.append(satisfy) 189 | 190 | new.loc[i] = row 191 | return new 192 | 193 | ###################################################################################################### -------------------------------------------------------------------------------- /bina/__init__.py: -------------------------------------------------------------------------------- 1 | from bina.bina import * 2 | -------------------------------------------------------------------------------- /bina/bina.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from binance import Client 3 | import pandas as pd 4 | import yaml 5 | import datetime 6 | 7 | # import config 8 | config = yaml.load(open('ignore/config.yml'), Loader=yaml.FullLoader) 9 | 10 | # create client 11 | try: 12 | client = Client(config['api_key'], config['api_secret']) 13 | except ConnectionError: 14 | print("check your internet connection\n") 15 | client = Client(config['api_key'], config['api_secret']) 16 | 17 | 18 | def store_ohlcv(symbol = "LINKUSDT", interval='1h', start_date=datetime.datetime(2018,8,18), name=""): 19 | # import ohlcv from binance starting from date 'start_date', that has to be in a string format of the timestamp in ms 20 | start_str = str(1000*start_date.timestamp()) 21 | klines = client.get_historical_klines_generator(symbol, interval, start_str) 22 | # create the DataFramePY 23 | df = pd.DataFrame(klines, columns=['time', 'open', 'high', 'low', 'close', 'volume', 'end_time', 'quote_volume', 'nbs_trades', 'buy_base_volume', 'buy_quote_volume', 'ignore']) 24 | # add data 25 | df.loc[:,'date'] = pd.to_datetime(df.time, unit='ms') 26 | # remove the useless column and the last row as it is the current candle, therefore is not completed 27 | df = df.drop('ignore', axis=1).iloc[:-1] 28 | # store data as a csv file 29 | df.to_csv(f"{name}/{symbol}_{interval}_{name}.csv") 30 | 31 | 32 | -------------------------------------------------------------------------------- /call_bina.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from bina.bina import store_ohlcv 3 | import datetime 4 | import time 5 | 6 | if len(sys.argv) > 0: 7 | name = sys.argv[1] 8 | file_name = sys.argv[2] 9 | year = int(sys.argv[3]) 10 | month = int(sys.argv[4]) 11 | day = int(sys.argv[5]) 12 | interval = sys.argv[6] 13 | 14 | start_date = datetime.datetime(year, month, day) 15 | kk = store_ohlcv(symbol = name, 16 | interval = interval, 17 | start_date = start_date, 18 | name = file_name) 19 | print(name) 20 | -------------------------------------------------------------------------------- /data/ADAUSDT2022-06-01 21.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbernaderen/machine-learning-signal-finder/38c934a468254421bb7f98f4d8f9722c41cb9f46/data/ADAUSDT2022-06-01 21.xlsx -------------------------------------------------------------------------------- /ignore/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbernaderen/machine-learning-signal-finder/38c934a468254421bb7f98f4d8f9722c41cb9f46/ignore/.gitignore -------------------------------------------------------------------------------- /ignore/config.yml: -------------------------------------------------------------------------------- 1 | api_key: API BINANCE 2 | api_secret: PASSWORD 3 | -------------------------------------------------------------------------------- /ignore/telconfig.yml: -------------------------------------------------------------------------------- 1 | api_key : 'telegram id' 2 | user_id : 'password' 3 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bernardo Derendinger 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 | -------------------------------------------------------------------------------- /messenger_d.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pandas as pd 3 | import datetime 4 | import time 5 | from bina.bina import store_ohlcv 6 | import pickle 7 | import telegram 8 | from tools.tools import RSI, name_col, macd 9 | import numpy as np 10 | from scipy.stats import linregress 11 | import yaml 12 | 13 | ###################################################################################################### 14 | # PROGRAM DEFINITIONS 15 | 16 | # Telegram user id and api_key 17 | 18 | config = yaml.load(open("ignore/telconfig.yml"), Loader=yaml.FullLoader) 19 | 20 | api_key = config["api_key"] 21 | user_id = config["user_id"] 22 | 23 | bot = telegram.Bot(token=api_key) 24 | 25 | # Number of candels to consider for the prediction 26 | 27 | rows = int( 28 | input( 29 | "Enter the number of candels (X) considered in the model for the prediction: \n" 30 | ) 31 | ) 32 | 33 | # The relative strength index (RSI) is a momentum indicator 34 | # used in technical analysis that measures the magnitude 35 | # of recent price changes to evaluate overbought or oversold 36 | # conditions in the price of a stock or other asset 37 | 38 | rsi_compare = int( 39 | input("Enter the rsi value to consider (30 recomended): \n" 40 | ) 41 | ) 42 | 43 | # The number of the previous candlesticks (periods) is the main setting 44 | # of the indicator called a period. By default Period = 14; this is the value the author used. 45 | 46 | periods = int( 47 | input("Enter the amount of periods for rsi calculation (14 recomended): \n") 48 | ) 49 | 50 | # a will allow us know if the volume of the candels is "a" times bigger than the mean volume 51 | # of the previus candels 52 | 53 | a = int( 54 | input("Enter how much to increase the mean volume value: \n" 55 | ) 56 | ) 57 | 58 | # name of the Crypto-currency to analyze 59 | 60 | name = input("Enter the name of the symbol, ex BTCUSDT:\n") 61 | 62 | # interval is the interval to consider, ex: 1d or 1h or 30m or 15m or 5m for each candlestick 63 | 64 | interval = input("Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m \n") 65 | 66 | # slope_ will be used to compare the candels slope (if it is negative is falling 67 | # and if it is positive is rising) 68 | 69 | slope_ = int( 70 | input("Enter the slope to take in reference, (0 recomended):\n" 71 | ) 72 | ) 73 | 74 | # The time interval must be seconds, so here we convert hour, day or minutes in seconds 75 | 76 | if "d" in interval: 77 | inter_ = 3600 * 24 78 | hours = 24 * 150 79 | elif "h" in interval: 80 | inter_ = 3600 81 | hours = 150 82 | else: 83 | inter_ = 60 * int(interval.replace("m", "")) 84 | hours = 150 85 | 86 | # interval_ will save the interval value, and inter_ will be changing in the while loop. 87 | 88 | interval_ = inter_ 89 | 90 | ###################################################################################################### 91 | 92 | ###################################################################################################### 93 | # MAIN PROGRAM 94 | 95 | # First we open the .sav file with the model generated with amplitudes 96 | # which name was entered as an argument, using pickle 97 | 98 | exc_1 = str(sys.argv[1]) 99 | filename_1 = f"{exc_1}.sav" 100 | rfc_1 = pickle.load(open(filename_1, "rb")) 101 | 102 | def main(): 103 | 104 | # Generate the x-axis list to analize the slope with linear regression from cero to rows 105 | 106 | X = [x for x in range(0, rows)] 107 | 108 | while True: 109 | # The signal finder is activated, and it will keep working 110 | # according to the interval choosed 111 | 112 | inter_ = interval_ 113 | 114 | # To make an analysis and determine if we have to buy or just wait, 115 | # we need to download several seconds before the last candle, 116 | # so the variable tt will be the start date of the data to download 117 | 118 | 119 | hour = datetime.timedelta(hours = hours) 120 | hour_ = datetime.datetime.utcnow() 121 | tt = hour_ - hour 122 | 123 | # If at the moment of download the historical data, 124 | # the internet is gone, the program will fail. 125 | # So we add an Exception 126 | 127 | try: 128 | kk = store_ohlcv( 129 | symbol = name, 130 | interval = interval, 131 | start_date = tt, 132 | name = "messenger" 133 | ) 134 | except ConnectionError: 135 | 136 | # If the internet is gone, we'll wait 60 seconds, and'll try again 137 | 138 | time.sleep(60) 139 | 140 | inter_ -= 60 141 | 142 | print("check your internet connection\n") 143 | 144 | kk = store_ohlcv( 145 | symbol = name, 146 | interval = interval, 147 | start_date = tt, 148 | name = "messenger" 149 | ) 150 | 151 | # wait to download the csv file 152 | 153 | time.sleep(30) 154 | inter_ -= 30 155 | # Every time that pass the interval of established time, 156 | # the csv file with the data is download in the "messenger" directory, 157 | # and this last file will replace the previous one and a DataFrame will be created 158 | 159 | file = pd.read_csv(f"messenger/{name}_{interval}_messenger.csv") 160 | 161 | # Then, the df must be edited and the RSI and macd columns must be appended 162 | 163 | # rsi is a list with each candel RSI value 164 | 165 | rsi = RSI(file["close"], periods) 166 | 167 | # Here the RSI list is added to the dataframe file 168 | 169 | file["rsi"] = rsi 170 | 171 | # The macd function receives a DataFrame and add to it the 172 | # macd, macd_h and macd_s columns 173 | 174 | file = macd(file) 175 | 176 | # Here we delete the first 95 columns because the firts RSI values are 177 | # erroneous 178 | 179 | file.drop(index=file.index[:95], axis=0, inplace=True) 180 | 181 | # Reset the index after the first 95 rows are been deleted 182 | 183 | file = file.reset_index() 184 | 185 | # Generate a list to analize the slope with linear regression whit the close values 186 | 187 | Y = [file["close"][t] for t in range(len(file) - rows, len(file))] 188 | 189 | # The slope of the candels is calculated 190 | 191 | slope, intercept, r_value, p_value_2, std_err = linregress(X, Y) 192 | 193 | # generate a list with every volume value of the rows 194 | 195 | vol = [file["volume"][x] for x in range(len(file) - rows, len(file))] 196 | 197 | # calculate the mean value of the volume list 198 | 199 | vol_prom = np.mean(vol) 200 | 201 | # Here we call the function make_prediction that receive a DataFrame 202 | # and return a one row df with the technical indicators of n = rows candles. 203 | 204 | new = make_prediction(file) 205 | 206 | # Here we calculate the prediction using the model rfc_1 and the one row DataFrame new, 207 | # where the columns date and close are dropped 208 | 209 | response_new = rfc_1.predict(new.drop(columns=["date", "close"])) 210 | 211 | # This print in console the response of the model, 212 | # that can be 0 or the value predicted with amplitudes.py (buy_decide) 213 | 214 | print(response_new, new["date"], " ", new["close"]) 215 | 216 | # 217 | 218 | coef_1 = float(response_new) 219 | 220 | # Here we apply the filter diffined before. 221 | # Then, if the data fit the filter and 222 | # the response of the predictor is bigger than 0, 223 | # the bot sends a message by Telegram with: 224 | # PRICE 225 | # TAKEPROFIT 226 | # STOPLOSS 227 | # ZERO LOSS PRICE 228 | # DATE 229 | 230 | if ( 231 | coef_1 > 0 232 | and slope < -0.01 233 | and ( 234 | file["volume"][len(file) - 1] > vol_prom * a 235 | or file["volume"][len(file) - 2] > vol_prom * a 236 | ) 237 | and file["rsi"][len(file) - 1] < rsi_compare 238 | ): 239 | coef = coef_1 240 | t = f'{name} \n BUY: {float(new["close"])} \n TAKEPROFIT: {float(new["close"])*(1+coef)} \n STOPLOSS: {float(new["close"])*(1-.005)} \n cero: {float(new["close"])*(1+0.0015)} \n {datetime.datetime.now()} ' 241 | print(t) 242 | bot.send_message(chat_id=user_id, text=t) 243 | 244 | # If the price or the values don't fit the filter, 245 | # a message is sent through Telegram to wait until the next analysis 246 | else: 247 | t = f"{name} \n don't buy, wait {inter_}" 248 | print(t) 249 | 250 | time.sleep(inter_) 251 | 252 | ###################################################################################################### 253 | 254 | ###################################################################################################### 255 | # FUNCTIONS 256 | 257 | def make_prediction(file): 258 | 259 | # this function generates a row with the candels choosen, so the predictor can make the prediction 260 | 261 | index_ = name_col(rows) 262 | index_.append("date") 263 | index_.append("close") 264 | new = pd.DataFrame(columns = index_) 265 | i = len(file) - 1 266 | row = list() 267 | for t in range(rows + 1): 268 | # volume relation normalized with the first element of the row 269 | row.append(file["volume"][i - t] / float(file["volume"][i - rows])) 270 | # value amplitude relation 1 271 | row.append( 272 | (file["open"][i - t] - file["close"][i - rows]) / file["low"][i - rows] 273 | ) 274 | # value amplitude relation 2 275 | row.append( 276 | (file["close"][i - t] - file["open"][i - rows]) / file["high"][i - rows] 277 | ) 278 | # value amplitude relation 3 279 | row.append((file["high"][i - t]) / (file["low"][i - rows])) 280 | row.append(file["rsi"][i - t]) 281 | row.append(file["macd"][i - t]) 282 | row.append(file["macd_h"][i - t]) 283 | row.append(file["macd_s"][i - t]) 284 | row.extend([file["date"][i], file["close"][i]]) 285 | new.loc[1] = row 286 | return new 287 | 288 | main() 289 | 290 | ###################################################################################################### 291 | -------------------------------------------------------------------------------- /messenger_h_rsi.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pandas as pd 3 | import datetime 4 | import time 5 | from bina.bina import store_ohlcv 6 | import telegram 7 | from tools.tools import RSI 8 | import numpy as np 9 | from scipy.stats import linregress 10 | import yaml 11 | 12 | ###################################################################################################### 13 | # PROGRAM DEFINITIONS 14 | 15 | # Telegram user id and api_key 16 | 17 | config = yaml.load(open("ignore/telconfig.yml"), Loader=yaml.FullLoader) 18 | 19 | # Telegram configuration 20 | 21 | api_key = config["api_key"] 22 | user_id = config["user_id"] 23 | bot = telegram.Bot(token=api_key) 24 | 25 | # Number of candels to consider for the prediction 26 | 27 | rows = int( 28 | input("Enter the number of candels to analize: \n" 29 | ) 30 | ) 31 | 32 | # The relative strength index (RSI) is a momentum indicator 33 | # used in technical analysis that measures the magnitude 34 | # of recent price changes to evaluate overbought or oversold 35 | # conditions in the price of a stock or other asset 36 | 37 | rsi_compare = int( 38 | input("Enter the rsi value to consider (30 recomended): \n" 39 | ) 40 | ) 41 | 42 | # The number of the previous candlesticks (periods) is the main setting 43 | # of the indicator called a period. By default Period = 14; this is the value the author used. 44 | 45 | periods = int( 46 | input("Enter the amount of periods for rsi calculation (14 recomended): \n") 47 | ) 48 | 49 | # a will allow us know if the volume of the candels is "a" times bigger than the mean volume 50 | # of the previus candels 51 | 52 | a = int( 53 | input("Enter how much to increase the mean volume value: \n" 54 | ) 55 | ) 56 | 57 | # name of the Crypto-currency to analyze 58 | 59 | name = input("Enter the name of the symbol, ex BTCUSDT:\n") 60 | 61 | # interval is the interval to consider, ex: 1d or 1h or 30m or 15m or 5m for each candlestick 62 | 63 | interval = input("Enter the interval to consider, ex: 1d or 1h or 30m or 15m or 5m \n") 64 | 65 | # slope_ will be used to compare the candels slope (if it is negative is falling 66 | # and if it is positive is rising) 67 | 68 | slope_ = int( 69 | input("Enter the slope to take in reference, (0 recomended):\n" 70 | ) 71 | ) 72 | 73 | # The time interval must be seconds, so here we convert hour, day or minutes in seconds 74 | 75 | if "d" in interval: 76 | inter_ = 3600 * 24 77 | hours = 24 * 150 78 | elif "h" in interval: 79 | inter_ = 3600 80 | hours = 150 81 | else: 82 | inter_ = 60 * int(interval.replace("m", "")) 83 | hours = 150 84 | 85 | interval_ = inter_ 86 | 87 | ###################################################################################################### 88 | 89 | ###################################################################################################### 90 | # MAIN PROGRAM 91 | 92 | # Generate the x-axis list to analize the slope with linear regression from cero to rows 93 | X = [x for x in range(0, rows)] 94 | 95 | while True: 96 | # The signal finder is activated, and it will keep working 97 | # according to the interval choosed 98 | 99 | inter_ = interval_ 100 | 101 | # To make an analysis and determine if we have to buy or just wait, 102 | # we need to download several seconds before the last candle, 103 | # so the variable tt will be the start date of the data to download 104 | 105 | hour = datetime.timedelta(hours = hours) 106 | hour_ = datetime.datetime.utcnow() 107 | tt = hour_ - hour 108 | 109 | # If at the moment of download the historical data, 110 | # the internet is gone, the program will fail. 111 | # So we add an Exception 112 | 113 | try: 114 | kk = store_ohlcv( 115 | symbol=name, 116 | interval=interval, 117 | start_date=tt, 118 | name = "messenger" 119 | ) 120 | 121 | except ConnectionError: 122 | # If the internet is gone, we'll wait 60 seconds, and'll try again 123 | 124 | time.sleep(60) 125 | 126 | inter_ -= 60 127 | 128 | print("check your internet connection\n") 129 | 130 | kk = store_ohlcv( 131 | symbol = name, 132 | interval = interval, 133 | start_date = tt, 134 | name = "messenger" 135 | ) 136 | # wait to download the csv file 137 | time.sleep(30) 138 | inter_ -= 30 139 | 140 | # Every time that pass the interval of established time, 141 | # the csv file with the data is download in the "messenger" directory, 142 | # and this last file will replace the previous one and a DataFrame will be created 143 | 144 | file = pd.read_csv(f"messenger/{name}_{interval}_messenger.csv") 145 | 146 | # Then, the df must be edited and the RSI column must be appended 147 | 148 | # Generate a list to analize the slope with linear regression whit the close values 149 | 150 | Y = [file["close"][t] for t in range(len(file) - rows, len(file))] 151 | 152 | # The slope of the candels is calculated 153 | 154 | slope, intercept, r_value, p_value_2, std_err = linregress(X, Y) 155 | 156 | # rsi is a list with each candel RSI value 157 | 158 | rsi = RSI(file["close"], periods) 159 | 160 | # Here the RSI list is added to the dataframe file 161 | 162 | file["rsi"] = rsi 163 | 164 | # Here we delete the first 95 columns because the firts RSI values are 165 | # erroneous 166 | 167 | file.drop(index=file.index[:95], axis=0, inplace=True) 168 | 169 | # Reset the index after the first 95 rows are been deleted 170 | 171 | file = file.reset_index() 172 | 173 | K = len(file) 174 | 175 | coef = float(file["close"][len(file) - 1]) 176 | 177 | # Here a list of all the volume values is set, so we can calculate the mean volume value 178 | 179 | vol = [file["volume"][x] for x in range(K - rows, K)] 180 | vol_prom = np.mean(vol) 181 | 182 | # Here we apply the filter diffined before. 183 | # Then, if the data fit the filter 184 | # the bot sends a message by Telegram with: 185 | # PRICE 186 | # TAKEPROFIT 187 | # STOPLOSS 188 | # ZERO LOSS PRICE 189 | # DATE 190 | 191 | if ( 192 | (file["volume"][K - 2] > vol_prom * a 193 | or file["volume"][K - 1] > vol_prom * a) 194 | and slope < slope_ 195 | and file["rsi"][K - 1] < rsi_compare 196 | ): 197 | t = f"{name} \n BUY: {coef} \n STOPLOSS: {coef*(1-0.01)} \n \n cero: {coef*(1+0.0015)} \n {datetime.datetime.now()} " 198 | print(t) 199 | bot.send_message(chat_id = user_id, text = t) 200 | # If the price or the values don't fit the filter, 201 | # a message is sent through Telegram to wait until the next analysis 202 | else: 203 | t = f"{name} \n don't buy, wait {interval}" 204 | print(t) 205 | 206 | time.sleep(inter_) 207 | 208 | ###################################################################################################### -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- 1 | from tools.tools import name_col 2 | from tools.tools import RSI 3 | from tools.tools import macd -------------------------------------------------------------------------------- /tools/tools.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from datetime import datetime 3 | import pandas as pd 4 | from sklearn.metrics import classification_report 5 | 6 | ###################################################################################################### 7 | # name generator 8 | 9 | def name_col(rows, k = 2): 10 | 11 | ind_row = rows + 1 12 | index_ = list() 13 | for cin in range(ind_row): 14 | 15 | index_.append(f"volume_{cin}") 16 | index_.append(f"mart_{cin}") 17 | index_.append(f"mart_inv_{cin}") 18 | index_.append(f"ampl_2{cin}") 19 | index_.append(f"rsi_{cin}") 20 | index_.append(f"macd_{cin}") 21 | index_.append(f"macd_h{cin}") 22 | index_.append(f"macd_s{cin}") 23 | 24 | if k == 2: 25 | 26 | return index_ 27 | 28 | if k == 1: 29 | 30 | index_.append("date") 31 | index_.append("close") 32 | index_.append("slope_prev") 33 | index_.append("slope_prev_short") 34 | index_.append("mean_rel_15_30") 35 | index_.append("mean_rel_30_60") 36 | index_.append("mean_rel_60_100") 37 | index_.append("buy") 38 | 39 | return index_ 40 | 41 | if k == 3: 42 | index_.append("slope_prev") 43 | index_.append("slope_prev_short") 44 | index_.append("mean_rel_15_30") 45 | index_.append("mean_rel_30_60") 46 | index_.append("mean_rel_60_100") 47 | 48 | return index_ 49 | 50 | if k == 4: 51 | index_.append("date") 52 | index_.append("slope_prev") 53 | index_.append("slope_prev_short") 54 | index_.append("mean_rel_15_30") 55 | index_.append("mean_rel_30_60") 56 | index_.append("mean_rel_60_100") 57 | 58 | return index_ 59 | ###################################################################################################### 60 | 61 | 62 | ###################################################################################################### 63 | def RSI(t, periods=10): 64 | 65 | # extracted from https://programmerclick.com/article/34731200625/ 66 | length = len(t) 67 | rsies = [np.nan]*length 68 | 69 | # La longitud de los datos no excede el período y no se puede calcular; 70 | 71 | if length <= periods: 72 | return rsies 73 | 74 | #Utilizado para cálculos rápidos; 75 | 76 | up_avg = 0 77 | down_avg = 0 78 | 79 | # Primero calcule el primer RSI, use los períodos anteriores + 1 dato para formar una secuencia de períodos; 80 | first_t = t[:periods+1] 81 | for i in range(1, len(first_t)): 82 | #Precio aumentado; 83 | if first_t[i] >= first_t[i-1]: 84 | up_avg += first_t[i] - first_t[i-1] 85 | #caída de los precios; 86 | else: 87 | down_avg += first_t[i-1] - first_t[i] 88 | up_avg = up_avg / periods 89 | down_avg = down_avg / periods 90 | rs = up_avg / down_avg 91 | rsies[periods] = 100 - 100/(1+rs) 92 | 93 | # Lo siguiente utilizará cálculo rápido; 94 | for j in range(periods+1, length): 95 | up = 0 96 | down = 0 97 | if t[j] >= t[j-1]: 98 | up = t[j] - t[j-1] 99 | down = 0 100 | else: 101 | up = 0 102 | down = t[j-1] - t[j] 103 | # Fórmula de cálculo similar a la media móvil; 104 | up_avg = (up_avg*(periods - 1) + up)/periods 105 | down_avg = (down_avg*(periods - 1) + down)/periods 106 | rs = up_avg/down_avg 107 | rsies[j] = 100 - 100/(1+rs) 108 | return rsies 109 | 110 | ###################################################################################################### 111 | 112 | ###################################################################################################### 113 | 114 | def macd(file): 115 | 116 | # A DataFrame is received 117 | 118 | df=file 119 | k = df['close'].ewm(span=12, adjust=False, min_periods=12).mean() 120 | 121 | # Get the 12-day EMA of the closing price 122 | 123 | d = df['close'].ewm(span=26, adjust=False, min_periods=26).mean() 124 | 125 | # Subtract the 26-day EMA from the 12-Day EMA to get the MACD 126 | 127 | macd = k - d 128 | 129 | # Get the 9-Day EMA of the MACD for the Trigger line 130 | 131 | macd_s = macd.ewm(span=9, adjust=False, min_periods=9).mean() 132 | 133 | # Calculate the difference between the MACD - Trigger for the Convergence/Divergence value 134 | 135 | macd_h = macd - macd_s 136 | 137 | # Add all of our new values for the MACD to the dataframe 138 | 139 | df['macd'] = df.index.map(macd) 140 | df['macd_h'] = df.index.map(macd_h) 141 | df['macd_s'] = df.index.map(macd_s) 142 | 143 | return df 144 | 145 | ###################################################################################################### 146 | 147 | ###################################################################################################### 148 | 149 | # name generator for the backtest 150 | 151 | def name_col_2(in_): 152 | ind_row = in_ 153 | index_ = list() 154 | for cin in range(ind_row): 155 | index_.append(f"salida_{cin}") 156 | for cin in range(ind_row): 157 | index_.append(f"salidam_{cin}") 158 | return index_ 159 | 160 | ###################################################################################################### 161 | 162 | ###################################################################################################### 163 | 164 | # hour and day converter through the date 165 | 166 | def weekday_convert(row): 167 | date_time_obj = datetime. strptime(row[0:20], '%Y-%m-%d %H:%M:%S') 168 | return date_time_obj.weekday() 169 | 170 | 171 | def hour_convert(row): 172 | date_time_obj = datetime. strptime(row[0:20], '%Y-%m-%d %H:%M:%S') 173 | return date_time_obj.hour 174 | 175 | 176 | ###################################################################################################### 177 | 178 | ###################################################################################################### 179 | # cut_labels make a continuos variable to a categorical one 180 | 181 | def cut_labels(df,columns_cut_dummi): 182 | 183 | list_dummies = list() 184 | 185 | for i in columns_cut_dummi: 186 | 187 | sep = (df[i].max() - df[i].min()) / 11 188 | 189 | list_dummies.append(f"{i}_cut") 190 | 191 | df[f"{i}_cut"] = pd.cut(x=df[i], 192 | bins=[df[i].min() + k * sep for k in range(0,12) ], 193 | labels=[-5,-4,-3,-2,-1,0, 1, 2, 3,4,5] 194 | ) 195 | 196 | list_dummies.extend(["hour","day"]) 197 | 198 | return df,list_dummies 199 | 200 | ###################################################################################################### 201 | 202 | ###################################################################################################### 203 | def edit_df(df, rows,prop = 1): 204 | 205 | # Take off the values outliers, or that ones that aren't frequent 206 | # this option is used for amplitudes.py 207 | if prop == 1: 208 | 209 | df = df[ df["close"] < 0.1] 210 | 211 | df = df[ df["close"] > -0.1] 212 | 213 | # columns to use to run the rfc 214 | 215 | features = name_col(rows, 3) 216 | 217 | # cut_labels make a continuos variable to a categorical one 218 | 219 | df,list_dummies= cut_labels(df,features) 220 | 221 | # add the day and hour columns 222 | 223 | df["day"] = df["date"].apply(weekday_convert) 224 | 225 | df["hour"] = df["date"].apply(hour_convert) 226 | 227 | return df,list_dummies 228 | 229 | ###################################################################################################### 230 | 231 | ###################################################################################################### 232 | def print_results(names,p,periods,in_,B,rows,vol_p,st,predictions, y_test): 233 | 234 | # RESULTS 235 | # name of the .sav file 236 | 237 | print(f"model_p_{p}_perio_{periods}_in_{in_}_{B}_{st[0:16]}.sav") 238 | 239 | # name is the list of the historical Crypto-currency data used 240 | 241 | print(names) 242 | 243 | # The classification report allows us to compare the precission, aquracy of different variables 244 | # used like B, in_, a also the different historical Crypto-currency data. 245 | 246 | print(classification_report(y_test, predictions)) 247 | 248 | # The next information is useful to know which variables were used to obtain 249 | # what the classification report indicates 250 | 251 | print(f"periods rsi: {periods}") 252 | print(f"Y candels: {in_}") 253 | print(f"rows: {rows}") 254 | print(f"Candels used to calculate volume: {vol_p}") --------------------------------------------------------------------------------