Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_tinkoffSDK-master/async_telebot_fb.py
5925 views
1
import asyncio
2
import logging
3
import os
4
from contextlib import suppress
5
6
from aiogram import Bot, Dispatcher, executor, types
7
from aiogram.dispatcher.filters import Text
8
from aiogram.dispatcher.filters.state import State, StatesGroup
9
from aiogram.utils.callback_data import CallbackData
10
from aiogram.utils.exceptions import (BotBlocked, MessageNotModified,
11
NetworkError)
12
13
import my_moving_average
14
import robot_fatbold
15
16
BOT_TOKEN = os.environ["INVEST_BOT_TOKEN"]
17
password = os.environ["INVEST_BOT_PASSWORD"]
18
available_passwords = [password]
19
20
21
# Состояния для проверки доступа к боту
22
class bot_access(StatesGroup):
23
waiting_for_password = State()
24
waiting_for_start = State()
25
26
27
""" Переключатели для понимания в каком состоянии находится торговый робот
28
if robot_must_work == False, то при следующем выходе из генератора, цикл
29
прервется и торговый робот будет отключен.
30
"""
31
trade_robot_states = {
32
"status_trade_robot": False,
33
"robot_must_work": True
34
}
35
36
bot_access.waiting_for_password.set()
37
38
# Объект бота
39
bot = Bot(token=BOT_TOKEN)
40
# Диспетчер для бота
41
dp = Dispatcher(bot)
42
# Включаем логирование, чтобы не пропустить важные сообщения
43
logging.basicConfig(level=logging.INFO)
44
45
help = """
46
/help - показывает подсказку \n
47
/set - устанавливает настройки \n
48
Cообщение "Запустить торгового робота" запускает робота с настройками,
49
которые ты задал или настройками по умолчанию. \n
50
Cообщение "Остановить торгового робота" останавливает робота
51
примерно за 60 сек. \n
52
Cообщение "Настройки" показывает способ задать настройки \n
53
54
Робот работает по стратегии: \n
55
short_ma > long_ma открывает позицию \n
56
short_ma < long_ma продает акции \n
57
58
59
"""
60
61
# Список доступных параметров
62
trade_parametrs = (
63
'long_ma',
64
'short_ma',
65
'std_period',
66
'start_balance_units',
67
'long_ma_min',
68
'long_ma_max',
69
'short_ma_min',
70
'short_ma_max',
71
'std_period_min',
72
'std_period_max',
73
)
74
75
76
# Настройки по умолчанию для боевого робота и песочницы
77
user_data = {
78
'long_ma': 15,
79
'short_ma': 3,
80
'std_period': 5,
81
'start_balance_units': 100000,
82
'long_ma_min': 13,
83
'long_ma_max': 15,
84
'short_ma_min': 3,
85
'short_ma_max': 4,
86
'std_period_min': 6,
87
'std_period_max': 8,
88
}
89
90
91
def check_access() -> bool:
92
if bot_access.waiting_for_password:
93
return False
94
95
96
def get_keyboard_fab(parametr: str) -> types.InlineKeyboardMarkup:
97
buttons = [
98
types.InlineKeyboardButton(
99
text="-1",
100
callback_data=callback_numbers.new(
101
parametr=parametr,
102
action="decr")),
103
types.InlineKeyboardButton(
104
text="+1",
105
callback_data=callback_numbers.new(
106
parametr=parametr,
107
action="incr")),
108
types.InlineKeyboardButton(
109
text="Подтвердить",
110
callback_data=callback_numbers.new(
111
parametr=parametr,
112
action="finish"))]
113
keyboard = types.InlineKeyboardMarkup(row_width=2)
114
keyboard.add(*buttons)
115
return keyboard
116
117
118
async def update_num_text_fab(message: types.Message,
119
parametr: str, new_value: int):
120
with suppress(MessageNotModified):
121
await message.edit_text(f"Укажите значение {parametr}: {new_value}",
122
reply_markup=get_keyboard_fab(parametr))
123
124
125
callback_numbers = CallbackData("numbers", "parametr", "action")
126
127
callback_sets = CallbackData("sets", "parametr")
128
129
130
@dp.callback_query_handler(callback_sets.filter())
131
async def callbacks_change_parametr(call: types.CallbackQuery,
132
callback_data: dict):
133
134
user_value = user_data[callback_data["parametr"]]
135
parametr = callback_data["parametr"]
136
137
await update_num_text_fab(call.message, parametr, user_value)
138
139
await call.answer()
140
141
142
@dp.callback_query_handler(callback_numbers.filter(action=["incr", "decr"]))
143
async def callbacks_num_change(call: types.CallbackQuery, callback_data: dict):
144
145
parametr = callback_data["parametr"]
146
user_value = user_data[parametr]
147
148
action = callback_data["action"]
149
150
if action == "incr":
151
user_data[parametr] = user_value + 1
152
await update_num_text_fab(call.message, parametr, user_value + 1)
153
elif action == "decr":
154
user_data[parametr] = user_value - 1
155
await update_num_text_fab(call.message, parametr, user_value - 1)
156
await call.answer()
157
158
159
@dp.callback_query_handler(callback_numbers.filter(action=["finish"]))
160
async def callbacks_num_finish_fab(call: types.CallbackQuery,
161
callback_data: dict):
162
163
parametr = callback_data["parametr"]
164
user_value = user_data[parametr]
165
await call.message.edit_text(f"""Установлено значение {parametr}:
166
{user_value}""")
167
168
169
@dp.message_handler(commands="start")
170
async def cmd_start(message: types.Message):
171
172
await message.answer("Введите пароль")
173
await bot_access.waiting_for_password.set()
174
175
176
async def check_password(message):
177
if message.text not in available_passwords:
178
await message.answer("Пароль неверный! Укажите правильный пароль:")
179
return
180
await bot_access.waiting_for_start.set()
181
182
"""Запускает бота"""
183
greeting = """Привет! Я робот. Меня зовут Толстый жирный.\n
184
Я умею торговать на бирже.
185
И делать твое депо толстым и жирным.\n
186
Ты можешь ввести команду /help и прочитать инструкцию
187
по работе со мной.\n Она выглядит так:
188
"""
189
await message.answer(greeting)
190
await message.answer(help)
191
192
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
193
buttons = [
194
"Запустить торгового робота",
195
"Тест в песочнице",
196
"Настройки",
197
"Инструкция",
198
"Баланс"
199
]
200
keyboard.add(*buttons)
201
await message.answer("Что делать, хозяин?", reply_markup=keyboard)
202
203
204
@dp.message_handler(Text(equals="главное меню"))
205
async def main_menu(message: types.Message):
206
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
207
buttons = [
208
"Запустить торгового робота",
209
"Тест в песочнице",
210
"Настройки",
211
"Инструкция",
212
"Баланс"
213
]
214
keyboard.add(*buttons)
215
await message.answer("Что делать, хозяин?", reply_markup=keyboard)
216
217
218
@dp.message_handler(Text(equals="Баланс"))
219
async def get_balance(message: types.Message):
220
221
# info = await robot_fatbold.get_balance()
222
await message.answer("""Пока не работает. Потому, что надо передавать
223
все параметры стратегии, чтобы получить данные
224
""")
225
226
227
@dp.message_handler(commands=password)
228
async def cmd_password(message: types.Message):
229
"""Запускает бота"""
230
greeting = """Привет! Я робот. Меня зовут Толстый жирный.\n
231
Я умею торговать на бирже.
232
И делать твое депо толстым и жирным.\n
233
Ты можешь ввести команду /help
234
и прочитать инструкцию по работе со мной.\n
235
Она выглядит так:
236
"""
237
await message.answer(greeting)
238
await message.answer(help)
239
240
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
241
buttons = [
242
"Запустить торгового робота",
243
"Тест в песочнице",
244
"Настройки",
245
"Инструкция",
246
"Баланс"
247
]
248
keyboard.add(*buttons)
249
await message.answer("Что делать, хозяин?", reply_markup=keyboard)
250
251
252
@dp.message_handler(Text(equals="Запустить торгового робота"))
253
async def start_trade(message: types.Message):
254
255
# if check_access() == False:
256
# await message.answer("У вас нет доступа к боту. Введите пароль:")
257
# return
258
259
if not trade_robot_states["status_trade_robot"]:
260
261
status = "Working"
262
263
gen = robot_fatbold.main(
264
long_ma=user_data["long_ma"],
265
short_ma=user_data["short_ma"],
266
std_period=user_data["std_period"])
267
268
flag = True # Чтобы один раз сообщить, что робот запущен.
269
270
while status == "Working" and trade_robot_states["robot_must_work"]:
271
response = await gen.__anext__()
272
273
trade_robot_states["status_trade_robot"] = True
274
275
status = response['status']
276
277
await asyncio.sleep(0.1)
278
279
if flag and status == "Working":
280
keyboard = types.ReplyKeyboardMarkup(
281
resize_keyboard=True, row_width=2)
282
buttons = [
283
"Остановить торгового робота",
284
"Тест в песочнице",
285
"Настройки",
286
"Инструкция",
287
"Баланс"
288
]
289
keyboard.add(*buttons)
290
await message.answer("Торговый робот запущен!",
291
reply_markup=keyboard)
292
flag = False
293
trade_robot_states["status_trade_robot"] = True
294
trade_robot_states["robot_must_work"] = True
295
296
# status = response['status']
297
# Робот работает ничего не делаем
298
299
trade_robot_states["status_trade_robot"] = False # Изменим статус
300
# Поднимим флаг, чтобы робот мог запуститься
301
trade_robot_states["robot_must_work"] = True
302
balance = response['balance']
303
profit = response['profit']
304
shares = response['shares']
305
send_message = f"""
306
Робот остановлен.
307
Текущий cтатус: {status} \n
308
Сумма на счете: {balance:.2f} \n
309
Стоимость акций: {shares:.2f} \n
310
Прибыль с момента запуска: {profit:.2f}
311
"""
312
313
await message.answer(send_message)
314
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
315
buttons = [
316
"Запустить торгового робота",
317
"Тест в песочнице",
318
"Настройки",
319
"Инструкция",
320
"Баланс"
321
]
322
keyboard.add(*buttons)
323
await message.answer("Что делать, хозяин?", reply_markup=keyboard)
324
325
elif trade_robot_states["status_trade_robot"]:
326
await message.answer("Робот уже запущен")
327
else:
328
await message.answer("Робот в непонятном состоянии")
329
330
331
@dp.message_handler(lambda message: message.text ==
332
"Остановить торгового робота")
333
async def stop_trade(message: types.Message):
334
335
if trade_robot_states['status_trade_robot']:
336
# Глобальная переменная для остановки робота
337
trade_robot_states['robot_must_work'] = False
338
await message.answer("Робот будет остановлен в течении ~60 сек.")
339
else:
340
keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
341
buttons = [
342
"Запустить торгового робота",
343
"Тест в песочнице",
344
"Настройки",
345
"Инструкция",
346
"Баланс"
347
]
348
keyboard.add(*buttons)
349
350
await message.answer("Робот и так не работал", reply_markup=keyboard)
351
352
353
@dp.message_handler(lambda message: message.text == "Тест в песочнице")
354
async def sandbox_test(message: types.Message):
355
send_message = "Запущен тест на песочнице:"
356
await message.answer(send_message)
357
358
results = my_moving_average.main(
359
user_data['start_balance_units'],
360
user_data['long_ma_min'],
361
user_data['long_ma_max'],
362
user_data['short_ma_min'],
363
user_data['short_ma_max'],
364
user_data['std_period_min'],
365
user_data['std_period_max']
366
)
367
368
send_message = "Тест на песочнице закончен:"
369
await message.answer(send_message)
370
371
best_settings = results[0]['settings']
372
best_result_message = f"""
373
Лучшие настройки:
374
Прибыль: {float(results[0]['profit']):.2f}
375
Инструмент: {best_settings['stock']}
376
long_ma: {best_settings['long_ma']}
377
short_ma: {best_settings['short_ma']}
378
std_period: {best_settings['std_period']}
379
tf: {best_settings['tf']}
380
period: {best_settings['period']}
381
"""
382
383
await message.answer(best_result_message)
384
385
send_message = "Остальные результаты:"
386
await message.answer(send_message)
387
388
for i in range(1, len(results)):
389
settings = results[i]['settings']
390
result_message = f"""
391
Прибыль: {float(results[i]['profit']):.2f}
392
Инструмент: {settings['stock']}
393
long_ma: {settings['long_ma']}
394
short_ma: {settings['short_ma']}
395
std_period: {settings['std_period']}
396
tf: {settings['tf']}
397
period: {settings['period']}
398
"""
399
await message.answer(result_message)
400
401
402
@dp.message_handler(lambda message: message.text == "Настройки")
403
async def settings_setup(message: types.Message):
404
405
buttons = []
406
407
for p in trade_parametrs:
408
# Получим текущее значение параметра для текста на кнопке
409
user_value = user_data[p]
410
button_text = p + ":" + str(user_value)
411
buttons.append(
412
types.InlineKeyboardButton(
413
text=button_text,
414
callback_data=callback_sets.new(
415
parametr=p)))
416
417
keyboard = types.InlineKeyboardMarkup(row_width=1)
418
keyboard.add(*buttons)
419
420
await message.answer("Выберите параметр:", reply_markup=keyboard)
421
422
423
@dp.message_handler(commands="set")
424
async def set(message: types.Message):
425
426
global long_ma
427
global short_ma
428
global std_period
429
430
m_message = message.text.split(sep=";")
431
432
long_ma = m_message[0].split()[1]
433
short_ma = m_message[1]
434
std_period = m_message[2]
435
436
await message.answer(f"""Установлены настройки робота \n
437
long_ma: {long_ma} \n
438
short_ma: {short_ma} \n
439
std_period: {std_period} \n
440
""")
441
442
443
@dp.message_handler(lambda message: message.text == "Инструкция")
444
async def show_help(message: types.Message):
445
446
await message.answer(help)
447
448
449
@dp.errors_handler(exception=BotBlocked)
450
async def error_bot_blocked(update: types.Update, exception: BotBlocked):
451
# Update: объект события от Telegram. Exception: объект исключения
452
# Здесь можно как-то обработать блокировку, например, удалить пользователя
453
# из БД
454
print(
455
f"""Меня заблокировал пользователь!\n
456
Сообщение: {update}\nОшибка: {exception}
457
""")
458
459
# Такой хэндлер должен всегда возвращать True,
460
# если дальнейшая обработка не требуется.
461
return True
462
463
464
@dp.errors_handler(exception=NetworkError)
465
async def error_Network_Error(update: types.Update, exception: NetworkError):
466
# Update: объект события от Telegram. Exception: объект исключения
467
# Здесь можно как-то обработать блокировку, например, удалить пользователя
468
# из БД
469
print(
470
f"""ClientConnectorError: Cannot connect to host
471
api.telegram.org:443 ssl:default [None] \n
472
Сообщение: {update}\nОшибка: {exception}
473
""")
474
475
# Такой хэндлер должен всегда возвращать True,
476
# если дальнейшая обработка не требуется.
477
return True
478
479
480
def register_handlers_access(dp: Dispatcher):
481
dp.register_message_handler(cmd_start, commands="start", state="*")
482
dp.register_message_handler(
483
check_password,
484
state=bot_access.waiting_for_password)
485
dp.register_message_handler(main_menu, state=bot_access.waiting_for_start)
486
487
488
if __name__ == "__main__":
489
# Запуск бота
490
executor.start_polling(dp, skip_updates=True)
491
492