Разработка Nikita Kolmogorov
1 619

Как я запустил симулятор торгов криптой «Мамкин Трейдер» за две недели

...и как у меня получилось не продать душу дьяволу за такое чудо. Читайте дальше, чтобы узнать все мои секреты по запуску продуктов за 14 дней.

В закладки
Чуть ниже еще рейтинг пользователей, но доступные функции примерно понятны

Привет, стартап-тусовка! На связи Никитка, автор таких щидивров, как @voicybot и @shieldy_bot для Тележеньки. Последние две недели я дерзко потел по 4-5 часов в сутки, чтобы запустить новое свое забавное детище — Мамкиного Трейдера — игру-симулятор торгов на криптовалютных биржах. Правила простые: регистрируетесь, получаете виртуальные $10 000, торгуете и соревнуетесь с другими Мамкиными Трейдерами, у кого в итоге больше, выше и длиннее — тот и победил.

Идея мне эта пришла в районе года назад, и вот только недавно, потратив 40 часов на челлендж для iOS разработчиков от Тележеньки и успешно не попав даже в шортлисты, я осознал, что свободное время-то есть — и его нужно использовать! Расчехлил свои программерские навыки и начал копать, что же мне заюзать такого, чтобы и кода поменьше написать, и на дизайнера не тратиться, и чтобы выглядело не угрюмо.

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

Предыстория

Я очень люблю опенсурс сообщества — и сам с примерной постоянностью как паблишу свой код, так и контрибьючу в чужие репозитории. Ну вот и решил год назад попробовать создать отдельное коммьюнити и попробовать из него поподвыжимать соки, воспользоваться бесплатной силой и энтузиазмом сообщества, так сказать. Создал Телеграм-группу, дал туда ссылку на своем канале, народ повалил, начали выбирать стек.

Стоит отметить, что я — ленивый стартапер. Если что-то можно автоматизировать или где-то эту автоматизацию упростить, я это делаю. Так и тут я всегда хотел попробовать написать сервачок на GraphQL — это такая альтернатива REST, местами более оптимизированная и работающая, как магия. Все это, конечно, захотелось обернуть в Docker, а на фронте использовать волшебный Vue с Element.js. И тут начались проблемы.

Так как с Докером у меня опыта было чуть меньше, чем не было, я подвис на пару недель ленивого изучения документации. Как можно так запутанно писать доки к девтулу, мне все еще непонятно. Потом пришла пора работать с графкулем, и там тоже опыта на тот момент у меня не было — в итоге что-то написали, но кода была такая уйма, что без ста грамм разобраться было невозможно. А я особо-то не пью и начинать пить в ближайшее время не собирался — так что и тут мы пришли к затыку. Что-то требовать по таскам от коммьюнити мне было стыдновато, а сам в коде я разобраться уже не имел возможности из-за ограничений по времени.

В итоге, год назад Мамкин Трейдер затух: и сервер, и фронт были написаны на малопонятных мне технологиях, поэтому в доках и коде черт бы ногу сломал, хотя, казалось бы, что было сложного в разработке столько простого сервиса?

Год спустя

И вот пришел Телеграм-челлендж, над которым я смело кроптел-потел недельку, смело его профукал — и пришло время выбирать новый пет-проект. Сначала я за пару дней создал TLGCoin, нагло скоммунизденный по концепции с века-коина, который оказался особо-то никому и не нужным. Ну и решил, что, почему бы мертвую кобылу не попинать — авось что получится. И дело пошло.

Я выгнал нафиг из мыслей концепцию траты времени на докер и графкуль в этот раз. Решил попользовать старый добрый рест и сервисы на убунте с самописным си-аем. На сервачке быстренько поднял MongoDB, окружил ее любовью, заботой и Node.js с православным TypeScript и экспресс-подобным Koa.js. Быстренько накинул пару-тройку плагинов, чтобы можно было укоротить количество кода для контроллеров и рутов.

