Универсальная аналитическая машина

Несколь­ко лет назад была насто­я­щая крип­то­ли­хо­рад­ка: люди поку­па­ли и про­да­ва­ли бит­ко­и­ны, май­ни­ли эфир, ску­па­ли видео­кар­ты и царил дух аван­тю­риз­ма. В те вре­ме­на все иска­ли про­ве­рен­ные источ­ни­ки, инсай­ды и ана­ли­ти­ку, кото­рые помо­гут при­нять вер­ное реше­ние о покуп­ке.

Как и все­гда, про­гно­зы — дело небла­го­дар­ное, и про­гноз из серии «Паль­цем в небо» будет вся­ко точ­нее. Поэто­му мы реши­ли авто­ма­ти­зи­ро­вать этот про­цесс: сде­лать уни­вер­саль­ную ана­ли­ти­че­скую маши­ну, кото­рая спо­соб­на про­гно­зи­ро­вать что угод­но, от кур­са дол­ла­ра до марш­ру­та лосо­ся на нерест. Метод про­гно­зи­ро­ва­ния хоро­шо изу­чен — наугад.

Сей­час наша маши­на уме­ет пред­ска­зы­вать пове­де­ние кур­са крип­то­ва­лю­ты в бли­жай­шей пер­спек­ти­ве:


Собрав такую же маши­ну у себя, вы смо­же­те пред­ска­зы­вать что угод­но. Вот как это сде­лать.

Что происходит на самом деле

Вся эта исто­рия про ана­ли­ти­че­скую маши­ну — пол­ный фейк. Мы при­ду­ма­ли этот про­ект от нача­ла и до кон­ца, и логи­ка была такая:

  1. Пока­зать кра­си­вый прогресс-бар.
  2. Запу­стить его по кноп­ке, что­бы он начал рабо­тать.
  3. Пока он дви­жет­ся, выво­дим вся­кие сооб­ще­ния, что­бы создать види­мость рабо­ты. Очень важ­но, что­бы чело­век на том кон­це думал, что маши­на реаль­но что-то про­гно­зи­ру­ет.
  4. В кон­це пока­зы­ва­ем какое-то сооб­ще­ние, мол, вот ваш про­гноз. Сооб­ще­ние может быть совер­шен­но ран­дом­ным, на каче­ство про­гно­за это не вли­я­ет.

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

Зачем этот проект

Этот про­ект — упраж­не­ние в глав­ной состав­ля­ю­щей про­грам­мист­ско­го дзе­на: бери чужое, вкру­чи­вай в своё. Мы будем поль­зо­вать­ся гото­вы­ми чужи­ми реше­ни­я­ми и полу­чим в ито­ге рабо­та­ю­щий и доволь­но полез­ный про­дукт.

Прогресс-бар

Сде­лать кра­си­вый прогресс-бар — дело не пяти минут, поэто­му мы поис­ка­ли уже гото­вые реше­ния в Сети и нашли вот такое:

Там было почти всё, что нам нуж­но, кро­ме двух вещей:

  • прогресс-бар запол­нял­ся слиш­ком быст­ро,
  • не меня­лись над­пи­си в про­цес­се рабо­ты.

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

Замед­ля­ем прогресс-бар. Смот­рим в function preload(imgArray):

Было:

