PRISM-INSIGHT
13개의 특화 AI 에이전트가 한국·미국 주식을 실시간 분석합니다. 오픈소스, 무료
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Личный трейд-журнал с риск-менеджментом. Вход по почте и паролю (Better Auth), у каждого пользователя свой торговый счёт. Next.js (App Router) + Postgres (Neon) через Prisma, деплой на Vercel.
npm install
cp .env.example .env # заполнить DATABASE_URL, BETTER_AUTH_*, OWNER_EMAIL, APP_PASSWORD
npx prisma migrate dev
npm run dev
Потом открыть /setup и создать учётку владельца (см. «Вход и пользователи»).
| Переменная | Назначение |
|---|---|
DATABASE_URL | строка подключения к Postgres (Neon) |
BETTER_AUTH_SECRET | секрет подписи сессий, свой на каждое окружение |
BETTER_AUTH_URL | адрес сайта; на превью не нужен — берутся адреса деплоя |
OWNER_EMAIL | почта владельца: роль admin и существующие сделки |
APP_PASSWORD | старый общий пароль, нужен один раз для /setup |
TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET | бот журнала (только прод): привязка Telegram, сигналы безопасности |
DATABASE_URL.BETTER_AUTH_SECRET, BETTER_AUTH_URL, OWNER_EMAIL
и APP_PASSWORD в Environment Variables.npm run build прогоняет prisma migrate deploy перед сборкой./setup и создать владельца — торговый счёт создастся или
привяжется сам.prisma/schema.prisma Account / BalanceEvent / Trade / Fix
src/lib/trading-math.ts формулы: risk, position size, закрытие сделки
src/lib/risk-budget.ts бюджет риска и его зоны
src/app/api/** route handlers (zod-валидация, транзакции)
src/app/(pages) дашборд, /trades/new, /trades/[id], /trades, /settings
lib/trading-math.ts — логика не продублирована.Trade хранит снапшоты depositAtEntry и ставок комиссии (makerFeePct,
takerFeePct), поэтому изменение настроек не меняет уже открытые сделки.позиция = риск / (d + fвх + fвых × стоп/вход), где d — расстояние до стопа в долях входа. Комиссия берётся с
номинала: лимитная заявка — maker, рыночная — taker; стоп всегда считается
рынком. Поэтому выбитая по стопу сделка — ровно −1R. Без этого на коротком стопе
(0,1–0,2%) комиссия добавляла к риску 50–100%.market / limit) хранится у каждого входа и каждой фиксации и
задаёт ставку. По умолчанию: вход — как в прошлый раз на этом устройстве,
ручная фиксация — лимит, стоп и б/у — рынок. Комиссия выхода — с номинала по
цене выхода, как на бирже.sizePct фиксаций достигает 100:
в одной транзакции считается результат, создаётся BalanceEvent типа
trade_settlement и обновляется баланс аккаунта.PATCH /api/account — только через
BalanceEvent или закрытие сделки.PATCH /api/trades/:id): riskAmount и
positionSize пересчитываются, а снапшоты депозита и комиссии остаются прежними.
У закрытой сделки правка запрещена.realizedRR — знаковый R-мультипликатор netPnL / riskAmount. Это осознанное
отступление от формулы раздела 4 ТЗ, которая брала модуль хода цены и поэтому
давала убыточной сделке положительный R:R.Trade.leverage, по умолчанию из Account.defaultLeverage, 5×) на риск
не влияет: убыток при стопе считается от номинала позиции. Плечо определяет
только маржу — positionSize / leverage, ту часть депозита, что реально уходит
в сделку. На дашборде под бюджетом риска отдельная шкала «Маржа в сделках»,
в форме — предупреждение, если маржа больше баланса.Account.riskLimitPct (потолок суммарного риска
открытых позиций в процентах от баланса, по умолчанию 3%), редактируется в
. Жёлтая зона начинается после 2/3 лимита.У сделки может быть несколько входов (TradeEntry), как у позиции на бирже в
одностороннем режиме: одна позиция, средний вход, один стоп. entryPrice,
positionSize и riskAmount сделки — производные от входов и стопа
(src/lib/entries-math.ts, покрыто тестами):
Добор — на странице открытой сделки, пока нет фиксаций; последний добор можно снять. Две открытые сделки по одной паре и в одну сторону (без фиксаций) можно объединить: более поздняя становится добором к более ранней, стоп берётся у поздней, теги объединяются, заметки склеиваются. Объединение необратимо и не оптимистично — ждёт ответа сервера.
У сделки с несколькими входами правка параметров переносит стоп с сохранением количества монет (риск пересчитывается); цену входа, риск и направление там не правят.
Страница сделки после первого SSR живёт в кеше TanStack Query (ключ
['trade', id], src/lib/query/). Фиксации добавляются и удаляются
оптимистично: onMutate патчит кеш и делает снимок, onError откатывает,
onSuccess кладёт серверный ответ, onSettled сверяется. Локальный расчёт —
src/lib/trade-local.ts на той же lib/trading-math, поэтому после
подтверждения цифры не меняются. Новая сделка открывается как карточка на
месте формы: DTO считается локально, URL подменяется через replaceState,
POST уходит в фоне. Id сделок и фиксаций генерирует клиент (uuid), сервер
идемпотентен по ним. Запросы, зависящие от ещё не сохранённой сделки, ждут её
через src/lib/query/pending.ts. Ошибки не молчат: полоска с причиной,
«Повторить» и «Скрыть». Devtools Query открываются кнопкой слева внизу.
Блок «Разбор» (теги причин входа и заметка) правится и у закрытых сделок.
Теги сделки лежат в кеше рядом со сделкой ({ trade, fixes, tagIds }), а не
внутри: ответы на правку сделки тегов не содержат и иначе затирали бы их.
Тег включается оптимистично, а запросы одной сделки уходят строго по очереди
(src/lib/query/serial.ts), чтобы быстрые тапы дошли до сервера в том же
порядке. Встроенный scope у мутаций не подошёл: он продолжает очередь только
на видимой странице. Заметка сохраняется сама — через паузу в наборе и при
уходе с поля.
Под заметкой — «Похоже на: + cme»: теги, упомянутые в тексте, но не отмеченные
(src/lib/tag-suggest.ts, без ИИ). Короткие теги (до 3 символов) ищутся только
целым словом, длинные — по основе, чтобы не мешали окончания: «дивергенция»
находится в «дивергенции». Правила покрыты тестами: npm test (встроенный
node:test, TypeScript через tsx).
user, session, authAccount в той же базе. Торговый Account
привязан к пользователю через userId, getAccount() берёт счёт по сессии.src/proxy.ts (в Next 16 так называется middleware) проверяет сессию на
каждом запросе целиком, а не только наличие cookie: сессия ищется в базе.
Кеш сессии в cookie выключен, поэтому выход на других устройствах и смена
пароля действуют сразу./api/login, эндпоинт Better Auth
/sign-in/email закрыт (disabledPaths), чтобы защиту ниже нельзя было обойти./setup: почта из OWNER_EMAIL, новый пароль и старый APP_PASSWORD как
подтверждение. Владелец получает роль admin и
существующий торговый счёт. После этого /setup отдаёт 404./invite/<токен>, пересылает её сам (писем приложение не
шлёт). Ссылка одноразовая, живёт 7 дней, её можно отозвать; новое
приглашение на ту же почту отзывает прежнее. В Invite хранится только
SHA-256 токена. По ссылке заводится учётка ровно на эту почту, с ролью user
и своим пустым торговым счётом./reset/<токен>, которую выписывает
админ: одноразовая, 24 часа, хранится только хеш. По ней задаётся новый
пароль, все прежние сессии пользователя удаляются. Админам ссылку не выписать —
иначе один админ мог бы войти в учётку другого; свой пароль админ меняет в
настройках.LoginAttempt до проверки пароля — так
пачка параллельных запросов не проскакивает проверку лимита.429 с
Retry-After. Во время блокировки даже верный пароль получает отказ, а новые
попытки продлевают блокировку. Неверный пароль отвечает с задержкой 0,5 с.APP_PASSWORD на /setup сравнивается
за постоянное время (timingSafeEqual по SHA-256).after(), вход не тормозят.SecurityAlert с уникальным индексом через ON CONFLICT DO NOTHING, сигнал
отправляет только тот запрос, чья вставка прошла.x-real-ip и
x-vercel-ip-country/-city — клиент их подделать не может. Журналы старше
90 дней чистятся при успешном входе.Бот живёт на тех же функциях Vercel: Telegram присылает сообщения на
/api/telegram/webhook с секретом TELEGRAM_WEBHOOK_SECRET в заголовке —
без него запрос отклоняется. Отдельный сервер не нужен.
BETTER_AUTH_URL), и если нет —
регистрирует его. Ручной запасной путь — «Пользователи → Бот Telegram →
Подключить заново».t.me/<бот>?start=link_<токен> (10 минут, в базе хеш). Telegram становится
вторым входом в ту же учётку: сделки и пароль остаются. Один Telegram — одна
учётка./?tgapp=1) — с живой сессией без промежуточного экрана. Без сессии proxy
ведёт на /login, и та внутри Telegram входит сама: Telegram передаёт
подписанные данные пользователя (initData),
плагин Better Auth (src/lib/telegram/auth-plugin.ts) проверяет подпись
токеном бота и свежесть (сутки), находит привязанную учётку и ставит cookie
сессии так же, как при входе по паролю. Повторное открытие с живой сессией
новую не создаёт. Проверка подписи покрыта тестами.TELEGRAM_CHAT_ID, если задан, перекрывает).extension/tradingview — пункт «Открыть сделку в журнале» в контекстном меню
инструмента «Длинная/Короткая позиция». Открывает форму новой сделки по
ссылке /trades/new?pair=…&side=…&entry=…&stop=…&source=tv. Установка —
в extension/tradingview/README.md.
Разовая миграция старого журнала. Таблица должна быть открыта по ссылке — экспорт CSV идёт без авторизации.
npx tsx scripts/import-sheet.ts <ссылка-на-таблицу> # показать, что будет
npx tsx scripts/import-sheet.ts <ссылка-на-таблицу> --apply # записать в БД
Результаты пересчитываются формулами приложения, а не берутся из колонок таблицы, поэтому расходятся с ней в пределах копейки на округлении. Повторный запуск безопасен: сделка с теми же входом, стопом и направлением пропускается. Незакрытые сделки (сумма фиксаций не равна 100%) не переносятся.
Интеграция с API биржи — не реализована. Разбор скриншотов моделью был и
убран: им не пользовались, а на насыщенных графиках он ошибался. Вход и стоп
теперь приходят из TradingView через расширение (extension/tradingview) или
вводятся руками.
/settings