// Dependencies import { Context } from 'koa' import { getOrCreateUser } from '../models' import { Controller, Post } from 'koa-router-ts' import Facebook = require('facebook-node-sdk') @Controller('/login') export default class { @Post('/facebook') async facebook(ctx: Context) { const fbProfile: any = await getFBUser(ctx.request.body.accessToken) const user = await getOrCreateUser({ name: fbProfile.name, email: fbProfile.email, facebookId: fbProfile.id, }) ctx.body = user.strippedAndFilled(true) } }
// Dependencies import { Context } from 'koa' import { Controller, Get } from 'koa-router-ts' import { tickers } from '../helpers/bitfinex' @Controller('/market') export default class { @Get('/tickers') async tickers(ctx: Context) { ctx.body = tickers } }
// Dependencies import { Context } from 'koa' import { Controller, Get } from 'koa-router-ts' import { stats } from '../helpers/stats' @Controller('/') export default class { @Get('stats') stats(ctx: Context) { ctx.body = stats } @Get('/') async index(ctx: Context) { ctx.redirect('https://github.com/mamkin-trade/backend/tree/master/docs') } }

Выше примеры кода, который получился. Покажите его любому программисту и он обомлеет, насколько оказывается просто можно писать серверную часть проекта. Только осторожно, у некоторых программистов, больных бешенством, может начать идти пена изо рта по той или иной причине при виде этих скриншотов. Их лучше оставить на неделю-другую порефлексировать на тему бытия, и пена пройдет. За сломанных программистов я не беру ответственности.

И тут же я начал быстренько применять свои скиллы серверного разработчика, создавай рут за рутом, контроллер за контроллером — и все в итоге очень красиво смотрелось в Insomnia. Но пришло время делать фронт и я, мягко сказать, труханул. Попросил знакомого разработчика — Сашу Никифорова — подсобить с визуальной частью проекта. Очень, кстати, советую нанять его к себе на работу. Таких людей с подобной обучаемостью и потенциалом не так много — говорю, как человек, профиль которого около-HR в Серверной Америке. Сделали первую версию сайтика, Саша помог мне с ней и настройкой окружения.

Потом у меня начало кончаться время на разработку и менеджмент — и я решил, фак ит, чтобы не тратить время на коммуникацию и ожидания, сам зафигачу код. Быстренько нагуглил, что там у нас на Vue новомодного и стабильного, нашел Vuetify — пару туториалов на ютьюбчике, просмотрел весь курс и решил попробовать свои силы. Оказалось, что Vuetify настолько простой и достаточно неуродливый, что прототипы и MVP на нем одно удовольствие собирать. Сверху накатили все тот же TypeScript, на сервачке поднял выплевывающий собранные бандлы Nginx, да сделал https при помощи Certbot. CI сделал все через тот же мой самописный сервис, что и на сервере. Прикладываю пример кода ниже.

<template lang="pug"> v-container v-btn(flat small @click='subscribe') {{$t('subscribe')}} v-layout(row wrap) v-flex.pa-1(xs12 md4 lg3) Balance(:userId='$route.params.id') v-flex.pa-1(xs12 md8 lg12) Orders(:userId='$route.params.id') .text-xs-center.pt-4 div(v-html='$t("support")') </template> <script lang="ts"> import Vue from "vue"; import Orders from "../components/Orders.vue"; import Balance from "../components/Balance.vue"; import Component from "vue-class-component"; @Component({ components: { Balance, Orders } }) export default class Cabinet extends Vue { subscribe() { window.open( `https://t.me/mamkintrade_bot?start=${this.$route.params.id}`, "_blank" ); } } </script>

Да! Это вообще все, что нужно написать для сложного пользовательского интерфейса! Я вообще офигел от того, насколько просто сегодня описывать код для фронтенда. И верстка даже оказалось предельно простой. Да и реактивность, адаптивность, гриды, флексбоксы и лейаут — все из коробки!

