Files
etf-gyro/tools/get_instrument.py

159 lines
6.3 KiB
Python

#!/usr/bin/env python3
import os
import sys
import logging
import argparse
from dotenv import load_dotenv
logging.getLogger("t_tech.invest._grpc_helpers").setLevel(logging.ERROR)
from t_tech.invest import Client
from t_tech.invest.utils import quotation_to_decimal as to_dec
load_dotenv()
def _account_type(t) -> str:
return {0: "неизвестно", 1: "Брокер", 2: "ИИС", 3: "Копилка", 4: "Фонд"}.get(t.value, f"тип:{t.value}")
def _account_status(s) -> str:
return {0: "неизвестно", 1: "новый", 2: "открыт", 3: "закрыт"}.get(s.value, f"статус:{s.value}")
def cmd_accounts(client, _args):
resp = client.users.get_accounts()
if not resp.accounts:
print("Счета не найдены.")
return
print(f"\n{'ID':<45} {'Тип':<12} {'Статус':<10} Название")
print("─" * 90)
for acc in resp.accounts:
print(f"{acc.id:<45} {_account_type(acc.type):<12} {_account_status(acc.status):<10} {acc.name}")
print()
def cmd_search(client, args):
resp = client.instruments.find_instrument(query=args.query)
if not resp.instruments:
print(f"Ничего не найдено по запросу '{args.query}'")
return
instruments = resp.instruments[: args.limit]
total = len(resp.instruments)
print(f"\nНайдено {total}, показано {len(instruments)}:\n")
print(f"{'Тикер':<14} {'Тип':<10} {'Лот':>5} {'Название':<42} UID")
print("─" * 110)
for i in instruments:
name = (i.name[:40] + "..") if len(i.name) > 40 else i.name
print(f"{i.ticker:<14} {i.instrument_type:<10} {i.lot:>5} {name:<42} {i.uid}")
print()
def cmd_yaml(client, args):
resp = client.instruments.find_instrument(query=args.query)
if not resp.instruments:
print(f"Ничего не найдено по запросу '{args.query}'")
return
instruments = resp.instruments[: args.limit]
if len(instruments) == 1:
instr = instruments[0]
else:
print(f"\nНайдено {len(resp.instruments)} результат(ов):\n")
for idx, i in enumerate(instruments):
name = (i.name[:42] + "..") if len(i.name) > 42 else i.name
print(f" [{idx}] {i.ticker:<14} {i.instrument_type:<10} {name}")
print()
try:
choice = int(input("Введите номер: "))
instr = instruments[choice]
except (ValueError, IndexError):
print("Неверный выбор.")
return
print(f"\n# Вставьте в config/target.yaml → portfolio:")
print(f" {instr.ticker}:")
print(f' name: "{instr.name}"')
print(f' figi: "{instr.figi}"')
print(f' uid: "{instr.uid}"')
print(f' weight: 0.00 # TODO: укажите целевой вес')
print()
def cmd_portfolio(client, args):
account_id = args.account_id or os.getenv("TINVEST_ACCOUNT_ID")
if not account_id:
print("TINVEST_ACCOUNT_ID не задан. Используйте --account или добавьте в .env")
print("Подсказка: запустите 'accounts', чтобы узнать ID счёта.")
sys.exit(1)
resp = client.operations.get_portfolio(account_id=account_id)
total = to_dec(resp.total_amount_portfolio)
positions = sorted(
resp.positions,
key=lambda p: to_dec(p.current_price) * to_dec(p.quantity),
reverse=True,
)
print(f"\nСтоимость портфеля: {total:.2f} RUB\n")
print(f"{'Тикер':<14} {'Тип':<12} {'Кол-во':>10} {'Цена':>12} {'Сумма':>14} {'Доля':>7} UID")
print("─" * 115)
for pos in positions:
qty = to_dec(pos.quantity)
price = to_dec(pos.current_price)
value = qty * price
share = float(value / total * 100) if total else 0
print(
f"{pos.ticker:<14} {pos.instrument_type:<12} {qty:>10.2f} "
f"{price:>12.4f} {value:>14.2f} {share:>6.2f}% {pos.instrument_uid}"
)
print()
def main():
parser = argparse.ArgumentParser(
description="Утилита поиска инструментов T-Invest",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""\
примеры:
python tools/get_instrument.py accounts
python tools/get_instrument.py search TMOS@
python tools/get_instrument.py search "Тинькофф" -n 5
python tools/get_instrument.py yaml TMOS@
python tools/get_instrument.py portfolio
python tools/get_instrument.py portfolio --account <id>
""",
)
parser.add_argument("--token", help="Токен T-Invest (переопределяет TINVEST_TOKEN из .env)")
sub = parser.add_subparsers(dest="cmd", required=True)
sub.add_parser("accounts", help="Список счетов и их ID")
p_search = sub.add_parser("search", help="Поиск инструментов по тикеру, ISIN или названию")
p_search.add_argument("query", help="Тикер, ISIN или часть названия")
p_search.add_argument("-n", "--limit", type=int, default=10, metavar="N", help="Макс. результатов (по умолчанию: 10)")
p_yaml = sub.add_parser("yaml", help="Сгенерировать сниппет для config/target.yaml")
p_yaml.add_argument("query", help="Тикер, ISIN или часть названия")
p_yaml.add_argument("-n", "--limit", type=int, default=8, metavar="N", help="Макс. результатов для выбора (по умолчанию: 8)")
p_port = sub.add_parser("portfolio", help="Текущие позиции портфеля")
p_port.add_argument("--account", dest="account_id", metavar="ID", help="ID счёта (переопределяет TINVEST_ACCOUNT_ID из .env)")
args = parser.parse_args()
token = args.token or os.getenv("TINVEST_TOKEN")
if not token:
print("Ошибка: TINVEST_TOKEN не задан. Передайте --token или добавьте в .env")
sys.exit(1)
with Client(token=token) as client:
{"accounts": cmd_accounts, "search": cmd_search, "yaml": cmd_yaml, "portfolio": cmd_portfolio}[args.cmd](client, args)
if __name__ == "__main__":
main()