Almost telegram integration + Bybit started
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
|||||||
src/__pycache__
|
src/__pycache__
|
||||||
src/test.py
|
src/test.py
|
||||||
src/credentials.py
|
src/credentials.py
|
||||||
|
exampleData.py
|
||||||
|
data.json
|
||||||
|
|||||||
78
src/arbus.py
Normal file
78
src/arbus.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
import options
|
||||||
|
|
||||||
|
|
||||||
|
async def parseParams(params):
|
||||||
|
paramsList = params.split()
|
||||||
|
paramsDict = {}
|
||||||
|
for i in range(len(options.paramsLines)):
|
||||||
|
paramsDict[options.paramsLines[i]] = paramsList[i]
|
||||||
|
return paramsDict
|
||||||
|
|
||||||
|
async def toDictPairParams(pair: str, params):
|
||||||
|
paramsList = params.split()
|
||||||
|
|
||||||
|
if len(paramsList) != len(options.paramsLines):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
paramsDict = {pair: {}}
|
||||||
|
for i in range(len(options.paramsLines)):
|
||||||
|
paramsDict[pair][options.paramsLines[i]] = paramsList[i]
|
||||||
|
return paramsDict
|
||||||
|
|
||||||
|
async def checkPair(pair: str):
|
||||||
|
currentData = {}
|
||||||
|
try:
|
||||||
|
with open('data.json', 'r') as f:
|
||||||
|
currentData = json.load(f)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
print('WARNING: JSON file is empty! Ignore if your installation is fresh.')
|
||||||
|
|
||||||
|
if pair in currentData:
|
||||||
|
print(pair, ' exists in data file.')
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
print(pair, ' not found in data file.')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
async def deletePair(pair: str):
|
||||||
|
currentData = {}
|
||||||
|
try:
|
||||||
|
with open('data.json', 'r') as f:
|
||||||
|
currentData = json.load(f)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
print('WARNING: JSON file is empty! Ignore if your installation is fresh.')
|
||||||
|
|
||||||
|
if pair in currentData:
|
||||||
|
print(pair, ' exists in data file.')
|
||||||
|
del currentData[pair]
|
||||||
|
with open('data.json', 'w', encoding = 'utf-8') as f:
|
||||||
|
json.dump(currentData, f, ensure_ascii = False, indent = 4)
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
print(pair, ' not found in data file.')
|
||||||
|
return -1
|
||||||
|
|
||||||
|
async def mainWrapper(pair: str, params):
|
||||||
|
newData = await toDictPairParams(pair, params)
|
||||||
|
|
||||||
|
if newData == -1:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
currentData = {}
|
||||||
|
try:
|
||||||
|
with open('data.json', 'r') as f:
|
||||||
|
currentData = json.load(f)
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
print('WARNING: JSON file is empty! Ignore if your installation is fresh.')
|
||||||
|
|
||||||
|
if pair in currentData:
|
||||||
|
print(pair, ' already exists.')
|
||||||
|
return -2
|
||||||
|
else:
|
||||||
|
with open('data.json', 'w', encoding = 'utf-8') as f:
|
||||||
|
currentData.update(newData)
|
||||||
|
json.dump(currentData, f, ensure_ascii = False, indent = 4)
|
||||||
|
print(pair, ' was added!')
|
||||||
|
return 0
|
||||||
75
src/bybit.py
Normal file
75
src/bybit.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import time
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from pybit.unified_trading import HTTP
|
||||||
|
|
||||||
|
import options
|
||||||
|
import credentials
|
||||||
|
|
||||||
|
import arbus
|
||||||
|
|
||||||
|
|
||||||
|
async def getClient(apiKey, apiSecret, testnet):
|
||||||
|
if testnet:
|
||||||
|
print('Using testnet API.')
|
||||||
|
else:
|
||||||
|
print('Using real API.')
|
||||||
|
|
||||||
|
client = HTTP(
|
||||||
|
testnet = testnet,
|
||||||
|
api_key = apiKey,
|
||||||
|
api_secret = apiSecret,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
response = client.get_account_info()
|
||||||
|
print('Auth succesful!')
|
||||||
|
print('Account info:', response.get('retMsg'))
|
||||||
|
return client
|
||||||
|
except Exception as e:
|
||||||
|
print('Auth failed! Check API key!')
|
||||||
|
print('Error:', e)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
async def strategy(client: HTTP, pair: str, params):
|
||||||
|
startTime = time.time()
|
||||||
|
print('Starting strategy with ', pair)
|
||||||
|
paramsDict = await arbus.parseParams(params)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
t = await arbus.checkPair(pair)
|
||||||
|
while t:
|
||||||
|
t = await arbus.checkPair(pair)
|
||||||
|
if t != 1:
|
||||||
|
break
|
||||||
|
|
||||||
|
# client = getClient(credentials.api_key, credentials.api_secret, options.testnet)
|
||||||
|
|
||||||
|
r1 = client.get_order_history(
|
||||||
|
category=options.category,
|
||||||
|
symbol=pair
|
||||||
|
)
|
||||||
|
print(r1, '\n')
|
||||||
|
|
||||||
|
r2 = client.place_order(
|
||||||
|
category = options.category,
|
||||||
|
symbol = pair,
|
||||||
|
side = 'BUY',
|
||||||
|
orderType = 'Market',
|
||||||
|
qty = paramsDict['orderSize'],
|
||||||
|
marketUnit = 'quoteCoin'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
print(r2, '\n')
|
||||||
|
if r2['retMsg'] == 'OK':
|
||||||
|
print('Order placed succesfully!')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await asyncio.sleep(20)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print('Ending strategy with ', pair)
|
||||||
|
print('Ended on the iteration number ', i)
|
||||||
|
return i
|
||||||
159
src/main.py
159
src/main.py
@ -1,4 +1,3 @@
|
|||||||
import time
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
@ -6,126 +5,158 @@ from aiogram.filters import Command
|
|||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from aiogram.client.default import DefaultBotProperties
|
from aiogram.client.default import DefaultBotProperties
|
||||||
from aiogram.enums import ParseMode
|
from aiogram.enums import ParseMode
|
||||||
|
from aiogram.types import BotCommand, BotCommandScopeDefault
|
||||||
|
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
from aiogram.utils.chat_action import ChatActionSender
|
|
||||||
|
import bybit
|
||||||
|
import arbus
|
||||||
|
|
||||||
import credentials
|
import credentials
|
||||||
import options
|
|
||||||
import strings
|
import strings
|
||||||
|
import options
|
||||||
|
|
||||||
|
|
||||||
# Custom types and vars
|
|
||||||
|
|
||||||
paramsLines = ['highEnd',
|
|
||||||
'lowEnd',
|
|
||||||
'highBreak',
|
|
||||||
'lowBreak',
|
|
||||||
'netLevelsAmount',
|
|
||||||
'netStep',
|
|
||||||
'takeDelta',
|
|
||||||
'stopDelta',
|
|
||||||
'orderSize'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Custom
|
|
||||||
|
|
||||||
async def parseParams(params: str):
|
|
||||||
paramsList = params.split()
|
|
||||||
paramsDict = {}
|
|
||||||
for i in range(len(paramsLines)):
|
|
||||||
paramsDict(paramsLines[i]) = paramsList[i]
|
|
||||||
return paramsDict
|
|
||||||
|
|
||||||
|
|
||||||
# Bybit
|
|
||||||
|
|
||||||
async def strategy(pair, params):
|
|
||||||
print(params)
|
|
||||||
await time.sleep(5)
|
|
||||||
print('Test succes')
|
|
||||||
result = pair + '\n' + params
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
# Telegram
|
|
||||||
|
|
||||||
# Входные поля для трейдинг-бота:
|
# Входные поля для трейдинг-бота:
|
||||||
# Верхняя граница ордеров
|
# Верхняя граница ордеров
|
||||||
# Нижняя граница ордеров
|
# Нижняя граница ордеров
|
||||||
# Верхняя граница для брейка
|
# Верхняя граница для брейка
|
||||||
# Нижняя граница для брейка
|
# Нижняя граница для брейка
|
||||||
# Количество уровней сетки
|
# Количество уровней сетки
|
||||||
# Шаг сетки
|
|
||||||
# Дельта для тейка
|
# Дельта для тейка
|
||||||
# Дельта для стопа
|
# Дельта для стопа
|
||||||
# Размер позиции на каждом уровне
|
# Размер позиции на каждом уровне
|
||||||
|
|
||||||
class Form(StatesGroup):
|
|
||||||
|
async def set_commands():
|
||||||
|
commands = [BotCommand(command='start', description='Старт'),
|
||||||
|
BotCommand(command='help', description='Инструкция'),
|
||||||
|
BotCommand(command='strategy', description='Запустить стратегию'),
|
||||||
|
BotCommand(command='stop', description='Остановить стратегию')
|
||||||
|
]
|
||||||
|
await bot.set_my_commands(commands, BotCommandScopeDefault())
|
||||||
|
|
||||||
|
|
||||||
|
class startForm(StatesGroup):
|
||||||
pair = State()
|
pair = State()
|
||||||
params = State()
|
params = State()
|
||||||
|
|
||||||
|
class stopForm(StatesGroup):
|
||||||
|
pair = State()
|
||||||
|
|
||||||
|
|
||||||
dp = Dispatcher(storage=MemoryStorage())
|
dp = Dispatcher(storage=MemoryStorage())
|
||||||
bot = Bot(
|
bot = Bot(
|
||||||
token=credentials.bot_token,
|
token=credentials.bot_token,
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
||||||
)
|
)
|
||||||
|
|
||||||
strategy_router = Router()
|
strategy_router = Router()
|
||||||
|
stop_router = Router()
|
||||||
|
|
||||||
|
|
||||||
@dp.message(Command("start"))
|
@dp.message(Command("start"))
|
||||||
async def commandStart(message: Message) -> None:
|
async def commandStart(message: Message) -> None:
|
||||||
|
print("Called function commandStart")
|
||||||
await message.answer(strings.startCommand)
|
await message.answer(strings.startCommand)
|
||||||
|
|
||||||
@dp.message(Command("help"))
|
@dp.message(Command("help"))
|
||||||
async def commandHelp(message: Message) -> None:
|
async def commandHelp(message: Message) -> None:
|
||||||
|
print("Called function commandHelp")
|
||||||
await message.answer(strings.helpCommand)
|
await message.answer(strings.helpCommand)
|
||||||
|
|
||||||
|
|
||||||
@strategy_router.message(Command("strategy"))
|
@strategy_router.message(Command("strategy"))
|
||||||
async def commandStrategy(message: Message, state: FSMContext):
|
async def commandStrategy(message: Message, state: FSMContext):
|
||||||
print("F command strategy + capture pair")
|
print("Called function commandStrategy")
|
||||||
async with ChatActionSender.typing(bot=bot, chat_id=message.chat.id):
|
await message.answer(strings.strategyCommand + '\n' + strings.askPair)
|
||||||
await asyncio.sleep(1)
|
await state.set_state(startForm.pair)
|
||||||
await message.answer(strings.strategyCommand + '\n' + strings.askPair)
|
|
||||||
await state.set_state(Form.pair)
|
|
||||||
|
|
||||||
@strategy_router.message(F.text, Form.pair)
|
@strategy_router.message(F.text, startForm.pair)
|
||||||
async def capture_pair(message: Message, state: FSMContext):
|
async def capture_start_pair(message: Message, state: FSMContext):
|
||||||
print("F capture pair + capture params")
|
print("Called function capture_start_pair")
|
||||||
await state.update_data(pair=message.text)
|
await state.update_data(pair=message.text)
|
||||||
async with ChatActionSender.typing(bot=bot, chat_id=message.chat.id):
|
data = await state.get_data()
|
||||||
await asyncio.sleep(1)
|
|
||||||
await message.answer(strings.askParams)
|
|
||||||
await state.set_state(Form.params)
|
|
||||||
|
|
||||||
@strategy_router.message(F.text, Form.params)
|
t = 0
|
||||||
|
if await arbus.checkPair(data.get("pair")) == 1:
|
||||||
|
msg_text = (f'Стратегия на паре <b>{data.get("pair")}</b> уже запущена.\nПожалуйста остановите стратегию либо введите другую пару.')
|
||||||
|
t = 1
|
||||||
|
else:
|
||||||
|
msg_text = strings.askParams
|
||||||
|
|
||||||
|
await message.answer(msg_text)
|
||||||
|
if t == 1:
|
||||||
|
print('Clearing state!')
|
||||||
|
await state.clear()
|
||||||
|
else:
|
||||||
|
await state.set_state(startForm.params)
|
||||||
|
|
||||||
|
@strategy_router.message(F.text, startForm.params)
|
||||||
async def capture_params(message: Message, state: FSMContext):
|
async def capture_params(message: Message, state: FSMContext):
|
||||||
print("F capture params + end")
|
print("Called function capture_params")
|
||||||
await state.update_data(params=message.text)
|
await state.update_data(params=message.text)
|
||||||
|
|
||||||
data = await state.get_data()
|
data = await state.get_data()
|
||||||
t = await strategy(pair=data.get("pair"), params=data.get("params"))
|
t = await arbus.mainWrapper(pair=data.get("pair"), params=data.get("params"))
|
||||||
msg_text = (f'Вы запускаете стратегию на паре <b>{data.get("pair")}</b> с данными параметрами\n<b>{data.get("params")}</b>\n')
|
if t == 0:
|
||||||
|
client = await bybit.getClient(credentials.api_key, credentials.api_secret, options.testnet)
|
||||||
|
if client == -1:
|
||||||
|
msg_text = (f'Аутентификация не удалась, сообщите администратору если увидете данное сообщение.')
|
||||||
|
else:
|
||||||
|
asyncio.create_task(bybit.strategy(client, data.get("pair"), data.get("params")))
|
||||||
|
msg_text = (f'Вы запустили стратегию на паре <b>{data.get("pair")}</b> с данными параметрами:\n<b>{data.get("params")}</b>\n')
|
||||||
|
elif t == -1:
|
||||||
|
msg_text = (f'Параметры введены в неверном формате, пожалуйста начните заново.')
|
||||||
|
elif t == -2:
|
||||||
|
msg_text = (f'Стратегия на паре <b>{data.get("pair")}</b> уже запущена.')
|
||||||
|
else:
|
||||||
|
msg_text = (f'Возникла непредвиденная ошибка. =(')
|
||||||
await message.answer(msg_text)
|
await message.answer(msg_text)
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
@dp.message(Command("stop"))
|
|
||||||
async def commandStop(message: Message) -> None:
|
@stop_router.message(Command("stop"))
|
||||||
await message.answer(strings.stopCommand + '\n' + strings.stopStrategy)
|
async def commandStop(message: Message, state: FSMContext):
|
||||||
|
print("Called function commandStop")
|
||||||
|
await message.answer(strings.stopCommand + '\n' + strings.askPair)
|
||||||
|
await state.set_state(stopForm.pair)
|
||||||
|
|
||||||
|
@stop_router.message(F.text, stopForm.pair)
|
||||||
|
async def capture_stop_pair(message: Message, state: FSMContext):
|
||||||
|
print("Called function capture_stop_pair")
|
||||||
|
await state.update_data(pair=message.text)
|
||||||
|
data = await state.get_data()
|
||||||
|
|
||||||
|
if await arbus.checkPair(data.get("pair")) == 1:
|
||||||
|
t = await arbus.deletePair(data.get("pair"))
|
||||||
|
if t == 0:
|
||||||
|
print('Deleted pair succesfuly')
|
||||||
|
else:
|
||||||
|
print('Error with deleting pair')
|
||||||
|
msg_text = strings.stopStrategy
|
||||||
|
else:
|
||||||
|
msg_text = strings.pairNotFound
|
||||||
|
|
||||||
|
await message.answer(msg_text)
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
|
async def start_bot():
|
||||||
|
await set_commands()
|
||||||
|
|
||||||
|
async def main() -> None:
|
||||||
|
dp.include_router(strategy_router)
|
||||||
|
dp.include_router(stop_router)
|
||||||
|
dp.startup.register(start_bot)
|
||||||
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
|
|
||||||
async def main() -> None:
|
|
||||||
dp.include_router(strategy_router)
|
|
||||||
await dp.start_polling(bot)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print('Started bot!')
|
print('Started bot!')
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@ -4,8 +4,20 @@ testnet = True # Use testnet or not
|
|||||||
pairSymbol = 'ETHUSDT' # Trading pair
|
pairSymbol = 'ETHUSDT' # Trading pair
|
||||||
mainSymbol = 'USDT' # Balance asset
|
mainSymbol = 'USDT' # Balance asset
|
||||||
timeScape = '15m' # Candle length
|
timeScape = '15m' # Candle length
|
||||||
|
category = 'spot'
|
||||||
leverage = 1 # Leverage
|
leverage = 1 # Leverage
|
||||||
|
|
||||||
notification = 1 # Telegram notifications
|
notification = 1 # Telegram notifications
|
||||||
|
|
||||||
loopSleepTime = 2 # Time passing between loops/checks
|
loopSleepTime = 2 # Time passing between loops/checks
|
||||||
|
|
||||||
|
paramsLines = ['highEnd',
|
||||||
|
'lowEnd',
|
||||||
|
'highBreak',
|
||||||
|
'lowBreak',
|
||||||
|
'netLevelsAmount',
|
||||||
|
'takeDelta',
|
||||||
|
'stopDelta',
|
||||||
|
'orderSize'
|
||||||
|
]
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
from aiogram.types import Message
|
||||||
|
from aiogram.client.default import DefaultBotProperties
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
|
|
||||||
# Strategy
|
# Strategy
|
||||||
|
|
||||||
startStrategy = "Стратегия запущена!"
|
startStrategy = "Стратегия запущена!"
|
||||||
@ -16,18 +20,17 @@ stopBot = "Бот остановлен!"
|
|||||||
|
|
||||||
startCommand = "Привет! Это приватный бот для полуавтоматической торговли криптовалютой. Хороших позиций!"
|
startCommand = "Привет! Это приватный бот для полуавтоматической торговли криптовалютой. Хороших позиций!"
|
||||||
|
|
||||||
stopCommand = "Стратегия остановлена!"
|
stopCommand = "Вы собираетесь остановить стратегию."
|
||||||
|
|
||||||
helpCommand = "При старте стратегии требуется задать параметры в следующем формате:\n" \
|
helpCommand = (f"При старте стратегии требуется задать параметры в следующем формате:\n<b>" \
|
||||||
"Верхняя граница ордеров\n" \
|
"Верхняя граница ордеров\n" \
|
||||||
"Нижняя граница ордеров\n" \
|
"Нижняя граница ордеров\n" \
|
||||||
"Верхняя граница для брейка\n" \
|
"Верхняя граница для брейка\n" \
|
||||||
"Нижняя граница для брейка\n" \
|
"Нижняя граница для брейка\n" \
|
||||||
"Количество уровней сетки\n" \
|
"Количество уровней сетки\n" \
|
||||||
"Шаг сетки\n" \
|
|
||||||
"Дельта для тейка\n" \
|
"Дельта для тейка\n" \
|
||||||
"Дельта для стопа\n" \
|
"Дельта для стопа\n" \
|
||||||
"Размер позиции на каждом уровне"
|
"Размер позиции на каждом уровне</b>")
|
||||||
|
|
||||||
strategyCommand = "Вы собираетесь запустить стратегию."
|
strategyCommand = "Вы собираетесь запустить стратегию."
|
||||||
|
|
||||||
@ -39,3 +42,5 @@ askPair = "Введите монетную пару:"
|
|||||||
askParams = "Введите параметры:"
|
askParams = "Введите параметры:"
|
||||||
|
|
||||||
gotParams = "Параметры заданы!"
|
gotParams = "Параметры заданы!"
|
||||||
|
|
||||||
|
pairNotFound = "Стратегия на данную монетную пару не найдена."
|
||||||
|
|||||||
Reference in New Issue
Block a user