Запустил Мамкиного Трейдера, рассказал о нем в нашей группе в Телеграме, жду реакции и тестов. И, о да, тестов и реакции было достаточно. Как полагается настоящему стартаперу, у меня получилось запустить нечто достаточно дырявое с точки зрения логики — тестить-то мне лень, пущай пользователи тестируют! Тут и округления работали плохо, и ограничений по минимуму или максимуму ордеров не было, и JavaScript показал свое известное личико с дробными числами. Ну, ничего страшного — записал все жалобы в блокнотик, сел и по одной начал избавляться от проблем. Через пару дней решил все существовавшие на тот момент затыки, рефакторнул код под Big.js и все снова начало работать как часы. И все еще на минимуме кода.

Пользователям начало нравиться, они закупились щиткоинами на подъеме рынка и на момент написания статьи кто-то получил 20%, а кто-то потерял 90% депозита из-за покупки ну совсем уж защекоинов. Пришло время трубить на всю округу о запуске продукта!

Трубадурим

"Ну все", — думал я — "наученный опытом, сейчас жуе-мое, на пх запустим, в фб напишем, в тг зальем на канал, на хн зафигачим, в реддит пульнем, все чики-пуки смотри только маслин не налови и я в шоколаде".

Суровая реальность снова показала свою злою рожу. На Хакер Ньюс никто не заметил поста, на Фейсбуке пара друзей прошлась по ссылке, с Телеграма пришла первая сотня пользователей, на Продакт Ханте лайкнули 37 раз и Мамкин Трейдер даже не вышел из ката. Печаль, судил я по прошлому своему опыту. Ведь если продукт не получает начальный трекшн, то и потом этого самого трекшена не будет.

Опять сняв розовые очки, я начал анализировать ситуацию. Казалось бы, шеф, все пропало! Но нет — в итоге я, при помощи этого обезьяннего маркетинга, привлек на платформу первые 317 пользователей, которые за первую неделю работы создали 1215 ордеров и наторговали на виртуальные $3 172 191! Значит, трекшен есть, нужно найти целевую аудиторию и начать ее долбить. Закупил рекламу на МДК в Тележеньке на вторник, обновлю статью, как получу данные с рекламы, сколько пришло пользователей.

В итоге, изначальным маркетингом я оказался, в принципе, довольным — учитывая полную его бесплатность.

Дополняем

Важный факт привлечения первых пользователей — это получение от них фидбека. Поэтому я создал отдельный чатик в Телеграме и оставил на него ссылочку на всех страницах сервиса. Первые отзывы не заставили себя ждать.

Сначала я добавил регистрацию через Телеграм и Гугл, помимо регистрации в Фейсбуке — очень просили пользователи. Потом я понемногу перебрал серверы на Digital Ocean (мой хостинг оф чойс), чтобы не были заблочены РКН. Потом прикрутил возможность посмотреть на профиль пользователя и поделиться своим (кстати, вот мой нищенский трейдерский профиль). Потом прикрутил пару-тройку виузальных изменений типа темной темы и затемнения уже исполненных ордеров. Потом начал показывать дополнительные данные типа цены покупки и продажи в списке тикеров (раньше была только последняя цена сделки). Потом прикрутил комиссию мейкеров и тейкеров, как на настоящей бирже (реализм!) Потом член сообщества помог прикрутить графики с TradingView — еще одно преимущество простого, открытого и короткого кода, помогать вам становится гораздо проще. Потом прикрутил тикеры в названиях вкладок и еще много всего.

Я был очень рад каждому отзову — ведь это, по факту, бесплатные указания для продакт менеджера, куда нужно идти и что делать. Начал я с невероятно простого минимума — и только по просьбам пользователей добавлял функции.

Итоги

