GithubHelp home page GithubHelp logo

deman4ik / cryptuoso Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 12.75 MB

Cryptuoso - Cryptocurrency Trading Robots

Home Page: https://cryptuoso.com

License: MIT License

JavaScript 0.09% TypeScript 99.89% Dockerfile 0.02% Shell 0.01%

cryptuoso's Introduction





Repository

Code organized using Nx.

Development

Services (applications)

Creating new service

  1. Run npm run gen:app -- my-app to generate new service (application).

  2. Add "build:my-app": "nx build my-app --prod" and "serve:my-app": "nx serve my-app" to package.json scripts sections

  3. Create file apps/my-app/src/app/service.ts with class which extends BaseService or HttpService from @cryptuoso/service.

  4. Update file apps/my-app/src/main.ts whith service starting script:

import Service from "./app/service";
import log from "@cryptuoso/logger";

const service = new Service();

async function start() {
    try {
        await service.startService();
    } catch (error) {
        log.error(`Failed to start service ${process.env.SERVICE}`, error);
        process.exit(1);
    }
}
start();
  1. Copy deployments/deployment-template.yaml file to deployments/my-app.yaml, add environment variables and change ports

Generate a library

Run npm run gen:lib -- my-lib to generate a library.

Build

Run nx build my-app to build the project. The build artifacts will be stored in the dist/ directory. Use the --prod flag for a production build.

Running unit tests

Run nx test my-app to execute the unit tests via Jest.

Run nx affected:test to execute the unit tests affected by a change.

Understand dependencies

Run nx dep-graph to see a diagram of the dependencies.

cryptuoso's People

Contributors

deman4ik avatar dependabot[bot] avatar yourconst avatar vanillint avatar

Watchers

 avatar

cryptuoso's Issues

Jobs queue (Robot, User Robot, Private Connector)

  • Сохранение стейта и статуса задачи в единой транзакции

  • Фиксирование количество ретраев и ошибок в каждой отдельной задаче

  • ? Правила приостановки обработки или пропуска текущей задачи в зависимости от типа

  • Отправка Error эвентов

[fix] HTTP тесты

В тестах HttpService и Auth запускать только ОДИН http сервер на весь тест, а не на каждый тест кейс

refactoring: stats-calc lib (trade-statistics)

  • Заменить все проверки объектов по ключам на проверку по схеме с помощью fastest-validator как это делается в HttpService или Events

  • Statistics также как и TradeStats должен быть просто интерфейсом

  • Убрать circular dependency между двумя файлами, провести реорганизацию по файлам всех функций и типов

BullMQ stats-calc-runner

  • Выставить настройки при добавлении задач в queue
{
    removeOnComplete: true,
    removeOnFail: 100,
    attempts: 3,
    backoff: { type: "exponential", delay: 10000 }
}
  • Проверить в какой момент приходит событие об ошибке выполнения задачи: после каждой попытки или только после всех возможных попытках
import { QueueEvents } from 'bullmq'

const queueEvents = new QueueEvents('calcStatistics')

