From 63e6b1adec4d7cd3d9469f409b19813d3af44f80 Mon Sep 17 00:00:00 2001 From: Lou Huang Date: Wed, 8 Apr 2020 02:35:32 -0400 Subject: [PATCH 1/3] feat: display %chance of each pattern if known --- index.html | 28 +++++++++++++++++++ js/predictions.js | 69 ++++++++++++++++++++++++++++++++++++++++++----- js/scripts.js | 30 +++++++++++++++------ 3 files changed, 113 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index 99944e2..ded86e6 100644 --- a/index.html +++ b/index.html @@ -37,6 +37,33 @@ +
+
+
Probability
+
+
+
+ + +
+
+
+
+ + Last week’s pattern affects the probability of patterns for this week. If you know it, select it to see probabilities for this week. (optional) + +
+
+
+
+
Daisy Mae
@@ -152,6 +179,7 @@ Pattern + % Chance Sunday Monday Tuesday diff --git a/js/predictions.js b/js/predictions.js index 3b3e4ae..5fa5a48 100644 --- a/js/predictions.js +++ b/js/predictions.js @@ -1,3 +1,37 @@ +const PATTERN = { + ROLLERCOASTER: 0, + LARGE_SPIKE: 1, + DECREASING: 2, + SMALL_SPIKE: 3, +}; + +const PROBABILITY_MATRIX = { + [PATTERN.ROLLERCOASTER]: { + [PATTERN.ROLLERCOASTER]: 0.20, + [PATTERN.LARGE_SPIKE]: 0.30, + [PATTERN.DECREASING]: 0.15, + [PATTERN.SMALL_SPIKE]: 0.35, + }, + [PATTERN.LARGE_SPIKE]: { + [PATTERN.ROLLERCOASTER]: 0.50, + [PATTERN.LARGE_SPIKE]: 0.05, + [PATTERN.DECREASING]: 0.20, + [PATTERN.SMALL_SPIKE]: 0.25, + }, + [PATTERN.DECREASING]: { + [PATTERN.ROLLERCOASTER]: 0.25, + [PATTERN.LARGE_SPIKE]: 0.45, + [PATTERN.DECREASING]: 0.05, + [PATTERN.SMALL_SPIKE]: 0.25, + }, + [PATTERN.SMALL_SPIKE]: { + [PATTERN.ROLLERCOASTER]: 0.45, + [PATTERN.LARGE_SPIKE]: 0.25, + [PATTERN.DECREASING]: 0.15, + [PATTERN.SMALL_SPIKE]: 0.15, + }, +}; + function minimum_rate_from_given_and_base(given_price, buy_price) { return 10000 * (given_price - 1) / buy_price; } @@ -177,7 +211,7 @@ function* generate_pattern_0_with_lengths(given_prices, high_phase_1_len, dec_ph }); } yield { - pattern_description: "high, decreasing, high, decreasing, high", + pattern_description: "Rollercoaster", pattern_number: 0, prices: predicted_prices }; @@ -285,7 +319,7 @@ function* generate_pattern_1_with_peak(given_prices, peak_start) { }); } yield { - pattern_description: "decreasing middle, high spike, random low", + pattern_description: "Large spike", pattern_number: 1, prices: predicted_prices }; @@ -351,7 +385,7 @@ function* generate_pattern_2(given_prices) { max_rate -= 300; } yield { - pattern_description: "consistently decreasing", + pattern_description: "Decreasing", pattern_number: 2, prices: predicted_prices }; @@ -531,7 +565,7 @@ function* generate_pattern_3_with_peak(given_prices, peak_start) { } yield { - pattern_description: "decreasing, spike, decreasing", + pattern_description: "Small spike", pattern_number: 3, prices: predicted_prices }; @@ -564,8 +598,31 @@ function* generate_possibilities(sell_prices, first_buy) { } } -function analyze_possibilities(sell_prices, first_buy) { +function get_probabilities(possibilities, previous_pattern) { + if (typeof previous_pattern === 'undefined' || Number.isNaN(previous_pattern) || previous_pattern === null || previous_pattern < 0 || previous_pattern > 3) { + return possibilities + } + + var unique = (value, index, self) => { + return self.indexOf(value) === index; + } + var max_percent = possibilities.map(function (poss) { + return poss.pattern_number; + }).filter(unique).map(function(poss) { + return PROBABILITY_MATRIX[previous_pattern][poss]; + }).reduce(function (prev, current) { + return prev + current; + }, 0); + + return possibilities.map(function (poss) { + poss.probability = PROBABILITY_MATRIX[previous_pattern][poss.pattern_number] / max_percent; + return poss; + }); +} + +function analyze_possibilities(sell_prices, first_buy, previous_pattern) { generated_possibilities = Array.from(generate_possibilities(sell_prices, first_buy)); + generated_possibilities = get_probabilities(generated_possibilities, previous_pattern); global_min_max = []; for (var day = 0; day < 14; day++) { @@ -585,7 +642,7 @@ function analyze_possibilities(sell_prices, first_buy) { } generated_possibilities.push({ - pattern_description: "predicted min/max across all patterns", + pattern_description: "All patterns", pattern_number: 4, prices: global_min_max, }); diff --git a/js/scripts.js b/js/scripts.js index 4a99fbe..7dd9bf6 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -10,10 +10,12 @@ const getSellFields = function () { const sell_inputs = getSellFields() const buy_input = $("#buy") const first_buy_field = $("#first_buy"); +const previous_pattern_input = $("#previous_pattern"); //Functions -const fillFields = function (prices, first_buy) { +const fillFields = function (prices, first_buy, previous_pattern) { first_buy_field.prop("checked", first_buy); + previous_pattern_input.val(previous_pattern); buy_input.focus(); buy_input.val(prices[0] || '') @@ -36,10 +38,11 @@ const initialize = function () { try { const prices = getPrices() const first_buy = getFirstBuyState(); + const previous_pattern = getPreviousPatternState(); if (prices === null) { - fillFields([], first_buy) + fillFields([], first_buy, previous_pattern) } else { - fillFields(prices, first_buy) + fillFields(prices, first_buy, previous_pattern) } $(document).trigger("input"); } catch (e) { @@ -48,15 +51,19 @@ const initialize = function () { $("#reset").on("click", function () { first_buy_field.prop('checked', false); + $("select").val(null); $("input").val(null).trigger("input"); }) + + $('select').formSelect(); } -const updateLocalStorage = function (prices, first_buy) { +const updateLocalStorage = function (prices, first_buy, previous_pattern) { try { if (prices.length !== 14) throw "The data array needs exactly 14 elements to be valid" localStorage.setItem("sell_prices", JSON.stringify(prices)) localStorage.setItem("first_buy", JSON.stringify(first_buy)); + localStorage.setItem("previous_pattern", JSON.stringify(previous_pattern)); } catch (e) { console.error(e) } @@ -71,6 +78,10 @@ const getFirstBuyState = function () { return JSON.parse(localStorage.getItem('first_buy')) } +const getPreviousPatternState = function () { + return JSON.parse(localStorage.getItem('previous_pattern')) +} + const getPricesFromLocalstorage = function () { try { const sell_prices = JSON.parse(localStorage.getItem("sell_prices")); @@ -112,14 +123,15 @@ const getSellPrices = function () { }) } -const calculateOutput = function (data, first_buy) { +const calculateOutput = function (data, first_buy, previous_pattern) { if (isEmpty(data)) { $("#output").html(""); return; } let output_possibilities = ""; - for (let poss of analyze_possibilities(data, first_buy)) { + for (let poss of analyze_possibilities(data, first_buy, previous_pattern)) { var out_line = "" + poss.pattern_description + "" + out_line += `${Number.isFinite(poss.probability) ? ((poss.probability * 100).toPrecision(3) + '%') : '—'}`; for (let day of poss.prices.slice(1)) { if (day.min !== day.max) { out_line += `${day.min}..${day.max}`; @@ -138,15 +150,17 @@ const update = function () { const sell_prices = getSellPrices(); const buy_price = parseInt(buy_input.val()); const first_buy = first_buy_field.is(":checked"); + const previous_pattern = parseInt(previous_pattern_input.val()); buy_input.prop('disabled', first_buy); const prices = [buy_price, buy_price, ...sell_prices]; if (!window.price_from_query) { - updateLocalStorage(prices, first_buy); + updateLocalStorage(prices, first_buy, previous_pattern); } - calculateOutput(prices, first_buy); + calculateOutput(prices, first_buy, previous_pattern); } $(document).ready(initialize); $(document).on("input", update); +$(previous_pattern_input).on("change", update); From 0b69cba80296c33cb716b4827436ef3d75a0ce16 Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Wed, 8 Apr 2020 18:15:56 +0100 Subject: [PATCH 2/3] Rename pattern and make probability per row --- index.html | 2 +- js/predictions.js | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index ded86e6..23bcb89 100644 --- a/index.html +++ b/index.html @@ -47,7 +47,7 @@ - + diff --git a/js/predictions.js b/js/predictions.js index 5fa5a48..a4358c1 100644 --- a/js/predictions.js +++ b/js/predictions.js @@ -1,31 +1,38 @@ const PATTERN = { - ROLLERCOASTER: 0, + FLUCTUATING: 0, LARGE_SPIKE: 1, DECREASING: 2, SMALL_SPIKE: 3, }; +const PATTERN_COUNTS = { + [PATTERN.FLUCTUATING]: 56, + [PATTERN.LARGE_SPIKE]: 7, + [PATTERN.DECREASING]: 1, + [PATTERN.SMALL_SPIKE]: 8, +} + const PROBABILITY_MATRIX = { - [PATTERN.ROLLERCOASTER]: { - [PATTERN.ROLLERCOASTER]: 0.20, + [PATTERN.FLUCTUATING]: { + [PATTERN.FLUCTUATING]: 0.20, [PATTERN.LARGE_SPIKE]: 0.30, [PATTERN.DECREASING]: 0.15, [PATTERN.SMALL_SPIKE]: 0.35, }, [PATTERN.LARGE_SPIKE]: { - [PATTERN.ROLLERCOASTER]: 0.50, + [PATTERN.FLUCTUATING]: 0.50, [PATTERN.LARGE_SPIKE]: 0.05, [PATTERN.DECREASING]: 0.20, [PATTERN.SMALL_SPIKE]: 0.25, }, [PATTERN.DECREASING]: { - [PATTERN.ROLLERCOASTER]: 0.25, + [PATTERN.FLUCTUATING]: 0.25, [PATTERN.LARGE_SPIKE]: 0.45, [PATTERN.DECREASING]: 0.05, [PATTERN.SMALL_SPIKE]: 0.25, }, [PATTERN.SMALL_SPIKE]: { - [PATTERN.ROLLERCOASTER]: 0.45, + [PATTERN.FLUCTUATING]: 0.45, [PATTERN.LARGE_SPIKE]: 0.25, [PATTERN.DECREASING]: 0.15, [PATTERN.SMALL_SPIKE]: 0.15, @@ -211,7 +218,7 @@ function* generate_pattern_0_with_lengths(given_prices, high_phase_1_len, dec_ph }); } yield { - pattern_description: "Rollercoaster", + pattern_description: "Fluctuating", pattern_number: 0, prices: predicted_prices }; @@ -615,7 +622,7 @@ function get_probabilities(possibilities, previous_pattern) { }, 0); return possibilities.map(function (poss) { - poss.probability = PROBABILITY_MATRIX[previous_pattern][poss.pattern_number] / max_percent; + poss.probability = PROBABILITY_MATRIX[previous_pattern][poss.pattern_number] / PATTERN_COUNTS[poss.pattern_number] / max_percent; return poss; }); } From 45e055718f1aa9baf6cc4da1cc38d5f0cbd219a6 Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Wed, 8 Apr 2020 18:29:33 +0100 Subject: [PATCH 3/3] Fix calculations --- js/predictions.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/js/predictions.js b/js/predictions.js index a4358c1..4db235a 100644 --- a/js/predictions.js +++ b/js/predictions.js @@ -605,24 +605,23 @@ function* generate_possibilities(sell_prices, first_buy) { } } +function row_probability(possibility, previous_pattern) { + return PROBABILITY_MATRIX[previous_pattern][possibility.pattern_number] / PATTERN_COUNTS[possibility.pattern_number]; +} + function get_probabilities(possibilities, previous_pattern) { if (typeof previous_pattern === 'undefined' || Number.isNaN(previous_pattern) || previous_pattern === null || previous_pattern < 0 || previous_pattern > 3) { return possibilities } - var unique = (value, index, self) => { - return self.indexOf(value) === index; - } var max_percent = possibilities.map(function (poss) { - return poss.pattern_number; - }).filter(unique).map(function(poss) { - return PROBABILITY_MATRIX[previous_pattern][poss]; + return row_probability(poss, previous_pattern); }).reduce(function (prev, current) { return prev + current; }, 0); return possibilities.map(function (poss) { - poss.probability = PROBABILITY_MATRIX[previous_pattern][poss.pattern_number] / PATTERN_COUNTS[poss.pattern_number] / max_percent; + poss.probability = row_probability(poss, previous_pattern) / max_percent; return poss; }); }