$(imgArray).each(function() {
$('<img>').attr("src", this).load(function() {
$progress.animate({
width: "+=" + increment + "%"
}, 100);
});

Ста­ло:

var i = 0;
    while (i < 300) {
$progress.animate({
width: "+=" + 0.25 + "%"
}, 100);
         i = i+1;
       
     }

Вме­сто того, что­бы уве­ли­чи­вать про­гресс на какую-то непо­нят­ную вели­чи­ну, мы при­бав­ля­ем к нему на каж­дом шаге все­го по 0.25 пунк­та. Это мало, поэто­му дви­гать­ся будет мед­лен­но. Чис­ло 300 в цик­ле мы подо­бра­ли опыт­ным путём: почему-то если при­бав­лять по 0.25, то имен­но за 300 шагов мы набе­рём 100%.

Выво­дим сооб­ще­ния по ходу рабо­ты. Смот­рим в ту же функ­цию.

Было: ниче­го, пото­му что в исход­ном про­ек­те это­го не тре­бо­ва­лось.

Ста­ло:

    
language: JavaScript
if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 1) {

      $('span').text(s1_final);

    } 

    

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 20) {

      $('span').text(s20_final);

    } 

 

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 40) {

      $('span').text(s40_final);

    } 

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 60) {

      $('span').text(s60_final);

    }

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 80) {

      $('span').text(s80_final);

    }

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 100) {

yes = false;

      $('span').text('Примерная точность прогноза: ' + percent_final + '%');

      $('p').text(result_final);

    }


Ско­пи­ро­вать код
Код ско­пи­ро­ван

Пер­вые 5 раз мы срав­ни­ва­ем пока­за­те­ли про­цен­тов на шка­ле с каким-то чис­лом и если сов­па­да­ем — выво­дим то, что лежит в пере­мен­ной. В послед­нем срав­не­нии выда­ём про­цен­ты и финаль­ный текст. Про пере­мен­ные рас­ска­жем ниже.

Готовим текст

У нас уже был про­ект, где мы гене­ри­ро­ва­ли текст слу­чай­ным обра­зом. Возь­мём отту­да немно­го кода и пере­пи­шем для себя:

    
language: JavaScript
// этапы в прогресс-баре

var s1 = ['Собираю данные из интернета','Читаю Википедию','Смотрю паблики во ВКонтакте'];

var s20 = ['Обучаю нейросеть','Анализирую увиденное','Выделяю важное'];

var s40 = ['Готовлю технический анализ','Думаю о будущем','Осознаю важность момента'];

var s60 = ['Постулирую неведомое','Инкапсулирую полиморфизм','Кристаллизую суть'];

var s80 = ['Тыкаю пальцем в небо','Формализую данность как класс','Удаляю противоречия и вношу ясность'];

var result = ['Сначала крипта подрастет, потом обвалится. Потом снова подрастет, потом снова обвалится.','Можно закупать, хотя лучше сначала продать, но если важнее купить, то можно и не продавать','По всем показателям рецессия будет следовать за отскоком, но это вряд ли отразится на текущей ситуации, поэтому необходимо обратить внимание на волатильность.'];

 

// генератор случайных чисел в диапазоне от минимального до максимального

function randz(min, max){

 

  return Math.floor(Math.random() * (max - min + 1)) + min;

 

}  

 

// готовим итоговые значения

 

var s1_final = s1[randz(0,2)];

var s20_final = s20[randz(0,2)];

var s40_final = s40[randz(0,2)];

var s60_final = s60[randz(0,2)];

var s80_final = s80[randz(0,2)];

var percent_final = randz(60,99);

var result_final = result[randz(0,2)];


Ско­пи­ро­вать код
Код ско­пи­ро­ван

Логи­ка про­стая:

  • гото­вим шаб­ло­ны тек­ста для каж­до­го эта­па;
  • встав­ля­ем функ­цию, кото­рая вер­нёт нам слу­чай­ное чис­ло в нуж­ном диа­па­зоне;
  • запол­ня­ем финаль­ные зна­че­ния слу­чай­ным обра­зом.

Вер­ни­тесь немно­го назад и посмот­ри­те: имен­но эти пере­мен­ные мы исполь­зо­ва­ли в раз­де­ле, когда срав­ни­ва­ли зна­че­ния с про­цен­та­ми на шка­ле и выво­ди­ли резуль­тат.

Собираем страницу

Основ­ное мы сде­ла­ли, оста­лось всё это кра­си­во раз­ме­стить в бра­у­зе­ре. Берём код стра­ни­цы исход­но­го про­ек­та, немно­го пра­вим и полу­ча­ем такое:

    
language: HTML
<!DOCTYPE html>

