Skip to content
This repository has been archived by the owner on May 13, 2024. It is now read-only.

Commit

Permalink
Merge branch 'release/0.4.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
beeb committed Jun 14, 2021
2 parents 95b6950 + 9143576 commit 3f762ae
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 104 deletions.
19 changes: 10 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM python:3.9-slim-buster AS build-deps
RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes build-essential curl && \
apt-get install --no-install-recommends --yes build-essential curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

Expand All @@ -19,6 +19,7 @@ ENV PYTHONUNBUFFERED=1 \
POETRY_NO_ANSI=1

# install poetry
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -

# copy source and install deps
Expand All @@ -35,22 +36,22 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app

ARG USER_ID=1000
ARG GROUP_ID=1000
RUN groupadd -r -g $GROUP_ID pancaketrade && \
useradd --no-log-init -rm -u $USER_ID -g pancaketrade -s /bin/bash pancaketrade
useradd --no-log-init -rm -u $USER_ID -g pancaketrade -s /bin/bash pancaketrade && \
chown pancaketrade:pancaketrade -R /app

WORKDIR /app
# the user needs to be able to write the database file to /app
RUN chown pancaketrade:pancaketrade /app
USER pancaketrade

ENV PYTHONUNBUFFERED=1 \
VENV_PATH="/app/.venv" \
PATH="/app/.venv/bin:$PATH"

COPY --from=build-deps /app .
PATH="/app/.venv/bin:$PATH" \
USER="pancaketrade"

USER pancaketrade
COPY --from=build-deps --chown=pancaketrade:pancaketrade /app .