queueEvents.on('failed', (jobId: string) => {
    // Called every time a job is failed in any worker.
});
  • Проверить количество вызовов при одновременном запуске двух экземпляров обработчиков queueEvents.on('failed'... в разных процессах (горизонтальное масштабирование)

  • Отправлять событие errors.stats-calc.${тип расчета} после провала всех попыток выполнить задачу

Проверка переменных окружения

Реализовывать предлагаю следующим образом:

  • В BaseService делаем схему валидации по умолчанию где будут зашиты проверки всех основных переменных окружения (строки подключений к БД, Redis и т.п.)

  • В каждом отдельном сервисе при необходимости мы делаем отдельную схему валидации где в default прописываем значения по умолчанию, добавляем эту схему в config которые передается в конструктор BaseService

  • При запуске сервиса мы мерджим 2 схемы валидации одну по умолчанию из BaseService и вторую из config.

  • По ключам из схемы собираем один объект из переменных окружения и валидируем по схеме, результат валидации с проставленными значениями по умолчанию сохраняем в config

  • Везде используем config вместо переменных окружения напрямую если это возможно (за исключением строк подключения конечно же)

Support mutations

Перенести API для мутаций supportMessage и replySupportMessage (services/db/db-messages.service.ts)

аналог эвента MESSAGE_SUPPORT_REPLY отлавливать в notifications #115

Инициализация user.settings

В сервис utils добавить метод для инициализации настроек нотификаций для всех пользователей
прям в sql запросе добавить в объект (см. другие примеры в utils)
news: { email: true, telegram: true}

trade-statistics refactoring

  • На входе и выходе функции calcStatisticsCumulatively должен быть обычный объект, а не экземпляр класса
    Этот объект необходимо описать как interface и у него не должно быть дополнительных динамических ключей
    [index: string]: any;
    Касается классов RobotStats и Statistics
  • Переименовать RobotStats в TradeStats и все остальные упоминания Robot переименовать в Trade или как-то иначе, статистика считается не только по робота, подобные наименования вводят в заблужение
  • Переименовать calcStatisticsCumulatively в просто calcStatistics, другого способа расчета у нас нет

[Test] Helpers

  • Дописать комментарии в стиле JSDOC к каждой функции в модуле libs/helpers желательно на английском
    Описание части функций можно найти тут

  • Проработать тест план

  • Реализовать unit-тесты согласное тест плану

Exwatcher - Subscribe

Отправлять повторный запрос на импорт текущих свечей, если в течении 2х минут не вернулся статус.

events-manager handle *.failed/error events

Вместо errors.* топика подписываемся на отдельные события (будет пополняться):

  • RobotWorkerEvents.ERROR

  • BacktesterWorkerEvents.FAILED

  • ImporterWorkerEvents.FAILED

[Test] BaseService, HttpService

  • Ознакомиться со структурой репозитория и инициализации сервисов

  • Составить тест-план для unit тестирования классов BaseService и HttpService (модуль libs/service) в комментарии к этому issue

  • После согласования тест-плана дополнить этот issue новыми пунктами с основными этапами реализации

  • Создать новую ветку от ветки dev и приступить к реализации unit-тестов согласно плану, по завершению создать pull request в ветку dev и проставить @deman4ik в качестве reviewer'а

Stats Calc Stage 2

  • Stats Calc Runner (аналогично importer-runner все эвенты пока остаются из cpz_platform)

  • В БД внесены следующие изменения

    • дополнительные поля user_aggr_stats
    • новая таблица robot_stats
    • новая таблица user_signal_stats
    • новая таблица user_robot_stats

Mapping полей в этих таблицах:

  • statistics (jsonb) - все простые расчетные значения ({ all: {...}, long: {...}, short: {...}})

  • last_position_exit_date - дата последней позиции

  • last_updated_at - дата последнего расчета статистики

  • equity (jsonb) - массив statistics.performance

  • equity_avg (jsonb) - массив equity.changes

  • Нужно переписать запросы на получения предыдущей статистики и сохранения новых значений

  • Переименовать
    calculatePerformance -> calculateEquity
    performance -> equity
    getEquityChanges -> calculateEquityAvg (а также перенести в statistics-calculator, а equity-calculator удалить)
    changes -> equityAvg

  • Покрыть тестами runner и worker

Что в итоге по временным таблицам или объединению нескольких задач?

User - lastActiveAt

В таблицу users добавлена новая колонка last_active_at (timestamp)

  • В методах _checAuth (HttpService), login и refreshToken (auth) добавить обновление этой колонки текущей датой

Volume calc assetDynamicDelta

Добавить новый вид расчета торгового объема для позиции рабочее название assetDynamicDelta можем переименовать в assetDynamicLevels или вроде того.
Делаем по аналогии с остальными типами тут libs/robot-settings/src/lib/calc.ts.

Для начала сделать функцию, которая получает на вход - начальный объем, дельту и текущее значение профита (сумма всех прошлых позиций) и выводит объем для новой позиции.

Пример в excel файле.

dynamicVolumeLevels.xlsx

[API] Hasura Actions

  • Auth

  • Backtester Runner

  • Events Manager

  • Exwatcher Runner

  • Importer Runner

  • Stats Calc Runner

  • Robot Runner

  • User Robot Runner

  • User Profile

Stats Calc Worker Stage 1

  • Изучить старую реализацию расчета статистики
    Статистика хранится в Robots, User Signals, User Robots и User Aggr Stats.
    Позиции считываются из Robot Positions и User Positions.
    Сервис запуска расчетов по событиям Stats Calc Runner.
    Сервис выполнения расчетов Stats Calc Worker

  • Создаем новый сервис stats-calc-worker на базе BaseService (можно смотреть на реализацию importer-worker)

  • Реализуем 1 метод расчета только по роботу. Метод должен работать в двух режимах:

    • Сохранять дату последней посчитанной позиции и считывать только те позиции где дата закрытия позиции больше предыдущей даты
    • При указании отдельного флага, считывать все позиции по роботу пачками через stream
    • Запаковываем вызов расчета в worker threads (аналогично импортеру)
    • Для удобства работы со стримами используем scramjet

Calc Statistics - Average Profit

Average Profit на данный момент считается неправильно и не учитывается отрицательные сделки.

Нужно оставить старые расчеты для Winners и Losses и добавить точно такой расчет Average Profit который будет учитывать все позиции и с положительным и отрицательным profit.

Events Manager

Новый сервис, который помогает управлять событиями при обработке которых произошла ошибка

  • Новая таблица dead_letters [ id (uuid), event_id (uuid), topic, type, data (jsonb), timestamp, resend (boolean), processed (boolean) ]

  • Подписка на dead-letter.* эвенты и сохранение их в таблице dead_letters

  • Job переодически проверяющий записи в таблице dead_letters у которых resend = true и отправляющий этот же эвент в нужный stream.

  • В каждую *-events либу добавить константы с названиями стримов

  • Создать общий конфиг, в котором будут прописаны общие правила очистки сообщений и дополнительно частные правила для отдельных типов сообщений.
    например все сообщения удаляются раз в неделю.
    out-exwatcher-worker.ticks - удалять все старые эвенты раз в час
    out-exwatcher-worker.candles - раз в день и т.п.

  • Процедура очистки старых эвентов из стримов согласно конфигу

  • Процедура очистки старых consumers которые больше не используются и у которых нет pending сообщений
    XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]