По факту, у меня получилось запустить прикольный и достаточно сложный продукт, который нравится пользователям, буквально за две недели, включая все дополнения. Из опыта создания Мамкиного Трейдера я вынес следующие уроки.

  • Дропайте технологии, которые замедляют разработку
  • Пишите минимум кода, лучше в общем доступе, чтобы коммьюнити помогало
  • Наберите первые 100-300 пользователей, просто рассказав всем друзьям, знакомым и бесплатным сервисам о продукте
  • Сначала сделайте голый минимум, и старайтесь добавлять функции только по запросу пользователей
  • Клевый стек — это Node + Koa + TypeScript на сервере, Vue + Vuetify + TypeScript + Nginx на фронте
  • Когда проверите трекшн пользователей, начинайте рекламу сервиса

Короче говоря, я теперь вообще не понимаю нытья стартаперов о том, что ничего без денег сделать не могут. MVP запускается за 2 недели, доказано личным опытом. Если за 2 недели не успели запустить MVP, то это не MVP.

Я надеюсь, мой опыт кому-то окажется полезным. Спасибо большое за прочтение моего недо-лонгрида! Задавайте любые вопросы в комментариях, с удовольствием отвечу.

Мир вам.

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Nikita Kolmogorov", "author_type": "self", "tags": [], "comments": 11, "likes": 31, "favorites": 23, "is_advertisement": false, "subsite_label": "dev", "id": 68012, "is_wide": false, "is_ugc": true, "date": "Sun, 19 May 2019 23:33:42 +0300" }
{ "id": 68012, "author_id": 60968, "diff_limit": 1000, "urls": {"diff":"\/comments\/68012\/get","add":"\/comments\/68012\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/68012"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
11 комментариев

Популярные

По порядку

Написать комментарий...
3

Круть ) я бы расширил время на MVP до 1 месяца, всё таки за пару недель не всякое можно запустить. А так респект!

Ответить
2

Псяб :3 да, за месяцок с хорошей командой вообще можно горы свернуть, согласен.

Ответить
2

Фулстак + дизайнер собирали на голом ангуляре и yii2, без дизайн-фреймворков образовательную платформу с курсами. Прогресс, каталог, разные виды материалов, тесты и т.п. И это меньше чем за месяц. Думаю, если бы мы воспользовались визуальным фреймворком в своё время, то вполне могли бы уложиться в пару недель =))
Так что стэк автора поста вполне может претендовать на средний проект, собранный за пару недель)

Ответить
2

koa мне не очень нравится именно из-за своего невнятного синтаксиса:
ctx.body = ...

в отличие от того же express, где все функциональненько:
res.json()

Ответить
1

На экспрессе тоже можно упростить все до уровня Коа, согласен — то есть "простота" Коа не такая сильная вещь, по сравнению с Экспрессом. Но и голой Коа пользоваться смысла нет — это же скелет, на который надеваешь плагины; то есть можно, наверняка, найти и функциональную обертку ;)

Ответить
2

Специально перед этим посмотрел возможные сравнения и нашёл только то, что express уступает в производительности лишь на пару процентов и (как я понял) не умеет получать файлы из formdata. Но как по мне, так его простота и удобство вполне себя оправдывают как минимум для простых проектов, вроде mvp.

Ответить
2

Да, соглашусь, если поменять Koa на Express в моем тексте, мало что изменится :) Просто я выбрал Коа (до этого всю жизнь пользовался Экспрессом). В любом случае, самой сути статьи это не должно поменять — и на Коа, и на Экспрессе можно спокойно написать MVP по-быстренькому. Спасибо за комментарии :)

Ответить
2

Спасибо за статью =)

Ответить
2

Тоже хотел написать про express, но по-скольку не так часто писал бэк на Nodejs, не стал лезть поперед батьки :D
Koa с плагинами вроде неплоха, но даже со всякими koa-body и доп парсерами это не так просто, как голый express из коробки.

Ответить
2

у тебя вроде повторяются скрины кода с серверной частью вместо vue или у меня очень сильный недосып? в любом случае, любопытно было читать, спасибо за статью!

Ответить
1

Спасибо, что заметили, поправил :) Рад, что понравилось!

Ответить
0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Хакеры смогли обойти двухфакторную
авторизацию с помощью уговоров
Подписаться на push-уведомления
{ "page_type": "default" }