Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1 containerize the bot #2

Merged
merged 36 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f16458d
Change: market price generator to Redis
grace0950 Jan 19, 2024
c4529e3
Update: requirement.txt
grace0950 Jan 19, 2024
00f5754
* feat(build.yaml): add GitHub Actions workflow to build and push Doc…
skyline9981 Jan 19, 2024
52d7212
Add: Dockerfile
grace0950 Jan 19, 2024
5c89850
* chore(build.yaml): add branch filters for "develop" and "1-containe…
skyline9981 Jan 19, 2024
d9aa683
* chore(Dockerfile): update base image to python:3.10-alpine
skyline9981 Jan 19, 2024
a65450d
Update: Dockerfile, requirement.txt
grace0950 Jan 19, 2024
7cf294c
Merge branch '1-containerize-the-bot' of https://github.com/Ton-Dynas…
skyline9981 Jan 19, 2024
662fcfa
Update: requirement
grace0950 Jan 19, 2024
1a4ebca
Downgrade: bitarray package
grace0950 Jan 19, 2024
1ccb67b
Update: requirement.txt
grace0950 Jan 19, 2024
03a6bfb
Update: Dockerfile
skyline9981 Jan 19, 2024
f4cfadc
Update Python version in Dockerfile
skyline9981 Jan 19, 2024
9c10450
Upgrade: bitarray
grace0950 Jan 19, 2024
e45bbec
Update: requirement!plzzzz!!
grace0950 Jan 19, 2024
0bf853e
* chore(.dockerignore): add .env** and venv/ to the .dockerignore file
skyline9981 Jan 19, 2024
30c5ed6
Add: docker compse
grace0950 Jan 19, 2024
5df2f3d
Use Redis to set price
grace0950 Jan 19, 2024
87f1edc
Fix: Redis get price
grace0950 Jan 19, 2024
f64c841
Add: mariadb connector
grace0950 Jan 19, 2024
93a7834
cry!!!
grace0950 Jan 19, 2024
8f9b8b8
Test: mariadb
grace0950 Jan 19, 2024
dc9e467
Test: mariadb
grace0950 Jan 19, 2024
c16b009
Change: csv file to mariadb
grace0950 Jan 19, 2024
39c7cbc
Fix: query erro
grace0950 Jan 19, 2024
7421406
Finish!!!!
grace0950 Jan 19, 2024
d535258
Update: dot env
grace0950 Jan 19, 2024
f8912d7
Add: logging
grace0950 Jan 19, 2024
e5c8577
Update: local logging
grace0950 Jan 19, 2024
15f8ef4
Add: Exception error check
grace0950 Jan 19, 2024
b40ad7e
Update: Exception error check
grace0950 Jan 19, 2024
936dda4
Add: README file
grace0950 Jan 20, 2024
17f611e
Update: env example
grace0950 Jan 20, 2024
e3aa4bd
Fix: database name error
grace0950 Jan 20, 2024
65a5b8a
Update README.md
grace0950 Jan 20, 2024
e6d6de8
Fix: Ring problem and Database overflow
grace0950 Jan 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env**
venv/
14 changes: 13 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
TEST_TONCENTER_API_KEY=
MNEMONICS=a b c d e f g
MNEMONICS=a b c d e f g
ORACLE_ADDRESS=kQCFEtu7e-su_IvERBf4FwEXvHISf99lnYuujdo0xYabZQgW
QUOTE_JETTON_WALLET_ADDRESS=

REDIS_HOST=ticton-oracle-bot-redis-1
REDIS_PORT=6379
REDIS_DB=0

MYSQL_HOST=ticton-oracle-bot-mariadb-1
MYSQL_PASSWORD=secure-password
MYSQL_ROOT_PASSWORD=secure.password
MYSQL_DATABASE=your_dbname
MYSQL_USER=your_name
56 changes: 56 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Build docker images and push
on:
push:
branches:
- "main"
- "develop"
- "1-containerize-the-bot"
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 }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,5 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
data/

.python-version
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3.10-slim

WORKDIR /usr/src/app

COPY . .

RUN pip install --no-cache-dir -r requirements.txt

CMD ["./run.sh"]

28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# 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.
- 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.
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.

86 changes: 55 additions & 31 deletions bot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import asyncio
import json
import os
from dotenv import load_dotenv
import logging


from tonsdk.utils import Address
from tonsdk.contract.wallet import Wallets, WalletVersionEnum
Expand All @@ -16,54 +17,65 @@
)
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
from mariadb_connector import get_alarm_from_db, update_alarm_to_db

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)
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")
PATH_TO_ALARM_JSON = "data/alarm.json"
QUOTE_JETTON_WALLET = Address(os.getenv(("QUOTE_JETTON_WALLET_ADDRESS")))


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):
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)
print("alarms: ", alarms_to_check)
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]}")
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"]
Expand Down Expand Up @@ -115,14 +127,15 @@ async def check_balance(
need_quote: int,
max_buy_num: int,
):
logger.info("Check Balance")
if base_bal < need_base:
print("Insufficient base asset balance")
logger.info("Insufficient Base Asset Balance")
return None
if quote_bal < need_quote:
print("Insufficient quote asset balance")
logger.info("Insufficient Quote Asset Balance")
return None
if max_buy_num == 0:
print("Max buy num is 0")
logger.info("Max Buy Num is 0")
return None

# Check if enough balance
Expand Down Expand Up @@ -154,9 +167,10 @@ 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")
logger.info(f"Alarm {alarm[0]} Wind Successfully")

print("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):
Expand Down Expand Up @@ -187,18 +201,28 @@ 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:
print("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)
if active_alarms == []:
print("No active alarms")
await tick_one_scale(price, base_bal, quote_bal)

await wind_alarms(active_alarms, price, base_bal, quote_bal)
while True:
try:
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 = 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 == []:
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


if __name__ == "__main__":
Expand Down
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: '3'
services:
app:
image: skyline9981/ticton-oracle-bot:1-containerize-the-bot
platform: linux/amd64
restart: always
# build:
# context: .
# dockerfile: ./Dockerfile
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:
Loading