Notifications handle types

  • ConnectorWorkerEvents.USER_EX_ACC_ERROR - отправляется мгновенно

Будет дополняться

Telegram Bot

  • Проверить возможности https://urban-bot.now.sh/

    • Если удовлетворяет всем требованиям, строим бота на базе этого фреймворка
  • Перевести все вызовы на gql

  • Учесть изменения в

    • хранении статистики
    • хранении настроек
    • формах динамического объема
  • Подключить Google Analytics, используя те же эвенты, что и в web

  • Поиск роботов(фильтрация) по запросу, помимо отбора по бирже/монете

  • При изменении настроек, подтверждать текущий выбор, пример:

    Вы отписались от уведомлений о сигналах в телеграм

  • ❓: Регистрация (в стадии обсуждения)

Регистрацию через сайт
на команду /start проверяем есть ли пользователь в базе
если пользователя нет, генерируем ссылку на страницу регистрации с вшитым telegram_id и telegram_username
пользователь опционально вводит email и пароль на сайте
в меню выдавать только кнопку /start
после повторного нажатия на /start появляется подтверждение регистрации и основное меню

или

Регистрация через telegram
на команду /start проверяем есть ли пользователь в базе
если пользователя нет, явно сообщаем пользователю что будет создан новый пользователь
пользователь опционально вводит email и подтверждает его в telegram
в меню выдавать только кнопку /start
после завершения регистрации появляется подтверждение и основное меню

