Skip to content

Commit

Permalink
Merge branch 'stock-backtest' of https://github.com/neo773/marketing
Browse files Browse the repository at this point in the history
…into pr/123
  • Loading branch information
Shpigford committed Sep 2, 2024
2 parents 57f339e + db14d6e commit 9d6434a
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 17 deletions.
23 changes: 15 additions & 8 deletions app/javascript/controllers/search_select_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static classes = ["active"];
static targets = ["option", "input", "list", "hiddenInput"];
static values = { selected: String, count: Number, list: String };
static values = { selected: String, count: Number, list: String, customFilter: String };

initialize() {
this.show = false;
Expand Down Expand Up @@ -163,13 +163,20 @@ export default class extends Controller {
}

const dataList = window[this.listValue];
const filteredList = dataList
.filter(item =>
item.name.toLowerCase().includes(filterValue) ||
item.value.toLowerCase().includes(filterValue)
)
.slice(0, 5);

let filteredList;

if (this.hasCustomFilterValue && typeof window[this.customFilterValue] === 'function') {
filteredList = window[this.customFilterValue](dataList, filterValue).slice(0, 5);
} else {
filteredList = dataList
.filter(item =>
item.name.toLowerCase().includes(filterValue) ||
item.value.toLowerCase().includes(filterValue)
)
.slice(0, 5);
}


if (filteredList.length > 0) {
filteredList.forEach(item => {
const li = document.createElement('li');
Expand Down
15 changes: 15 additions & 0 deletions app/javascript/controllers/stock_portfolio_backtest_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ export default class extends Controller {
return data;
}

distributeEvenly() {
const allocatorControllers = this.application.controllers.filter(
controller => controller.identifier === 'stocks-allocator' && !controller.element.classList.contains('hidden')
);
const count = allocatorControllers.length;
if (count > 0) {
const evenAllocation = Math.floor(100 / count);
allocatorControllers.forEach((controller, index) => {
const newAllocation = index === count - 1 ? 100 - (evenAllocation * (count - 1)) : evenAllocation;
controller.allocationTarget.value = newAllocation;
controller.updateAllocation();
});
}
}

/**
* @param {Event} event
*/
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/controllers/stocks_allocator_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ export default class extends Controller {
const nextAllocator = document.querySelector(`[data-controller='stocks-allocator'][data-index='${currentIndex + 1}']`);
if (nextAllocator && this.countVisibleAllocators() < 10) {
nextAllocator.classList.remove("hidden");
nextAllocator.querySelector('input[type="number"]').setAttribute('required', '');
nextAllocator.querySelector('input[type="text"]').setAttribute('required', '');
}
}

removeStockSelector(event) {
const currentAllocator = event.target.closest("[data-controller='stocks-allocator']");
if (this.countVisibleAllocators() > 1) {
currentAllocator.classList.add("hidden");
currentAllocator.querySelector('input[type="number"]').removeAttribute('required');
currentAllocator.querySelector('input[type="text"]').removeAttribute('required');

const searchSelectController = this.getControllerByIdentifier("search-select", currentAllocator);
if (searchSelectController) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ export default class extends Controller {
series: { type: Object, default: {} },
data: { type: Array, default: [] },
useLabels: { type: Boolean, default: true },
showLegend: { type: Boolean, default: true },
};

#initialElementWidth = 0;
#initialElementHeight = 0;

connect() {
console.log(this.showLegendValue, this.values);
this.#rememberInitialElementSize();
this.#drawGridlines();
this.#drawBogleheadsGrowthChart();
if (this.useLabelsValue) {
this.#drawXAxis();
}
if (this.showLegendValue) {
this.#drawLegend();
}
this.#installTooltip();
Expand Down
4 changes: 2 additions & 2 deletions app/views/shared/_search_select.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
<% if local_assigns[:label].present? %>
<label class="absolute top-2 left-3 text-xs text-gray-500 z-10"><%= label %></label>
<% end %>
<input type="text" data-search-select-target="input" class="form-field flex rounded-[10px] <%= local_assigns[:label].present? ? 'h-14 pt-7' : 'h-9' %> shadow-xs w-full text-sm justify-start items-center px-3 pr-7 text-ellipsis" placeholder="Search for a stock">
<input type="text" data-search-select-target="input" class="form-field flex rounded-[10px] <%= local_assigns[:label].present? ? 'h-14 pt-7' : 'h-9' %> shadow-xs w-full text-sm justify-start items-center px-3 pr-7 text-ellipsis" placeholder="<%= placeholder %>" <%= 'required' if defined?(required) && required %>>
<%= lucide_icon("search", class: "h-4 w-4 text-gray-500 absolute right-3 #{local_assigns[:label].present? ? 'top-[67%]' : 'top-1/2'} transform -translate-y-1/2") %>
</div>

<input type="hidden" name="<%= name %>" data-search-select-target="hiddenInput" data-auto-submit-form-target="auto">

<ul data-search-select-target="list" class="hidden absolute z-10 top-[110%] right-0 w-full border border-black/10 bg-white rounded shadow-xs p-1"></ul>
<ul data-search-select-target="list" class="hidden absolute z-10 top-[110%] right-0 w-full border border-black/10 bg-white rounded shadow-xs p-1 z-30"></ul>

</div>
31 changes: 27 additions & 4 deletions app/views/shared/_stocks_allocator.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<div data-controller="stocks-allocator" data-index="<%= local_assigns[:index] %>" class="flex items-center gap-2 <%= local_assigns[:index] == 0 ? "" : "hidden" %>">
<div data-controller="stocks-allocator" data-index="<%= local_assigns[:index] %>" class="flex items-center gap-2 <%= local_assigns[:index] == 0 ? "" : "hidden" %>" data-search-select-custom-filter-value="prioritizeTickerFilter">
<%= render partial: "shared/search_select", locals: {
list: local_assigns[:list],
label: local_assigns[:label],
name: "stock_#{local_assigns[:index]}"
name: "stock_#{local_assigns[:index]}",
required: local_assigns[:required],
placeholder: "Search for a stock"
} %>

<div class="relative w-16">
<input type="number" min="0" max="100" step="1" name="stock_allocation_<%= local_assigns[:index] %>" data-stocks-allocator-target="allocation" data-action="input->stocks-allocator#updateAllocation" class="form-field w-[68px] text-left rounded-[10px] h-9 shadow-xs text-sm !appearance-none" placeholder="0">
<input type="number" min="0" max="100" step="1" name="stock_allocation_<%= local_assigns[:index] %>" data-stocks-allocator-target="allocation" data-action="input->stocks-allocator#updateAllocation" class="form-field w-[68px] text-left rounded-[10px] h-9 shadow-xs text-sm !appearance-none" placeholder="0" <%= "required" if local_assigns[:required] %>>
<span class="absolute inset-y-0 right-2 flex items-center pointer-events-none text-gray-500 text-xs">%</span>
</div>

Expand All @@ -18,4 +20,25 @@
<%= lucide_icon("plus", class: "stroke-[3px]") %>
</button>
</div>
</div>
</div>


<script>
window.prioritizeTickerFilter = function(dataList, filterValue) {
return dataList
.filter(item => {
const ticker = item.name.match(/\((.*?)\)/)?.[1] || '';
return ticker.toLowerCase().includes(filterValue) ||
item.name.toLowerCase().includes(filterValue);
})
.sort((a, b) => {
const tickerA = a.name.match(/\((.*?)\)/)?.[1] || '';
const tickerB = b.name.match(/\((.*?)\)/)?.[1] || '';
const aStartsWithFilter = tickerA.toLowerCase().startsWith(filterValue);
const bStartsWithFilter = tickerB.toLowerCase().startsWith(filterValue);
if (aStartsWithFilter && !bStartsWithFilter) return -1;
if (!aStartsWithFilter && bStartsWithFilter) return 1;
return 0;
});
};
</script>
8 changes: 5 additions & 3 deletions app/views/tools/_stock_portfolio_backtest.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
<%= render partial: 'shared/section_divider', locals: { section_name: 'Portfolio' } %>
<% 10.times do |i| %>
<%= render partial: "shared/stocks_allocator", locals: { list: "stocksData", index: i } %>
<%= render partial: "shared/stocks_allocator", locals: { list: "stocksData", index: i, required: i == 0 } %>
<% end %>

<div class="flex items-center justify-end gap-2">
<span class="text-gray-700 text-xs">Total allocation</span>
<div class="flex items-center gap-[6px] px-[14px] py-2 h-9 bg-green-400/5 border border-gray-200 rounded-md">
<span class="text-black text-sm" data-stock-portfolio-backtest-target="totalAllocation">0%</span>
<%= lucide_icon("align-vertical-distribute-center", class: "h-4 w-4 text-gray-500") %>
<%= lucide_icon("align-vertical-distribute-center", class: "h-4 w-4 text-gray-500 cursor-pointer", 'data-action': "click->stock-portfolio-backtest#distributeEvenly") %>
</div>
</div>

<%= render partial: 'shared/section_divider', locals: { section_name: 'Settings' } %>
<%= render partial: "shared/search_select", locals: { list: "stocksData", label: "Benchmark", name: "benchmarkStock" } %>
<%= render partial: "shared/search_select", locals: { list: "stocksData", label: "Benchmark", name: "benchmarkStock", required: true, placeholder: "Search for a stock" } %>

<div data-controller="synchronized-input" class="flex flex-col gap-2">
<%= form.unit_field :investment_amount, label: "Investment amount", value: 10000, unit_symbol: "$", data: { controller: "autonumeric", action: "input->stock-portfolio-backtest#calculate" } %>
Expand Down Expand Up @@ -93,6 +93,8 @@
controller: "time-series-bogleheads-growth-chart",
},
"t-attr:data-time-series-bogleheads-growth-chart-series-value": "legendData",
"t-attr:data-time-series-bogleheads-growth-chart-series-value": "legendData",
"t-attr:data-time-series-bogleheads-growth-chart-show-legend-value": "false",
"t-attr:data-time-series-bogleheads-growth-chart-data-value": "JSON.stringify(chartData)"
)
%>
Expand Down

0 comments on commit 9d6434a

Please sign in to comment.