Repository: brianleect/binance-pump-alerts
Branch: master
Commit: f867f31b06f0
Files: 18
Total size: 47.1 KB
Directory structure:
gitextract_g_svvtu5/
├── .dockerignore
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── Dockerfile
├── README.md
├── alerter/
│ ├── BinancePumpAndDumpAlerter.py
│ └── __init__.py
├── config.yml
├── docker-compose.yml
├── entrypoint.sh
├── pumpAlerts.py
├── reporter/
│ ├── ReportGenerator.py
│ └── __init__.py
├── requirements.txt
├── sender/
│ ├── TelegramSender.py
│ └── __init__.py
└── utils/
├── ConversionUtils.py
└── __init__.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
.env
.idea
.gitignore
venv
config.dev.yml
docker-compose.yml
Readme.md
================================================
FILE: .github/workflows/main.yml
================================================
# This is a basic workflow to help you get started with Actions
name: Publish Docker image
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ github.actor }}/binance-pump-alerts
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .gitignore
================================================
config.dev.yml
# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm+all,intellij+all,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm+all,intellij+all,visualstudiocode
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### PyCharm+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# AWS User-specific
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### PyCharm+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
# Sonarlint plugin
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
================================================
FILE: Dockerfile
================================================
FROM python:3.9-slim-buster as base
# Setup env
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONFAULTHANDLER 1
ENV PATH=/home/bpauser/.local/bin:$PATH
ENV FT_APP_ENV="docker"
# Prepare environment
RUN mkdir /binance-pump-alerts \
&& apt-get update \
&& apt-get -y install sudo \
&& apt-get clean \
&& useradd -u 1000 -G sudo -U -m bpauser \
&& chown bpauser:bpauser /binance-pump-alerts \
# Allow sudoers
&& echo "bpauser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers
WORKDIR /binance-pump-alerts
# Install dependencies
FROM base as python-deps
RUN apt-get update \
&& apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \
&& apt-get clean \
&& pip install --upgrade pip
# Install dependencies
COPY --chown=bpauser:bpauser requirements.txt /binance-pump-alerts/
USER bpauser
RUN pip install --user --no-cache-dir -r requirements.txt
# Copy dependencies to runtime-image
FROM base as runtime-image
COPY --from=python-deps /usr/local/lib /usr/local/lib
ENV LD_LIBRARY_PATH /usr/local/lib
COPY --from=python-deps --chown=bpauser:bpauser /home/bpauser/.local /home/bpauser/.local
USER bpauser
COPY --chown=bpauser:bpauser . /binance-pump-alerts/
RUN chmod a+x entrypoint.sh
ENTRYPOINT ["./entrypoint.sh", "python", "pumpAlerts.py"]
================================================
FILE: README.md
================================================
# Binance Pump Alerts
BPA is a simple application which gets the price data from Binance Spot or Futures API and sends Telegram messages based on parameters set used to detect pumps and dumps on the Binance Exchange.
[Demo Telegram Channel](https://t.me/binance_pump_alerts) hosted on AWS ec2 running the 'Base Stable Version' release 24/7.

## Manual Setup
1. On the command-line, run the command `pip install -r requirements.txt` while located at folder with code.
1. Create a new telegram bot token from [@botfather](https://t.me/BotFather).
1. Get telegram `chat_id` from [@get_id_bot](https://telegram.me/get_id_bot).
- Alternatively, a `channel_id` can be used as well.
1. Add pairs to watch into the watchlist or to ignore in blacklist or leave it empty to monitor all tickers on Binance.
1. Run the script with the command `python pumpAlerts.py`.
## Docker Setup
1. Use environment variables in the `docker-compse.yml` file to provide your config.
- See `entrypoint.sh` for environment variable names and the config possibilities.
- You can also use a `.env` file during development.
- If changing the config parameters, you have to make sure that search and replace will place the right parameter in the `config.yml`
- Emojis are more tricky therefore defining it with some tricks e.g. `PUMP_EMOJI="! \"\\\\U0001F4B9\""`
1. On the command line run `docker-compose up -d --build` to create and run the docker image/container.
## Configuration
### Mandatory Params
1. `telegramToken`: The token obtained from[@botfather](https://t.me/BotFather).
2. `telegramChatId`: The bot will send the messages to this `chat_id`. It can be a group or channel as well.
## Main Customizable Params
1. `chartIntervals`: Can be modified to consider other timeframes, follow the format of 's' for seconds, 'm' for minutes, 'h' for hours.
1. `outlierIntervals`: (0.01 -> 1% , 0.1 -> 10%), modify accordingly based on needs. Avoid setting it too low to avoid noise.
1. `extractInterval`: Default is `1s`, Interval at which we retrieve the price information from Binance.
1. `pairsOfInterest`: Default is _USDT_. Other options include BUSD, BTC, ETH etc.
1. `topReportIntervals`: Default is `1h`,`3h`and `6h` Intervals for top pump and dump reports to be sent, ensure it is in chartIntervals + outlierIntervals as well.
### Optional features to enable
1. `watchlist`: Default if left empty it'll look at ALL symbols after filtering by pairs of interest. If pairs are added to the watchlist, the application will _only TRACK the pairs specified_. pairsOfInterest will be ignored.
1. `blacklist`: Default if left empty it'll look at ALL symbols after filtering by pairs of interest. If pairs are added to the blacklist, the application will ignore pairs specified. pairs of Interest will NOT be impacted.
1. `dumpEnabled`: If `True`, the application will alert on dumps as well.
#### Top Pump & Dump Params
1. `topPumpEnabled`: If `True`, the application will send the Top X pumps at the defined interval.
1. `topDumpEnabled`: If `True`, the application will send the Top X dumps at the defined interval.
- Together with pump information, if enabled.
1. `noOfReportedCoins`: Top X amount of coins shown, adjust to show more or less within the timeframe.
1. `telegramAlertChatId`: Insert the alert chat_id for top pump dump alert, if left at `0`, it'll send messages to the telegram `chat_Id`.
For params not indicated above, refer to comments besides parameter for its use.
#### Debug Params (Avoid modifying if possible!)
1. `debug`: Default is `False`. Please, only enable for debugging purposes. Default logging set to info level.
1. `resetInterval`: Default `12h`. It clears the array used to store data price points to prevent memory issues.
1. `priceRetryInterval`: Default `5s`. In the case of get price fail, this is the time delay before re-attempt
1. `checkNewListingEnabled`: Default `True`. Enables checking and adding of new listing pairs.
## Todo
1. Integrate with Binance API to make trades on pumps.
1. Integrate with Binance Websocket API to get volume information.
1. Integrate with listing-predictor to monitor movements for potential listings.
## Completed features
1. Telegram integration
1. Price update every 1s
1. Adjustable alert % param (outliers)
1. Watchlist feature
1. Monitor future markets
1. Optional alert on dumps
1. Customizable minimum alert interval for spam prevention
1. Option to disable print debugs on extraction
1. [Test] Volume Change Updates (TEST_VOL version)
1. Allows long period of running without memory issues
1. Send periodic Top X Pump & Dump reports
1. Docker integration (Thanks to [@patbaumgartner](https://github.com/patbaumgartner))
1. Logging integration (Thanks to [@patbaumgartner](https://github.com/patbaumgartner))
1. Major Refactoring and cleanup (Thanks to [@patbaumgartner](https://github.com/patbaumgartner))
1. Blacklist feature
================================================
FILE: alerter/BinancePumpAndDumpAlerter.py
================================================
import logging
import requests
import time
from time import sleep
from utils import ConversionUtils
class BinancePumpAndDumpAlerter:
def __init__(
self,
api_url,
watchlist,
blacklist,
pairs_of_interest,
chart_intervals,
outlier_intervals,
top_report_intervals,
extract_interval,
retry_interval,
reset_interval,
top_pump_enabled,
top_dump_enabled,
additional_statistics_enabled,
no_of_reported_coins,
dump_enabled,
check_new_listing_enabled,
top_report_nearest_hour,
telegram,
report_generator,
):
self.api_url = api_url
self.watchlist = watchlist
self.blacklist = blacklist
self.pairs_of_interest = pairs_of_interest
self.outlier_intervals = outlier_intervals
self.extract_interval = extract_interval
self.retry_interval = retry_interval
self.reset_interval = reset_interval
self.top_pump_enabled = top_pump_enabled
self.top_dump_enabled = top_dump_enabled
self.additional_statistics_enabled = additional_statistics_enabled
self.no_of_reported_coins = no_of_reported_coins
self.dump_enabled = dump_enabled
self.check_new_listing_enabled = check_new_listing_enabled
self.telegram = telegram
self.report_generator = report_generator
self.logger = logging.getLogger("pump-and-dump-alerter")
self.initial_time = int(time.time())
nearest_hour = self.initial_time - (self.initial_time % 3600) + 3600
self.logger.info(
"Nearest hour is %i seconds away", nearest_hour - self.initial_time
)
self.chart_intervals = {}
for interval in chart_intervals:
self.chart_intervals[interval] = {}
self.chart_intervals[interval][
"value"
] = ConversionUtils.duration_to_seconds(interval)
self.top_report_intervals = {}
for interval in top_report_intervals:
self.top_report_intervals[interval] = {}
# Determine initial start time for TPD. Should conveniently solve original 0% issue together.
if top_report_nearest_hour:
self.top_report_intervals[interval]["start"] = nearest_hour
else:
self.top_report_intervals[interval]["start"] = self.initial_time
self.top_report_intervals[interval][
"value"
] = ConversionUtils.duration_to_seconds(interval)
@staticmethod
def extract_ticker_data(symbol, assets):
for asset in assets:
if asset["symbol"] == symbol:
return asset
@staticmethod
def create_new_asset(symbol, chart_intervals):
asset = {"symbol": symbol, "price": [], "volume": []}
for interval in chart_intervals:
asset[interval] = {}
asset[interval]["change_current"] = 0
asset[interval]["change_last"] = 0
asset[interval]["change_volume"] = 0
return asset
def retrieve_exchange_assets(self, api_url):
try:
self.logger.debug(
"Retrieving price information from the ticker. ApiUrl: %s.", api_url
)
return requests.get(api_url).json()
except Exception as e:
self.logger.error(
"Issue occurred while getting prices. Error: %s.",
e,
exc_info=True,
)
sleep(5) # Sleep 5s and try again
return self.retrieve_exchange_assets(api_url)
def is_symbol_valid(self, symbol, watchlist, blacklist, pairs_of_interest):
# Filter symbols in watchlist if set - This disables the pairsOfInterest feature
if len(watchlist) > 0:
if symbol not in watchlist:
self.logger.debug("Ignoring symbol not in watchlist: %s.", symbol)
return False
return True
# Filter symbols in blacklist if set - This DOES NOT IMPACT the pairsOfInterest feature
if len(blacklist) > 0:
if symbol in blacklist:
self.logger.info(
"Ignoring symbol found in blacklist: %s.", symbol)
return False
# Filter pairsOfInterest to reduce the noise. E.g. BUSD, USDT, ETH, BTC
is_in_pairs_of_interest = False
for pair in pairs_of_interest:
if symbol.endswith(pair):
is_in_pairs_of_interest = True
break
if not is_in_pairs_of_interest:
self.logger.debug("Ignoring symbol not in pairsOfInterests: %s.", symbol)
return False
# Filter leverage symbols
for pair in pairs_of_interest:
coin = symbol.replace(pair, "")
if (
coin.endswith("UP")
or coin.endswith("DOWN")
or coin.endswith("BULL")
or coin.endswith("BEAR")
):
self.logger.debug("Ignoring leverage symbol: %s.", symbol)
return False
return True
def filter_and_convert_assets(
self, exchange_assets, watchlist, blacklist, pairs_of_interest, chart_intervals
):
filtered_assets = []
for exchange_asset in exchange_assets:
symbol = exchange_asset["symbol"]
if self.is_symbol_valid(symbol, watchlist, blacklist, pairs_of_interest):
filtered_assets.append(self.create_new_asset(symbol, chart_intervals))
self.logger.info("Adding symbol: %s.", symbol)
return filtered_assets
def update_all_monitored_assets_and_send_news_messages(
self,
monitored_assets,
exchange_assets,
current_time,
dump_enabled,
chart_intervals,
extract_interval,
outlier_intervals,
):
for asset in monitored_assets:
exchange_asset = self.extract_ticker_data(asset["symbol"], exchange_assets)
asset["price"].append(float(exchange_asset["price"]))
self.calculate_asset_change(
asset,
chart_intervals,
extract_interval,
)
self.report_generator.send_pump_dump_message(
asset,
chart_intervals,
outlier_intervals,
current_time,
dump_enabled,
)
def calculate_asset_change(
self,
asset,
chart_intervals,
extract_interval,
):
asset_length = len(asset["price"])
for interval in chart_intervals:
data_points = chart_intervals[interval]["value"] // extract_interval
# If data is not enough yet after restart for interval, stop here.
if data_points >= asset_length:
self.logger.debug(
"Not enough datapoints (%s/%s) for interval: %s",
asset_length,
data_points,
interval,
)
break
# Gets change in % from last alert trigger.
current_price = asset["price"][-1]
if current_price == 0:
self.logger.warning(
"Received zero price for asset %s, skipping calculation",
asset["symbol"]
)
change = 0
else:
price_delta = current_price - asset["price"][-1 - data_points]
change = price_delta / current_price
self.logger.debug(
"Calculated asset: %s for interval: %s with change: %s",
asset["symbol"],
interval,
change,
)
# Set last change for next interval iteration
asset[interval]["change_last"] = asset[interval]["change_current"]
# Stores change for the current interval into asset dict.
asset[interval]["change_current"] = change
return asset
def reset_prices_data_when_due(
self,
initial_time,
current_time,
reset_interval,
extract_interval,
assets,
chart_intervals,
):
if current_time - initial_time > reset_interval:
message = "Emptying price data to prevent memory errors."
self.logger.debug(message)
self.telegram.send_generic_message(message, is_alert_chat=True)
# Do not delete everything, only elements older than the last monitored interval
lastInterval = "1s"
for interval in chart_intervals:
lastInterval = interval
data_points = chart_intervals[lastInterval]["value"] // extract_interval
for asset in assets:
asset["price"] = asset["price"][-1 - data_points :]
initial_time = current_time
return initial_time
def add_new_asset_listings(
self,
initial_assets,
filtered_assets,
exchange_assets,
watchlist,
blacklist,
pairs_of_interest,
chart_intervals,
):
if len(initial_assets) >= len(exchange_assets):
# If initial_assets has more than assets we just ignore it
self.logger.debug("No new listing found.")
return filtered_assets
init_symbols = [asset["symbol"] for asset in initial_assets]
retrieved_symbols_to_add = [
exchange_asset["symbol"]
for exchange_asset in exchange_assets
if exchange_asset["symbol"] not in init_symbols
]
self.logger.debug("New listings found: %s.", retrieved_symbols_to_add)
filtered_symbols_to_add = []
for symbol in retrieved_symbols_to_add:
if self.is_symbol_valid(symbol, watchlist, blacklist, pairs_of_interest):
filtered_symbols_to_add.append(symbol)
filtered_assets.append(self.create_new_asset(symbol, chart_intervals))
self.logger.debug("Filtered new listings found: %s.", filtered_symbols_to_add)
if len(filtered_symbols_to_add) > 0:
self.report_generator.send_new_listings(filtered_symbols_to_add)
return filtered_assets
def check_and_send_top_pump_dump_statistics_report(
self,
assets,
current_time,
top_report_intervals,
top_pump_enabled,
top_dump_enabled,
additional_stats_enabled,
no_of_reported_coins,
):
for interval in top_report_intervals:
if (
current_time
> top_report_intervals[interval]["start"]
+ top_report_intervals[interval]["value"]
+ 1
):
# Update time for new trigger, rounded down to nearest interval. Avoid delay over time.
top_report_intervals[interval]["start"] = current_time - (current_time % ConversionUtils.duration_to_seconds(interval))
self.logger.debug(
"Sending out top pump dump report. Interval: %s.", interval
)
self.report_generator.send_top_pump_dump_statistics_report(
assets,
interval,
top_pump_enabled,
top_dump_enabled,
additional_stats_enabled,
no_of_reported_coins,
)
def run(self):
initial_assets = self.retrieve_exchange_assets(self.api_url)
filtered_assets = self.filter_and_convert_assets(
initial_assets,
self.watchlist,
self.blacklist,
self.pairs_of_interest,
self.chart_intervals,
)
message = "*Bot has started.* Following _{0}_ pairs."
self.telegram.send_generic_message(message, len(filtered_assets))
if self.telegram.is_alert_chat_enabled():
self.telegram.send_generic_message(
message,
len(filtered_assets),
is_alert_chat=True,
)
while True:
start_loop_time = time.time()
loop_time = int(start_loop_time)
exchange_assets = self.retrieve_exchange_assets(self.api_url)
if self.check_new_listing_enabled:
filtered_assets = self.add_new_asset_listings(
initial_assets,
filtered_assets,
exchange_assets,
self.watchlist,
self.blacklist,
self.pairs_of_interest,
self.chart_intervals,
)
# Reset initial exchange asset
initial_assets = exchange_assets
self.update_all_monitored_assets_and_send_news_messages(
filtered_assets,
exchange_assets,
loop_time,
self.dump_enabled,
self.chart_intervals,
self.extract_interval,
self.outlier_intervals,
)
self.check_and_send_top_pump_dump_statistics_report(
filtered_assets,
loop_time,
self.top_report_intervals,
self.top_pump_enabled,
self.top_dump_enabled,
self.additional_statistics_enabled,
self.no_of_reported_coins,
)
self.initial_time = self.reset_prices_data_when_due(
self.initial_time,
loop_time,
self.reset_interval,
self.extract_interval,
filtered_assets,
self.chart_intervals,
)
# Sleeps for the remainder of 1s, or loops through if extraction takes longer
end_loop_time = time.time()
self.logger.info(
"Extracting loop started at %d and finished at %d. Taking %f seconds.",
start_loop_time,
end_loop_time,
end_loop_time - start_loop_time,
)
if end_loop_time < start_loop_time + self.extract_interval:
sleep_time = start_loop_time + self.extract_interval - end_loop_time
self.logger.debug("Now sleeping %f seconds.", sleep_time)
sleep(sleep_time)
================================================
FILE: alerter/__init__.py
================================================
from .BinancePumpAndDumpAlerter import BinancePumpAndDumpAlerter
================================================
FILE: config.yml
================================================
# Binance Pump and Dump Alert Configuration
# Using Spot API with url set to https://api.binance.com/api/v3/ticker/price
# Using Futures API with url set to: https://fapi.binance.com/fapi/v1/ticker/price
apiUrl: https://api.binance.com/api/v3/ticker/price
# Intervals which are monitored. Add or remove intervals as you like.
chartIntervals:
- 1s
- 5s
- 15s
- 30s
- 1m
- 5m
- 15m
- 30m
- 1h
- 3h
- 6h
# Values in % at which an alert is triggered. Ensure interval exists in chartIntervals as well.
outlierIntervals:
"1s": 0.02
"5s": 0.05
"15s": 0.06
"30s": 0.08
"1m": 0.1
"5m": 0.10
"15m": 0.15
"30m": 0.20
"1h": 0.30
"3h": 0.4
"6h": 0.5
# Used for telegram bot updates
# Insert telegramToken obtained from @BotFather here
telegramToken: mySecretToken
# Insert Chat ID obtained from @get_id_bot here
telegramChatId: mySecretChatId
# Insert Chat ID for top pump dump alert, if left on `0` it'll send the message to telegram chat id
telegramAlertChatId: 0
# Useful Params
# Interval between each price extract from Binance REST API
extractInterval: 1s
# Watchlist only mode, if enabled, ONLY pairs in watchlist will be monitored
# E.g. 'ADAUSDT', 'ETHUSDT'
# watchlist:
# - ADAUSDT
# - ETHUSDT
# Blacklist only mode, if enabled, pairs in blacklist will be IGNORED it DOES NOT IMPACT pairsOfInterest
blacklist:
- NBTUSDT
# - ETHUSDT
# List the trading currency you are interested in. Other options include 'BUSD', 'BTC' , 'ETH'
pairsOfInterest:
- USDT
# Feature params
# Determine whether to look at DUMPs
dumpEnabled: True
# Top Pump & Dump Feature Params
# Set to false if not interested in top pump info
topPumpEnabled: True
# Set to false if not interested in top dump info
topDumpEnabled: True
# Set to false if not interested in net movement of coins
additionalStatsEnabled: True
# Top X amount of coins shown in the interval report, adjust to show more or less within the timeframe
noOfReportedCoins: 5
# Intervals for top pump and dump to be sent, ensure its in chartIntervals + outlierIntervals as well
topReportIntervals:
- 3h
- 6h
# Define your own bot emojis.
botEmoji: ! "\U0001F916" # 🤖
pumpEmoji: ! "\U0001F7E2" # 🟢 or '\U0001F4C8' 📈 '\U0001F53C'🔼
dumpEmoji: ! "\U0001F534" # 🔴 or '\U0001F4C9' 📉 '\U0001F53D'🔽
topEmoji: ! "\U0001F3C6" # 🏆
newsEmoji: ! "\U0001F4F0" # 📰 or '\U0001F680' 🚀
# Debug Params (Avoid touching it if there's no issues)
# If False we do not print unnecessary messages
debug: False
# Skip alert at higher timeframes when change in % did not change value by threshold in percentage points
alertSkipThreshold: 0.75
# Interval for clearing array to prevent memory can handle up to 12h+ depending on system
resetInterval: 6h
# In the case of get price fail, this is the time delay before re-attempt
priceRetryInterval: 5s
# Disables checking and adding of new listing pairs
checkNewListingEnabled: True
# Disables rounding to nearest hour for first TPD if false
topReportNearestHour: True
================================================
FILE: docker-compose.yml
================================================
version: "3.8"
services:
binance-pump-alerts:
build:
context: .
dockerfile: "./Dockerfile"
image: patbaumgartner/binance-pump-alerts:latest
# image: brianleect/binance-pump-alerts:latest
restart: "no"
environment:
- TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
- TELEGRAM_ALERT_CHAT_ID=${TELEGRAM_ALERT_CHAT_ID}
- PUMP_EMOJI=${PUMP_EMOJI}
- DUMP_EMOJI=${DUMP_EMOJI}
- NO_OF_REPORTED_COINS=${NO_OF_REPORTED_COINS}
- DEBUG=${DEBUG}
- RESET_INTERVAL=${RESET_INTERVAL}
================================================
FILE: entrypoint.sh
================================================
#!/bin/bash
set -e
process_config() {
# Please mount your own config file into the container for unsupported env parameters!
# Currently not supported config parameters: chartIntervals, outlierIntervals, pairsOfInterest, watchlist, blacklist, topReportIntervals
if [[ -n $API_URL ]]; then
sed -i "s/apiUrl.*/apiUrl: ${API_URL}/" config.yml
fi
if [[ -n $TELEGRAM_TOKEN ]]; then
sed -i "s/telegramToken.*/telegramToken: ${TELEGRAM_TOKEN}/" config.yml
fi
if [[ -n $TELEGRAM_CHAT_ID ]]; then
sed -i "s/telegramChatId.*/telegramChatId: ${TELEGRAM_CHAT_ID}/" config.yml
fi
if [[ -n $TELEGRAM_ALERT_CHAT_ID ]]; then
sed -i "s/telegramAlertChatId.*/telegramAlertChatId: ${TELEGRAM_ALERT_CHAT_ID}/" config.yml
fi
if [[ -n $EXTRACT_INTERVAL ]]; then
sed -i "s/extractInterval.*/extractInterval: ${EXTRACT_INTERVAL}/" config.yml
fi
if [[ -n $DUMP_ENABLED ]]; then
sed -i "s/dumpEnabled.*/dumpEnabled: ${DUMP_ENABLED}/" config.yml
fi
if [[ -n $TOP_PUMP_ENABLED ]]; then
sed -i "s/topPumpEnabled.*/topPumpEnabled: ${TOP_PUMP_ENABLED}/" config.yml
fi
if [[ -n $TOP_DUMP_ENABLED ]]; then
sed -i "s/topDumpEnabled.*/topDumpEnabled: ${TOP_DUMP_ENABLED}/" config.yml
fi
if [[ -n $ADDITIONAL_STATS_ENABLED ]]; then
sed -i "s/additionalStatsEnabled.*/additionalStatsEnabled: ${ADDITIONAL_STATS_ENABLED}/" config.yml
fi
if [[ -n $NO_OF_REPORTED_COINS ]]; then
sed -i "s/noOfReportedCoins.*/noOfReportedCoins: ${NO_OF_REPORTED_COINS}/" config.yml
fi
if [[ -n $BOT_EMOJI ]]; then
sed -i "s/botEmoji.*/botEmoji: ${BOT_EMOJI}/" config.yml
fi
if [[ -n $PUMP_EMOJI ]]; then
sed -i "s/pumpEmoji.*/pumpEmoji: ${PUMP_EMOJI}/" config.yml
fi
if [[ -n $DUMP_EMOJI ]]; then
sed -i "s/dumpEmoji.*/dumpEmoji: ${DUMP_EMOJI}/" config.yml
fi
if [[ -n $TOP_EMOJI ]]; then
sed -i "s/topEmoji.*/topEmoji: ${TOP_EMOJI}/" config.yml
fi
if [[ -n $NEWS_EMOJI ]]; then
sed -i "s/newsEmoji.*/newsEmoji: ${NEWS_EMOJI}/" config.yml
fi
if [[ -n $DEBUG ]]; then
sed -i "s/debug.*/debug: ${DEBUG}/" config.yml
fi
if [[ -n $ALERT_SKIP_THRESHOLD ]]; then
sed -i "s/alertSkipThreshold.*/alertSkipThreshold: ${ALERT_SKIP_THRESHOLD}/" config.yml
fi
if [[ -n $RESET_INTERVAL ]]; then
sed -i "s/resetInterval.*/resetInterval: ${RESET_INTERVAL}/" config.yml
fi
if [[ -n $PRICE_RETRY_INTERVAL ]]; then
sed -i "s/priceRetryInterval.*/priceRetryInterval: ${PRICE_RETRY_INTERVAL}/" config.yml
fi
if [[ -n $CHECK_NEW_LISTING_ENABLED ]]; then
sed -i "s/checkNewListingEnabled.*/checkNewListingEnabled: ${CHECK_NEW_LISTING_ENABLED}/" config.yml
fi
if [[ -n $TOP_REPORT_NEAREST_HOUR ]]; then
sed -i "s/topReportNearestHour.*/topReportNearestHour: ${TOP_REPORT_NEAREST_HOUR}/" config.yml
fi
}
# Adding parameters set from the environment variables to the config yaml file.
process_config
echo
echo "Running $@"
echo
exec "$@"
================================================
FILE: pumpAlerts.py
================================================
import colorlog, logging
import os
import yaml
from alerter import BinancePumpAndDumpAlerter
from reporter import ReportGenerator
from sender import TelegramSender
from utils import ConversionUtils
# Read config
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
config_file = "config.yml"
# Using dev config while development
config_dev_file = "config.dev.yml"
if os.path.isfile(config_dev_file):
config_file = config_dev_file
yaml_file = open(os.path.join(__location__, config_file), "r", encoding="utf-8")
config = yaml.load(yaml_file, Loader=yaml.FullLoader)
# Define the log format
bold_seq = "\033[1m"
log_format = "[%(asctime)s] %(processName)-12s %(threadName)-23s %(levelname)-8s %(name)-23s %(message)s"
color_format = f"{bold_seq} " "%(log_color)s " f"{log_format}"
colorlog.basicConfig(
# Define logging level according to the configuration
level=logging.DEBUG if config["debug"] is True else logging.INFO,
# Declare the object we created to format the log messages
format=color_format,
# Declare handlers for the Console
handlers=[logging.StreamHandler()],
)
# Define your logger name
logger = logging.getLogger("binance-pump-alerts-app")
# Logg whole configuration during the startup
logger.info("Using config file: %s", config_file)
logger.debug("Config: %s", config)
def main():
telegram = TelegramSender(
token=config["telegramToken"],
chat_id=config["telegramChatId"],
alert_chat_id=config["telegramAlertChatId"]
if "telegramAlertChatId" in config and config["telegramAlertChatId"] != 0
else config["telegramChatId"],
bot_emoji=config["botEmoji"],
top_emoji=config["topEmoji"],
news_emoji=config["newsEmoji"],
)
reporter = ReportGenerator(
telegram=telegram,
alert_skip_threshold=config["alertSkipThreshold"],
pump_emoji=config["pumpEmoji"],
dump_emoji=config["dumpEmoji"],
)
alerter = BinancePumpAndDumpAlerter(
api_url=config["apiUrl"],
watchlist=[] if "watchlist" not in config else config["watchlist"],
blacklist=[] if "blacklist" not in config else config["blacklist"],
pairs_of_interest=config["pairsOfInterest"],
chart_intervals=config["chartIntervals"],
outlier_intervals=config["outlierIntervals"],
top_report_intervals=config["topReportIntervals"],
extract_interval=ConversionUtils.duration_to_seconds(config["extractInterval"]),
retry_interval=ConversionUtils.duration_to_seconds(
config["priceRetryInterval"]
),
reset_interval=ConversionUtils.duration_to_seconds(config["resetInterval"]),
top_pump_enabled=config["topPumpEnabled"],
top_dump_enabled=config["topDumpEnabled"],
additional_statistics_enabled=config["additionalStatsEnabled"],
no_of_reported_coins=config["noOfReportedCoins"],
dump_enabled=config["dumpEnabled"],
check_new_listing_enabled=config["checkNewListingEnabled"],
top_report_nearest_hour=config["topReportNearestHour"],
telegram=telegram,
report_generator=reporter,
)
alerter.run()
if __name__ == "__main__":
main()
================================================
FILE: reporter/ReportGenerator.py
================================================
import logging
from datetime import datetime
class ReportGenerator:
def __init__(
self,
telegram,
alert_skip_threshold,
pump_emoji="\U0001F7E2", # 🟢
dump_emoji="\U0001F534", # 🔴
):
self.telegram = telegram
self.alert_skip_threshold = alert_skip_threshold
self.pump_emoji = pump_emoji
self.dump_emoji = dump_emoji
self.logger = logging.getLogger("report-generator")
def send_pump_message(self, symbol, interval, change, price):
self.telegram.send_message(
"""\
{0} *{1} [{2} Interval]* | Change: _{3:.3f}%_ | Price: _{4:.10f}_
Open in [Binance Spot](https://www.binance.com/en/trade/{1})\
""".format(
self.pump_emoji, symbol, interval, change * 100, price
),
is_alert_chat=False,
)
def send_dump_message(self, symbol, interval, change, price):
self.telegram.send_message(
"""\
{0} *{1} [{2} Interval]* | Change: _{3:.3f}%_ | Price: _{4:.10f}_
Open in [Binance Spot](https://www.binance.com/en/trade/{1})\
""".format(
self.dump_emoji, symbol, interval, change * 100, price
),
is_alert_chat=False,
)
def send_new_listings(self, symbols_to_add):
message = """\
*New Listings*"
{0} new pairs found, adding to monitored list."
*Adding Pairs:*\
""".format(
len(symbols_to_add)
)
message += "\n"
for symbol in symbols_to_add:
message += "- _{0}_\n".format(symbol)
self.telegram.send_news_message(message, is_alert_chat=True)
def send_pump_dump_message(
self,
asset,
chart_intervals,
outlier_intervals,
current_time,
dump_enabled=True,
):
change_biggest_delta = 0
no_of_alerts = 0
message = ""
for interval in chart_intervals:
change = asset[interval]["change_current"]
# Skip report if no outlier
if abs(change) < outlier_intervals[interval]:
self.logger.debug(
"Change for asset: %s for interval: %s is to low: %s. Skipping report creation.",
asset["symbol"],
interval,
change,
)
continue
# Remember biggest change of all intervals, to skip later notification
change_last = asset[interval]["change_last"]
change_delta = change - change_last
if abs(change_delta) > abs(change_biggest_delta):
change_biggest_delta = change_delta
# Remember the total number of alerts
no_of_alerts += 1
if change > 0:
message += "{0} *{1} Interval* | Change: _{2:.3f}%_\n".format(
self.pump_emoji,
interval,
change * 100,
asset["price"][-1],
)
if change < 0 and dump_enabled:
message += "{0} *{1} Interval* | Change: _{2:.3f}%_\n".format(
self.dump_emoji,
interval,
change * 100,
asset["price"][-1],
)
# Skip alert if change is not big enough to avoid spam
if abs(change_biggest_delta) < (self.alert_skip_threshold / 100):
self.logger.debug(
"Change for asset: %s on all intervals is to low: %s. Skipping this alert report.",
asset["symbol"],
change_biggest_delta,
)
return
news_message = """\
*{0}* | {1} Alert(s) | {2}
Price: _{3:.10f}_ | Volume: _{4}_
{5}
Open in [Binance Spot](https://www.binance.com/en/trade/{0})\
""".format(
asset["symbol"],
no_of_alerts,
datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S"),
asset["price"][-1],
0,
message,
)
self.telegram.send_news_message(news_message)
def send_top_pump_dump_statistics_report(
self,
assets,
interval,
top_pump_enabled=True,
top_dump_enabled=True,
additional_stats_enabled=True,
no_of_reported_coins=5,
):
if not top_pump_enabled or not top_dump_enabled:
return
message = "*[{0} Interval]*\n\n".format(interval)
if top_pump_enabled:
pump_sorted_list = sorted(
assets,
key=lambda item: item[interval]["change_current"],
reverse=True,
)[0:no_of_reported_coins]
message += "{0} *Top {1} Pumps*\n".format(
self.pump_emoji, no_of_reported_coins
)
for asset in pump_sorted_list:
message += "- {0}: _{1:.2f}_%\n".format(
asset["symbol"], asset[interval]["change_current"] * 100
)
message += "\n"
if top_dump_enabled:
dump_sorted_list = sorted(
assets, key=lambda item: item[interval]["change_current"]
)[0:no_of_reported_coins]
message += "{0} *Top {1} Dumps*\n".format(
self.dump_emoji, no_of_reported_coins
)
for asset in dump_sorted_list:
message += "- {0}: _{1:.2f}_%\n".format(
asset["symbol"], asset[interval]["change_current"] * 100
)
if additional_stats_enabled:
if top_pump_enabled or top_dump_enabled:
message += "\n"
message += self.generate_additional_statistics_report(assets, interval)
self.telegram.send_report_message(message, is_alert_chat=True)
def generate_additional_statistics_report(self, assets, interval):
up = 0
down = 0
sum_change = 0
for asset in assets:
if asset[interval]["change_current"] > 0:
up += 1
elif asset[interval]["change_current"] < 0:
down += 1
sum_change += asset[interval]["change_current"]
avg_change = sum_change / len(assets)
return "*Average Change:* {0:.2f}%\n {1} {2} / {3} {4}".format(
avg_change * 100,
self.pump_emoji,
up,
self.dump_emoji,
down,
)
================================================
FILE: reporter/__init__.py
================================================
from .ReportGenerator import ReportGenerator
================================================
FILE: requirements.txt
================================================
colorlog
requests
pyyaml
python-telegram-bot==13.13
================================================
FILE: sender/TelegramSender.py
================================================
import logging
from concurrent.futures import ThreadPoolExecutor
from telegram import Bot, ParseMode
from telegram.error import RetryAfter
from telegram.utils.request import Request
from time import sleep
class TelegramSender:
def __init__(
self,
token,
chat_id,
alert_chat_id=0,
bot_emoji="\U0001F916", # 🤖
top_emoji="\U0001F3C6", # 🏆
news_emoji="\U0001F4F0", # 📰
):
self.token = token
self.chat_id = chat_id
self.alert_chat_id = alert_chat_id
self.bot_emoji = bot_emoji
self.top_emoji = top_emoji
self.news_emoji = news_emoji
self.telegram_executor = ThreadPoolExecutor(max_workers=3)
self.request = Request(con_pool_size=3)
self.bot = Bot(self.token, request=self.request)
self.logger = logging.getLogger("telegram-sender")
def is_alert_chat_enabled(self):
return self.alert_chat_id != 0 and self.alert_chat_id != self.chat_id
def send_message(self, message, is_alert_chat=False):
chat_id = self.chat_id if not is_alert_chat else self.alert_chat_id
def push_message(bot, chat_id, message):
self.logger.info(message)
try:
bot.send_message(
chat_id=chat_id,
text=message,
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
except RetryAfter as e:
self.logger.error(
"Flood limit is exceeded. Sleep {} seconds.", e.retry_after
)
sleep(e.retry_after)
# Resend message to the queue
self.send_message(message, is_alert_chat)
except Exception as e:
self.logger.error(str(e))
self.telegram_executor.submit(
lambda p: push_message(*p), (self.bot, chat_id, message)
)
def send_generic_message(self, message, args=None, is_alert_chat=False):
if args is not None:
message = message.format(args)
self.send_message(self.bot_emoji + " " + message, is_alert_chat)
def send_report_message(self, message, args=None, is_alert_chat=False):
if args is not None:
message = message.format(args)
self.send_message(self.top_emoji + " " + message, is_alert_chat)
def send_news_message(self, message, args=None, is_alert_chat=False):
if args is not None:
message = message.format(args)
self.send_message(self.news_emoji + " " + message, is_alert_chat)
================================================
FILE: sender/__init__.py
================================================
from .TelegramSender import TelegramSender
================================================
FILE: utils/ConversionUtils.py
================================================
class ConversionUtils:
@staticmethod
def duration_to_seconds(duration):
unit = duration[-1]
if unit == "s":
unit = 1
elif unit == "m":
unit = 60
elif unit == "h":
unit = 3600
return int(duration[:-1]) * unit
================================================
FILE: utils/__init__.py
================================================
from .ConversionUtils import ConversionUtils
gitextract_g_svvtu5/
├── .dockerignore
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── Dockerfile
├── README.md
├── alerter/
│ ├── BinancePumpAndDumpAlerter.py
│ └── __init__.py
├── config.yml
├── docker-compose.yml
├── entrypoint.sh
├── pumpAlerts.py
├── reporter/
│ ├── ReportGenerator.py
│ └── __init__.py
├── requirements.txt
├── sender/
│ ├── TelegramSender.py
│ └── __init__.py
└── utils/
├── ConversionUtils.py
└── __init__.py
SYMBOL INDEX (31 symbols across 5 files)
FILE: alerter/BinancePumpAndDumpAlerter.py
class BinancePumpAndDumpAlerter (line 9) | class BinancePumpAndDumpAlerter:
method __init__ (line 10) | def __init__(
method extract_ticker_data (line 79) | def extract_ticker_data(symbol, assets):
method create_new_asset (line 85) | def create_new_asset(symbol, chart_intervals):
method retrieve_exchange_assets (line 96) | def retrieve_exchange_assets(self, api_url):
method is_symbol_valid (line 111) | def is_symbol_valid(self, symbol, watchlist, blacklist, pairs_of_inter...
method filter_and_convert_assets (line 151) | def filter_and_convert_assets(
method update_all_monitored_assets_and_send_news_messages (line 165) | def update_all_monitored_assets_and_send_news_messages(
method calculate_asset_change (line 193) | def calculate_asset_change(
method reset_prices_data_when_due (line 241) | def reset_prices_data_when_due(
method add_new_asset_listings (line 270) | def add_new_asset_listings(
method check_and_send_top_pump_dump_statistics_report (line 308) | def check_and_send_top_pump_dump_statistics_report(
method run (line 344) | def run(self):
FILE: pumpAlerts.py
function main (line 45) | def main():
FILE: reporter/ReportGenerator.py
class ReportGenerator (line 6) | class ReportGenerator:
method __init__ (line 7) | def __init__(
method send_pump_message (line 21) | def send_pump_message(self, symbol, interval, change, price):
method send_dump_message (line 33) | def send_dump_message(self, symbol, interval, change, price):
method send_new_listings (line 45) | def send_new_listings(self, symbols_to_add):
method send_pump_dump_message (line 61) | def send_pump_dump_message(
method send_top_pump_dump_statistics_report (line 140) | def send_top_pump_dump_statistics_report(
method generate_additional_statistics_report (line 193) | def generate_additional_statistics_report(self, assets, interval):
FILE: sender/TelegramSender.py
class TelegramSender (line 10) | class TelegramSender:
method __init__ (line 11) | def __init__(
method is_alert_chat_enabled (line 36) | def is_alert_chat_enabled(self):
method send_message (line 39) | def send_message(self, message, is_alert_chat=False):
method send_generic_message (line 66) | def send_generic_message(self, message, args=None, is_alert_chat=False):
method send_report_message (line 71) | def send_report_message(self, message, args=None, is_alert_chat=False):
method send_news_message (line 76) | def send_news_message(self, message, args=None, is_alert_chat=False):
FILE: utils/ConversionUtils.py
class ConversionUtils (line 1) | class ConversionUtils:
method duration_to_seconds (line 3) | def duration_to_seconds(duration):
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (52K chars).
[
{
"path": ".dockerignore",
"chars": 73,
"preview": ".env\n.idea\n.gitignore\n\nvenv\n\nconfig.dev.yml\ndocker-compose.yml\nReadme.md\n"
},
{
"path": ".github/workflows/main.yml",
"chars": 1395,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: Publish Docker image\n\n# Controls when the workflo"
},
{
"path": ".gitignore",
"chars": 5738,
"preview": "\nconfig.dev.yml\n\n# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm+all,intellij+all,visualstud"
},
{
"path": "Dockerfile",
"chars": 1335,
"preview": "FROM python:3.9-slim-buster as base\n\n# Setup env\nENV LANG C.UTF-8\nENV LC_ALL C.UTF-8\nENV PYTHONDONTWRITEBYTECODE 1\nENV P"
},
{
"path": "README.md",
"chars": 5036,
"preview": "# Binance Pump Alerts\n\nBPA is a simple application which gets the price data from Binance Spot or Futures API and sends "
},
{
"path": "alerter/BinancePumpAndDumpAlerter.py",
"chars": 14991,
"preview": "import logging\r\nimport requests\r\nimport time\r\n\r\nfrom time import sleep\r\nfrom utils import ConversionUtils\r\n\r\n\r\nclass Bin"
},
{
"path": "alerter/__init__.py",
"chars": 65,
"preview": "from .BinancePumpAndDumpAlerter import BinancePumpAndDumpAlerter\n"
},
{
"path": "config.yml",
"chars": 2990,
"preview": "# Binance Pump and Dump Alert Configuration\n\n# Using Spot API with url set to https://api.binance.com/api/v3/ticker/pric"
},
{
"path": "docker-compose.yml",
"chars": 575,
"preview": "version: \"3.8\"\n\nservices:\n binance-pump-alerts:\n build:\n context: .\n dockerfile: \"./Dockerfile\"\n image:"
},
{
"path": "entrypoint.sh",
"chars": 3117,
"preview": "#!/bin/bash\nset -e\n\nprocess_config() {\n\n # Please mount your own config file into the container for unsupported env p"
},
{
"path": "pumpAlerts.py",
"chars": 3246,
"preview": "import colorlog, logging\nimport os\nimport yaml\n\nfrom alerter import BinancePumpAndDumpAlerter\nfrom reporter import Repor"
},
{
"path": "reporter/ReportGenerator.py",
"chars": 6502,
"preview": "import logging\n\nfrom datetime import datetime\n\n\nclass ReportGenerator:\n def __init__(\n self,\n telegram,"
},
{
"path": "reporter/__init__.py",
"chars": 45,
"preview": "from .ReportGenerator import ReportGenerator\n"
},
{
"path": "requirements.txt",
"chars": 52,
"preview": "colorlog\nrequests\npyyaml\npython-telegram-bot==13.13\n"
},
{
"path": "sender/TelegramSender.py",
"chars": 2636,
"preview": "import logging\n\nfrom concurrent.futures import ThreadPoolExecutor\nfrom telegram import Bot, ParseMode\nfrom telegram.erro"
},
{
"path": "sender/__init__.py",
"chars": 43,
"preview": "from .TelegramSender import TelegramSender\n"
},
{
"path": "utils/ConversionUtils.py",
"chars": 305,
"preview": "class ConversionUtils:\r\n @staticmethod\r\n def duration_to_seconds(duration):\r\n unit = duration[-1]\r\n "
},
{
"path": "utils/__init__.py",
"chars": 45,
"preview": "from .ConversionUtils import ConversionUtils\n"
}
]
About this extraction
This page contains the full source code of the brianleect/binance-pump-alerts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (47.1 KB), approximately 11.8k tokens, and a symbol index with 31 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.