Exwatcher Runner

  • HTTP - Добавление нового маркета

  • CRON - Обновление маркетов

DB: Markets

  • объем роботов по умолчанию

  • минимальный объем в $

[API] Auth: setTelegram

  • Перенести мутацию setTelegram из auth.service.ts в сервис auth

В доступных ролях не только user а еще 2 роли
roles: [UserRoles.user, UserRoles.vip, UserRoles.manager],

Locks stats-calc-worker

Предлагаю вместо отдельных локов в каждой задаче использовать уникальный jobId при создании job'а.

это может быть robotId/userSignalId/userRobotId/userId
с префиксом по типу задачу + exchange/asset
в зависимости от задачи

Backtester

  • Перенос существующего функционала

  • Отдельный класс для управления стейтом бэктеста

  • Scramjet

  • Запуск несколько бэктестов с разными параметрами

  • Дополнительные настройки

    • Сохранять позиции

[DB] Indexes

Оптимизация индексов, особенно в свеже созданных таблицах

[HTTP Service] User Profile

Новый API сервис user-profile
Для всех эндпоинтов настраиваем следующие права
roles: [UserRoles.user, UserRoles.vip, UserRoles.manager], auth: true
Учесть возможность переноса некоторых эндпоинтов в другие сервисы (по возможности не использовать завязанные друг на друге функции)

User Signals

  • Перенос мутаций userSignalSusbcribe и userSignalUnsusbcribe из db/user/db-user-signals.service.ts со следующими изменениями:

    • userSignalSubscribe - volume проставляется в user_signal_settings с текущей датой и не изменяет его как в текущей реализации. Если уже подписаны - успех.
    • новый метод userSignalEdit - меняет volume. Дополнительно создается новая запись в таблицеuser_signal_settings если новый volume не равен volume из текущих настроек (которые проще всего отбирать с помощью представления v_user_signal_settings где уже зашиты все условия).
    • userSignalUnsusbcribe - удаляем запись только в user_signals все остальные таблицы должны очиститься каскадно
  • Дополнить схему параметров и структуру настроек согласно типу UserSignalSettings
    На данный момент поддерживается только 2 типа assetStatic и currencyDynamic

    В userSignalEdit при изменении volumeType всегда создаем новую запись.
    Запрашивать markets теперь можно с помощью представления v_user_markets указав exchange, asset, currency и user_id.
    Объем для assetStatic проверяется по limits.userSignal.min.amount, а для currencyDynamic - amountUSD.
    Аналогично для userRobot и max, думаю должно быть понятно

User Exchange Accounts

  • Перенос мутаций userExchangeAccUpsert, userExchangeAccChangeName и userExchangeAccDelete из db/user/db-user-exchange-accs.service.ts

    ! Функцию checkAPIKeys пока пропускаем. #75 на @deman4ik
    ! Функцию encrypt реализуем через worker threads.

User Robots

  • Перенос мутаций userRobotCreate, userRobotEdit, userRobotDelete из db/user/db-user-robots.service.ts.
    сохранение настроек аналогично сигналам, только по UserRobotSettings (еще один дополнительный volumeType)

    В userRobotEdit условие

      if (userRobotExists.status !== cpz.Status.stopped)
         throw new Error("User Robot is not stopped");

    убираем. Менять настройки можно будет и при запущенном роботе.

User Settings

  • Перенос мутаций setNotificationSettings и changeName из db/user/db-users.service.ts.

E2E Tests

  • Замокать БД

Robot

  • Перенос существующего функционала

  • Event - Обработка тиков, проверка алертов (Robot Worker)

  • Сохранение схемы параметров стратегии в отдельной колонке таблицы

  • Проверка пропущенный свечей

  • Проверка пропущенных задач

  • Сохранение сигналов, истории запуска и остановки, ошибок для каждого отдельного робота

Importer Runner

  • В методе start при указании id проверить статус выполнения задачи перед запуском

