From f16458d2096822e747dfec8fef5088a9e707c2f5 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 20:38:32 +0800 Subject: [PATCH 01/35] Change: market price generator to Redis --- bot.py | 6 +++--- market_price.py | 28 +++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/bot.py b/bot.py index 2ea2a82..7a18752 100644 --- a/bot.py +++ b/bot.py @@ -16,7 +16,7 @@ ) from oracle_interface import to_usdt, to_ton, to_bigint from utils import float_conversion, int_conversion -from market_price import ton_usdt_prices_generator +from market_price import get_ton_usdt_price load_dotenv() @@ -187,8 +187,8 @@ async def tick_one_scale(price, base_bal, quote_bal): async def main(): - price_generator = ton_usdt_prices_generator() - async for price in price_generator: + while True: + price = await get_ton_usdt_price() print("Price:", price) base_bal = await get_address_balance(WALLET.address.to_string()) quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) diff --git a/market_price.py b/market_price.py index f56cf92..3e1fcdf 100644 --- a/market_price.py +++ b/market_price.py @@ -1,5 +1,14 @@ import ccxt.async_support as ccxt import asyncio +import os +import redis +from dotenv import load_dotenv + +load_dotenv() + +REDIS_HOST = os.getenv("REDIS_HOST", "localhost") +REDIS_PORT = int(os.getenv("REDIS_PORT", 6379)) +REDIS_DB = int(os.getenv("REDIS_DB", 0)) EXCHANGE_LIST = [ "bybit", @@ -7,6 +16,10 @@ "okx", ] +redis_client = redis.StrictRedis( + host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, decode_responses=True +) + async def fetch_price_from_exchange(exchange_id, symbol="TON/USDT"): try: @@ -27,10 +40,19 @@ async def fetch_ton_usdt_prices(): return [price for price in prices if price is not None] -async def ton_usdt_prices_generator(): +async def set_ton_usdt_prices(): while True: prices = await fetch_ton_usdt_prices() if prices: - yield sum(prices) / len(prices) + price = sum(prices) / len(prices) else: - yield None + continue + redis_client.set("ton_usdt_price", price) + + +async def get_ton_usdt_price(): + return redis_client.get("ton_usdt_price") + + +if __name__ == "__main__": + asyncio.run(set_ton_usdt_prices()) From c4529e3c78ee7b1565381cb2207cab8be4c75d4d Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 20:55:33 +0800 Subject: [PATCH 02/35] Update: requirement.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 8332813..7620c5c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -154,6 +154,7 @@ PyYAML==6.0.1 pyzmq==24.0.1 qtconsole==5.4.0 QtPy==2.3.0 +redis==5.0.1 regex==2023.10.3 requests==2.28.1 requests-oauthlib==1.3.1 From 00f57546b8dd6bdf86dba2deed1bfb4f2e8b439f Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 21:07:05 +0800 Subject: [PATCH 03/35] * feat(build.yaml): add GitHub Actions workflow to build and push Docker images --- .github/workflows/build.yaml | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..f4fe175 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,54 @@ +name: Build docker images and push +on: + push: + branches: + - "main" + tags: + - "*" + paths-ignore: + - "**.md" + - "*.toml" + - "*.env*" + +jobs: + build-and-push: + name: Push Docker image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: | + skyline9981/ticton-oracle-bot + ghcr.io/${{ github.repository }} + - name: Build and push Docker images + uses: docker/build-push-action@v5 + with: + context: . + file: "Dockerfile" + push: true + provenance: false + sbom: false + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} From 52d721277f1f7b215bd7e460df54be1645760038 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:08:12 +0800 Subject: [PATCH 04/35] Add: Dockerfile --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d6ffcee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.11-alpine + +WORKDIR /usr/src/app + +COPY . . + +RUN pip install --no-cache-dir -r requirements.txt + +CMD ["python", "./bot.py"] From 5c898501dcf21fda81cbda3c89e8edd15685e512 Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 21:10:41 +0800 Subject: [PATCH 05/35] * chore(build.yaml): add branch filters for "develop" and "1-containerize-the-bot" branches --- .github/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f4fe175..5c52c42 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,6 +3,8 @@ on: push: branches: - "main" + - "develop" + - "1-containerize-the-bot" tags: - "*" paths-ignore: From d9aa683cb9cc512e76d0218e1f2d8cd5fcfdfc4c Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 21:22:57 +0800 Subject: [PATCH 06/35] * chore(Dockerfile): update base image to python:3.10-alpine --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d6ffcee..54e901c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-alpine +FROM python:3.10-alpine WORKDIR /usr/src/app From a65450d9e43e9b8191a8b893a1b96ce0886294f4 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:29:57 +0800 Subject: [PATCH 07/35] Update: Dockerfile, requirement.txt --- Dockerfile | 2 +- requirements.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d6ffcee..928d84e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-alpine +FROM python = 3.10.9 WORKDIR /usr/src/app diff --git a/requirements.txt b/requirements.txt index 7620c5c..35c4328 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,6 @@ click==8.1.7 coloredlogs==15.0.1 configparser==5.3.0 contourpy==1.2.0 -cramjam==2.6.2 crc16==0.1.1 crc32c==2.3.post0 cryptography==41.0.7 From 662fcfac7105cac1030de57ced25b7af7892c9f1 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:49:56 +0800 Subject: [PATCH 08/35] Update: requirement --- requirements.txt | 206 ++++------------------------------------------- 1 file changed, 14 insertions(+), 192 deletions(-) diff --git a/requirements.txt b/requirements.txt index 35c4328..9031ecc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,213 +1,35 @@ -absl-py==2.0.0 aiodns==3.1.1 aiohttp==3.9.1 aiosignal==1.3.1 -altair==5.1.2 -annotated-types==0.6.0 -anyio==3.6.2 -appnope==0.1.3 -argon2-cffi==21.3.0 -argon2-cffi-bindings==21.2.0 -asttokens==2.1.0 -astunparse==1.6.3 async-timeout==4.0.3 -attrs==22.1.0 -autopep8==2.0.0 -backcall==0.2.0 -beautifulsoup4==4.11.1 -bitarray==2.6.0 -bitstring==4.0.2 -bleach==5.0.1 -blinker==1.7.0 -cachetools==5.3.2 -ccxt==4.2.13 -certifi==2022.9.24 -cffi==1.15.1 -charset-normalizer==2.1.1 -click==8.1.7 -coloredlogs==15.0.1 -configparser==5.3.0 -contourpy==1.2.0 -crc16==0.1.1 +attrs==23.2.0 +bitarray==2.9.2 +bitstring==4.1.4 +ccxt==4.2.18 +certifi==2023.11.17 +cffi==1.16.0 +charset-normalizer==3.3.2 crc32c==2.3.post0 cryptography==41.0.7 -cycler==0.12.1 -debugpy==1.6.3 -decorator==5.1.1 -defusedxml==0.7.1 -dnspython==2.4.2 -entrypoints==0.4 -et-xmlfile==1.1.0 exceptiongroup==1.2.0 -executing==1.2.0 -fastjsonschema==2.16.2 -fastparquet==2023.7.0 -filelock==3.13.1 -flatbuffers==23.5.26 -fonttools==4.44.0 frozenlist==1.4.1 -fsspec==2023.6.0 -gast==0.5.4 -gitdb==4.0.11 -GitPython==3.1.40 -google-auth==2.23.4 -google-auth-oauthlib==1.1.0 -google-pasta==0.2.0 -graphql-core==3.2.3 -graphql_query==1.0.3 -grpcio==1.59.3 -h11==0.14.0 -h5py==3.10.0 -httpcore==1.0.2 -httpx==0.25.2 -huggingface-hub==0.19.4 -humanfriendly==10.0 -idna==3.4 -imbalanced-learn==0.11.0 -imblearn==0.0 -importlib-metadata==6.8.0 +idna==3.6 iniconfig==2.0.0 -ipykernel==6.17.1 -ipython==8.6.0 -ipython-genutils==0.2.0 -ipywidgets==8.0.2 -jedi==0.18.2 -Jinja2==3.1.2 -joblib==1.3.2 -jsonschema==4.17.1 -jupyter==1.0.0 -jupyter-console==6.4.4 -jupyter-server==1.23.3 -jupyter_client==7.4.7 -jupyter_core==5.0.0 -jupyterlab-pygments==0.2.2 -jupyterlab-widgets==3.0.3 -keras==2.15.0 -kiwisolver==1.4.5 -libclang==16.0.6 loguru==0.7.2 -Markdown==3.5.1 -markdown-it-py==3.0.0 -MarkupSafe==2.1.1 -matplotlib==3.8.1 -matplotlib-inline==0.1.6 -mdurl==0.1.2 -mistune==2.0.4 -ml-dtypes==0.2.0 -mlxtend==0.23.0 -mock==2.0.0 -mpmath==1.3.0 multidict==6.0.4 -nbclassic==0.4.8 -nbclient==0.7.0 -nbconvert==7.2.5 -nbformat==5.7.0 -nest-asyncio==1.5.6 -networkx==3.2.1 -notebook==6.5.2 -notebook_shim==0.2.2 -numpy==1.26.2 -oauthlib==3.2.2 -onnxruntime==1.16.3 -opencv-python==4.6.0.66 -openpyxl==3.1.2 -opt-einsum==3.3.0 -packaging==21.3 -pandas==1.5.2 -pandocfilters==1.5.0 -parso==0.8.3 -pbr==5.11.1 -pexpect==4.8.0 -pickleshare==0.7.5 -Pillow==9.4.0 -platformdirs==2.5.4 +packaging==23.2 pluggy==1.3.0 -prometheus-client==0.15.0 -prompt-toolkit==3.0.33 -protobuf==4.21.12 -psutil==5.9.4 -ptyprocess==0.7.0 -pure-eval==0.2.2 -pyarrow==12.0.1 -pyasn1==0.5.1 -pyasn1-modules==0.3.0 pycares==4.4.0 -pycodestyle==2.10.0 pycparser==2.21 -pydantic==1.10.13 -pydantic_core==2.14.5 -pydeck==0.8.1b0 -Pygments==2.13.0 -pymongo==4.6.1 -PyMySQL==1.0.2 PyNaCl==1.5.0 -pyparsing==3.0.9 -pyrsistent==0.19.2 -pytest==7.4.3 -python-dateutil==2.8.2 +pytest==7.4.4 python-dotenv==1.0.0 -python-telegram-bot==20.7 -pytonlib==0.0.53 -pytz==2022.6 -PyYAML==6.0.1 -pyzmq==24.0.1 -qtconsole==5.4.0 -QtPy==2.3.0 redis==5.0.1 -regex==2023.10.3 -requests==2.28.1 -requests-oauthlib==1.3.1 -rich==13.6.0 -rsa==4.9 -safetensors==0.4.0 -scikit-learn==1.3.2 -scipy==1.11.3 -seaborn==0.13.0 -Send2Trash==1.8.0 -six==1.16.0 -sklearn==0.0.post11 -smmap==5.0.1 -sniffio==1.3.0 -soupsieve==2.3.2.post1 -stack-data==0.6.1 -streamlit==1.28.1 -sympy==1.12 -tenacity==8.2.3 -tensorboard==2.15.1 -tensorboard-data-server==0.7.2 -tensorflow==2.15.0 -tensorflow-estimator==2.15.0 -tensorflow-io-gcs-filesystem==0.34.0 -tensorflow-macos==2.15.0 -termcolor==2.3.0 -terminado==0.17.0 -threadpoolctl==3.2.0 -tinycss2==1.2.1 -tokenizers==0.15.0 -toml==0.10.2 +requests==2.31.0 tomli==2.0.1 -ton==0.26 tonpy==0.0.0.1.2b0 tonsdk==1.0.13 -TonTools==2.1.2 -toolz==0.12.0 -torch==2.1.1 -torchvision==0.16.1 -tornado==6.2 -tqdm==4.66.1 -traitlets==5.5.0 -transformers==4.35.2 -tvm-valuetypes==0.0.10 -typing_extensions==4.8.0 -tzlocal==5.2 -Unidecode==1.3.7 -urllib3==1.26.13 -validators==0.22.0 -wcwidth==0.2.5 -webencodings==0.5.1 -websocket-client==1.4.2 -Werkzeug==3.0.1 -widgetsnbextension==4.0.3 -wrapt==1.14.1 +tvm-valuetypes==0.0.12 +typing_extensions==4.9.0 +urllib3==2.1.0 yarl==1.9.4 -zipp==3.17.0 From 1a4ebca73b66518d95a87e003e5ec26d1c6dc126 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:54:06 +0800 Subject: [PATCH 09/35] Downgrade: bitarray package --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9031ecc..ce2c124 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ aiohttp==3.9.1 aiosignal==1.3.1 async-timeout==4.0.3 attrs==23.2.0 -bitarray==2.9.2 +bitarray==2.6.0 bitstring==4.1.4 ccxt==4.2.18 certifi==2023.11.17 From 1ccb67b817f49b6580abe5a69cd32ff2d9658713 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:55:14 +0800 Subject: [PATCH 10/35] Update: requirement.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ce2c124..7243551 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ python-dotenv==1.0.0 redis==5.0.1 requests==2.31.0 tomli==2.0.1 -tonpy==0.0.0.1.2b0 +tonpy tonsdk==1.0.13 tvm-valuetypes==0.0.12 typing_extensions==4.9.0 From 03a6bfb6e9d08f86af5061ed41cb0d046fe66545 Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 21:56:35 +0800 Subject: [PATCH 11/35] Update: Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 54e901c..d6ffcee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-alpine +FROM python:3.11-alpine WORKDIR /usr/src/app From f4cfadca902ab0e6228e7060ba5a3c9f50b4a864 Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 21:57:58 +0800 Subject: [PATCH 12/35] Update Python version in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d6ffcee..d0b4ec7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-alpine +FROM python:3.10-slim WORKDIR /usr/src/app From 9c104503514da07798eea7eb4f52a36fd88f377e Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 21:59:23 +0800 Subject: [PATCH 13/35] Upgrade: bitarray --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7243551..3b6163c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ aiohttp==3.9.1 aiosignal==1.3.1 async-timeout==4.0.3 attrs==23.2.0 -bitarray==2.6.0 +bitarray==2.9.2 bitstring==4.1.4 ccxt==4.2.18 certifi==2023.11.17 From e45bbecd1906173a27fc805fb1c054398e425605 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 22:02:21 +0800 Subject: [PATCH 14/35] Update: requirement!plzzzz!! --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3b6163c..36cd9c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,8 +3,8 @@ aiohttp==3.9.1 aiosignal==1.3.1 async-timeout==4.0.3 attrs==23.2.0 -bitarray==2.9.2 -bitstring==4.1.4 +bitarray==2.6.0 +bitstring==4.0.2 ccxt==4.2.18 certifi==2023.11.17 cffi==1.16.0 From 0bf853edad235989baa2df56f487846dd087e07d Mon Sep 17 00:00:00 2001 From: skyline9981 Date: Fri, 19 Jan 2024 22:22:07 +0800 Subject: [PATCH 15/35] * chore(.dockerignore): add .env** and venv/ to the .dockerignore file --- .dockerignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..eb42b74 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.env** +venv/ \ No newline at end of file From 30c5ed6a7a83f3fcd89ca4f7b4227d38eee7af31 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 22:50:56 +0800 Subject: [PATCH 16/35] Add: docker compse --- docker-compose.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e7ab8a0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' +services: + app: + image: skyline9981/ticton-oracle-bot:1-containerize-the-bot + platform: linux/amd64 + restart: always + env_file: + - .env + depends_on: + - redis + - mariadb + + redis: + image: "redis:alpine" + restart: always + ports: + - "6379:6379" + + mariadb: + image: "mariadb" + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - maria-db:/var/lib/mysql + +volumes: + maria-db: From 5df2f3d300d29ea2965e7f9bff86414caeecd526 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 22:51:18 +0800 Subject: [PATCH 17/35] Use Redis to set price --- bot.py | 2 ++ market_price.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 7a18752..1388b50 100644 --- a/bot.py +++ b/bot.py @@ -189,6 +189,8 @@ async def tick_one_scale(price, base_bal, quote_bal): async def main(): while True: price = await get_ton_usdt_price() + if price is None: + continue print("Price:", price) base_bal = await get_address_balance(WALLET.address.to_string()) quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) diff --git a/market_price.py b/market_price.py index 3e1fcdf..d037698 100644 --- a/market_price.py +++ b/market_price.py @@ -45,9 +45,9 @@ async def set_ton_usdt_prices(): prices = await fetch_ton_usdt_prices() if prices: price = sum(prices) / len(prices) + redis_client.set("ton_usdt_price", price) else: continue - redis_client.set("ton_usdt_price", price) async def get_ton_usdt_price(): From 87f1edc36fad16cb8c31e48add90faa496c08411 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 23:24:51 +0800 Subject: [PATCH 18/35] Fix: Redis get price --- Dockerfile | 3 ++- bot.py | 2 +- run.sh | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100755 run.sh diff --git a/Dockerfile b/Dockerfile index d0b4ec7..f2ee3a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,5 @@ COPY . . RUN pip install --no-cache-dir -r requirements.txt -CMD ["python", "./bot.py"] +CMD ["./run.sh"] + diff --git a/bot.py b/bot.py index 1388b50..fe28710 100644 --- a/bot.py +++ b/bot.py @@ -191,7 +191,7 @@ async def main(): price = await get_ton_usdt_price() if price is None: continue - print("Price:", price) + print("Current price:", price) base_bal = await get_address_balance(WALLET.address.to_string()) quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) active_alarms = await find_active_alarm() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..dadf7ea --- /dev/null +++ b/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh +python ./market_price.py & +python ./bot.py & +wait From f64c8410bdb53ae00990df577431f1014b798799 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 23:51:12 +0800 Subject: [PATCH 19/35] Add: mariadb connector --- .gitignore | 2 ++ mariadb.py | 35 +++++++++++++++++++++++++++++++++++ requirements.txt | 1 + run.sh | 7 ++++--- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 mariadb.py diff --git a/.gitignore b/.gitignore index 6231e91..cb389d9 100644 --- a/.gitignore +++ b/.gitignore @@ -159,3 +159,5 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ data/ + +.python-version diff --git a/mariadb.py b/mariadb.py new file mode 100644 index 0000000..52011d1 --- /dev/null +++ b/mariadb.py @@ -0,0 +1,35 @@ +import os +from dotenv import load_dotenv + +import mysql.connector as connector +import mysql.connector.errors as Error + +load_dotenv() + + +def create_connection(): + try: + connection = connector.connect( + host=os.getenv("MYSQL_HOST"), # Assuming MariaDB is running on localhost + database=os.getenv("MYSQL_DATABASE"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + ) + if connection.is_connected(): + db_info = connection.get_server_info() + print("Successfully connected to MariaDB server version ", db_info) + return connection + except Error as e: + print("Error while connecting to MariaDB", e) + return None + + +def main(): + connection = create_connection() + if connection is not None and connection.is_connected(): + # Add your database operations here + connection.close() + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index 36cd9c4..d280a5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ idna==3.6 iniconfig==2.0.0 loguru==0.7.2 multidict==6.0.4 +mysql-connector-python==8.3.0 packaging==23.2 pluggy==1.3.0 pycares==4.4.0 diff --git a/run.sh b/run.sh index dadf7ea..270e904 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,5 @@ #!/bin/sh -python ./market_price.py & -python ./bot.py & -wait +# python ./market_price.py & +# python ./bot.py & +# +python ./mariadb.py & From 93a7834f5aed4004398f0134f173d0d9a82b7c69 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Fri, 19 Jan 2024 23:58:26 +0800 Subject: [PATCH 20/35] cry!!! --- run.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/run.sh b/run.sh index 270e904..e1c05aa 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,2 @@ #!/bin/sh -# python ./market_price.py & -# python ./bot.py & -# python ./mariadb.py & From 8f9b8b873c69da1d3537bb233f1c5690f42906be Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 00:12:54 +0800 Subject: [PATCH 21/35] Test: mariadb --- mariadb.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mariadb.py b/mariadb.py index 52011d1..7844090 100644 --- a/mariadb.py +++ b/mariadb.py @@ -19,6 +19,9 @@ def create_connection(): db_info = connection.get_server_info() print("Successfully connected to MariaDB server version ", db_info) return connection + else: + print("Failed to connect to MariaDB server") + return None except Error as e: print("Error while connecting to MariaDB", e) return None From dc9e4673d4dd979404f3d01e82184420e8b65b55 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 00:17:41 +0800 Subject: [PATCH 22/35] Test: mariadb --- run.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run.sh b/run.sh index e1c05aa..54be014 100755 --- a/run.sh +++ b/run.sh @@ -1,2 +1,3 @@ #!/bin/sh -python ./mariadb.py & + +python ./mariadb.py From c16b009b33a5e69d07dd0f30829b792371b752be Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 01:11:58 +0800 Subject: [PATCH 23/35] Change: csv file to mariadb --- bot.py | 11 +++-- mariadb.py | 38 --------------- mariadb_connector.py | 107 +++++++++++++++++++++++++++++++++++++++++++ oracle_interface.py | 41 +++++++++-------- run.sh | 8 +++- 5 files changed, 141 insertions(+), 64 deletions(-) delete mode 100644 mariadb.py create mode 100644 mariadb_connector.py diff --git a/bot.py b/bot.py index fe28710..1511773 100644 --- a/bot.py +++ b/bot.py @@ -17,6 +17,7 @@ from oracle_interface import to_usdt, to_ton, to_bigint from utils import float_conversion, int_conversion from market_price import get_ton_usdt_price +from mariadb_connector import get_alarm_from_db, update_alarm_to_db load_dotenv() @@ -31,23 +32,23 @@ workchain=0, ) QUOTE_JETTON_WALLET = Address("kQCQ1B7B7-CrvxjsqgYT90s7weLV-IJB2w08DBslDdrIXucv") -PATH_TO_ALARM_JSON = "data/alarm.json" async def load_alarms(): - with open(PATH_TO_ALARM_JSON, "r") as file: - return json.load(file) + return await get_alarm_from_db() async def save_alarms(updated_alarms): - with open(PATH_TO_ALARM_JSON, "w") as file: - json.dump(updated_alarms, file, indent=4) + await update_alarm_to_db(updated_alarms) async def find_active_alarm(): alarms = await load_alarms() total_alarms = await get_total_amount() + if alarms is None: + return [] + # Determine if there are new alarms and which are active alarms_to_check = [] for i in range(total_alarms): diff --git a/mariadb.py b/mariadb.py deleted file mode 100644 index 7844090..0000000 --- a/mariadb.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -from dotenv import load_dotenv - -import mysql.connector as connector -import mysql.connector.errors as Error - -load_dotenv() - - -def create_connection(): - try: - connection = connector.connect( - host=os.getenv("MYSQL_HOST"), # Assuming MariaDB is running on localhost - database=os.getenv("MYSQL_DATABASE"), - user=os.getenv("MYSQL_USER"), - password=os.getenv("MYSQL_PASSWORD"), - ) - if connection.is_connected(): - db_info = connection.get_server_info() - print("Successfully connected to MariaDB server version ", db_info) - return connection - else: - print("Failed to connect to MariaDB server") - return None - except Error as e: - print("Error while connecting to MariaDB", e) - return None - - -def main(): - connection = create_connection() - if connection is not None and connection.is_connected(): - # Add your database operations here - connection.close() - - -if __name__ == "__main__": - main() diff --git a/mariadb_connector.py b/mariadb_connector.py new file mode 100644 index 0000000..1096516 --- /dev/null +++ b/mariadb_connector.py @@ -0,0 +1,107 @@ +import os +from dotenv import load_dotenv +import asyncio + +import mysql.connector as connector +import mysql.connector.errors as Error + +load_dotenv() + +MYSQL_DATABASE = os.getenv("MYSQL_DATABASE") + + +async def create_connection(): + try: + connection = connector.connect( + host=os.getenv("MYSQL_HOST"), # Assuming MariaDB is running on localhost + database=os.getenv("MYSQL_DATABASE"), + user=os.getenv("MYSQL_USER"), + password=os.getenv("MYSQL_PASSWORD"), + ) + if connection.is_connected(): + db_info = connection.get_server_info() + print("Successfully connected to MariaDB server version ", db_info) + return connection + else: + print("Failed to connect to MariaDB server") + return None + except Error as e: + print("Error while connecting to MariaDB", e) + return None + + +async def init(): + connection = await create_connection() + if connection is not None and connection.is_connected(): + cursor = connection.cursor() + create_table_sql = """ + CREATE TABLE IF NOT EXISTS your_table ( + id INT PRIMARY KEY, + address VARCHAR(255), + state VARCHAR(100), + price DECIMAL(10, 2) + ) + """ + cursor.execute(create_table_sql) + connection.commit() + connection.close() + + +async def get_alarm_from_db(): + try: + connection = await create_connection() + if connection is not None and connection.is_connected(): + cursor = connection.cursor() + select_sql = "SELECT * FROM %s" + cursor.execute(select_sql, [MYSQL_DATABASE]) + result = {} + for id, address, state, price in cursor.fetchall(): + result[id] = {} + result[id]["address"] = address + result[id]["state"] = state + result[id]["price"] = price + + cursor.close() + connection.close() + return result + + except Error as e: + print("Error while getting alarm info", e) + return None + + +async def update_alarm_to_db(alarm_dict): + try: + if alarm_dict is None: + return None + connection = await create_connection() + if connection is not None and connection.is_connected(): + cursor = connection.cursor() + update_sql = """ + INSERT INTO %s (id, address, state, price) + VALUES (%s, %s, %s, %s) + ON DUPLICATE KEY UPDATE address = VALUES(address), state = VALUES(state), price = VALUES(price) + """ + insert_list = [] + for alarm_id, alarm_info in alarm_dict.items(): + insert_list.append( + ( + MYSQL_DATABASE, + alarm_id, + alarm_info["address"], + alarm_info["state"], + alarm_info["price"], + ) + ) + cursor.executemany(update_sql, insert_list) + connection.commit() + cursor.close() + connection.close() + print("Successfully updated alarm info") + + except Error as e: + print("Error while updating alarm info", e) + + +if __name__ == "__main__": + asyncio.run(init()) diff --git a/oracle_interface.py b/oracle_interface.py index 8708e95..be144de 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -14,6 +14,7 @@ from utils import float_conversion, int_conversion, to_token from ton_center_client import TonCenterTonClient +from mariadb_connector import get_alarm_from_db, update_alarm_to_db load_dotenv() @@ -29,7 +30,6 @@ version=WalletVersionEnum.v4r2, workchain=0, ) -PATH_TO_ALARM_JSON = "data/alarm.json" def to_usdt(amount: Union[int, float, str, Decimal]) -> Decimal: @@ -89,9 +89,11 @@ async def get_token_balance(address: str): async def check_alarms(alarm_id_list: list): client = TonCenterTonClient(API_KEY) - # open alarm.json - with open(PATH_TO_ALARM_JSON, "r") as f: - alarm_dict = json.load(f) + + # get alarm dict + alarm_dict = await get_alarm_from_db() + if alarm_dict is None: + return [] # get alarm address bytes tasks = [ @@ -130,9 +132,9 @@ async def check_alarms(alarm_id_list: list): alarm_info = alarm_dict[str(alarm_id)] if alarm_info["state"] == "active": result.append((alarm_id, alarm_info["address"])) - # save alarm.json - with open(PATH_TO_ALARM_JSON, "w") as f: - json.dump(alarm_dict, f, indent=4) + + # update alarm dict to db + await update_alarm_to_db(alarm_dict) return result @@ -237,15 +239,14 @@ async def wind( wind_result = results[1] if wind_result["@type"] == "ok": - with open(PATH_TO_ALARM_JSON, "r") as f: - alarm_dict = json.load(f) - alarm_dict[str(alarm_id)] = { - "address": "is Mine", - "state": "active", - "price": to_bigint(new_price), + update_alarm_dict = { + str(alarm_id): { + "address": "is Mine", + "state": "active", + "price": to_bigint(new_price), + } } - with open(PATH_TO_ALARM_JSON, "w") as f: - json.dump(alarm_dict, f, indent=4) + await update_alarm_to_db(update_alarm_dict) return wind_result, alarm_id @@ -272,11 +273,11 @@ async def ring( ) boc = query["message"].to_boc(False) - with open(PATH_TO_ALARM_JSON, "r") as f: - alarm_dict = json.load(f) - alarm_dict[str(alarm_id)]["state"] = "uninitialied" - with open(PATH_TO_ALARM_JSON, "w") as f: - json.dump(alarm_dict, f, indent=4) + update_alarm_dict = { + str(alarm_id): {"address": "is Mine", "state": "uninitialied", "price": 0} + } + + await update_alarm_to_db(update_alarm_dict) return await client.send_boc(boc) diff --git a/run.sh b/run.sh index 54be014..2d1c3cc 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,9 @@ #!/bin/sh -python ./mariadb.py +# init database +python ./mariadb_connector.py + +# run market price and bot +python ./market_price.py & +python ./bot.py & +wait From 39c7cbcaade61797f11fb4f6ec2ce4eadab5fbe7 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 01:16:21 +0800 Subject: [PATCH 24/35] Fix: query erro --- mariadb_connector.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mariadb_connector.py b/mariadb_connector.py index 1096516..6749aac 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -52,8 +52,9 @@ async def get_alarm_from_db(): connection = await create_connection() if connection is not None and connection.is_connected(): cursor = connection.cursor() - select_sql = "SELECT * FROM %s" - cursor.execute(select_sql, [MYSQL_DATABASE]) + select_sql = "SELECT * FROM {}" + select_sql = select_sql.format(MYSQL_DATABASE) + cursor.execute(select_sql) result = {} for id, address, state, price in cursor.fetchall(): result[id] = {} @@ -78,15 +79,15 @@ async def update_alarm_to_db(alarm_dict): if connection is not None and connection.is_connected(): cursor = connection.cursor() update_sql = """ - INSERT INTO %s (id, address, state, price) + INSERT INTO {} (id, address, state, price) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE address = VALUES(address), state = VALUES(state), price = VALUES(price) """ + update_sql = update_sql.format(MYSQL_DATABASE) insert_list = [] for alarm_id, alarm_info in alarm_dict.items(): insert_list.append( ( - MYSQL_DATABASE, alarm_id, alarm_info["address"], alarm_info["state"], From 7421406b378548ff5c7c29955a3835919f604414 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 03:14:16 +0800 Subject: [PATCH 25/35] Finish!!!! --- .env.example | 12 +++++++++- bot.py | 7 +++--- docker-compose.yml | 3 +++ mariadb_connector.py | 53 +++++++++++++++++++++++++++----------------- oracle_interface.py | 45 +++++++++++++++++++++++-------------- 5 files changed, 78 insertions(+), 42 deletions(-) diff --git a/.env.example b/.env.example index f6ffd4c..5cd0e66 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,12 @@ TEST_TONCENTER_API_KEY= -MNEMONICS=a b c d e f g \ No newline at end of file +MNEMONICS=a b c d e f g + +REDIS_HOST=docker-redis-container-name +REDIS_PORT=6379 +REDIS_DB=0 + +MYSQL_HOST=docker-mysql-container-name +MYSQL_PASSWORD= +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= diff --git a/bot.py b/bot.py index 1511773..b42f7a7 100644 --- a/bot.py +++ b/bot.py @@ -52,11 +52,10 @@ async def find_active_alarm(): # Determine if there are new alarms and which are active alarms_to_check = [] for i in range(total_alarms): - str_i = str(i) - if str_i not in alarms or ( - alarms[str_i]["address"] != "is Mine" and alarms[str_i]["state"] == "active" + if i not in alarms or ( + alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" ): - alarms_to_check.append(str_i) + alarms_to_check.append(i) print("alarms: ", alarms_to_check) # Check alarms and get active alarms [(id, address)] active_alarms = await check_alarms(alarms_to_check) diff --git a/docker-compose.yml b/docker-compose.yml index e7ab8a0..252457d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,9 @@ services: image: skyline9981/ticton-oracle-bot:1-containerize-the-bot platform: linux/amd64 restart: always + # build: + # context: . + # dockerfile: ./Dockerfile env_file: - .env depends_on: diff --git a/mariadb_connector.py b/mariadb_connector.py index 6749aac..9381f68 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -3,7 +3,6 @@ import asyncio import mysql.connector as connector -import mysql.connector.errors as Error load_dotenv() @@ -25,26 +24,33 @@ async def create_connection(): else: print("Failed to connect to MariaDB server") return None - except Error as e: + except Exception as e: print("Error while connecting to MariaDB", e) - return None async def init(): - connection = await create_connection() - if connection is not None and connection.is_connected(): - cursor = connection.cursor() - create_table_sql = """ - CREATE TABLE IF NOT EXISTS your_table ( - id INT PRIMARY KEY, - address VARCHAR(255), - state VARCHAR(100), - price DECIMAL(10, 2) - ) - """ - cursor.execute(create_table_sql) - connection.commit() - connection.close() + try: + connection = await create_connection() + if connection is not None and connection.is_connected(): + cursor = connection.cursor() + create_table_sql = """ + CREATE TABLE IF NOT EXISTS alarms ( + id INT PRIMARY KEY, + address VARCHAR(255), + state VARCHAR(100), + price DECIMAL(10, 2) + ) + """ + cursor.execute(create_table_sql) + connection.commit() + connection.close() + print("Successfully created table") + + return True + + except Exception as e: + print("Error while creating table", e) + return False async def get_alarm_from_db(): @@ -54,6 +60,7 @@ async def get_alarm_from_db(): cursor = connection.cursor() select_sql = "SELECT * FROM {}" select_sql = select_sql.format(MYSQL_DATABASE) + print("Select SQL", select_sql) cursor.execute(select_sql) result = {} for id, address, state, price in cursor.fetchall(): @@ -66,7 +73,7 @@ async def get_alarm_from_db(): connection.close() return result - except Error as e: + except Exception as e: print("Error while getting alarm info", e) return None @@ -100,9 +107,15 @@ async def update_alarm_to_db(alarm_dict): connection.close() print("Successfully updated alarm info") - except Error as e: + except Exception as e: print("Error while updating alarm info", e) +async def main(): + flag = False + while not flag: + flag = await init() + + if __name__ == "__main__": - asyncio.run(init()) + asyncio.run(main()) diff --git a/oracle_interface.py b/oracle_interface.py index be144de..1e6972c 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -94,21 +94,24 @@ async def check_alarms(alarm_id_list: list): alarm_dict = await get_alarm_from_db() if alarm_dict is None: return [] - + print("@ Alarm Dict", alarm_dict) # get alarm address bytes - tasks = [ - client.run_get_method( - ORACLE.to_string(), "getAlarmAddress", [["num", alarm_id]] - ) - for alarm_id in alarm_id_list - ] - results = await asyncio.gather(*tasks) - address_bytes_list = [result["bytes"] for result in results] + address_bytes_list = [] + # 5 tasks one time + for i in range(0, len(alarm_id_list), 5): + tasks = [ + client.run_get_method( + ORACLE.to_string(), "getAlarmAddress", [["num", alarm_id]] + ) + for alarm_id in alarm_id_list[i : i + 5] + ] + results = await asyncio.gather(*tasks) + address_bytes_list += [result["bytes"] for result in results] # get alarm address address_list = [] for alarm_id, address_bytes in zip(alarm_id_list, address_bytes_list): - alarm_info = alarm_dict.get(str(alarm_id)) + alarm_info = alarm_dict.get(alarm_id) if alarm_info and alarm_info.get("address"): address_list.append(alarm_info["address"]) else: @@ -116,23 +119,31 @@ async def check_alarms(alarm_id_list: list): # cell_bytes = base64.b64decode(result[0][1]["bytes"]) cs = CellSlice(address_bytes) address = cs.load_address() - alarm_dict[str(alarm_id)] = {"address": address} + alarm_dict[alarm_id] = {"address": address} address_list.append(address) # get alarm state - tasks = [client.get_address_state(address) for address in address_list] - alarm_state_list = await asyncio.gather(*tasks) + alarm_state_list = [] + # 5 tasks one time + for i in range(0, len(address_list), 5): + tasks = [ + client.get_address_state(address) for address in address_list[i : i + 5] + ] + alarm_state_list += await asyncio.gather(*tasks) # update alarm dict for alarm_id, alarm_state in zip(alarm_id_list, alarm_state_list): - alarm_dict[str(alarm_id)]["state"] = alarm_state + alarm_dict[alarm_id]["state"] = alarm_state + alarm_dict[alarm_id]["price"] = -1 result = [] for alarm_id in alarm_id_list: - alarm_info = alarm_dict[str(alarm_id)] + alarm_info = alarm_dict[alarm_id] if alarm_info["state"] == "active": result.append((alarm_id, alarm_info["address"])) + print("@ Alarm Dict", alarm_dict) + # update alarm dict to db await update_alarm_to_db(alarm_dict) @@ -240,7 +251,7 @@ async def wind( if wind_result["@type"] == "ok": update_alarm_dict = { - str(alarm_id): { + alarm_id: { "address": "is Mine", "state": "active", "price": to_bigint(new_price), @@ -274,7 +285,7 @@ async def ring( boc = query["message"].to_boc(False) update_alarm_dict = { - str(alarm_id): {"address": "is Mine", "state": "uninitialied", "price": 0} + alarm_id: {"address": "is Mine", "state": "uninitialied", "price": 0} } await update_alarm_to_db(update_alarm_dict) From d53525853bcdff2d5de00b016ac6e2a7e9eb5cc5 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 04:18:52 +0800 Subject: [PATCH 26/35] Update: dot env --- .env.example | 2 ++ bot.py | 5 ++--- oracle_interface.py | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 5cd0e66..d42e7ea 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,7 @@ TEST_TONCENTER_API_KEY= MNEMONICS=a b c d e f g +ORACLE_ADDRESS= +QUOTE_JETTON_WALLET_ADDRESS= REDIS_HOST=docker-redis-container-name REDIS_PORT=6379 diff --git a/bot.py b/bot.py index b42f7a7..dbc046b 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,4 @@ import asyncio -import json import os from dotenv import load_dotenv @@ -24,14 +23,14 @@ THRESHOLD_PRICE = float_conversion(1) * to_usdt(1) // to_ton(1) MIN_BASEASSET_THRESHOLD = to_ton(1) EXTRA_FEES = to_ton(1) -ORACLE = Address("kQCFEtu7e-su_IvERBf4FwEXvHISf99lnYuujdo0xYabZQgW") +ORACLE = Address(os.getenv("ORACLE_ADDRESS")) MNEMONICS, PUB_K, PRIV_K, WALLET = Wallets.from_mnemonics( mnemonics=str(os.getenv("MNEMONICS")).split(" "), version=WalletVersionEnum.v4r2, workchain=0, ) -QUOTE_JETTON_WALLET = Address("kQCQ1B7B7-CrvxjsqgYT90s7weLV-IJB2w08DBslDdrIXucv") +QUOTE_JETTON_WALLET = Address(os.getenv(("QUOTE_JETTON_WALLET_ADDRESS"))) async def load_alarms(): diff --git a/oracle_interface.py b/oracle_interface.py index 1e6972c..7d33c3b 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -10,7 +10,6 @@ import os import time import asyncio -import json from utils import float_conversion, int_conversion, to_token from ton_center_client import TonCenterTonClient @@ -23,7 +22,7 @@ GAS_FEE = to_nano(1, "ton") API_KEY = os.getenv("TEST_TONCENTER_API_KEY") -ORACLE = Address("kQCFEtu7e-su_IvERBf4FwEXvHISf99lnYuujdo0xYabZQgW") +ORACLE = Address(os.getenv("ORACLE_ADDRESS")) MNEMONICS, PUB_K, PRIV_K, WALLET = Wallets.from_mnemonics( mnemonics=str(os.getenv("MNEMONICS")).split(" "), @@ -193,7 +192,7 @@ async def tick( ) query = watchmaker.create_transfer_message( - to_addr="kQCQ1B7B7-CrvxjsqgYT90s7weLV-IJB2w08DBslDdrIXucv", + to_addr=os.getenv("QUOTE_JETTON_WALLET_ADDRESS"), amount=to_nano(4, "ton"), seqno=int(seqno, 16), payload=body, @@ -236,7 +235,7 @@ async def wind( .end_cell() ) query = timekeeper.create_transfer_message( - to_addr="kQCQ1B7B7-CrvxjsqgYT90s7weLV-IJB2w08DBslDdrIXucv", + to_addr=os.getenv("QUOTE_JETTON_WALLET_ADDRESS"), amount=to_bigint(need_base_asset) + GAS_FEE, seqno=int(seqno, 16), payload=body, From f8912d7d99790b11fc35c5fb3011bf545ab43d36 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 04:59:25 +0800 Subject: [PATCH 27/35] Add: logging --- bot.py | 27 ++++++++++++++++++--------- log_config.py | 10 ++++++++++ mariadb_connector.py | 21 +++++++++++---------- market_price.py | 7 ++++++- oracle_interface.py | 17 ++++++++++++++--- 5 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 log_config.py diff --git a/bot.py b/bot.py index dbc046b..d9d12af 100644 --- a/bot.py +++ b/bot.py @@ -1,6 +1,9 @@ import asyncio import os from dotenv import load_dotenv +from log_config import setup_logging +import logging + from tonsdk.utils import Address from tonsdk.contract.wallet import Wallets, WalletVersionEnum @@ -18,6 +21,8 @@ from market_price import get_ton_usdt_price from mariadb_connector import get_alarm_from_db, update_alarm_to_db +setup_logging() + load_dotenv() THRESHOLD_PRICE = float_conversion(1) * to_usdt(1) // to_ton(1) @@ -55,7 +60,7 @@ async def find_active_alarm(): alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" ): alarms_to_check.append(i) - print("alarms: ", alarms_to_check) + logging.info(f"Alarms to Check: {alarms_to_check}") # Check alarms and get active alarms [(id, address)] active_alarms = await check_alarms(alarms_to_check) @@ -63,6 +68,7 @@ async def find_active_alarm(): async def estimate(alarm: tuple, price: float, base_bal, quote_bal): + logging.info("Estimate Alarm", alarm[0]) alarm_info = await get_alarm_info(alarm[1]) # alarm[1] is address new_price = float_conversion(price) * to_usdt(1) // to_ton(1) old_price = alarm_info["base_asset_price"] @@ -114,14 +120,15 @@ async def check_balance( need_quote: int, max_buy_num: int, ): + logging.info("Check Balance") if base_bal < need_base: - print("Insufficient base asset balance") + logging.info("Insufficient Base Asset Balance") return None if quote_bal < need_quote: - print("Insufficient quote asset balance") + logging.info("Insufficient Quote Asset Balance") return None if max_buy_num == 0: - print("Max buy num is 0") + logging.info("Max Buy Num is 0") return None # Check if enough balance @@ -153,9 +160,9 @@ async def wind_alarms(active_alarms, price, base_bal, quote_bal): ) base_bal -= alarm_info["need_base_asset"] quote_bal -= alarm_info["need_quote_asset"] - print("Alarm", alarm[0], "finished") + logging.info("Alarm", alarm[0], "Wind Successfully") - print("Alarm", alarm[0], "no need to wind") + logging.info("Alarm", alarm[0], "No Need to Wind") async def tick_one_scale(price, base_bal, quote_bal): @@ -190,13 +197,15 @@ async def main(): price = await get_ton_usdt_price() if price is None: continue - print("Current price:", price) + # =========== New Price Get =========== + logging.info("========== New Price Get ===========") + logging.info(f"New Price: {price}") base_bal = await get_address_balance(WALLET.address.to_string()) quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) active_alarms = await find_active_alarm() - print("Active alarms:", active_alarms) + logging.info(f"Active Alarms: {active_alarms}") if active_alarms == []: - print("No active alarms") + logging.info("No Active Alarms") await tick_one_scale(price, base_bal, quote_bal) await wind_alarms(active_alarms, price, base_bal, quote_bal) diff --git a/log_config.py b/log_config.py new file mode 100644 index 0000000..a0827d1 --- /dev/null +++ b/log_config.py @@ -0,0 +1,10 @@ +import logging + + +def setup_logging(): + logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(funcName)s:%(lineno)d)", + datefmt="%Y-%m-%d %H:%M:%S", + handlers=[logging.FileHandler("debug.log"), logging.StreamHandler()], + ) diff --git a/mariadb_connector.py b/mariadb_connector.py index 9381f68..68334d1 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -4,6 +4,12 @@ import mysql.connector as connector +from log_config import setup_logging +import logging + +setup_logging() + + load_dotenv() MYSQL_DATABASE = os.getenv("MYSQL_DATABASE") @@ -18,14 +24,11 @@ async def create_connection(): password=os.getenv("MYSQL_PASSWORD"), ) if connection.is_connected(): - db_info = connection.get_server_info() - print("Successfully connected to MariaDB server version ", db_info) return connection else: - print("Failed to connect to MariaDB server") return None except Exception as e: - print("Error while connecting to MariaDB", e) + logging.error("Error while connecting to MariaDB", e) async def init(): @@ -44,12 +47,12 @@ async def init(): cursor.execute(create_table_sql) connection.commit() connection.close() - print("Successfully created table") + logging.info("Successfully Initialized MariaDB") return True except Exception as e: - print("Error while creating table", e) + logging.error("Error while initializing MariaDB", e) return False @@ -60,7 +63,6 @@ async def get_alarm_from_db(): cursor = connection.cursor() select_sql = "SELECT * FROM {}" select_sql = select_sql.format(MYSQL_DATABASE) - print("Select SQL", select_sql) cursor.execute(select_sql) result = {} for id, address, state, price in cursor.fetchall(): @@ -74,7 +76,7 @@ async def get_alarm_from_db(): return result except Exception as e: - print("Error while getting alarm info", e) + logging.error("Error while getting alarm info", e) return None @@ -105,10 +107,9 @@ async def update_alarm_to_db(alarm_dict): connection.commit() cursor.close() connection.close() - print("Successfully updated alarm info") except Exception as e: - print("Error while updating alarm info", e) + logging.error("Error while updating alarm info", e) async def main(): diff --git a/market_price.py b/market_price.py index d037698..e4c6865 100644 --- a/market_price.py +++ b/market_price.py @@ -4,6 +4,11 @@ import redis from dotenv import load_dotenv +from log_config import setup_logging +import logging + +setup_logging() + load_dotenv() REDIS_HOST = os.getenv("REDIS_HOST", "localhost") @@ -30,7 +35,7 @@ async def fetch_price_from_exchange(exchange_id, symbol="TON/USDT"): ticker = await exchange.fetch_ticker(symbol) return ticker["last"] except Exception as e: - print(f"Error fetching from {exchange_id}: {str(e)}") + logging.error(f"Error while fetching price from {exchange_id}", e) return None diff --git a/oracle_interface.py b/oracle_interface.py index 7d33c3b..1dc68ad 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -7,6 +7,8 @@ from decimal import Decimal from typing import Union from dotenv import load_dotenv +from log_config import setup_logging +import logging import os import time import asyncio @@ -15,6 +17,8 @@ from ton_center_client import TonCenterTonClient from mariadb_connector import get_alarm_from_db, update_alarm_to_db +setup_logging() + load_dotenv() QUOTEASSET_DECIMALS = 6 @@ -87,13 +91,13 @@ async def get_token_balance(address: str): async def check_alarms(alarm_id_list: list): + logging.info("Checking Alarms State") client = TonCenterTonClient(API_KEY) # get alarm dict alarm_dict = await get_alarm_from_db() if alarm_dict is None: return [] - print("@ Alarm Dict", alarm_dict) # get alarm address bytes address_bytes_list = [] # 5 tasks one time @@ -141,8 +145,6 @@ async def check_alarms(alarm_id_list: list): if alarm_info["state"] == "active": result.append((alarm_id, alarm_info["address"])) - print("@ Alarm Dict", alarm_dict) - # update alarm dict to db await update_alarm_to_db(alarm_dict) @@ -160,6 +162,9 @@ async def tick( expire_at=int(time.time()) + 1000, extra_fees=2, ): + logging.info("Tick") + logging.info("quote_asset_to_transfer: ", quote_asset_to_transfer) + logging.info("base_asset_to_transfer: ", base_asset_to_transfer) base_asset_price = float_conversion(to_usdt(quote_asset_to_transfer)) // to_ton( base_asset_to_transfer ) @@ -211,6 +216,10 @@ async def tick( async def wind( timekeeper, oracle, alarm_id, buy_num, new_price, need_quoate_asset, need_base_asset ): + logging.info("Wind") + logging.info("alarm_id: ", alarm_id) + logging.info("buy_num: ", buy_num) + logging.info("new_price: ", new_price) client = TonCenterTonClient(API_KEY) seqno = await client.run_get_method(timekeeper.address.to_string(), "seqno", []) forward_info = ( @@ -266,6 +275,8 @@ async def ring( oracle, alarm_id, ): + logging.info("Ring") + logging.info("alarm_id: ", alarm_id) client = TonCenterTonClient(API_KEY) seqno = await client.run_get_method(watchmaker.address.to_string(), "seqno", []) body = ( From e5c8577061f3be4d324b97a443ee3ffd3a5eaa6f Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 05:54:05 +0800 Subject: [PATCH 28/35] Update: local logging --- bot.py | 35 +++++++++++++++++++++-------------- log_config.py | 10 ---------- mariadb_connector.py | 21 ++++++++++++++------- market_price.py | 13 ++++++++++--- oracle_interface.py | 33 +++++++++++++++++++++------------ 5 files changed, 66 insertions(+), 46 deletions(-) delete mode 100644 log_config.py diff --git a/bot.py b/bot.py index d9d12af..215de52 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,6 @@ import asyncio import os from dotenv import load_dotenv -from log_config import setup_logging import logging @@ -21,10 +20,18 @@ from market_price import get_ton_usdt_price from mariadb_connector import get_alarm_from_db, update_alarm_to_db -setup_logging() - load_dotenv() +# set up logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) + THRESHOLD_PRICE = float_conversion(1) * to_usdt(1) // to_ton(1) MIN_BASEASSET_THRESHOLD = to_ton(1) EXTRA_FEES = to_ton(1) @@ -60,7 +67,7 @@ async def find_active_alarm(): alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" ): alarms_to_check.append(i) - logging.info(f"Alarms to Check: {alarms_to_check}") + logger.info(f"Alarms to Check: {alarms_to_check}") # Check alarms and get active alarms [(id, address)] active_alarms = await check_alarms(alarms_to_check) @@ -68,7 +75,7 @@ async def find_active_alarm(): async def estimate(alarm: tuple, price: float, base_bal, quote_bal): - logging.info("Estimate Alarm", alarm[0]) + logger.info(f"Estimate Alarm {alarm[0]}") alarm_info = await get_alarm_info(alarm[1]) # alarm[1] is address new_price = float_conversion(price) * to_usdt(1) // to_ton(1) old_price = alarm_info["base_asset_price"] @@ -120,15 +127,15 @@ async def check_balance( need_quote: int, max_buy_num: int, ): - logging.info("Check Balance") + logger.info("Check Balance") if base_bal < need_base: - logging.info("Insufficient Base Asset Balance") + logger.info("Insufficient Base Asset Balance") return None if quote_bal < need_quote: - logging.info("Insufficient Quote Asset Balance") + logger.info("Insufficient Quote Asset Balance") return None if max_buy_num == 0: - logging.info("Max Buy Num is 0") + logger.info("Max Buy Num is 0") return None # Check if enough balance @@ -160,9 +167,9 @@ async def wind_alarms(active_alarms, price, base_bal, quote_bal): ) base_bal -= alarm_info["need_base_asset"] quote_bal -= alarm_info["need_quote_asset"] - logging.info("Alarm", alarm[0], "Wind Successfully") + logger.info(f"Alarm {alarm[0]} Wind Successfully") - logging.info("Alarm", alarm[0], "No Need to Wind") + logger.info(f"Alarm {alarm[0]} No Need to Wind") async def tick_one_scale(price, base_bal, quote_bal): @@ -198,12 +205,12 @@ async def main(): if price is None: continue # =========== New Price Get =========== - logging.info("========== New Price Get ===========") - logging.info(f"New Price: {price}") + logger.info("========== New Price Get ===========") + logger.info(f"New Price: {price}") base_bal = await get_address_balance(WALLET.address.to_string()) quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) active_alarms = await find_active_alarm() - logging.info(f"Active Alarms: {active_alarms}") + logger.info(f"Active Alarms: {active_alarms}") if active_alarms == []: logging.info("No Active Alarms") await tick_one_scale(price, base_bal, quote_bal) diff --git a/log_config.py b/log_config.py deleted file mode 100644 index a0827d1..0000000 --- a/log_config.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging - - -def setup_logging(): - logging.basicConfig( - level=logging.DEBUG, - format="%(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(funcName)s:%(lineno)d)", - datefmt="%Y-%m-%d %H:%M:%S", - handlers=[logging.FileHandler("debug.log"), logging.StreamHandler()], - ) diff --git a/mariadb_connector.py b/mariadb_connector.py index 68334d1..246aee4 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -4,10 +4,17 @@ import mysql.connector as connector -from log_config import setup_logging import logging -setup_logging() +# set up logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) load_dotenv() @@ -28,7 +35,7 @@ async def create_connection(): else: return None except Exception as e: - logging.error("Error while connecting to MariaDB", e) + logger.error(f"Error while connecting to MariaDB {e}") async def init(): @@ -47,12 +54,12 @@ async def init(): cursor.execute(create_table_sql) connection.commit() connection.close() - logging.info("Successfully Initialized MariaDB") + logger.info("Successfully Initialized MariaDB") return True except Exception as e: - logging.error("Error while initializing MariaDB", e) + logger.error("Error while initializing MariaDB", e) return False @@ -76,7 +83,7 @@ async def get_alarm_from_db(): return result except Exception as e: - logging.error("Error while getting alarm info", e) + logger.error(f"Error while fetching alarm info from MariaDB {e}") return None @@ -109,7 +116,7 @@ async def update_alarm_to_db(alarm_dict): connection.close() except Exception as e: - logging.error("Error while updating alarm info", e) + logger.error(f"Error while updating alarm info to MariaDB {e}") async def main(): diff --git a/market_price.py b/market_price.py index e4c6865..457b138 100644 --- a/market_price.py +++ b/market_price.py @@ -4,10 +4,17 @@ import redis from dotenv import load_dotenv -from log_config import setup_logging import logging -setup_logging() +# set up logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) load_dotenv() @@ -35,7 +42,7 @@ async def fetch_price_from_exchange(exchange_id, symbol="TON/USDT"): ticker = await exchange.fetch_ticker(symbol) return ticker["last"] except Exception as e: - logging.error(f"Error while fetching price from {exchange_id}", e) + logger.error(f"Error while fetching price from {exchange_id} {e}") return None diff --git a/oracle_interface.py b/oracle_interface.py index 1dc68ad..4460ca3 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -7,7 +7,6 @@ from decimal import Decimal from typing import Union from dotenv import load_dotenv -from log_config import setup_logging import logging import os import time @@ -17,7 +16,15 @@ from ton_center_client import TonCenterTonClient from mariadb_connector import get_alarm_from_db, update_alarm_to_db -setup_logging() +# set up logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) load_dotenv() @@ -91,7 +98,7 @@ async def get_token_balance(address: str): async def check_alarms(alarm_id_list: list): - logging.info("Checking Alarms State") + logger.info("Checking Alarms State") client = TonCenterTonClient(API_KEY) # get alarm dict @@ -108,6 +115,7 @@ async def check_alarms(alarm_id_list: list): ) for alarm_id in alarm_id_list[i : i + 5] ] + logger.info(f"Getting Alarms Address {alarm_id_list[i : i + 5]}") results = await asyncio.gather(*tasks) address_bytes_list += [result["bytes"] for result in results] @@ -132,6 +140,7 @@ async def check_alarms(alarm_id_list: list): tasks = [ client.get_address_state(address) for address in address_list[i : i + 5] ] + logger.info(f"Checking Alarms State {address_list[i : i + 5]}") alarm_state_list += await asyncio.gather(*tasks) # update alarm dict @@ -162,9 +171,9 @@ async def tick( expire_at=int(time.time()) + 1000, extra_fees=2, ): - logging.info("Tick") - logging.info("quote_asset_to_transfer: ", quote_asset_to_transfer) - logging.info("base_asset_to_transfer: ", base_asset_to_transfer) + logger.info("Tick") + logger.info(f"quote_asset_to_transfer: {quote_asset_to_transfer}") + logger.info(f"base_asset_to_transfer: {base_asset_to_transfer}") base_asset_price = float_conversion(to_usdt(quote_asset_to_transfer)) // to_ton( base_asset_to_transfer ) @@ -216,10 +225,10 @@ async def tick( async def wind( timekeeper, oracle, alarm_id, buy_num, new_price, need_quoate_asset, need_base_asset ): - logging.info("Wind") - logging.info("alarm_id: ", alarm_id) - logging.info("buy_num: ", buy_num) - logging.info("new_price: ", new_price) + logger.info("Wind") + logger.info(f"alarm_id: {alarm_id}") + logger.info(f"buy_num: {buy_num}") + logger.info(f"new_price: {new_price}") client = TonCenterTonClient(API_KEY) seqno = await client.run_get_method(timekeeper.address.to_string(), "seqno", []) forward_info = ( @@ -275,8 +284,8 @@ async def ring( oracle, alarm_id, ): - logging.info("Ring") - logging.info("alarm_id: ", alarm_id) + logger.info("Ring") + logger.info(f"alarm_id: {alarm_id}") client = TonCenterTonClient(API_KEY) seqno = await client.run_get_method(watchmaker.address.to_string(), "seqno", []) body = ( From 15f8ef4dc22666cfc90630ec84b54b8e2e744122 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 05:59:52 +0800 Subject: [PATCH 29/35] Add: Exception error check --- bot.py | 71 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/bot.py b/bot.py index 215de52..ae83569 100644 --- a/bot.py +++ b/bot.py @@ -54,25 +54,29 @@ async def save_alarms(updated_alarms): async def find_active_alarm(): - alarms = await load_alarms() - total_alarms = await get_total_amount() - - if alarms is None: + try: + alarms = await load_alarms() + total_alarms = await get_total_amount() + + if alarms is None: + return [] + + # Determine if there are new alarms and which are active + alarms_to_check = [] + for i in range(total_alarms): + if i not in alarms or ( + alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" + ): + alarms_to_check.append(i) + logger.info(f"Alarms to Check: {alarms_to_check}") + # Check alarms and get active alarms [(id, address)] + active_alarms = await check_alarms(alarms_to_check) + + return active_alarms + except Exception as e: + logger.error(f"Error while finding active alarms {e}") return [] - # Determine if there are new alarms and which are active - alarms_to_check = [] - for i in range(total_alarms): - if i not in alarms or ( - alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" - ): - alarms_to_check.append(i) - logger.info(f"Alarms to Check: {alarms_to_check}") - # Check alarms and get active alarms [(id, address)] - active_alarms = await check_alarms(alarms_to_check) - - return active_alarms - async def estimate(alarm: tuple, price: float, base_bal, quote_bal): logger.info(f"Estimate Alarm {alarm[0]}") @@ -201,21 +205,26 @@ async def tick_one_scale(price, base_bal, quote_bal): async def main(): while True: - price = await get_ton_usdt_price() - if price is None: + try: + price = await get_ton_usdt_price() + if price is None: + continue + # =========== New Price Get =========== + logger.info("========== New Price Get ===========") + logger.info(f"New Price: {price}") + base_bal = await get_address_balance(WALLET.address.to_string()) + quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) + active_alarms = await find_active_alarm() + logger.info(f"Active Alarms: {active_alarms}") + if active_alarms == []: + logging.info("No Active Alarms") + await tick_one_scale(price, base_bal, quote_bal) + + await wind_alarms(active_alarms, price, base_bal, quote_bal) + + except Exception as e: + logger.error(f"Error while running bot {e}") continue - # =========== New Price Get =========== - logger.info("========== New Price Get ===========") - logger.info(f"New Price: {price}") - base_bal = await get_address_balance(WALLET.address.to_string()) - quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) - active_alarms = await find_active_alarm() - logger.info(f"Active Alarms: {active_alarms}") - if active_alarms == []: - logging.info("No Active Alarms") - await tick_one_scale(price, base_bal, quote_bal) - - await wind_alarms(active_alarms, price, base_bal, quote_bal) if __name__ == "__main__": From b40ad7ed298a390d7bc1652ca4c441e6197bd402 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 06:04:48 +0800 Subject: [PATCH 30/35] Update: Exception error check --- bot.py | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/bot.py b/bot.py index ae83569..570631f 100644 --- a/bot.py +++ b/bot.py @@ -54,29 +54,25 @@ async def save_alarms(updated_alarms): async def find_active_alarm(): - try: - alarms = await load_alarms() - total_alarms = await get_total_amount() - - if alarms is None: - return [] - - # Determine if there are new alarms and which are active - alarms_to_check = [] - for i in range(total_alarms): - if i not in alarms or ( - alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" - ): - alarms_to_check.append(i) - logger.info(f"Alarms to Check: {alarms_to_check}") - # Check alarms and get active alarms [(id, address)] - active_alarms = await check_alarms(alarms_to_check) - - return active_alarms - except Exception as e: - logger.error(f"Error while finding active alarms {e}") + alarms = await load_alarms() + total_alarms = await get_total_amount() + + if alarms is None: return [] + # Determine if there are new alarms and which are active + alarms_to_check = [] + for i in range(total_alarms): + if i not in alarms or ( + alarms[i]["address"] != "is Mine" and alarms[i]["state"] == "active" + ): + alarms_to_check.append(i) + logger.info(f"Alarms to Check: {alarms_to_check}") + # Check alarms and get active alarms [(id, address)] + active_alarms = await check_alarms(alarms_to_check) + + return active_alarms + async def estimate(alarm: tuple, price: float, base_bal, quote_bal): logger.info(f"Estimate Alarm {alarm[0]}") From 936dda4bca93c777a9dd29e83f39d86746728492 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 14:29:47 +0800 Subject: [PATCH 31/35] Add: README file --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e1770c..f16b40b 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -# ticton-oracleBot +# Ticton Oracle Bot + +## Overview +The **Ticton Oracle Bot** is an arbitrage bot that operates by fetching the average market price of TON/USDT from multiple exchanges. It then quotes this price to the Ticton Oracle. In doing so, it seeks arbitrage opportunities among other quoters. + +## Prerequisites +- Docker +- Docker Compose + +## Setting Up the Environment +1. **Clone the Repository**: Clone this repository to your local machine. + +2. **Environment Variables**: + - Create a `.env` file in the root directory of the project. + - Fill out your `.env` file using `.env.example` as a guide. + +## Running the Application +1. **Docker Compose**: Navigate to the root directory of the project where the `docker-compose.yml` file is located. +2. **Start the Application**: + - Run the following command: + ``` + docker-compose up -d + ``` + - This command will start all the services defined in your `docker-compose.yml` file. + - Ensure that the `.env` file is correctly set up, as the Docker containers will rely on these environment variables. + From 17f611e3b532d8508797f40431825a0f9e5539bc Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 14:39:33 +0800 Subject: [PATCH 32/35] Update: env example --- .env.example | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index d42e7ea..83a8e08 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,14 @@ TEST_TONCENTER_API_KEY= MNEMONICS=a b c d e f g -ORACLE_ADDRESS= +ORACLE_ADDRESS=kQCFEtu7e-su_IvERBf4FwEXvHISf99lnYuujdo0xYabZQgW QUOTE_JETTON_WALLET_ADDRESS= -REDIS_HOST=docker-redis-container-name +REDIS_HOST=ticton-oracle-bot-redis-1 REDIS_PORT=6379 REDIS_DB=0 -MYSQL_HOST=docker-mysql-container-name -MYSQL_PASSWORD= -MYSQL_ROOT_PASSWORD= -MYSQL_DATABASE= -MYSQL_USER= +MYSQL_HOST=ticton-oracle-bot-mariadb-1 +MYSQL_PASSWORD=secure-password +MYSQL_ROOT_PASSWORD=secure.password +MYSQL_DATABASE=your_dbname +MYSQL_USER=your_name From e3aa4bd1d3ffae2154922d94747cfb9421552d16 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 15:05:01 +0800 Subject: [PATCH 33/35] Fix: database name error --- mariadb_connector.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mariadb_connector.py b/mariadb_connector.py index 246aee4..b553bfd 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -19,8 +19,6 @@ load_dotenv() -MYSQL_DATABASE = os.getenv("MYSQL_DATABASE") - async def create_connection(): try: @@ -69,7 +67,7 @@ async def get_alarm_from_db(): if connection is not None and connection.is_connected(): cursor = connection.cursor() select_sql = "SELECT * FROM {}" - select_sql = select_sql.format(MYSQL_DATABASE) + select_sql = select_sql.format("alarms") cursor.execute(select_sql) result = {} for id, address, state, price in cursor.fetchall(): @@ -99,7 +97,7 @@ async def update_alarm_to_db(alarm_dict): VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE address = VALUES(address), state = VALUES(state), price = VALUES(price) """ - update_sql = update_sql.format(MYSQL_DATABASE) + update_sql = update_sql.format("alarms") insert_list = [] for alarm_id, alarm_info in alarm_dict.items(): insert_list.append( From 65a5b8a59aee1a7993075857266faba590555cb4 Mon Sep 17 00:00:00 2001 From: grace0950 <36180214+grace0950@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:08:59 +0800 Subject: [PATCH 34/35] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f16b40b..f8cf0d9 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ The **Ticton Oracle Bot** is an arbitrage bot that operates by fetching the aver 2. **Environment Variables**: - Create a `.env` file in the root directory of the project. - Fill out your `.env` file using `.env.example` as a guide. + - Obtain your Ton Center Testnet API key from [@tonapibot](https://t.me/tonapibot) ## Running the Application 1. **Docker Compose**: Navigate to the root directory of the project where the `docker-compose.yml` file is located. From e6d6de89f9e55c64748f30e2317d6d0d0867a9e1 Mon Sep 17 00:00:00 2001 From: grace0950 Date: Sat, 20 Jan 2024 17:18:04 +0800 Subject: [PATCH 35/35] Fix: Ring problem and Database overflow --- bot.py | 8 +++++--- mariadb_connector.py | 2 +- oracle_interface.py | 30 ++++++++++++++++-------------- ton_center_client.py | 2 +- utils.py | 2 +- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/bot.py b/bot.py index 570631f..3486111 100644 --- a/bot.py +++ b/bot.py @@ -169,7 +169,8 @@ async def wind_alarms(active_alarms, price, base_bal, quote_bal): quote_bal -= alarm_info["need_quote_asset"] logger.info(f"Alarm {alarm[0]} Wind Successfully") - logger.info(f"Alarm {alarm[0]} No Need to Wind") + else: + logger.info(f"Alarm {alarm[0]} No Need to Wind") async def tick_one_scale(price, base_bal, quote_bal): @@ -205,11 +206,12 @@ async def main(): price = await get_ton_usdt_price() if price is None: continue + price = round(float(price), 9) # =========== New Price Get =========== logger.info("========== New Price Get ===========") logger.info(f"New Price: {price}") - base_bal = await get_address_balance(WALLET.address.to_string()) - quote_bal = await get_token_balance(QUOTE_JETTON_WALLET.to_string()) + base_bal = int(await get_address_balance(WALLET.address.to_string())) + quote_bal = int(await get_token_balance(QUOTE_JETTON_WALLET.to_string())) active_alarms = await find_active_alarm() logger.info(f"Active Alarms: {active_alarms}") if active_alarms == []: diff --git a/mariadb_connector.py b/mariadb_connector.py index b553bfd..18e1178 100644 --- a/mariadb_connector.py +++ b/mariadb_connector.py @@ -46,7 +46,7 @@ async def init(): id INT PRIMARY KEY, address VARCHAR(255), state VARCHAR(100), - price DECIMAL(10, 2) + price DECIMAL(16, 9) ) """ cursor.execute(create_table_sql) diff --git a/oracle_interface.py b/oracle_interface.py index 4460ca3..d26a6a1 100644 --- a/oracle_interface.py +++ b/oracle_interface.py @@ -267,11 +267,12 @@ async def wind( wind_result = results[1] if wind_result["@type"] == "ok": + new_price = int_conversion(new_price * to_ton(1) / to_usdt(1)) update_alarm_dict = { alarm_id: { "address": "is Mine", "state": "active", - "price": to_bigint(new_price), + "price": round(new_price, 9), } } await update_alarm_to_db(update_alarm_dict) @@ -291,7 +292,7 @@ async def ring( body = ( begin_cell() .store_uint(0xC3510A29, 32) - .store_uint(0, 257) + .store_uint(alarm_id, 257) .store_uint(alarm_id, 257) .end_cell() ) @@ -304,24 +305,25 @@ async def ring( boc = query["message"].to_boc(False) update_alarm_dict = { - alarm_id: {"address": "is Mine", "state": "uninitialied", "price": 0} + alarm_id: {"address": "is Mine", "state": "uninitialied", "price": -1} } + result = await client.send_boc(boc) await update_alarm_to_db(update_alarm_dict) - return await client.send_boc(boc) + return result async def main(): - # print( - # await tick( - # watchmaker=WALLET, - # oracle=ORACLE, - # quote_asset_to_transfer=2, - # base_asset_to_transfer=1, - # ) - # ) - print(await ring(WALLET, ORACLE, 10)) + print( + await tick( + watchmaker=WALLET, + oracle=ORACLE, + quote_asset_to_transfer=4, + base_asset_to_transfer=1, + ) + ) + # print(await ring(WALLET, ORACLE, 25)) # print( # await wind( # timekeeper=WALLET, @@ -334,7 +336,7 @@ async def main(): # ) # ) # print(await get_total_amount()) - # print(await check_alarms([1])) + # print(await check_alarms([1, 2, 3])) # print(await get_alarm_info("EQAWIJ3mBo990Ui8kinaodH3AlMi6Q3aPuhUNoFySO08uhEP")) diff --git a/ton_center_client.py b/ton_center_client.py index a87c19d..dd782a5 100644 --- a/ton_center_client.py +++ b/ton_center_client.py @@ -67,7 +67,7 @@ async def send_boc(self, boc): return await self._run(self.provider.raw_send_message(boc)) async def _run(self, to_run): - timeout = aiohttp.ClientTimeout(total=5) + timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession(timeout=timeout) as session: func = to_run["func"] args = to_run["args"] diff --git a/utils.py b/utils.py index 0666302..3e45e76 100644 --- a/utils.py +++ b/utils.py @@ -9,7 +9,7 @@ def float_conversion(value, precision=64): def int_conversion(value): - return Decimal(value) // (Decimal(2) ** 64) + return Decimal(value) / (Decimal(2) ** 64) def to_token(value, decimals):