<html>

<head>

  <meta charset="UTF-8">

  <title>Image Preloader Progress Bar</title>

  <link rel="stylesheet" href="./style.css">

 

</head>

<style type="text/css">

button{

font-size: 100%;

padding:5px 10px 10px 10px;

margin-top: 10%;

}

 

</style>

<body>

 

<!-- кнопка для запуска -->

    <button value="Получить предсказание по крипте" onclick="tested();">Получить прогноз по криптовалюте</button>

    

<!-- наш прогресс-бар -->

<div class="loader">

<div class="progress-bar"><div class="progress-stripes"></div><div class="percentage">0%</div></div>

</div>

 

<span>Система готова</span>

 

<p> 

<!-- тут будет финальный текст -->

</p>

<!-- подключаем jQuery и сам скрипт -->

  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script><script  src="./script.js"></script>

 

</body>

</html>


Ско­пи­ро­вать код
Код ско­пи­ро­ван

Мы доба­ви­ли кноп­ку и раз­дел с тегом <p> для финаль­но­го тек­ста. Всё осталь­ное дона­стро­им в скрип­те.

Запуск по кнопке

В исход­ном про­ек­те прогресс-бар начи­на­ет рабо­тать авто­ма­ти­че­ски сра­зу после загруз­ки стра­ни­цы. Нам такое не нуж­но, а нуж­но, что­бы всё запус­ка­лось после нажа­тия на кноп­ку. Поэто­му сно­ва пра­вим скрипт.