[Test] Events

  • Поставить локально Redis минимум 5ой версии

  • Ознакомиться с документацией Redis Streams

  • Ознакомиться с документация ioredis

  • Попробовать написать 2 небольших скрипта для отправки и приема сообщение через redis streams (с использованием consumer group и без)

  • К модулю libs/events составить тест план для

    • end to end тестирования используя локальный редис
    • unit-тестирования с mockами redisa
  • Согласовать тест план и оценить время выполнения

  • Реализовать тесты по плану

  • Дописать комментарии для методов модуля events и внутри них если что-то покажется не очевидным

HttpService _checkAuth

  • Добавить запрос на получение и проверку текущего пользователя в middleware _checkAuth по session_variables["x-hasura-user-id"]

  • Если status = -1 доступ пользователю запрещен

  • Если у роута auth = true и roles указаны - сверять роли со списком allowedRoles из базы, а не переданным в session_variables["x-hasura-role"]

  • Если у роута auth = false и roles указаны - сверять роли с session_variables["x-hasura-role"]

  • Сохранить объект с текущим пользователем в req.meta.user (id, status, name, email, telegram_id, telegram_username, roles)

  • Закешировать объект с текущим пользователем в redis с ключем cpz:users:${userId} с временем жизни 1 минута
    При последующих запросах брать сначала значение из кэша если в кэше нет - из базы

  • Поправить тесты в связи с этими изменениями

Auth

  • Создать новый сервис auth на базе HTTPService следуя инструкции из README

  • Перенести существующий функционал по авторизации в новый сервис используя преднастроенные роуты

  • Добавить в методы confirmChangeEmail, confirmPasswordReset принудительную генерацию нового refresh токена и отправлять новый куки

  • Покрыть код unit или end-to-end тестами по усмотрению

  • Перенести из PR #8 библиотеку libs/mail

  • Реализовать отправку почты из класса Auth

Notifications Stage 1

  • Изучить старую реализацию рассылки нотификаций
    Нотификации хранятся в Notifications здесь же отлавливаются все события и сохраняются новые нотификации.
    Настройки пользователя хранятся в Users.
    Рассылка сообщений в телеграм происходит в Publisher.
    Текстовые шаблоны для телеграм хранятся в json.
    Почта отправляет сервисом Mail.

  • В libs создаем новую библиотеку mail и реализуем основные функции отправки почты с помощью mailgun-js аналогично старому сервису Mail.

  • Создаем новый сервис mail-publisher на базе BaseService (можно смотреть на реализацию importer-worker)

  • Реализуем методы и емейл шаблоны для отправки емейлов для каждого вида нотификаций из таблицы с возможностью группировки т.е. отправка нескольких видов событий в одном письме.
    Например: список сигналов, список трейдов, список статусов/ошибок.

Все это пока без привязки к bullmq/эвентам и т.п. только считывание из таблицы нотификаций.

На сколько помню сейчас используется вот этот темплейт если вдруг найдешь какое-то более подходящее решение - готов обсудить

refactoring: stats-calc-worker

  • Тип UserSignalStats в разных местах используется по разному, в одном запрашивается volume в другом volumes. Скорее всего решится за счет следующего пункта

  • Упростить выборку и логику подготовки позиций за счет представлений v_robot_positions и v_user_signal_positions где зашита выборка volume и расчет profit(fee уже учитывается)

  • makeChunksGenerator перенесен в @cryptuoso/postgres. Заменить локальную версию

  • Очистка таблиц с агрегационной статистикой, при отсутствии сигналов / роботов / пользовательских роботов в соответствующих разрезах.

Fix: Events e2e test

Поправить e2e тесты либы events #30

Сейчас тесты не выполняются успешно после повторного запуска.

  • Вынести запуск e2e теста в отдельный файл (тест будет запускаться отдельно вручную, а не вместо со всеми)

  • Переименовать хэндлеры эвентов и названия топиков и груп в более однообразный формат, завести константы

  • Корректно завершать работу и очищать экземпляр events после каждого теста

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.