ENTRYPOINT [ "trade" ]
CMD [ "user_data/config.yml" ]
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ secrets:
admin_chat_id: 123456 # enter your chat ID/user ID to prevent other users to use the bot
```
## Updating the bot
When a new version is released, if you cloned the repository with git, you can simply perform a `git pull` on the master
branch. After that, run the `poetry install --no-dev` command again to update dependencies.

## Use docker

This bot now gets published as docker images on [Docker Hub](https://hub.docker.com/repository/docker/vbersier/pancaketrade).
Expand Down
6 changes: 3 additions & 3 deletions pancaketrade/conversations/addorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ def command_addorder(self, update: Update, context: CallbackContext):
reply_markup = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton('Stop loss sell', callback_data='stop_loss'),
InlineKeyboardButton('Take profit sell', callback_data='limit_sell'),
InlineKeyboardButton('🚫 Stop loss sell', callback_data='stop_loss'),
InlineKeyboardButton('💰 Take profit sell', callback_data='limit_sell'),
],
[
InlineKeyboardButton('Limit buy', callback_data='limit_buy'),
InlineKeyboardButton('💵 Limit buy', callback_data='limit_buy'),
InlineKeyboardButton('❌ Cancel', callback_data='cancel'),
],
]
Expand Down
12 changes: 11 additions & 1 deletion pancaketrade/conversations/edittoken.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,24 @@ def command_edittoken_buyprice(self, update: Update, context: CallbackContext):
assert update.message.text
user_input = update.message.text.strip().lower()
if 'bnb' in user_input:
balance = self.net.get_token_balance(token_address=token.address)
if balance == 0: # would lead to division by zero
chat_message(
update,
context,
text='⚠️ The token balance is zero, can\'t use calculation from BNB amount. '
+ 'Try again with a price instead:',
edit=False,
)
return self.next.BUYPRICE
try:
buy_amount = Decimal(user_input[:-3])
except Exception:
chat_message(
update, context, text='⚠️ The BNB amount you inserted is not valid. Try again:', edit=False
)
return self.next.BUYPRICE
effective_buy_price = buy_amount / self.net.get_token_balance(token_address=token.address)
effective_buy_price = buy_amount / balance
else:
try:
effective_buy_price = Decimal(user_input)
Expand Down
27 changes: 18 additions & 9 deletions pancaketrade/network/bsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from web3 import Web3
from web3.contract import Contract, ContractFunction
from web3.exceptions import ABIFunctionNotFound, ContractLogicError
from web3.types import ChecksumAddress, HexBytes, Nonce, TxParams, TxReceipt, Wei
from web3.middleware import geth_poa_middleware
from web3.types import BlockIdentifier, ChecksumAddress, HexBytes, Nonce, TxParams, TxReceipt, Wei

GAS_LIMIT_FAILSAFE = Wei(1000000) # if the estimated limit is above this one, don't use the estimated price

Expand Down Expand Up @@ -60,6 +61,7 @@ def __init__(self, rpc: str, wallet: ChecksumAddress, min_pool_size_bnb: float,
session.mount('https://', adapter)
w3_provider = Web3.HTTPProvider(endpoint_uri=rpc, session=session)
self.w3 = Web3(provider=w3_provider)
self.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
self.addr = NetworkAddresses()
self.contracts = NetworkContracts(addr=self.addr, w3=self.w3)
self.max_approval_hex = f"0x{64 * 'f'}"
Expand Down Expand Up @@ -193,14 +195,21 @@ def get_token_price(
return min(price_v1, price_v2), price_v2 < price_v1

def get_token_price_by_lp(
self, token_contract: Contract, token_lp: ChecksumAddress, token_decimals: int, ignore_poolsize: bool = False
self,
token_contract: Contract,
token_lp: ChecksumAddress,
token_decimals: int,
ignore_poolsize: bool = False,
block_identifier: BlockIdentifier = 'latest',
) -> Decimal:
lp_bnb_amount = Decimal(self.contracts.wbnb.functions.balanceOf(token_lp).call())
lp_bnb_amount = Decimal(
self.contracts.wbnb.functions.balanceOf(token_lp).call(block_identifier=block_identifier)
)
if lp_bnb_amount / Decimal(10 ** 18) < self.min_pool_size_bnb and not ignore_poolsize: # not enough liquidity
return Decimal(0)
lp_token_amount = Decimal(token_contract.functions.balanceOf(token_lp).call()) * Decimal(
10 ** (18 - token_decimals)
)
lp_token_amount = Decimal(
token_contract.functions.balanceOf(token_lp).call(block_identifier=block_identifier)
) * Decimal(10 ** (18 - token_decimals))
# normalize to 18 decimals
try:
bnb_per_token = lp_bnb_amount / lp_token_amount
Expand Down Expand Up @@ -310,7 +319,7 @@ def buy_tokens(
final_gas_price = self.w3.eth.gas_price
if gas_price is not None and gas_price.startswith('+'):
offset = Web3.toWei(Decimal(gas_price) * Decimal(10 ** 9), unit='wei')
final_gas_price += offset
final_gas_price = Wei(final_gas_price + offset)
elif gas_price is not None:
final_gas_price = Web3.toWei(gas_price, unit='wei')
router_contract = self.contracts.router_v2 if v2 else self.contracts.router_v1
Expand Down Expand Up @@ -388,7 +397,7 @@ def sell_tokens(
final_gas_price = self.w3.eth.gas_price
if gas_price is not None and gas_price.startswith('+'):
offset = Web3.toWei(Decimal(gas_price) * Decimal(10 ** 9), unit='wei')
final_gas_price += offset
final_gas_price = Wei(final_gas_price + offset)
elif gas_price is not None:
final_gas_price = Web3.toWei(gas_price, unit='wei')
router_contract = self.contracts.router_v2 if v2 else self.contracts.router_v1
Expand Down Expand Up @@ -417,7 +426,7 @@ def sell_tokens(
continue
if log['args']['src'] != router_contract.address:
continue
amount_out = Web3.fromWei(log['args']['wad'], unit='ether')
amount_out = Decimal(Web3.fromWei(log['args']['wad'], unit='ether'))
break
logger.success(f'Sell transaction succeeded at tx {txhash}')
return True, amount_out, txhash
Expand Down
3 changes: 2 additions & 1 deletion pancaketrade/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ def parse_config_file(path: Path) -> Config:
conf['_pk'] = os.environ.get('WALLET_PK')
if not conf['_pk'] or len(conf['_pk']) != 64 or not all(c in string.hexdigits for c in conf['_pk']):
conf['_pk'] = questionary.password(
f'In order to make transactions, I need the private key for wallet {conf["wallet"]}:',
'In order to make transactions, I need the private key for your wallet:',
validate=PrivateKeyValidator,
).ask()
account = Account.from_key(conf['_pk'])
conf['wallet'] = account.address
logger.info(f'Using wallet address {conf["wallet"]}.')
conf['config_file'] = str(path)
return Config(**conf)

Expand Down
29 changes: 17 additions & 12 deletions pancaketrade/watchers/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def __repr__(self) -> str:
trailing = f' tsl {self.trailing_stop}%' if self.trailing_stop is not None else ''
order_id = f'<u>#{self.order_record.id}</u>' if self.min_price or self.max_price else f'#{self.order_record.id}'
limit_price = f'{self.limit_price:.3g} BNB' if self.limit_price is not None else 'market price'
icon = '🟢' if self.type == 'buy' else '🔴'
type_icon = self.get_type_icon()
return (
f'💱 {order_id}: {self.token_record.symbol} <code>{comparison} {limit_price}</code> - '
+ f'{icon}<b>{type_name}</b> {format_token_amount(amount)} {unit}{trailing}'
f'{type_icon} {order_id}: {self.token_record.symbol} <code>{comparison} {limit_price}</code> - '
+ f'<b>{type_name}</b> {format_token_amount(amount)} {unit}{trailing}'
)

def long_repr(self) -> str:
Expand All @@ -63,7 +63,7 @@ def long_repr(self) -> str:
else f'network default {self.gas_price} Gwei'
)
order_id = f'<u>#{self.order_record.id}</u>' if self.min_price or self.max_price else f'#{self.order_record.id}'
type_icon = '🟢' if self.type == 'buy' else '🔴'
type_icon = self.get_type_icon()
limit_price = f'{self.limit_price:.3g} BNB' if self.limit_price is not None else 'market price'
return (
f'{icon}{self.token_record.symbol} - ({order_id}) <b>{type_name}</b> {type_icon}\n'
Expand All @@ -86,10 +86,7 @@ def price_update(self, sell_price: Decimal, buy_price: Decimal, sell_v2: bool, b

def price_update_buy(self, buy_price: Decimal, sell_v2: bool, buy_v2: bool):
if buy_price == 0:
logger.error(f'Price of {self.token_record.symbol} is zero or not available')
self.dispatcher.bot.send_message(
chat_id=self.chat_id, text=f'⛔️ Price of {self.token_record.symbol} is zero or not available.'
)
logger.warning(f'Price of {self.token_record.symbol} is zero or not available')
return
limit_price = (
self.limit_price if self.limit_price is not None else buy_price
Expand All @@ -116,10 +113,7 @@ def price_update_buy(self, buy_price: Decimal, sell_v2: bool, buy_v2: bool):

def price_update_sell(self, sell_price: Decimal, sell_v2: bool, buy_v2: bool):
if sell_price == 0:
logger.error(f'Price of {self.token_record.symbol} is zero or not available')
self.dispatcher.bot.send_message(
chat_id=self.chat_id, text=f'⛔️ Price of {self.token_record.symbol} is zero or not available.'
)
logger.warning(f'Price of {self.token_record.symbol} is zero or not available')
return
limit_price = (
self.limit_price if self.limit_price is not None else sell_price
Expand Down Expand Up @@ -300,6 +294,17 @@ def get_type_name(self) -> str:
else 'unknown'
)

def get_type_icon(self) -> str:
return (
'💵'
if self.type == 'buy' and not self.above
else '🚫'
if self.type == 'sell' and not self.above
else '💰'
if self.type == 'sell' and self.above
else ''
)

def get_comparison_symbol(self) -> str:
return '=' if self.limit_price is None else '&gt;' if self.above else '&lt;'

Expand Down
2 changes: 1 addition & 1 deletion pancaketrade/watchers/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(
]
self.interval = self.config.monitor_interval
self.scheduler = BackgroundScheduler(
job_defaults={'coalesce': True, 'max_instances': 1, 'misfire_grace_time': 0.8 * self.interval}
job_defaults={'coalesce': True, 'max_instances': 1, 'misfire_grace_time': max(1, int(0.8 * self.interval))}
)
self.last_status_message_id: Optional[int] = None
self.start_monitoring()
Expand Down
Loading

0 comments on commit 3f762ae

Please sign in to comment.