Уби­ра­ем авто­за­груз­ку. За это отве­ча­ет свой­ство $(window).load(function(). Нахо­дим его и пол­но­стью уда­ля­ем эту функ­цию.

При­вя­зы­ва­ем запуск к кноп­ке. Помни­те, мы пра­ви­ли функ­цию function preload(imgArray)? Имен­но она отве­ча­ет за рабо­ту все­го прогресс-бара. Что­бы при­вя­зать её к кноп­ке, обо­ра­чи­ва­ем вызов функ­ции в такой код:

function launch() {
preload(demoImgArray);
}

Смот­рим, что­бы в опи­са­нии кноп­ки на стра­ни­це тоже всё было пра­виль­но:

<button value="Получить предсказание по крипте" onclick="launch();">Получить прогноз по криптовалюте</button>

Стро­ка onclick="launch()" озна­ча­ет, что при нажа­тии на кноп­ку сра­бо­та­ет функ­ция launch(), а имен­но она у нас в скрип­те отве­ча­ет за запуск прогресс-бара.

Дела­ем кноп­ку одно­ра­зо­вой. Что­бы всё рабо­та­ло без сбо­ев и пред­ска­зу­е­мо, давай­те запре­тим нажи­мать на кноп­ку боль­ше одно­го раза. Для это­го исполь­зу­ем jQuery в функ­ции preload(imgArray):

$('button').prop('disabled', true);

Этот код дела­ет сле­ду­ю­щее:

  • Нахо­дит на стра­ни­це эле­мент <button>.
  • При­сва­и­ва­ет свой­ству кноп­ки disabled зна­че­ние true. Это зна­чит, что кноп­ка ста­но­вит­ся неак­тив­ной — она вид­на, но нажать на неё нель­зя. То что нуж­но.

Что в результате

Мы раз­ме­сти­ли свою вер­сию про­ек­та на сай­те — мож­но поль­зо­вать­ся само­му и делить­ся с дру­зья­ми. Если вы хоти­те сде­лать себе такое же и даже кру­че — дер­жи­те исход­ный код:

index.html

    
language: HTML
<!DOCTYPE html>

<html>

<head>

  <meta charset="UTF-8">

  <title>Image Preloader Progress Bar</title>

  <link rel="stylesheet" href="./style.css">

 

</head>

<style type="text/css">

button{

font-size: 100%;

padding:5px 10px 10px 10px;

margin-top: 10%;

}

 

</style>

<body>

 

<!-- кнопка для запуска -->

    <button value="Получить предсказание по крипте" onclick="launch();">Получить прогноз по криптовалюте</button>

    

<!-- наш прогресс-бар -->

<div class="loader">

<div class="progress-bar"><div class="progress-stripes"></div><div class="percentage">0%</div></div>

</div>

 

<span>Система готова</span>

 

<p> 

<!-- тут будет финальный текст -->

</p>

<!-- подключаем jQuery и сам скрипт -->

  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script><script  src="./script.js"></script>

 

</body>

</html>


Ско­пи­ро­вать код
Код ско­пи­ро­ван
style.css

    
language: CSS
body{

  background: #e7e7e7;

  font-family: Arial, Helvetica, Sans-serif;

  text-align: center;

  padding: 5px;

  margin: 0;

}

 

.loader{

  margin: 50px auto 10px;

  width: 300px;

  height: 25px;

  border-radius: 14px;

  border-bottom: 1px solid #fff;

  border-top: 1px solid #999;

  background: #ccc;

  overflow: hidden;

  position: relative;

}

 

.loader.gray{

  background: #999;

}

 

.progress-bar{

  height: inherit;

  width: 0%;

  border-radius: inherit;

  position: relative;

  overflow: hidden;

}

 

.progress-stripes{

  width: inherit;

  height: inherit;

  font-size: 180px;

  font-weight: bold;

  margin-top: -50px;

  letter-spacing: -14px;

}

 

.percentage{

  position: absolute;

  top: 4px;

  right: 5px;

  font-weight: bold;

  font-size: 16px;

}

 

/***************************************/

/* BELOW HERE IS SOLELY FOR AESTHETICS */

/*_____________________________________*/

 

/*** COLOR SCHEMES ***/

 

/* RED */

.red .progress-bar{

  background: #e74c3c;

}

.red .progress-stripes{

  color: #c0392b;

}

.red .percentage{

  color: #eee;

}

 

/* BLUE */

.blue .progress-bar{

  background: #3498db;

}

.blue .progress-stripes{

  color: #2980b9;

}

.blue .percentage{

  color: #eee;

}

 

/* GREEN */

.green .progress-bar{

  background: #2ecc71;

}

.green .progress-stripes{

  color: #27ae60;

}

.green .percentage{

  color: #fff;

}

 

/* YELLOW */

.yellow .progress-bar{

  background: #f1c40f;

}

.yellow .progress-stripes{

  color: #f39c12;

}

.yellow .percentage{

  color: #fff;

}

 

/* PURPLE */

.purple .progress-bar{

  background: #9b59b6;

}

.purple .progress-stripes{

  color: #8e44ad;

}

.purple .percentage{

  color: #eee;

}

 

/* GRAY */

.gray .progress-bar{

  background: #ecf0f1;

}

.gray .progress-stripes{

  color: #bdc3c7;

}

.gray .percentage{

  color: #333;

}

 

/*** LOADED ***/

 

span{

  color: #888;

  text-shadow: 0 1px 0 #fff;

}

 

span.loaded.red{

  color: #c0392b;

}

 

span.loaded.blue{

  color: #2980b9;

}

 

span.loaded.green{

  color: #27ae60;

}

 

span.loaded.yellow{

  color: #d35400;

}

 

span.loaded.purple{

  color: #8e44ad;

}

 

span.loaded.gray{

  color: #444;

}

 

/*** SKINS ***/

.skins{

  padding: 4px 0 8px;

  cursor: default;

  font-size: 14px;

  color: #666;

  background: #fff;

  opacity: .5;

  -moz-transition: opacity .25s linear;

  -webkit-transition: opacity .25s linear;

  transition: opacity .25s linear;

}

 

.skins:hover{

  opacity: 1;

}

 

.skin{

  width: 20px;

  height: 20px;

  cursor: pointer;

  margin-bottom: -7px;

  border: 1px solid #fff;

  display: inline-block;

    *display: inline;

    zoom: 1.0;

}

#red{

  background: #c0392b;

}

