Перейти к публикации
Форум ботоводов

Таблица лидеров

  1. admin

    admin

    Administrators


    • Баллы

      51

    • Публикации

      145


  2. Andrei22

    Andrei22

    Members


    • Баллы

      11

    • Публикации

      47


  3. Astra

    Astra

    Members


    • Баллы

      3

    • Публикации

      17


  4. MotR

    MotR

    Members


    • Баллы

      2

    • Публикации

      1


Популярные публикации

Отображаются публикации с наибольшей репутацией начиная с 21.11.2018 в Сообщения

  1. Всем привет! Сегодня речь пойдет о создании бота на базе самодельного телеграм-клиента. Т.е. смысл такой - берутся исходники телеграма, компилируются, получаем библиотеку. С этой библиотекой работаем от своего имени на питоне - такой бот будет работать от лица пользователя, и иметь те же возможности, которые имеет каждый, у кого есть телеграм - читать, пересылать, удалять и т.п. Например, если у вас есть доступ к платному закрытому каналу, или шифрованному чату, вы сможете пересылать оттуда информацию в любой другой чат, где у вас есть право голоса - и никаких ботов не нужно будет приглашать, а тем более давать им права админа. Создание такого бота обойдется в 3 этапа, первый из которых можно пропустить, если у вас Windows 10 64 bit - просто возьмите мои готовые библиотеки. Этап 1 - получить исходники телеграм, установить дополнительные нужные модули, скомпилировать, получить на выходе нужную библиотеку dll. Об этом будет следующий пост. Вы можете пропустить его и скачать готовую tdjson.dll для своей платформы. Я выложу для Windows 10 64, а так она кросплатформенная, можете компилировать себе под любые современные ОС. Этап 2 - Скопировать библиотеку в нужную папку, зарегистрировать, создать application в telegram и получить API ключи. Этап 3 - непосредственно запустить код на питоне, указав API ключи и свой номер телефона, смотреть как переселаются сообщения и пить пиво
    7 баллов
  2. Этап 3 - непосредственно запуск и работа В директории C:\code\examples\telegram создайте файл main.py со следующим содержимым: # -*- coding: utf-8 -*- # # Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 # # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # from ctypes.util import find_library from ctypes import * import json import sys API_KEY = 111111 API_HASH = '........' PHONE_NUMBER = '+7913212313' # load shared library tdjson_path = find_library("tdjson") or "tdjson.dll" if tdjson_path is None: print('can\'t find tdjson library') quit() tdjson = CDLL(tdjson_path) # load TDLib functions from shared library td_json_client_create = tdjson.td_json_client_create td_json_client_create.restype = c_void_p td_json_client_create.argtypes = [] td_json_client_receive = tdjson.td_json_client_receive td_json_client_receive.restype = c_char_p td_json_client_receive.argtypes = [c_void_p, c_double] td_json_client_send = tdjson.td_json_client_send td_json_client_send.restype = None td_json_client_send.argtypes = [c_void_p, c_char_p] td_json_client_execute = tdjson.td_json_client_execute td_json_client_execute.restype = c_char_p td_json_client_execute.argtypes = [c_void_p, c_char_p] td_json_client_destroy = tdjson.td_json_client_destroy td_json_client_destroy.restype = None td_json_client_destroy.argtypes = [c_void_p] td_set_log_file_path = tdjson.td_set_log_file_path td_set_log_file_path.restype = c_int td_set_log_file_path.argtypes = [c_char_p] td_set_log_max_file_size = tdjson.td_set_log_max_file_size td_set_log_max_file_size.restype = None td_set_log_max_file_size.argtypes = [c_longlong] td_set_log_verbosity_level = tdjson.td_set_log_verbosity_level td_set_log_verbosity_level.restype = None td_set_log_verbosity_level.argtypes = [c_int] fatal_error_callback_type = CFUNCTYPE(None, c_char_p) td_set_log_fatal_error_callback = tdjson.td_set_log_fatal_error_callback td_set_log_fatal_error_callback.restype = None td_set_log_fatal_error_callback.argtypes = [fatal_error_callback_type] # initialize TDLib log with desired parameters def on_fatal_error_callback(error_message): print('TDLib fatal error: ', error_message) td_set_log_verbosity_level(2) c_on_fatal_error_callback = fatal_error_callback_type(on_fatal_error_callback) td_set_log_fatal_error_callback(c_on_fatal_error_callback) # create client client = td_json_client_create() # simple wrappers for client usage def td_send(query): query = json.dumps(query).encode('utf-8') td_json_client_send(client, query) def td_receive(): result = td_json_client_receive(client, 1.0) if result: result = json.loads(result.decode('utf-8')) return result def td_execute(query): query = json.dumps(query).encode('utf-8') result = td_json_client_execute(client, query) if result: result = json.loads(result.decode('utf-8')) return result td_send({'@type': 'getAuthorizationState', '@extra': 1.01234}) chat_ids = [] chats = [] choosen_chat_id = 0 non_bmp_map = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd) # main events cycle while True: event = td_receive() if event: # if client is closed, we need to destroy it and create new client if event['@type'] == 'updateAuthorizationState' and event['authorization_state']['@type'] == 'authorizationStateClosed': break if event['@type'] in ['updateSupergroup', 'updateNewChat', 'updateChatLastMessage', 'updateUser']: continue if event.get('@extra') == 1001001: chat_ids = event['chat_ids'] for chat_id in chat_ids: td_send({'@type':'getChat', 'chat_id':chat_id, 'extra':1001011}) if event['@type'] == 'updateNewMessage' and event['message']['chat_id'] == choosen_chat_id: message_type = event['message']['content']['@type'] print("*"*80) print("Получено:", message_type) print("-"*80) if message_type == 'messageText': message_text = event['message']['content']['text']['text'].translate(non_bmp_map) print(message_text) td_send({'@type':'sendMessage', 'chat_id':target_chat_id, 'input_message_content': { '@type': 'inputMessageText', 'text': { '@type': 'formattedText', 'text': message_text }}} ) print("-"*80) print(event) print("*"*80) if event['@type'] == 'chat': title = event['title'].translate(non_bmp_map) print('Получен чат', len(chats), title, event['id']) chats.append({'title': title, 'id': event['id']}) if len(chats) == len(chat_ids): chat_id = int(input('Выберите чат для мониторинга (1 - {m_ch})'.format(m_ch=len(chats)))) print("Выбран чат", chats[chat_id]) choosen_chat_id = chats[chat_id]['id'] chat_id = int(input('Выберите чат для пересылки (1 - {m_ch})'.format(m_ch=len(chats)))) print("Выбран чат", chats[chat_id]) target_chat_id = chats[chat_id]['id'] if target_chat_id == choosen_chat_id: print("Нельзя два раза выбрать одно и то же!!!!") break if event['@type'] == "authorizationStateWaitTdlibParameters": td_send({'@type': 'setTdlibParameters', 'parameters': {'use_test_dc': False, 'api_id':API_KEY, 'api_hash': API_HASH, 'device_model': 'Desktop', 'system_version': 'Unknown', 'application_version': "0.0", 'system_language_code': 'en', 'database_directory': 'Database', 'files_directory': 'Files', 'use_file_database': True, 'use_chat_info_database': True, 'use_message_database': True, } }) td_send({'@type': 'checkDatabaseEncryptionKey',}) elif event['@type'] == 'updateAuthorizationState' and event['authorization_state']['@type'] == 'authorizationStateWaitPhoneNumber': td_send({'@type': 'setAuthenticationPhoneNumber', 'phone_number': PHONE_NUMBER}) elif event['@type'] == 'updateAuthorizationState' and event['authorization_state']['@type'] == 'authorizationStateWaitCode': code = input("Enter code:") td_send({'@type': 'checkAuthenticationCode', 'code': code}) elif event['@type'] == 'updateAuthorizationState' and event['authorization_state']['@type'] == 'authorizationStateReady': td_send({'@type': 'getChats', 'limit': 100, 'offset_order':2**63-1, '@extra': 1001001}) td_send({'@type': 'getMe', '@extra': 1001111}) sys.stdout.flush() td_json_client_destroy(client) Я взял пример из C:\code\examples\telegram\src\example\python и подогнал под себя. Тут смысл такой - вам нужно в строках API_KEY = 111111 API_HASH = '........' PHONE_NUMBER = '+7913212313' указать API_KEY и Hash из телеграмма, которые вы получили на этапе 2. Так же нужно указать свой номер, под которым вы зарегистрированы в телеграмме. Для чистоты эксперимента желательно что бы телеграм был запущен у вас на компе или телефоне, что бы туда пришел код подтверждения - через СМС я не добавлял. Этот скрипт нужно запустить из питона 64 битной версии! Это важно. Открываете в Idle, меняете апи-ключи и телефон и нажимаете F5 - скрипт запускается, при первом запуске должна появиться надпись Enter code: А в телеграм придет сообщение с кодом. Введите код, и в телеграм придет второе сообщение о найденом новом устройстве. Потом, и при любом другом запуске, вам будет выдаваться список из 100 ваших чатов, примерно в том же порядке, в каком вы их видите в телеграмме, сначала прикрепленные чаты, потом самые свежеобновленные. А потом, на основании списка, вам будет предложено выбрать чат/канал источник, и чат/канал, куда пересылать сообщения В указанном примере, все текстовые сообщения из канала Mash будут от моего имени переправляться в группу Баблофил. Разумеется, это только для примера.
    5 баллов
  3. Этап 1 - сложный, его можно пропустить Для начала будет нужно скомпилировать себе библиотеку от телеграма Для этого понадобится программа Git for Windows Скачиваем, устанавливаем, теперь давайте создадим папку, где будем ТВОРИТЬ! У меня это будет папка C:/code/examples/telegram – в ней будет всё, как промежуточное, так и итоговое. В этой папке теперь создадим папку src. На новой папке src щелкаем правой кнопкой мыши, и выбираем в контекстном меню Git Bash Here: В появившемся окне пишем (обратите внимание на точку в конце: git clone https://github.com/tdlib/td.git . Жмем Enter Иходники библиотеки начинают скачиваться После окончания в папке src должно появиться множество файлов, окно с git можно закрыть Теперь нам потребуется: Microsoft Visual Studio – можете скачать с официального сайта последнюю версию Community, при установке укажите, что собираетесь программировать на C++ (и на чем угодно еще, если хотите). Обязательно установите английский языковой пакет. Вот тут небольшая справка по установке Если у кого-то уже студия стоит, то нужно запустить инсталлер, в нём выбрать языковые пакеты и добавить английский. Скрин для всех: Без английского ничего не получится, уот так вот. Gperf – просто скачайте и кликайте да, да, да, далее. Потом нужно будет добавить путь в переменные окружения: vcpkg – тут сложнее, создайте папку C:\vcpkg, на ней правой кнопкой, Git Bash Here, в окне вбейте git clone https://github.com/Microsoft/vcpkg.git . Жмете Enter, ждете, пока скачается, потом НЕ закрывайте окно (а если закрыли, снова правой кнопкой на папке -> Git Bash Here). В этом окне выполните команду: ./bootstrap-vcpkg.bat Будет довольно много движений, в итоге всё установится Теперь установим всё, что нужно для сборки исходников телеграмма. В этом же окне выполним команду: ./vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows Если увидите что-то такое, то пролистайте статью вверх – вам нужна Visual Studio с английским языковым пакетом: А вообще, если процесс пошел, то на этом этапе можно пойти покушать, это надолго. Нормальная установка выглядит как то так: И заканчивается примерно так: Скачайте и установите CMake. При установке выберите вот этот пункт: Теперь давайте попробуем собрать исходники в работающую библиотеку. Для этого запустим командную строку (cmd), в ней выполним следующие команды: cd C:\code\examples\telegram\src\cmake cmake -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake -A x64 .. cmake --build . --config Release Если вы хотите 32 битную версию, уберите из строк выше -A x64, скомпилируется 32 Итог выполнения сборки будет таким: А в папке Cmake\Debug должны появиться нужные нам DLL Нас, в частности, интересует файл tdjson.dll На этом этапе работа завершена - мы получили файл tdjson.dll и можем работать с ним из любого языка программирования, который умеет вызывать сишные функции. В нашем случае это Python. Небольшая ремарка - если вы собираетесь писать код на C++, Java, .Net, то достаточно подключить в проект соответствующие файлы. Примеры работы на разных языках есть в том же скачанном репозитории, C:\code\examples\telegram\src\example
    4 балла
  4. Привет, друзья. Опубликована новая статья и выложен скрипт для трейлинга. Можем обсудить тут
    3 балла
  5. Так-с, ну собственно задача выполнена. Немного об ограничениях - сейчас берутся 100 самых свежих чатов, из-за особенностей работы АПИ. Можно сделать больше, но это нужно больше кода писать, сейчас не вижу в этом смысла. Оба канала - источник и приемник, должны быть в первой сотне. Можете их прикрепить, тогда они всегда гарантировано будут сверху. Можете захардкодить ID каналов, что бы сразу пересылать из одного в другой. Еще бот пересылает текстовые сообщения - пусть с картинками и прочим. Это большинство сообщений телеграмма в большинстве каналов, но бот не будет пересылать пост-картинку. Если выложили картинку, с описанием или нет, бот её проигнорит. Можно, конечно, добавить обработку и этого, но опять же нужно разбираться с этим и дописывать код. Ну и еще разного рода смайлики могут быть обрезаны/заменены, т.к. TK, на котором написан Idle, не может их отображать. Ну а в остальном всё работает из коробки и задел у такого большой, так что удачи, что бы вы там не планировали 🙂 Прикладываю файлы Клиент telegram, скрипт для копирования сообщений канала
    3 балла
  6. Этап 2 - подготовка На этом этапе подразумевается, что у вас уже есть файл tdjson.dll - вы или скомпилировали его, или взяли мой, или скачали откуда-то из интернета для своей платформы. Рекомендую положить его в C:\Windows\System32 (нужны права администратора), а так же скопировать в папку с программой C:\code\examples\telegram. Запустите командную строку от имени администратора, и в ней выполните команду regsvr32 tdjson.dll Впрочем, оно может выдавать ошибки, и не факт, что по делу, так что можете пока не обращать на ошибки внимания. Теперь нужно завести application в телеграмме, что бы можно было использовать вашу программу. Перейдите по этой ссылке, увидите такое: Перейдите в API development tools Заполните нужные поля и введите описание (platform - Desktop) И на следующем шагу получите API ключи и всё такое: Отлично. Теперь можно полноценно пользоваться телеграммом.
    3 балла
  7. # Ф-ция, которая приводит любое число к числу, кратному шагу, указанному биржей # Если передать параметр increase=True то округление произойдет к следующему шагу def adjust_to_step(value, step, increase=False): return ((int(value * 100000000) - int(value * 100000000) % int( float(step) * 100000000)) / 100000000)+(float(step) if increase else 0) если пара BNBUSDT, ее CURR_LIMITS['filters'][2]['stepSize'] получит с биржи 0.01000000 если settings['amount'] дать цифру 0.58 (settings['amount']=0.58) quantity = adjust_to_step(settings['amount'], CURR_LIMITS['filters'][2]['stepSize']) quantity будет равна 0.57 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! пол дня голову ломал, решение нашел только такое: перед вызовом функции adjust_to_step сделать settings['amount'] = float(settings['amount'] + 0.00000001) а далее уже quantity = adjust_to_step(settings['amount'], CURR_LIMITS['filters'][2]['stepSize']) Проблемные цифры нашел только 0.57, 0.58, 1.13 ))) Та же проблема если округлять так: ticks = {} for filt in CURR_LIMITS['filters']: if filt['filterType'] == 'LOT_SIZE': ticks[pair] = filt['stepSize'].find('1') - 1 break quantity = settings['amount'] order_quantity = math.floor(quantity * 10 ** ticks[pair]) / float(10 ** ticks[pair]) *************************** settings['amount'] = float(settings['amount'] + 0.00000001) - спасает
    2 балла
  8. Нет. На примере бинанса: Бинанс разрешает что ваше время не может быть больше чем на 1 секунду чем время сервера, иными словами вы не можете ордер поставить из будущего. Одну сек они видимо на погрешность дают. Ваше время может быть меньше чем на бинансе, если я правильно помню они дают 5 сек на это, здесь уже расчет идет на то что сеть может быть загружена, интернет плохой т.п. Другими словами они считают нормой 5 сек если ордер затеряется, но они позволяют и вручную создавать это окно времени в которое по вашему времени может быть нормальная задержка. Это параметр recWindow, он если память не изменяет допустим до 60 сек., иными словами если ордер дойдет до биржи в течении минуты то его примут, только это время в прошлом а не в будущем ! Если не указать recWindow то примется стандартное окно (толи 5, толи 15 сек). Синхронизация нужна только для `авторизованных` запросов, там где ключи используются. Для свечек, курсов не важна синхронизация, там придет когда придет (запрос-ответ).
    2 балла
  9. у меня так работает: local_time = int(time.time()) restart_time = local_time + 82800 # 23 часа (82800 сек) def on_message(ws, message): global restart_time if 'e' in socket_data: ....... if 's' in socket_data: ......... local_time = int(time.time()) if local_time > restart_time: print("время рестарта") time.sleep(30) local_time = int(time.time()) restart_time = local_time + 82800 def on_error(ws, error): print(error) def on_close(ws): print("### closed ###") def on_open(ws): print("### connected ###") if __name__ == "__main__": ws = websocket.WebSocketApp( "wss:............. on_message=on_message, on_error=on_error, on_close=on_close) ws.on_open = on_open while True: try: ws.run_forever() except: pass
    2 балла
  10. Я делал на голанге, но общий принцип одинаков: Сначала надо отправить обычный запрос (такой же, как и другие в binance_api, с заголовком ) на /api/v3/userDataStream https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md Он вам даст ключ (строку) С этой строкой надо подлючиться через сокеты на wss://stream.binance.com:9443/ws/<listenKey> Потом он начнет спамить всё про изменения баланса и про создание/изменение статусов всех ордеров. Остается только фильтровать.. Важный момент, что нужно раз в сутки этот ключ пересоздавать, т.к. бинанс рубит коннект
    2 балла
  11. Здравствуйте! Подскажите, пожалуйста, есть ли возможность интегрировать "Бот для trailing stop на Binance" в "Бот для Binance с индикаторами"? Торгую с профитом 0,5%, но очень часто упускаю значительный рост. Спасибо!
    2 балла
  12. Ну, а теперь последний шаг - давайте постить всё найденное на форум (IPB в данном случае). В админке я создал API ключ для своего скрипта (но вам не покажу), и познал сокрушительный удар - оказывается, Invision прячется за CloudFront, а те режут Post запросы, поля которых содержат больше 2кб.... Ну что ж делать, не выбрасывать же скрипт.. Теперь будут вороваться те новости, в которых меньше 2000 байт :) import os import re import time from bs4 import BeautifulSoup import requests from xml.etree import ElementTree as etree forum_id = 13 api_key = '....' author_id=3 max_text_size=2000 # Сюда будем писать последнее полученное ID last_id_file = "./forklog_last_id" last_id = 0 # Если файла не существует, то создадим и запишем туда 0 if not os.path.exists(last_id_file): with open(last_id_file, "w") as out: out.write(str(last_id)) else: with open(last_id_file, 'r+') as inp: last_id = int(inp.read()) # Получить содержимое feed-ленты headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'} res = requests.get("https://forklog.com/feed/", headers=headers, verify=False) # Разобрать полученный xml на элементы root = etree.fromstring(res.text) item = root.findall('channel/item') new_last_id = last_id for pos, entry in enumerate(item): try: # Вычленить ID новости permalink = entry.findtext('guid') post_id = int(re.sub("\D", "", permalink)) link = entry.findtext('link') title = entry.findtext('title') # Текущая новость уже была, нет смысла смотреть дальше if post_id <= last_id: print("Наткнулся на старую новость, выход\n", title, link ) break if pos == 0: # т.к. мы уйдем ниже по списку новостей, запомним самую последнюю new_last_id = post_id news_res = requests.get(link, verify=False) soup = BeautifulSoup(news_res.text, 'html.parser') article_text = '' article = soup.find("section", {"id":"article_content"}).findAll('p') for element in article: #article_text += '\n' + ''.join(element.findAll(text = True)) article_text += str(element) print(link) print(post_id, permalink) print(title) print('-'*80) print(len(article_text), article_text) print('-'*80) if article_text: article_text += '<p><a href="'+link+'" target="_blank">Ссылка на оригинал статьи</a></p>' if len(article_text) < max_text_size: res = requests.post( 'https://forum.bablofil.ru/api/forums/topics?key=' + api_key, params={ 'forum':forum_id, 'author':author_id, 'title':title, 'post':article_text }, headers=headers, verify=False ) print(res.text) print('**********************') # Что бы не мешать частыми запросами и что бы не забанили time.sleep(13) except Exception as e: print('Что то пошло не так', e) # Обновим записанное в файле значение, что бы потом не трогать старые новости if new_last_id != last_id: with open(last_id_file, "w") as out: out.write(str(new_last_id)) А результат вы можете видеть в разделе новостей на этом форуме. За сим всё, оставил скрипт работать на сервере. Надеюсь, будет кому-то полезным
    2 балла
  13. Напишу тут про синхронизацию ))) Все равно не работает синхронизация времени как написано в "Бот для бинанса с индикаторами". Я что то не до понимаю )) if __name__ == "__main__": sync_time(bot, log, False, limits) t1 = threading.Thread(target=main_flow) t2 = threading.Thread(target=sync_time, args=(bot, log, True, limits)) threads = [t1, t2] for t in threads: t.start() for t in threads: t.join() этот блок кода создаст второй поток и запустит функцию sync_time передав туда limits для определения серверного времени в функции же включится цикл и limits то ему передастся только один раз при включении !! Или я не прав? def sync_time(bot, log, pause, limits): while True: try: # Получаем ограничения торгов по всем парам с биржи local_time = int(time.time()) server_time = int(limits['serverTime']) // 1000 # Бесконечный цикл программы shift_seconds = server_time - local_time if local_time + shift_seconds != server_time: bot.set_shift_seconds(shift_seconds) получается что когда пройдет таймаут if pause: time.sleep(10000) цикл начнется заново, local_time возьмет через time.time(), а вот server_time то останется прежним !!! shift_second станет -10000 и условие ниже никогда не выполнится. Голову сломал как второй поток берет заново время биржи. 2 вопроса: Может где то в main потоке надо как то передавать повторно в поток 2 limits? Или в функции где if pause после time.sleep брать новый limits?
    1 балл
  14. Выше я видел, что у людей такая же проблема, но не увидел решения. dll файлы и сам код скачал у автора. Все dll файлы скопировал в папку system32. Код запускается в проекте pycharm. Второй этап я сделал и получил все нужные данные, когда их вписал в код, то ничего не сработало. Если что, у меня windows 10 64 bit.
    1 балл
  15. Сочинил микробота оповещения, хочу поделиться ... доступен на гитхабе https://github.com/OVELYY/Signal.Pump.Bot.XXI.BINANCE пользуйтесь - очень помогает)
    1 балл
  16. После срабатывания ордера нужно получать его историю сделок. В истории сделок указывается, какие были сделки по ордеру, на какие суммы и какие комиссии в какой валюте были удержаны. На основе этих данных можно высчитать уже и кол-во полученных монет в результате исполнения ордера, кол-во потраченных денег в той или иной валюте и т.п.
    1 балл
  17. Отображение инфы - в отдельном скрипте from tkinter import * from tkinter.ttk import * import json import time import sqlite3 # создание окна tkinter root = Tk() root.title('BTC USDT Binance') root.geometry("500x500") root.config(bg="purple") def tablo(): conn = sqlite3.connect("tplong.db") cursor = conn.cursor() cursor.execute('SELECT * FROM albums ') raw = cursor.fetchall() pr=raw [0] print(pr) bid=(pr [0]) bid=float(bid) bid=str(bid) startrate=float(pr [1]) startrate=str(startrate) appl=str(pr [2]) sl=str(pr [3]) strat=(pr [4]) am=str(pr [5]) perc=str(pr [6]) lbl.config(text = 'APPLY ' + appl) lbl1.config(text = 'BID ' + bid) lbl2.config(text = 'AMOUNT ' + am) lbl3.config(text = 'StopLoss ' + sl) lbl4.config(text = 'Strategy '+strat) lbl5.config(text = 'PERC ' + perc) lbl6.config(text = 'Startrate ' + startrate) lbl.after(1000, tablo) lbl = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl.place(relx=.25, rely=.01) lbl1 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl1.place(relx=.25, rely=.1) lbl2 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl2.place(relx=.25, rely=.2) lbl3 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl3.place(relx=.25, rely=.3) lbl4 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl4.place(relx=.25, rely=.4) lbl5 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl5.place(relx=.25, rely=.5) lbl6 = Label(root, font = ('calibri', 20, 'bold'), background = 'purple', foreground = 'white') lbl6.place(relx=.25, rely=.6) tablo() mainloop()
    1 балл
  18. В Питоне есть Ткинтер, вот в нем и стоит сделать ГЮИ # Биткоин тикер, отображает курс БТЦ к Тезер на бирже Бинанс from tkinter import * from tkinter.ttk import * import json import time import requests # создание окна tkinter root = Tk() root.title('BTC USDT Binance') # Эта функция используется для # отображать курса биткоин на этикетке def ticker(): try: b=requests.get('https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT') # сюда можно вставить другую пару lastprice = float(json.loads(b.text)["price"]) print(lastprice) string = str(lastprice) lbl.config(text = string) lbl.after(500, ticker) except requests.ConnectionError: print ("Error CONNECT") pass lbl.after(100, ticker) # Стилизуем виджет метки так, чтобы тикер # выглядел более привлекательно lbl = Label(root, font = ('calibri', 200, 'bold'), background = '#002137', foreground = 'orange') # Размещение в центре # окна tkinter lbl.pack(anchor = 'center') ticker() mainloop() # для чего это не знаю, может работать и без этой строки clockrest!.exe
    1 балл
  19. создай бат файл там же где лежит бот, например start.bat внутрь: @ECHO OFF TIMEOUT /T 30 MODE 120,20 title Yobit bot ))) setlocal set PYTHONPATH=./ python ./yobit_perc.py endlocal pause далее, ПКМ создать ярлык (ярлык это ссылка на этот бат файл) далее ПКМ на Пуск -> выполнить -> пишешь shell:startup (откроется папка автозагрузки) далее ПКМ на ярлык - > копировать -> вставить в папку автозагрузки звук проверь что диск с ))) и есть ли этот wave там: import winsound soundfile = "c:/Windows/Media/chimes.wav" winsound.PlaySound(soundfile, winsound.SND_FILENAME | winsound.SND_ASYNC)
    1 балл
  20. Это ограничение API Bittrex. Впрочем, можно брать минутные свечи или любые другие, и агрегировать их в нужный интервал, например по три шт.
    1 балл
  21. Приветствую, бот binance с индикаторами можно сделать, чтобы он продолжал торговать после каждой убыточной сделки и рассчитывал суммарно средневзвешенную цену для закрытия всех сделок с установленным профитом? Если требуется оплата за доработку прошу написать в личном сообщении.
    1 балл
  22. Подскажите что делать дальше. Дошел до момента Enter code, мне пришёл код на ТГ, после чего нажимаю Enter, и глухо В папке telegram создается две папки, общий вес которых 104 кб. Пробовал скачивать VPN, так же не помогло. Что делать? Есть ли решение?
    1 балл
  23. Учите программирование, если хотите развиваться и что-то менять. Иначе так и будете простыни посылать, а толку никакого. Эта функция , как видно из файла котоырй вы привели , поиском находится в строке: from misc import adjust_to_step, sync_time, calc_buy_avg_rate, calc_sell_avg_rate, get_order_trades Эта строка говорит что функция adjust_to_step находится и берется из файла misc.py - там ее и смотрим и правим Ошибка действительно подтвердилась, это легко проверить, если поставить точку останова в самой функции и по подставлять туда разные значения. Скорее всего эта ошибка связана с особенностями окргления чисел внутри самого питона. Поскольку внутри функции есть операция деления , то при каких то комбинациях данных ошибка и проявляется. Вот эта функция - находится в файле misc.py def adjust_to_step(value, step, increase=False): return ((int(value * 100000000) - int(value * 100000000) % int( float(step) * 100000000)) / 100000000)+(float(step) if increase else 0) Но проще всего наверное поменять в ней самой, тогда нигде ничего не надо будет больше изменять - сделать вот так: def adjust_to_step(value, step, increase=False): value += 0.000000001 return ((int(value * 100000000) - int(value * 100000000) % int( float(step) * 100000000)) / 100000000)+(float(step) if increase else 0)
    1 балл
  24. Отличный бот. Спасибо. Подскажите, как можно бота заставить править некоторые сообщения, если они имеют одинаковое наполнение. Например, сначала пришло сообщение в котором текст "Петров - козел", через несколько сообщений пришло сообщение "Петров - козел и дурак", можно сделать, чтобы бот второе сообщение про петрова не новым отправлял, а правил старое. Также в тексте этих сообщений имеются одинаковые даты и время.
    1 балл
  25. Можно например не закупать, если цена выше 70% от суточной, а так же выше 70% от месячной. Но не закупая на хаях можно пропустить весь рост, почему бы не продумать стратегию безопасного ухода с купленного хая (например, так)
    1 балл
  26. А как его использовать? У меня правда такая ошибка... ====================================================== RESTART: C:\Telegram\Resend\main.py ====================================================== Traceback (most recent call last): File "C:\Telegram\Resend\main.py", line 25, in <module> tdjson = CDLL(tdjson_path) File "C:\Users\novikov_a\AppData\Local\Programs\Python\Python38\lib\ctypes\__init__.py", line 373, in __init__ self._handle = _dlopen(self._name, mode) FileNotFoundError: Could not find module 'C:\WINDOWS\system32\tdjson.dll'. Try using the full path with constructor syntax. Файл в system32 лежит, если что... А также изначально не вышло зарегистрировать библиотеку, типо модуль не найден. Win10x64-1909 P.S. Отвечу сам себе. Я, идиот эдакий, не поместил в system32 остальные dll. Посыпаю голову пеплом за невнимательность.
    1 балл
  27. virtualB это не функция, уберите скобки for struct in virtualB['balances']:
    1 балл
  28. Так вы монету не передаёте, замените get_virtual_balance() на get_virtual_balance("BTC")
    1 балл
  29. Вообще такое запрещено настройками безопасности IOS или Android, ведь тогда какие-то сторонние приложения могли бы читать данные клиент-банков, смс и тп. Время от времени появляются какие-то черные способы обхода, но и их оперативно фиксят. Если уж очень нужно то наверняка можно извернуться, например получая push уведомления на какой-то менее защищенный девайс, типа смарт-часов, или снимать изображение с экрана и распознавать его через машинное зрение, все технологии для этого есть. Как вариант можно анализировать трафик, которым обменивается телефон с интернетом, ведь сигналы приходят извне, но и тут будут свои заморочки. В общем, простого и быстрого способа лично я не знаю )
    1 балл
  30. В силу стратегии бот торгует не ровно, один месяц может 20% выжать, другой 0, но в целом это очень хороший сервис. Регистрируйтесь по моей ссылке и я помогу и расскажу что и как настраивать и что от чего зависит.
    1 балл
  31. типа вот так: 'marginLoanG': {'url': 'sapi/v1/margin/loan', 'method': 'GET', 'private': True}, 'marginRepayG': {'url': 'sapi/v1/margin/repay', 'method': 'GET', 'private': True}, а иначе он постом продолжает занимать )))
    1 балл
  32. Сорри за долгий ответ, расширил код, можете взять со статьи или гитхаба
    1 балл
  33. Шалом Туда, где requests.request, завести словарь и добавить его к реквесту, вот так: proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } response = requests.request(method=self.methods[command]['method'], ...., headers=headers, proxies=proxies)
    1 балл
  34. Можно указать часть названия сайта, любое слово которое может встречаться в тексте
    1 балл
  35. Выложен бот для Binance с индикаторами, вот бот Бот для Binance с индикаторами, вот статья https://bablofil.ru/bot-dlya-binance-s-indikatorami/ Можете мучать/тестировать)
    1 балл
  36. Входите с той суммой, которую не жалко потерять :) Для человека без опыта зарабатывать что с ботом что без, очень сложно. Все равно приходится распределять средства, выбирать пары, иногда что то продать с убытком, иногда что-то придержать. Бот это автомат, а трейдер - солдат, и если выпустить неопытного солдата с автоматом в бой.. Ну вы поняли
    1 балл
  37. Небольшой кусок кода для выставления вилки цен на Binance - как известно, нельзя штатными средствами выставить И тейк-профит, и стоп-лосс. В коде указываете пары, указываете цены sl и tp (стоп-лосс и тейк профит соответственно), и кол-во к продаже. Когда текущая цена окажется ниже первой или выше второй, бот продаст указанное кол-во по рынку. import sqlite3 import logging import time import os from datetime import datetime from binance_api import Binance bot = Binance( API_KEY='', API_SECRET='' ) pairs = { 'LTCBTC': { 'sl':0.00810600, 'tp':0.009098, 'amount': 0.01, 'order_placed': False }, 'ETHBTC': { 'sl':0.00809700, 'tp':0.009098, 'amount': 0.01, 'order_placed': False }, } while True: for pair in pairs: if pairs[pair]['order_placed']: print('По паре ', pair, ' ордер уже выставлен') else: try: print('Проверяю пару', pair) current_rates = bot.depth(symbol=pair, limit=5) bid=float(current_rates['bids'][0][0]) ask=float(current_rates['asks'][0][0]) print("Текущие курсы bid {bid:0.8f}, ask {ask:0.8f} (нужно {sl:0.8f} или {tp:0.8f})".format( bid=bid, ask=ask, sl=pairs[pair]['sl'], tp=pairs[pair]['tp'] )) if bid <= pairs[pair]['sl'] or bid >= pairs[pair]['tp']: print('Цена вышла за диапазон, продажа по рынку') res = bot.createOrder( symbol=pair, recvWindow=15000, side='SELL', type='MARKET', quantity=pairs[pair]['amount'] ) print('Результат создания ордера', res) pairs[pair]['order_placed'] = True except Exception as e: print(e) time.sleep(1) #print(bot.myTrades(symbol='PPTETH')) binance_api.py binance_sl.py
    1 балл
  38. Оно работает в потоках, т.е. каждая пара независимо от других лезет на биржу, делает свои дела и потом спит сколько скажут Вы можете изменить код примерно так: Код run.py замените на import threading from config import Config from main import process_market if __name__ == '__main__': while True: for market in Config.MARKETS: process_market(market) А в main.py после строки time.sleep(Config.MARKET_WAIT_TIME) добавьте break time.sleep(Config.MARKET_WAIT_TIME) break Это такой хак будет, они пойдут друг за другом и каждый будет ждать
    1 балл
  39. В общем тут защита от ДДОСа стоит, показывается капча и т.п. Защищаются от кого-то, ввели видимо лимиты. Придется либо ждать, либо использовать прокси
    1 балл
  40. Поменяйте на market_log.warning(res.text) или на print(res.text) пожалуйста, мало данных
    1 балл
  41. Вот эта строка print(res) Должна выводить ответ биржи, включая и ошибки Попробуйте засечь, что там за текст Если быстро всё меняется, замените на market_log.warning(res.text) и смотрите логи пар, по которым проблема
    1 балл
  42. А сейчас работает? Проверьте, открывается ли ссылка в браузере https://api.bittrex.com/api/v1.1/public/getmarkethistory?market=USDT-BTC И вот эта https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=USDT-BTC&tickInterval=thirtyMin Еще может быть какие-то пары убрали с горя (им вчера отказали в лицензии в Нью-Йорке) Еще попробуйте временно заменить res = requests.get("https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=" + market + "&tickInterval="+period, verify=Config.SECURE).json() на res = requests.get("https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=" + market + "&tickInterval="+period, verify=Config.SECURE) print(res) res = res.json() и посмотреть, что напишет
    1 балл
  43. Стандартные сервера для обновления времени по интернету вечно заняты и обновляются с 20ой попытки, и если учесть что попытка эта делается раз в неделю по дефолту, то практически время автоматом и не обновляется. Вот кому надо способ: Сервера синхронизации для РФ: 0.ru.pool.ntp.org, 1.ru.pool.ntp.org, 2.ru.pool.ntp.org Как изменить период синхронизации времени в Windows: Все довольно просто. Открываем редактор реестра, для этого выбираем Пуск-Выполнить и вводим команду regedit далее следуем по следующему пути: HKEY_LOCAL_MACHINE SYSTEM CurrentControlSet Services W32Time TimeProviders NtpClient SpecialPollInterval параметр SpecialPollInterval именно то, что нам нужно. Он содержит в себе количество секунд — период обновления синхронизации времени в системе. По умолчанию равен 604800 секунд (не забываем для удобства поставить десятичную систему счисления вместо шестнадцатеричной) Таким образом если мы хотим к примеру, чтобы время синхронизировалось раз в 3 часа, то 60 секунд в минуте*60 минут в часе * 3 часа = 10800 секунд. Меняем значение параметра SpecialPollInterval на ваше количество секунд (не забываем посмотреть чтобы была отмечена десятичная система) Чтобы измененения вступили в силу, необходимо либо перезагрузить систему, либо выполнить следующую команду, которая обновит данные w32tm /config /update Делаем это также как запускали редактор реестра (Пуск-Выполнить) Проверяем, обновляя время кнопкой «Обновить сейчас«, и смотрим строчку «Следующее выполнение синхронизации:
    1 балл
  44. У вас всё есть уже, вам надо просто совместить два скрипта, вот как то так, приложил это еще и в файлах import sqlite3 import logging import time import os import math from datetime import datetime from binance_api import Binance bot = Binance( API_KEY='', API_SECRET='' ) def process_trailing(settings): multiplier = -1 if settings['strategy'] == "Long" else 1 print("Получаем настройки пар с биржи") symbols = bot.exchangeInfo()['symbols'] step_sizes = {symbol['symbol']:symbol for symbol in symbols} for symbol in symbols: for f in symbol['filters']: if f['filterType'] == 'LOT_SIZE': step_sizes[symbol['symbol']] = float(f['stepSize']) while True: try: print('Проверяю пару {pair}, стратегия {strategy}'.format(pair=settings['symbol'], strategy=settings['strategy'])) # Получаем текущие курсы по паре current_rates = bot.depth(symbol=settings['symbol'], limit=5) bid=float(current_rates['bids'][0][0]) ask=float(current_rates['asks'][0][0]) # Если играем на повышение, то ориентируемся на цены, по которым продают, иначе на цены, по которым покупают curr_rate = bid if settings['strategy'] == "Long" else ask if settings['stop_loss_fixed'] == 0: settings['stop_loss_fixed'] = (curr_rate/100) * (settings['stop_loss_perc']*multiplier+100) print("Текущие курсы bid {bid:0.8f}, ask {ask:0.8f}, выбрана {cr:0.8f} stop_loss {sl:0.8f}".format( bid=bid, ask=ask, cr=curr_rate, sl=settings['stop_loss_fixed'] )) # Считаем, каким был бы stop-loss, если применить к нему % curr_rate_applied = (curr_rate/100) * (settings['stop_loss_perc']*multiplier+100) if settings['strategy'] == "Long": # Выбрана стратегия Long, пытаемся продать монеты как можно выгоднее if curr_rate > settings['stop_loss_fixed']: print("Текущая цена выше цены Stop-Loss") if curr_rate_applied > settings['stop_loss_fixed']: print("Пора изменять stop-loss, новое значение {sl:0.8f}".format(sl=curr_rate_applied)) settings['stop_loss_fixed'] = curr_rate_applied else: # Текущая цена ниже или равна stop loss, продажа по рынку res = bot.createOrder( symbol=settings['symbol'], recvWindow=15000, side='SELL', type='MARKET', quantity=settings['amount'] ) print('Результат создания ордера', res) if 'orderId' in res: # Создание ордера прошло успешно, выход break else: # Выбрана стратегия Short, пытаемся купить монеты как можно выгоднее if curr_rate < settings['stop_loss_fixed']: print("Текущая цена ниже stop-loss") if curr_rate_applied < settings['stop_loss_fixed']: print("Пора изменять stop-loss, новое значение {sl:0.8f}".format(sl=curr_rate_applied)) settings['stop_loss_fixed'] = curr_rate_applied else: # Цена поднялась выше Stop-Loss, Покупка по рынку quantity = math.floor((settings['amount']/curr_rate)*(1/step_sizes[settings['symbol']]))/(1/step_sizes[settings['symbol']]) print("Цена поднялась выше Stop-Loss, Покупка по рынку, кол-во монет {quantity:0.8f}".format(quantity=quantity)) # math.Floor(coins*(1/stepSize)) / (1 / stepSize) res = bot.createOrder( symbol=settings['symbol'], recvWindow=15000, side='BUY', type='MARKET', quantity=quantity ) print('Результат создания ордера', res) if 'orderId' in res: # Создание ордера прошло успешно, выход break except Exception as e: print(e) time.sleep(1) # Ваш код тут # .... # .... if smth: # какое то там условие из индикаторов # тут на основе своих данных выбираете пару, тип ордера, курс, объем и т.п. curr_pair = 'EOSBTC' curr_amount = 0.0015 curr_rate = 0.1 # создаете ордер, покупаете монеты new_order = bot.createOrder( symbol=curr_pair, recvWindow=5000, side='BUY', type='LIMIT', timeInForce='GTC', # Good Till Cancel quantity=curr_amount, price=curr_rate, newOrderRespType='FULL' ) # Если ордер создался без ошибок, начинаем трейлинг продажи купленных монет if 'orderId' in new_order: settings = dict( symbol=curr_pair, # Пара для отслеживания strategy="Long", # Стратегия - Long (повышение), Short (понижение) stop_loss_perc = 0.5, # % оставания от цены stop_loss_fixed = 0, # Изначальный stop-loss, можно установить руками нужную сумму, потом бот подтянет. # Можно указать 0, тогда бот высчитает, возьмет текущую цену и применит к ней процент amount = curr_amount # Кол-во монет, которое планируем продать (в случае Long) или купить (в случае Short) # Если указываем Long, то альты для продажи (Например, продать 0.1 ETH в паре ETHBTC) # Если Short, то кол-во, на которое покупать, например купить на 0.1 BTC по паре ETHBTC ) # запускается функция и перехватыет управление на себя, когда она отработает процесс пойдет дальше process_trailing(settings) binance_api.py tv.py
    1 балл
  45. Можно каждую пару в отдельном потоке пускать, но все равно будет запаздывать. Поэтому нужно получать курсы по сокетам, в отдельном потоке, а в других потоках переставлять 🙂 Но проще запустить несколько скриптов одновременно и не париться
    1 балл
  46. Периодически мне приходила мысль, что для разработки определенных стратегий необходима история торгов за длительные промежутки времени. К сожалению многие биржи довольно скупо отдают информацию лишь за небольшие промежутки времени. Обнаружил, что многие "криптоспекулянты" постоянно ее собирают кусками, через АПИ бирж, и складируют в базу на ПК. Естественно я тоже собирался идти тем же путем. Но буквально вчера обнаружил некий ресурс: https://translate.googleusercontent.com/translate_c?depth=1&amp;hl=ru&amp;rurl=translate.google.com&amp;sl=auto&amp;sp=nmt4&amp;tl=ru&amp;u=https://www.cryptocurrencychart.com/&amp;xid=25657,15700002,15700021,15700124,15700186,15700191,15700201,15700248&amp;usg=ALkJrhgitI3R_h4nrU5ml6Rc-nDvtnVmJg на котором размещена история торгов и иная информации огромного количества криптовалют. И самое главное, у них есть бесплатное АПИ!!! Для получения ключей нужно ввести email и все. В Python запрос такого вида: http://xxxxx api key xxxx :xxxxx api secret xxxx @www.cryptocurrencychart.com/api/coin/list. . Количество запросов ограничено, не более 600 в месяц. Как вариант можно сделать модуль, наподобие binance_api и прикручивать его к скриптам. Сам, честно говоря еще не тестировал. Вообщем попробуйте, отпишитесь. Если у кого-то также есть ссылки на подобные ресурсы, поделитесь, не жадничайте.
    1 балл
  47. Итак, теперь мы можем получать ссылки на самые последние статьи. Но нам-то нужен полный текст новости со всем содержимым - его не будет в RSS. Давайте парсить страницы. Логика усложнится - теперь мы для каждой новой новости (пфф) будем получать текст её страницы, и разбирать её на составляющие. Можно делать это регулярными выражениями или вообще голой логикой в стиле программирования на C, но мы пойдем ленивым путем и поставим модуль beautifulsoup4 pip install beautifulsoup4 И давайте испытывать её в деле. Откроем исходный код страницы любой новости с сайта и изучим. Найдем интересующие места: Нас интересует всё, что есть в тегах <p> в теге <section id="article_content">. Давайте просто это заберем import os import re import time from bs4 import BeautifulSoup import requests from xml.etree import ElementTree as etree # Сюда будем писать последнее полученное ID last_id_file = "./forklog_last_id" last_id = 0 # Если файла не существует, то создадим и запишем туда 0 if not os.path.exists(last_id_file): with open(last_id_file, "w") as out: out.write(str(last_id)) else: with open(last_id_file, 'r+') as inp: last_id = int(inp.read()) # Получить содержимое feed-ленты res = requests.get("https://forklog.com/feed/", verify=False) # Разобрать полученный xml на элементы root = etree.fromstring(res.text) item = root.findall('channel/item') new_last_id = last_id for pos, entry in enumerate(item): try: # Вычленить ID новости permalink = entry.findtext('guid') post_id = int(re.sub("\D", "", permalink)) link = entry.findtext('link') title = entry.findtext('title') # Текущая новость уже была, нет смысла смотреть дальше if post_id <= last_id: print("Наткнулся на старую новость, выход\n", title, link ) break if pos == 0: # т.к. мы уйдем ниже по списку новостей, запомним самую последнюю new_last_id = post_id news_res = requests.get(link, verify=False) soup = BeautifulSoup(news_res.text, 'html.parser') article_text = '' article = soup.find("section", {"id":"article_content"}).findAll('p') for element in article: article_text += '\n' + ''.join(element.findAll(text = True)) print(link) print(post_id, permalink) print(title) print('-'*80) print(article_text) print('-'*80) print('**********************') # Что бы не мешать частыми запросами и что бы не забанили time.sleep(2) except Exception as e: print('Что то пошло не так', e) # Обновим записанное в файле значение, что бы потом не трогать старые новости if new_last_id != last_id: with open(last_id_file, "w") as out: out.write(str(new_last_id)) Готово, теперь бот может забирать тексты статей. Изображения будут тянуться напрямую с сайта Форклога. Конечно, это не очень хорошо, и надо бы их перекачивать себе, но пусть пока что будет так :) Результат выглядит вот так (статья сменилась, пока тестировал):
    1 балл
  48. Демонстрация работы с биржей indx.ru (от вебмани) Полный список методов - тут Код получает баланс пользователя (подставьте свои значения). Login и пароль можно получить в личном кабинете пользователя import requests import hmac, hashlib import base64 login = "WU...V" wmid = "5...5" password = "AT...ug" culture = "ru-RU" url = "https://api.indx.ru/api/v2/trade/Balance" headers = {'Content-type': 'text/json', 'Accept': 'text/json'} # Как по ссылке, в том же порядке, везде разные payload_str = login+ ';' + password+ ';' + culture+ ';' +wmid # А тут одинаковое везде h = base64.b64encode( hashlib.new('sha256', payload_str.encode('utf-8')).digest() ) signature = str(h.decode('utf-8')) # А тут опять разное req_body = { "ApiContext": { "Login":login, "Wmid":wmid, "Culture":culture, "Signature":signature } } res = requests.post(url, headers=headers, json=req_body, verify=True) print(res.text) indx.py
    1 балл
×
×
  • Создать...