#red:hover{

  background: #e74c3c;

}

#blue{

  background: #2980b9;

}

#blue:hover{

  background: #3498db;

}

#green{

  background: #27ae60;

}

#green:hover{

  background: #2ecc71;

}

#yellow{

  background: #f39c12;

}

#yellow:hover{

  background: #f1c40f;

}

#purple{

  background: #8e44ad;

}

#purple:hover{

  background: #9b59b6;

}

#gray{

  background: #7f8c8d;

}

#gray:hover{

  background: #95a5a6;

}


Ско­пи­ро­вать код
Код ско­пи­ро­ван
script.js

    
language: JavaScript
// этапы в прогресс-баре

var s1 = ['Собираю данные из интернета','Читаю Википедию','Смотрю паблики во ВКонтакте'];

var s20 = ['Обучаю нейросеть','Анализирую увиденное','Выделяю важное'];

var s40 = ['Готовлю технический анализ','Думаю о будущем','Осознаю важность момента'];

var s60 = ['Постулирую неведомое','Инкапсулирую полиморфизм','Кристаллизую суть'];

var s80 = ['Тыкаю пальцем в небо','Формализую данность как класс','Удаляю противоречия и вношу ясность'];

var result = ['Сначала крипта подрастет, потом обвалится. Потом снова подрастет, потом снова обвалится.','Можно закупать, хотя лучше сначала продать, но если важнее купить, то можно и не продавать','По всем показателям рецессия будет следовать за отскоком, но это вряд ли отразится на текущей ситуации, поэтому необходимо обратить внимание на волатильность.'];

 

// генератор случайных чисел в диапазоне от минимального до максимального

function randz(min, max){

 

  return Math.floor(Math.random() * (max - min + 1)) + min;

 

}  

 

// готовим итоговые значения

 

var s1_final = s1[randz(0,2)];

var s20_final = s20[randz(0,2)];

var s40_final = s40[randz(0,2)];

var s60_final = s60[randz(0,2)];

var s80_final = s80[randz(0,2)];

var percent_final = randz(60,99);

var result_final = result[randz(0,2)];

 

/* SET RANDOM LOADER COLORS FOR DEMO PURPOSES */

var demoColorArray = ['red','blue','green','yellow','purple','gray'];

var colorIndex = Math.floor(Math.random()*demoColorArray.length);

setSkin(demoColorArray[colorIndex]);

 

var yes = true;

 

/* RANDOM LARGE IMAGES FOR DEMO PURPOSES */

var demoImgArray = ['http://www.hdwallpapers.in/walls/halloween_2013-wide.jpg', 'http://www.hdwallpapers.in/walls/2013_print_tech_lamborghini_aventador-wide.jpg', 'http://www.hdwallpapers.in/walls/ama_dablam_himalaya_mountains-wide.jpg', 'http://www.hdwallpapers.in/walls/arrow_tv_series-wide.jpg', 'http://www.hdwallpapers.in/walls/anna_in_frozen-wide.jpg', 'http://www.hdwallpapers.in/walls/frozen_elsa-wide.jpg', 'http://www.hdwallpapers.in/walls/shraddha_kapoor-wide.jpg', 'http://www.hdwallpapers.in/walls/sahara_force_india_f1_team-HD.jpg', 'http://www.hdwallpapers.in/walls/lake_sunset-wide.jpg', 'http://www.hdwallpapers.in/walls/2013_movie_cloudy_with_a_chance_of_meatballs_2-wide.jpg', 'http://www.hdwallpapers.in/walls/bates_motel_2013_tv_series-wide.jpg', 'http://www.hdwallpapers.in/walls/krrish_3_movie-wide.jpg', 'http://www.hdwallpapers.in/walls/universe_door-wide.jpg', 'http://www.hdwallpapers.in/walls/night_rider-HD.jpg', 'http://www.hdwallpapers.in/walls/tide_and_waves-wide.jpg', 'http://www.hdwallpapers.in/walls/2014_lamborghini_veneno_roadster-wide.jpg', 'http://www.hdwallpapers.in/walls/peeta_katniss_the_hunger_games_catching_fire-wide.jpg', 'http://www.hdwallpapers.in/walls/captain_america_the_winter_soldier-wide.jpg', 'http://www.hdwallpapers.in/walls/puppeteer_ps3_game-wide.jpg', 'http://www.hdwallpapers.in/walls/lunar_space_galaxy-HD.jpg', 'http://www.hdwallpapers.in/walls/2013_wheelsandmore_lamborghini_aventador-wide.jpg', 'http://www.hdwallpapers.in/walls/destiny_2014_game-wide.jpg', 'http://www.hdwallpapers.in/colors_of_nature-wallpapers.html', 'http://www.hdwallpapers.in/walls/sunset_at_laguna_beach-wide.jpg'];

// Stripes interval

var stripesAnim;

var calcPercent;

$progress = $('.progress-bar');

$percent = $('.percentage');

$stripes = $('.progress-stripes');

$stripes.text('////////////////////////');

/* CHANGE LOADER SKIN */

$('.skin').click(function(){

var whichColor = $(this).attr('id');

setSkin(whichColor);

});

// Call function to load array of images

function launch() {

preload(demoImgArray);

}

// Call function to animate stripes

stripesAnimate(); 

/*** FUNCTIONS ***/

/* LOADING */

function preload(imgArray) {

$('button').prop('disabled', true);

var increment = Math.floor(100 / imgArray.length);

    var i = 0;

    while (i < 300) { 

      

$progress.animate({

width: "+=" + 0.25 + "%"

}, 100);

        i = i+1;

       

     }

    

calcPercent = setInterval(function() {

//loop through the items

$percent.text(Math.floor(($progress.width() / $('.loader').width()) * 100) + '%');

 

if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 1) {

          $('span').text(s1_final);

        } 

       

     if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 20) {

              $('span').text(s20_final);

            } 

      

      if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 40) {

              $('span').text(s40_final);

            } 

      if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 60) {

              $('span').text(s60_final);

            }

      if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 80) {

              $('span').text(s80_final);

            }

      if (Math.floor(($progress.width() / $('.loader').width()) * 100)  == 100) {

       yes = false;

              $('span').text('Примерная точность прогноза: ' + percent_final + '%');

              $('p').text(result_final);

            } 

});

}

/* STRIPES ANIMATION */

function stripesAnimate() {

if (yes) {

animating();

stripesAnim = setInterval(animating, 2500);

}

}

 

function animating() {

if (yes){

$stripes.animate({

marginLeft: "-=30px"

}, 2500, "linear").append('/');

}

function setSkin(skin){

$('.loader').attr('class', 'loader '+skin);

$('span').hasClass('loaded') ? $('span').attr('class', 'loaded '+skin) : $('span').attr('class', skin);

}


Ско­пи­ро­вать код
Код ско­пи­ро­ван

В этом коде мно­го лиш­них строк, он неопти­маль­ный и его мож­но суще­ствен­но улуч­шить. Но глав­ное — он уже рабо­та­ет, и его мож­но быст­ро допи­лить под свои зада­чи. Поэто­му вот домаш­нее зада­ние для тех, кто хочет иде­ал:

  • най­ти и убрать лиш­ние функ­ции;
  • заме­нить насто­я­щую загруз­ку кар­ти­нок (посмот­ри­те вни­ма­тель­но код) на что-то не настоль­ко нагру­зоч­ное;
  • сде­лать боль­ше вари­ан­тов тек­ста на каж­дом эта­пе.
Предсказываем будущее рынка труда
В буду­щем оста­нут­ся толь­ко раз­ра­бот­чи­ки и мас­са­жи­сты. Если вы не мас­са­жист, при­хо­ди­те учить­ся на раз­ра­бот­чи­ка. Мяу.