Initial commit with translated description

This commit is contained in:
2026-03-29 10:21:46 +08:00
commit 18e90b0b09
67 changed files with 20609 additions and 0 deletions

281
htmlcov/class_index.html generated Normal file
View File

@@ -0,0 +1,281 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="indexfile">
<header>
<div class="content">
<h1>Coverage report:
<span class="pc_cov">48%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>f</kbd>
<kbd>n</kbd>
<kbd>s</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
<kbd>c</kbd>
&nbsp; change column sorting
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter...">
<div>
<input id="hide100" type="checkbox" >
<label for="hide100">hide covered</label>
</div>
</form>
<h2>
<a class="button" href="index.html">Files</a>
<a class="button" href="function_index.html">Functions</a>
<a class="button current">Classes</a>
</h2>
<p class="text">
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</header>
<main id="index">
<table class="index" data-sortable>
<thead>
<tr class="tablehead" title="Click to sort">
<th id="file" class="name" aria-sort="none" data-shortcut="f">File<span class="arrows"></span></th>
<th id="region" class="name" aria-sort="none" data-default-sort-order="ascending" data-shortcut="n">class<span class="arrows"></span></th>
<th class="spacer">&nbsp;</th>
<th id="statements" aria-sort="none" data-default-sort-order="descending" data-shortcut="s">statements<span class="arrows"></span></th>
<th id="missing" aria-sort="none" data-default-sort-order="descending" data-shortcut="m">missing<span class="arrows"></span></th>
<th id="excluded" aria-sort="none" data-default-sort-order="descending" data-shortcut="x">excluded<span class="arrows"></span></th>
<th class="spacer">&nbsp;</th>
<th id="coverage" aria-sort="none" data-shortcut="c">coverage<span class="arrows"></span></th>
</tr>
</thead>
<tbody>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_alerts_py.html">scripts&#8201;/&#8201;alerts.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_alerts_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>292</td>
<td>118</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="174 292">60%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_briefing_py.html">scripts&#8201;/&#8201;briefing.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_briefing_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>87</td>
<td>38</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="49 87">56%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_earnings_py.html#t507">scripts&#8201;/&#8201;earnings.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_earnings_py.html#t507"><data value='Args'>get_briefing_section.Args</data></a></td>
<td class="spacer">&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 0">100%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_earnings_py.html">scripts&#8201;/&#8201;earnings.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_earnings_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>329</td>
<td>181</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="148 329">45%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_fetch_news_py.html#t109">scripts&#8201;/&#8201;fetch_news.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_fetch_news_py.html#t109"><data value='PortfolioError'>PortfolioError</data></a></td>
<td class="spacer">&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 0">100%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_fetch_news_py.html">scripts&#8201;/&#8201;fetch_news.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_fetch_news_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>589</td>
<td>377</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="212 589">36%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_portfolio_py.html">scripts&#8201;/&#8201;portfolio.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_portfolio_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>183</td>
<td>124</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="59 183">32%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_ranking_py.html">scripts&#8201;/&#8201;ranking.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_ranking_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>147</td>
<td>21</td>
<td>9</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="126 147">86%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_research_py.html">scripts&#8201;/&#8201;research.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_research_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>130</td>
<td>45</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="85 130">65%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_setup_py.html">scripts&#8201;/&#8201;setup.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_setup_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>168</td>
<td>124</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="44 168">26%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_stocks_py.html">scripts&#8201;/&#8201;stocks.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_stocks_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>184</td>
<td>87</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="97 184">53%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t64">scripts&#8201;/&#8201;summarize.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t64"><data value='MoverContext'>MoverContext</data></a></td>
<td class="spacer">&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 0">100%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t76">scripts&#8201;/&#8201;summarize.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t76"><data value='SectorCluster'>SectorCluster</data></a></td>
<td class="spacer">&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 0">100%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t86">scripts&#8201;/&#8201;summarize.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html#t86"><data value='WatchpointsData'>WatchpointsData</data></a></td>
<td class="spacer">&nbsp;</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 0">100%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html">scripts&#8201;/&#8201;summarize.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>972</td>
<td>462</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="510 972">52%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_translate_portfolio_py.html">scripts&#8201;/&#8201;translate_portfolio.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_translate_portfolio_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>88</td>
<td>88</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 88">0%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_utils_py.html">scripts&#8201;/&#8201;utils.py</a></td>
<td class="name"><a href="z_de1a740d5dc98ffd_utils_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
<td class="spacer">&nbsp;</td>
<td>34</td>
<td>10</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="24 34">71%</td>
</tr>
</tbody>
<tfoot>
<tr class="total">
<td class="name">Total</td>
<td class="name">&nbsp;</td>
<td class="spacer">&nbsp;</td>
<td>3203</td>
<td>1675</td>
<td>29</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="1528 3203">48%</td>
</tr>
</tfoot>
</table>
<p id="no_rows">
No items found using the specified filter.
</p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
<aside class="hidden">
<a id="prevFileLink" class="nav" href=""></a>
<a id="nextFileLink" class="nav" href=""></a>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</footer>
</body>
</html>

735
htmlcov/coverage_html_cb_dd2e7eb5.js generated Normal file
View File

@@ -0,0 +1,735 @@
// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
// For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
// Coverage.py HTML report browser code.
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
/*global coverage: true, document, window, $ */
coverage = {};
// General helpers
function debounce(callback, wait) {
let timeoutId = null;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
callback.apply(this, args);
}, wait);
};
};
function checkVisible(element) {
const rect = element.getBoundingClientRect();
const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight);
const viewTop = 30;
return !(rect.bottom < viewTop || rect.top >= viewBottom);
}
function on_click(sel, fn) {
const elt = document.querySelector(sel);
if (elt) {
elt.addEventListener("click", fn);
}
}
// Helpers for table sorting
function getCellValue(row, column = 0) {
const cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
if (cell.childElementCount == 1) {
var child = cell.firstElementChild;
if (child.tagName === "A") {
child = child.firstElementChild;
}
if (child instanceof HTMLDataElement && child.value) {
return child.value;
}
}
return cell.innerText || cell.textContent;
}
function rowComparator(rowA, rowB, column = 0) {
let valueA = getCellValue(rowA, column);
let valueB = getCellValue(rowB, column);
if (!isNaN(valueA) && !isNaN(valueB)) {
return valueA - valueB;
}
return valueA.localeCompare(valueB, undefined, {numeric: true});
}
function sortColumn(th) {
// Get the current sorting direction of the selected header,
// clear state on other headers and then set the new sorting direction.
const currentSortOrder = th.getAttribute("aria-sort");
[...th.parentElement.cells].forEach(header => header.setAttribute("aria-sort", "none"));
var direction;
if (currentSortOrder === "none") {
direction = th.dataset.defaultSortOrder || "ascending";
}
else if (currentSortOrder === "ascending") {
direction = "descending";
}
else {
direction = "ascending";
}
th.setAttribute("aria-sort", direction);
const column = [...th.parentElement.cells].indexOf(th)
// Sort all rows and afterwards append them in order to move them in the DOM.
Array.from(th.closest("table").querySelectorAll("tbody tr"))
.sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (direction === "ascending" ? 1 : -1))
.forEach(tr => tr.parentElement.appendChild(tr));
// Save the sort order for next time.
if (th.id !== "region") {
let th_id = "file"; // Sort by file if we don't have a column id
let current_direction = direction;
const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
if (stored_list) {
({th_id, direction} = JSON.parse(stored_list))
}
localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({
"th_id": th.id,
"direction": current_direction
}));
if (th.id !== th_id || document.getElementById("region")) {
// Sort column has changed, unset sorting by function or class.
localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
"by_region": false,
"region_direction": current_direction
}));
}
}
else {
// Sort column has changed to by function or class, remember that.
localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
"by_region": true,
"region_direction": direction
}));
}
}
// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key.
coverage.assign_shortkeys = function () {
document.querySelectorAll("[data-shortcut]").forEach(element => {
document.addEventListener("keypress", event => {
if (event.target.tagName.toLowerCase() === "input") {
return; // ignore keypress from search filter
}
if (event.key === element.dataset.shortcut) {
element.click();
}
});
});
};
// Create the events for the filter box.
coverage.wire_up_filter = function () {
// Populate the filter and hide100 inputs if there are saved values for them.
const saved_filter_value = localStorage.getItem(coverage.FILTER_STORAGE);
if (saved_filter_value) {
document.getElementById("filter").value = saved_filter_value;
}
const saved_hide100_value = localStorage.getItem(coverage.HIDE100_STORAGE);
if (saved_hide100_value) {
document.getElementById("hide100").checked = JSON.parse(saved_hide100_value);
}
// Cache elements.
const table = document.querySelector("table.index");
const table_body_rows = table.querySelectorAll("tbody tr");
const no_rows = document.getElementById("no_rows");
const footer = table.tFoot.rows[0];
const ratio_columns = Array.from(footer.cells).map(cell => Boolean(cell.dataset.ratio));
// Observe filter keyevents.
const filter_handler = (event => {
// Keep running total of each metric, first index contains number of shown rows
const totals = ratio_columns.map(
is_ratio => is_ratio ? {"numer": 0, "denom": 0} : 0
);
var text = document.getElementById("filter").value;
// Store filter value
localStorage.setItem(coverage.FILTER_STORAGE, text);
const casefold = (text === text.toLowerCase());
const hide100 = document.getElementById("hide100").checked;
// Store hide value.
localStorage.setItem(coverage.HIDE100_STORAGE, JSON.stringify(hide100));
// Hide / show elements.
table_body_rows.forEach(row => {
var show = false;
// Check the text filter.
for (let column = 0; column < totals.length; column++) {
cell = row.cells[column];
if (cell.classList.contains("name")) {
var celltext = cell.textContent;
if (casefold) {
celltext = celltext.toLowerCase();
}
if (celltext.includes(text)) {
show = true;
}
}
}
// Check the "hide covered" filter.
if (show && hide100) {
const [numer, denom] = row.cells[row.cells.length - 1].dataset.ratio.split(" ");
show = (numer !== denom);
}
if (!show) {
// hide
row.classList.add("hidden");
return;
}
// show
row.classList.remove("hidden");
totals[0]++;
for (let column = 0; column < totals.length; column++) {
// Accumulate dynamic totals
cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
if (cell.matches(".name, .spacer")) {
continue;
}
if (ratio_columns[column] && cell.dataset.ratio) {
// Column stores a ratio
const [numer, denom] = cell.dataset.ratio.split(" ");
totals[column]["numer"] += parseInt(numer, 10); // nosemgrep: eslint.detect-object-injection
totals[column]["denom"] += parseInt(denom, 10); // nosemgrep: eslint.detect-object-injection
}
else {
totals[column] += parseInt(cell.textContent, 10); // nosemgrep: eslint.detect-object-injection
}
}
});
// Show placeholder if no rows will be displayed.
if (!totals[0]) {
// Show placeholder, hide table.
no_rows.style.display = "block";
table.style.display = "none";
return;
}
// Hide placeholder, show table.
no_rows.style.display = null;
table.style.display = null;
// Calculate new dynamic sum values based on visible rows.
for (let column = 0; column < totals.length; column++) {
// Get footer cell element.
const cell = footer.cells[column]; // nosemgrep: eslint.detect-object-injection
if (cell.matches(".name, .spacer")) {
continue;
}
// Set value into dynamic footer cell element.
if (ratio_columns[column]) {
// Percentage column uses the numerator and denominator,
// and adapts to the number of decimal places.
const match = /\.([0-9]+)/.exec(cell.textContent);
const places = match ? match[1].length : 0;
const { numer, denom } = totals[column]; // nosemgrep: eslint.detect-object-injection
cell.dataset.ratio = `${numer} ${denom}`;
// Check denom to prevent NaN if filtered files contain no statements
cell.textContent = denom
? `${(numer * 100 / denom).toFixed(places)}%`
: `${(100).toFixed(places)}%`;
}
else {
cell.textContent = totals[column]; // nosemgrep: eslint.detect-object-injection
}
}
});
document.getElementById("filter").addEventListener("input", debounce(filter_handler));
document.getElementById("hide100").addEventListener("input", debounce(filter_handler));
// Trigger change event on setup, to force filter on page refresh
// (filter value may still be present).
document.getElementById("filter").dispatchEvent(new Event("input"));
document.getElementById("hide100").dispatchEvent(new Event("input"));
};
coverage.FILTER_STORAGE = "COVERAGE_FILTER_VALUE";
coverage.HIDE100_STORAGE = "COVERAGE_HIDE100_VALUE";
// Set up the click-to-sort columns.
coverage.wire_up_sorting = function () {
document.querySelectorAll("[data-sortable] th[aria-sort]").forEach(
th => th.addEventListener("click", e => sortColumn(e.target))
);
// Look for a localStorage item containing previous sort settings:
let th_id = "file", direction = "ascending";
const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
if (stored_list) {
({th_id, direction} = JSON.parse(stored_list));
}
let by_region = false, region_direction = "ascending";
const sorted_by_region = localStorage.getItem(coverage.SORTED_BY_REGION);
if (sorted_by_region) {
({
by_region,
region_direction
} = JSON.parse(sorted_by_region));
}
const region_id = "region";
if (by_region && document.getElementById(region_id)) {
direction = region_direction;
}
// If we are in a page that has a column with id of "region", sort on
// it if the last sort was by function or class.
let th;
if (document.getElementById(region_id)) {
th = document.getElementById(by_region ? region_id : th_id);
}
else {
th = document.getElementById(th_id);
}
th.setAttribute("aria-sort", direction === "ascending" ? "descending" : "ascending");
th.click()
};
coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT_2";
coverage.SORTED_BY_REGION = "COVERAGE_SORT_REGION";
// Loaded on index.html
coverage.index_ready = function () {
coverage.assign_shortkeys();
coverage.wire_up_filter();
coverage.wire_up_sorting();
on_click(".button_prev_file", coverage.to_prev_file);
on_click(".button_next_file", coverage.to_next_file);
on_click(".button_show_hide_help", coverage.show_hide_help);
};
// -- pyfile stuff --
coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
coverage.pyfile_ready = function () {
// If we're directed to a particular line number, highlight the line.
var frag = location.hash;
if (frag.length > 2 && frag[1] === "t") {
document.querySelector(frag).closest(".n").classList.add("highlight");
coverage.set_sel(parseInt(frag.substr(2), 10));
}
else {
coverage.set_sel(0);
}
on_click(".button_toggle_run", coverage.toggle_lines);
on_click(".button_toggle_mis", coverage.toggle_lines);
on_click(".button_toggle_exc", coverage.toggle_lines);
on_click(".button_toggle_par", coverage.toggle_lines);
on_click(".button_next_chunk", coverage.to_next_chunk_nicely);
on_click(".button_prev_chunk", coverage.to_prev_chunk_nicely);
on_click(".button_top_of_page", coverage.to_top);
on_click(".button_first_chunk", coverage.to_first_chunk);
on_click(".button_prev_file", coverage.to_prev_file);
on_click(".button_next_file", coverage.to_next_file);
on_click(".button_to_index", coverage.to_index);
on_click(".button_show_hide_help", coverage.show_hide_help);
coverage.filters = undefined;
try {
coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
} catch(err) {}
if (coverage.filters) {
coverage.filters = JSON.parse(coverage.filters);
}
else {
coverage.filters = {run: false, exc: true, mis: true, par: true};
}
for (cls in coverage.filters) {
coverage.set_line_visibilty(cls, coverage.filters[cls]); // nosemgrep: eslint.detect-object-injection
}
coverage.assign_shortkeys();
coverage.init_scroll_markers();
coverage.wire_up_sticky_header();
document.querySelectorAll("[id^=ctxs]").forEach(
cbox => cbox.addEventListener("click", coverage.expand_contexts)
);
// Rebuild scroll markers when the window height changes.
window.addEventListener("resize", coverage.build_scroll_markers);
};
coverage.toggle_lines = function (event) {
const btn = event.target.closest("button");
const category = btn.value
const show = !btn.classList.contains("show_" + category);
coverage.set_line_visibilty(category, show);
coverage.build_scroll_markers();
coverage.filters[category] = show;
try {
localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
} catch(err) {}
};
coverage.set_line_visibilty = function (category, should_show) {
const cls = "show_" + category;
const btn = document.querySelector(".button_toggle_" + category);
if (btn) {
if (should_show) {
document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls));
btn.classList.add(cls);
}
else {
document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls));
btn.classList.remove(cls);
}
}
};
// Return the nth line div.
coverage.line_elt = function (n) {
return document.getElementById("t" + n)?.closest("p");
};
// Set the selection. b and e are line numbers.
coverage.set_sel = function (b, e) {
// The first line selected.
coverage.sel_begin = b;
// The next line not selected.
coverage.sel_end = (e === undefined) ? b+1 : e;
};
coverage.to_top = function () {
coverage.set_sel(0, 1);
coverage.scroll_window(0);
};
coverage.to_first_chunk = function () {
coverage.set_sel(0, 1);
coverage.to_next_chunk();
};
coverage.to_prev_file = function () {
window.location = document.getElementById("prevFileLink").href;
}
coverage.to_next_file = function () {
window.location = document.getElementById("nextFileLink").href;
}
coverage.to_index = function () {
location.href = document.getElementById("indexLink").href;
}
coverage.show_hide_help = function () {
const helpCheck = document.getElementById("help_panel_state")
helpCheck.checked = !helpCheck.checked;
}
// Return a string indicating what kind of chunk this line belongs to,
// or null if not a chunk.
coverage.chunk_indicator = function (line_elt) {
const classes = line_elt?.className;
if (!classes) {
return null;
}
const match = classes.match(/\bshow_\w+\b/);
if (!match) {
return null;
}
return match[0];
};
coverage.to_next_chunk = function () {
const c = coverage;
// Find the start of the next colored chunk.
var probe = c.sel_end;
var chunk_indicator, probe_line;
while (true) {
probe_line = c.line_elt(probe);
if (!probe_line) {
return;
}
chunk_indicator = c.chunk_indicator(probe_line);
if (chunk_indicator) {
break;
}
probe++;
}
// There's a next chunk, `probe` points to it.
var begin = probe;
// Find the end of this chunk.
var next_indicator = chunk_indicator;
while (next_indicator === chunk_indicator) {
probe++;
probe_line = c.line_elt(probe);
next_indicator = c.chunk_indicator(probe_line);
}
c.set_sel(begin, probe);
c.show_selection();
};
coverage.to_prev_chunk = function () {
const c = coverage;
// Find the end of the prev colored chunk.
var probe = c.sel_begin-1;
var probe_line = c.line_elt(probe);
if (!probe_line) {
return;
}
var chunk_indicator = c.chunk_indicator(probe_line);
while (probe > 1 && !chunk_indicator) {
probe--;
probe_line = c.line_elt(probe);
if (!probe_line) {
return;
}
chunk_indicator = c.chunk_indicator(probe_line);
}
// There's a prev chunk, `probe` points to its last line.
var end = probe+1;
// Find the beginning of this chunk.
var prev_indicator = chunk_indicator;
while (prev_indicator === chunk_indicator) {
probe--;
if (probe <= 0) {
return;
}
probe_line = c.line_elt(probe);
prev_indicator = c.chunk_indicator(probe_line);
}
c.set_sel(probe+1, end);
c.show_selection();
};
// Returns 0, 1, or 2: how many of the two ends of the selection are on
// the screen right now?
coverage.selection_ends_on_screen = function () {
if (coverage.sel_begin === 0) {
return 0;
}
const begin = coverage.line_elt(coverage.sel_begin);
const end = coverage.line_elt(coverage.sel_end-1);
return (
(checkVisible(begin) ? 1 : 0)
+ (checkVisible(end) ? 1 : 0)
);
};
coverage.to_next_chunk_nicely = function () {
if (coverage.selection_ends_on_screen() === 0) {
// The selection is entirely off the screen:
// Set the top line on the screen as selection.
// This will select the top-left of the viewport
// As this is most likely the span with the line number we take the parent
const line = document.elementFromPoint(0, 0).parentElement;
if (line.parentElement !== document.getElementById("source")) {
// The element is not a source line but the header or similar
coverage.select_line_or_chunk(1);
}
else {
// We extract the line number from the id
coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
}
}
coverage.to_next_chunk();
};
coverage.to_prev_chunk_nicely = function () {
if (coverage.selection_ends_on_screen() === 0) {
// The selection is entirely off the screen:
// Set the lowest line on the screen as selection.
// This will select the bottom-left of the viewport
// As this is most likely the span with the line number we take the parent
const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement;
if (line.parentElement !== document.getElementById("source")) {
// The element is not a source line but the header or similar
coverage.select_line_or_chunk(coverage.lines_len);
}
else {
// We extract the line number from the id
coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
}
}
coverage.to_prev_chunk();
};
// Select line number lineno, or if it is in a colored chunk, select the
// entire chunk
coverage.select_line_or_chunk = function (lineno) {
var c = coverage;
var probe_line = c.line_elt(lineno);
if (!probe_line) {
return;
}
var the_indicator = c.chunk_indicator(probe_line);
if (the_indicator) {
// The line is in a highlighted chunk.
// Search backward for the first line.
var probe = lineno;
var indicator = the_indicator;
while (probe > 0 && indicator === the_indicator) {
probe--;
probe_line = c.line_elt(probe);
if (!probe_line) {
break;
}
indicator = c.chunk_indicator(probe_line);
}
var begin = probe + 1;
// Search forward for the last line.
probe = lineno;
indicator = the_indicator;
while (indicator === the_indicator) {
probe++;
probe_line = c.line_elt(probe);
indicator = c.chunk_indicator(probe_line);
}
coverage.set_sel(begin, probe);
}
else {
coverage.set_sel(lineno);
}
};
coverage.show_selection = function () {
// Highlight the lines in the chunk
document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight"));
for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) {
coverage.line_elt(probe).querySelector(".n").classList.add("highlight");
}
coverage.scroll_to_selection();
};
coverage.scroll_to_selection = function () {
// Scroll the page if the chunk isn't fully visible.
if (coverage.selection_ends_on_screen() < 2) {
const element = coverage.line_elt(coverage.sel_begin);
coverage.scroll_window(element.offsetTop - 60);
}
};
coverage.scroll_window = function (to_pos) {
window.scroll({top: to_pos, behavior: "smooth"});
};
coverage.init_scroll_markers = function () {
// Init some variables
coverage.lines_len = document.querySelectorAll("#source > p").length;
// Build html
coverage.build_scroll_markers();
};
coverage.build_scroll_markers = function () {
const temp_scroll_marker = document.getElementById("scroll_marker")
if (temp_scroll_marker) temp_scroll_marker.remove();
// Don't build markers if the window has no scroll bar.
if (document.body.scrollHeight <= window.innerHeight) {
return;
}
const marker_scale = window.innerHeight / document.body.scrollHeight;
const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10);
let previous_line = -99, last_mark, last_top;
const scroll_marker = document.createElement("div");
scroll_marker.id = "scroll_marker";
document.getElementById("source").querySelectorAll(
"p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par"
).forEach(element => {
const line_top = Math.floor(element.offsetTop * marker_scale);
const line_number = parseInt(element.querySelector(".n a").id.substr(1));
if (line_number === previous_line + 1) {
// If this solid missed block just make previous mark higher.
last_mark.style.height = `${line_top + line_height - last_top}px`;
}
else {
// Add colored line in scroll_marker block.
last_mark = document.createElement("div");
last_mark.id = `m${line_number}`;
last_mark.classList.add("marker");
last_mark.style.height = `${line_height}px`;
last_mark.style.top = `${line_top}px`;
scroll_marker.append(last_mark);
last_top = line_top;
}
previous_line = line_number;
});
// Append last to prevent layout calculation
document.body.append(scroll_marker);
};
coverage.wire_up_sticky_header = function () {
const header = document.querySelector("header");
const header_bottom = (
header.querySelector(".content h2").getBoundingClientRect().top -
header.getBoundingClientRect().top
);
function updateHeader() {
if (window.scrollY > header_bottom) {
header.classList.add("sticky");
}
else {
header.classList.remove("sticky");
}
}
window.addEventListener("scroll", updateHeader);
updateHeader();
};
coverage.expand_contexts = function (e) {
var ctxs = e.target.parentNode.querySelector(".ctxs");
if (!ctxs.classList.contains("expanded")) {
var ctxs_text = ctxs.textContent;
var width = Number(ctxs_text[0]);
ctxs.textContent = "";
for (var i = 1; i < ctxs_text.length; i += width) {
key = ctxs_text.substring(i, i + width).trim();
ctxs.appendChild(document.createTextNode(contexts[key]));
ctxs.appendChild(document.createElement("br"));
}
ctxs.classList.add("expanded");
}
};
document.addEventListener("DOMContentLoaded", () => {
if (document.body.classList.contains("indexfile")) {
coverage.index_ready();
}
else {
coverage.pyfile_ready();
}
});

1851
htmlcov/function_index.html generated Normal file

File diff suppressed because it is too large Load Diff

216
htmlcov/index.html generated Normal file
View File

@@ -0,0 +1,216 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="indexfile">
<header>
<div class="content">
<h1>Coverage report:
<span class="pc_cov">48%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>f</kbd>
<kbd>s</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
<kbd>c</kbd>
&nbsp; change column sorting
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter...">
<div>
<input id="hide100" type="checkbox" >
<label for="hide100">hide covered</label>
</div>
</form>
<h2>
<a class="button current">Files</a>
<a class="button" href="function_index.html">Functions</a>
<a class="button" href="class_index.html">Classes</a>
</h2>
<p class="text">
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</header>
<main id="index">
<table class="index" data-sortable>
<thead>
<tr class="tablehead" title="Click to sort">
<th id="file" class="name" aria-sort="none" data-shortcut="f">File<span class="arrows"></span></th>
<th class="spacer">&nbsp;</th>
<th id="statements" aria-sort="none" data-default-sort-order="descending" data-shortcut="s">statements<span class="arrows"></span></th>
<th id="missing" aria-sort="none" data-default-sort-order="descending" data-shortcut="m">missing<span class="arrows"></span></th>
<th id="excluded" aria-sort="none" data-default-sort-order="descending" data-shortcut="x">excluded<span class="arrows"></span></th>
<th class="spacer">&nbsp;</th>
<th id="coverage" aria-sort="none" data-shortcut="c">coverage<span class="arrows"></span></th>
</tr>
</thead>
<tbody>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_alerts_py.html">scripts&#8201;/&#8201;alerts.py</a></td>
<td class="spacer">&nbsp;</td>
<td>292</td>
<td>118</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="174 292">60%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_briefing_py.html">scripts&#8201;/&#8201;briefing.py</a></td>
<td class="spacer">&nbsp;</td>
<td>87</td>
<td>38</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="49 87">56%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_earnings_py.html">scripts&#8201;/&#8201;earnings.py</a></td>
<td class="spacer">&nbsp;</td>
<td>329</td>
<td>181</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="148 329">45%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_fetch_news_py.html">scripts&#8201;/&#8201;fetch_news.py</a></td>
<td class="spacer">&nbsp;</td>
<td>589</td>
<td>377</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="212 589">36%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_portfolio_py.html">scripts&#8201;/&#8201;portfolio.py</a></td>
<td class="spacer">&nbsp;</td>
<td>183</td>
<td>124</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="59 183">32%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_ranking_py.html">scripts&#8201;/&#8201;ranking.py</a></td>
<td class="spacer">&nbsp;</td>
<td>147</td>
<td>21</td>
<td>9</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="126 147">86%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_research_py.html">scripts&#8201;/&#8201;research.py</a></td>
<td class="spacer">&nbsp;</td>
<td>130</td>
<td>45</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="85 130">65%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_setup_py.html">scripts&#8201;/&#8201;setup.py</a></td>
<td class="spacer">&nbsp;</td>
<td>168</td>
<td>124</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="44 168">26%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_stocks_py.html">scripts&#8201;/&#8201;stocks.py</a></td>
<td class="spacer">&nbsp;</td>
<td>184</td>
<td>87</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="97 184">53%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_summarize_py.html">scripts&#8201;/&#8201;summarize.py</a></td>
<td class="spacer">&nbsp;</td>
<td>972</td>
<td>462</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="510 972">52%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_translate_portfolio_py.html">scripts&#8201;/&#8201;translate_portfolio.py</a></td>
<td class="spacer">&nbsp;</td>
<td>88</td>
<td>88</td>
<td>2</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="0 88">0%</td>
</tr>
<tr class="region">
<td class="name"><a href="z_de1a740d5dc98ffd_utils_py.html">scripts&#8201;/&#8201;utils.py</a></td>
<td class="spacer">&nbsp;</td>
<td>34</td>
<td>10</td>
<td>0</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="24 34">71%</td>
</tr>
</tbody>
<tfoot>
<tr class="total">
<td class="name">Total</td>
<td class="spacer">&nbsp;</td>
<td>3203</td>
<td>1675</td>
<td>29</td>
<td class="spacer">&nbsp;</td>
<td data-ratio="1528 3203">48%</td>
</tr>
</tfoot>
</table>
<p id="no_rows">
No items found using the specified filter.
</p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
<aside class="hidden">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_utils_py.html"></a>
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_alerts_py.html"></a>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</footer>
</body>
</html>

1
htmlcov/status.json generated Normal file
View File

@@ -0,0 +1 @@
{"note":"This file is an internal implementation detail to speed up HTML report generation. Its format can change at any time. You might be looking for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json","format":5,"version":"7.13.2","globals":"4be31ca40797e8400fa13be69cbf6b96","files":{"z_de1a740d5dc98ffd_alerts_py":{"hash":"9256045bbdf042ec8ac79100b07f6e16","index":{"url":"z_de1a740d5dc98ffd_alerts_py.html","file":"scripts/alerts.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":292,"n_excluded":2,"n_missing":118,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_briefing_py":{"hash":"8762987b6cbda4b3959d184f0fd43f44","index":{"url":"z_de1a740d5dc98ffd_briefing_py.html","file":"scripts/briefing.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":87,"n_excluded":2,"n_missing":38,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_earnings_py":{"hash":"313062c04b56cd9a2f238c0c041e795c","index":{"url":"z_de1a740d5dc98ffd_earnings_py.html","file":"scripts/earnings.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":329,"n_excluded":2,"n_missing":181,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_fetch_news_py":{"hash":"6cc00fcf9c47d99abd6109edce33ab1c","index":{"url":"z_de1a740d5dc98ffd_fetch_news_py.html","file":"scripts/fetch_news.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":589,"n_excluded":2,"n_missing":377,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_portfolio_py":{"hash":"291475985d04ed7b91150b8eb45bb333","index":{"url":"z_de1a740d5dc98ffd_portfolio_py.html","file":"scripts/portfolio.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":183,"n_excluded":2,"n_missing":124,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_ranking_py":{"hash":"1118174517ba630eb85b35f61798c37f","index":{"url":"z_de1a740d5dc98ffd_ranking_py.html","file":"scripts/ranking.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":147,"n_excluded":9,"n_missing":21,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_research_py":{"hash":"f70f5afdad459e2a82b06d76961b0502","index":{"url":"z_de1a740d5dc98ffd_research_py.html","file":"scripts/research.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":130,"n_excluded":2,"n_missing":45,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_setup_py":{"hash":"2b936e494283c91a1b0c1ac177ca3d23","index":{"url":"z_de1a740d5dc98ffd_setup_py.html","file":"scripts/setup.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":168,"n_excluded":2,"n_missing":124,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_stocks_py":{"hash":"a631cf0e894b87b0a89f70f06987e155","index":{"url":"z_de1a740d5dc98ffd_stocks_py.html","file":"scripts/stocks.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":184,"n_excluded":2,"n_missing":87,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_summarize_py":{"hash":"d04f1c3e26fe60ac2710db72a49b8e21","index":{"url":"z_de1a740d5dc98ffd_summarize_py.html","file":"scripts/summarize.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":972,"n_excluded":2,"n_missing":462,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_translate_portfolio_py":{"hash":"74687196bc47c7bcc8dd5ef4e7a118d2","index":{"url":"z_de1a740d5dc98ffd_translate_portfolio_py.html","file":"scripts/translate_portfolio.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":88,"n_excluded":2,"n_missing":88,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_de1a740d5dc98ffd_utils_py":{"hash":"fd9700472399838a648d2182ce916cd4","index":{"url":"z_de1a740d5dc98ffd_utils_py.html","file":"scripts/utils.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":34,"n_excluded":0,"n_missing":10,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}}}}

389
htmlcov/style_cb_9ff733b0.css generated Normal file
View File

@@ -0,0 +1,389 @@
@charset "UTF-8";
/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
/* For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt */
/* Don't edit this .css file. Edit the .scss file instead! */
html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
@media (prefers-color-scheme: dark) { body { color: #eee; } }
html > body { font-size: 16px; }
a:active, a:focus { outline: 2px dashed #007acc; }
p { font-size: .875em; line-height: 1.4em; }
table { border-collapse: collapse; }
td { vertical-align: top; }
table tr.hidden { display: none !important; }
p#no_rows { display: none; font-size: 1.15em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
a.nav { text-decoration: none; color: inherit; }
a.nav:hover { text-decoration: underline; color: inherit; }
.hidden { display: none; }
header { background: #f8f8f8; width: 100%; z-index: 2; border-bottom: 1px solid #ccc; }
@media (prefers-color-scheme: dark) { header { background: black; } }
@media (prefers-color-scheme: dark) { header { border-color: #333; } }
header .content { padding: 1rem 3.5rem; }
header h2 { margin-top: .5em; font-size: 1em; }
header h2 a.button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
@media (prefers-color-scheme: dark) { header h2 a.button { background: #333; } }
@media (prefers-color-scheme: dark) { header h2 a.button { border-color: #444; } }
header h2 a.button.current { border: 2px solid; background: #fff; border-color: #999; cursor: default; }
@media (prefers-color-scheme: dark) { header h2 a.button.current { background: #1e1e1e; } }
@media (prefers-color-scheme: dark) { header h2 a.button.current { border-color: #777; } }
header p.text { margin: .5em 0 -.5em; color: #666; font-style: italic; }
@media (prefers-color-scheme: dark) { header p.text { color: #aaa; } }
header.sticky { position: fixed; left: 0; right: 0; height: 2.5em; }
header.sticky .text { display: none; }
header.sticky h1, header.sticky h2 { font-size: 1em; margin-top: 0; display: inline-block; }
header.sticky .content { padding: 0.5rem 3.5rem; }
header.sticky .content p { font-size: 1em; }
header.sticky ~ #source { padding-top: 6.5em; }
main { position: relative; z-index: 1; }
footer { margin: 1rem 3.5rem; }
footer .content { padding: 0; color: #666; font-style: italic; }
@media (prefers-color-scheme: dark) { footer .content { color: #aaa; } }
#index { margin: 1rem 0 0 3.5rem; }
h1 { font-size: 1.25em; display: inline-block; }
#filter_container { float: right; margin: 0 2em 0 0; line-height: 1.66em; }
#filter_container #filter { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
@media (prefers-color-scheme: dark) { #filter_container #filter { border-color: #444; } }
@media (prefers-color-scheme: dark) { #filter_container #filter { background: #1e1e1e; } }
@media (prefers-color-scheme: dark) { #filter_container #filter { color: #eee; } }
#filter_container #filter:focus { border-color: #007acc; }
#filter_container :disabled ~ label { color: #ccc; }
@media (prefers-color-scheme: dark) { #filter_container :disabled ~ label { color: #444; } }
#filter_container label { font-size: .875em; color: #666; }
@media (prefers-color-scheme: dark) { #filter_container label { color: #aaa; } }
header button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
@media (prefers-color-scheme: dark) { header button { background: #333; } }
@media (prefers-color-scheme: dark) { header button { border-color: #444; } }
header button:active, header button:focus { outline: 2px dashed #007acc; }
header button.run { background: #eeffee; }
@media (prefers-color-scheme: dark) { header button.run { background: #373d29; } }
header button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
@media (prefers-color-scheme: dark) { header button.run.show_run { background: #373d29; } }
header button.mis { background: #ffeeee; }
@media (prefers-color-scheme: dark) { header button.mis { background: #4b1818; } }
header button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
@media (prefers-color-scheme: dark) { header button.mis.show_mis { background: #4b1818; } }
header button.exc { background: #f7f7f7; }
@media (prefers-color-scheme: dark) { header button.exc { background: #333; } }
header button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
@media (prefers-color-scheme: dark) { header button.exc.show_exc { background: #333; } }
header button.par { background: #ffffd5; }
@media (prefers-color-scheme: dark) { header button.par { background: #650; } }
header button.par.show_par { background: #ffa; border: 2px solid #bbbb00; margin: 0 .1em; }
@media (prefers-color-scheme: dark) { header button.par.show_par { background: #650; } }
#help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
#help_panel_wrapper { float: right; position: relative; }
#keyboard_icon { margin: 5px; }
#help_panel_state { display: none; }
#help_panel { top: 25px; right: 0; padding: .75em; border: 1px solid #883; color: #333; }
#help_panel .keyhelp p { margin-top: .75em; }
#help_panel .legend { font-style: italic; margin-bottom: 1em; }
.indexfile #help_panel { width: 25em; }
.pyfile #help_panel { width: 18em; }
#help_panel_state:checked ~ #help_panel { display: block; }
kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; border-radius: 3px; }
#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
#source p { position: relative; white-space: pre; }
#source p * { box-sizing: border-box; }
#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; user-select: none; }
@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
#source p .n.highlight { background: #ffdd00; }
#source p .n a { scroll-margin-top: 6em; text-decoration: none; color: #999; }
@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
#source p .n a:hover { text-decoration: underline; color: #999; }
@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
#source p .t:hover { background: #f2f2f2; }
@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
#source p .t:hover ~ .r .annotate.long { display: block; }
#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
@media (prefers-color-scheme: dark) { #source p .t .com { color: #6a9955; } }
#source p .t .key { font-weight: bold; line-height: 1px; }
#source p .t .str, #source p .t .fst { color: #0451a5; }
@media (prefers-color-scheme: dark) { #source p .t .str, #source p .t .fst { color: #9cdcfe; } }
#source p.mis .t { border-left: 0.2em solid #ff0000; }
#source p.mis.show_mis .t { background: #fdd; }
@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
#source p.mis.show_mis .t:hover { background: #f2d2d2; }
@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
#source p.mis.mis2 .t { border-left: 0.2em dotted #ff0000; }
#source p.mis.mis2.show_mis .t { background: #ffeeee; }
@media (prefers-color-scheme: dark) { #source p.mis.mis2.show_mis .t { background: #351b1b; } }
#source p.mis.mis2.show_mis .t:hover { background: #f2d2d2; }
@media (prefers-color-scheme: dark) { #source p.mis.mis2.show_mis .t:hover { background: #532323; } }
#source p.run .t { border-left: 0.2em solid #00dd00; }
#source p.run.show_run .t { background: #dfd; }
@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
#source p.run.show_run .t:hover { background: #d2f2d2; }
@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
#source p.run.run2 .t { border-left: 0.2em dotted #00dd00; }
#source p.run.run2.show_run .t { background: #eeffee; }
@media (prefers-color-scheme: dark) { #source p.run.run2.show_run .t { background: #2b2e24; } }
#source p.run.run2.show_run .t:hover { background: #d2f2d2; }
@media (prefers-color-scheme: dark) { #source p.run.run2.show_run .t:hover { background: #404633; } }
#source p.exc .t { border-left: 0.2em solid #808080; }
#source p.exc.show_exc .t { background: #eee; }
@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
#source p.exc.show_exc .t:hover { background: #e2e2e2; }
@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
#source p.exc.exc2 .t { border-left: 0.2em dotted #808080; }
#source p.exc.exc2.show_exc .t { background: #f7f7f7; }
@media (prefers-color-scheme: dark) { #source p.exc.exc2.show_exc .t { background: #292929; } }
#source p.exc.exc2.show_exc .t:hover { background: #e2e2e2; }
@media (prefers-color-scheme: dark) { #source p.exc.exc2.show_exc .t:hover { background: #3c3c3c; } }
#source p.par .t { border-left: 0.2em solid #bbbb00; }
#source p.par.show_par .t { background: #ffa; }
@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
#source p.par.show_par .t:hover { background: #f2f2a2; }
@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
#source p.par.par2 .t { border-left: 0.2em dotted #bbbb00; }
#source p.par.par2.show_par .t { background: #ffffd5; }
@media (prefers-color-scheme: dark) { #source p.par.par2.show_par .t { background: #423a0f; } }
#source p.par.par2.show_par .t:hover { background: #f2f2a2; }
@media (prefers-color-scheme: dark) { #source p.par.par2.show_par .t:hover { background: #6d5d0c; } }
#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
#source p .annotate.short:hover ~ .long { display: block; }
#source p .annotate.long { width: 30em; right: 2.5em; }
#source p input { display: none; }
#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
#source p input ~ .r label.ctx::before { content: "▶ "; }
#source p input ~ .r label.ctx:hover { background: #e8f4ff; color: #666; }
@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
#source p input:checked ~ .r label.ctx { background: #d0e8ff; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
#source p input:checked ~ .r label.ctx::before { content: "▼ "; }
#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #d0e8ff; border-radius: .25em; margin-right: 1.75em; text-align: right; }
@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
#index table.index { margin-left: -.5em; }
#index td, #index th { text-align: right; vertical-align: baseline; padding: .25em .5em; border-bottom: 1px solid #eee; }
@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
#index td.name, #index th.name { text-align: left; width: auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; min-width: 15em; }
#index td.left, #index th.left { text-align: left; }
#index td.spacer, #index th.spacer { border: none; padding: 0; }
#index td.spacer:hover, #index th.spacer:hover { background: inherit; }
#index th { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-style: italic; color: #333; border-color: #ccc; cursor: pointer; }
@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
@media (prefers-color-scheme: dark) { #index th { border-color: #444; } }
#index th:hover { background: #eee; }
@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
#index th .arrows { color: #666; font-size: 85%; font-family: sans-serif; font-style: normal; pointer-events: none; }
#index th[aria-sort="ascending"], #index th[aria-sort="descending"] { white-space: nowrap; background: #eee; padding-left: .5em; }
@media (prefers-color-scheme: dark) { #index th[aria-sort="ascending"], #index th[aria-sort="descending"] { background: #333; } }
#index th[aria-sort="ascending"] .arrows::after { content: " ▲"; }
#index th[aria-sort="descending"] .arrows::after { content: " ▼"; }
#index tr.grouphead th { cursor: default; font-style: normal; border-color: #999; }
@media (prefers-color-scheme: dark) { #index tr.grouphead th { border-color: #777; } }
#index td.name { font-size: 1.15em; }
#index td.name a { text-decoration: none; color: inherit; }
#index td.name .no-noun { font-style: italic; }
#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-bottom: none; }
#index tr.region:hover { background: #eee; }
@media (prefers-color-scheme: dark) { #index tr.region:hover { background: #333; } }
#index tr.region:hover td.name { text-decoration: underline; color: inherit; }
#scroll_marker { position: fixed; z-index: 3; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }

597
htmlcov/z_de1a740d5dc98ffd_alerts_py.html generated Normal file
View File

@@ -0,0 +1,597 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/alerts.py: 60%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;alerts.py</b>:
<span class="pc_cov">60%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">292 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">174<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">118<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="index.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_briefing_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Price Target Alerts - Track buy zone alerts for stocks.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">Features:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="str">- Set price target alerts (buy zone triggers)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="str">- Check alerts against current prices</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="str">- Snooze, update, delete alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="str">- Multi-currency support (USD, EUR, JPY, SGD, MXN)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="str">Usage:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="str"> alerts.py list # Show all alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="str"> alerts.py set CRWD 400 --note 'Kaufzone' # Set alert</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="str"> alerts.py check # Check triggered alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="str"> alerts.py delete CRWD # Delete alert</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t"><span class="str"> alerts.py snooze CRWD --days 7 # Snooze for 7 days</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="str"> alerts.py update CRWD 380 # Update target price</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span><span class="op">,</span> <span class="nam">timedelta</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"><span class="key">from</span> <span class="nam">utils</span> <span class="key">import</span> <span class="nam">ensure_venv</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"><span class="nam">ensure_venv</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"><span class="com"># Lazy import to avoid numpy issues at module load</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"><span class="nam">fetch_market_data</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"><span class="key">def</span> <span class="nam">get_fetch_market_data</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="key">global</span> <span class="nam">fetch_market_data</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="key">if</span> <span class="nam">fetch_market_data</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="key">from</span> <span class="nam">fetch_news</span> <span class="key">import</span> <span class="nam">fetch_market_data</span> <span class="key">as</span> <span class="nam">fmd</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="nam">fetch_market_data</span> <span class="op">=</span> <span class="nam">fmd</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="key">return</span> <span class="nam">fetch_market_data</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"><span class="nam">SCRIPT_DIR</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"><span class="nam">CONFIG_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"><span class="nam">ALERTS_FILE</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"alerts.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"><span class="nam">SUPPORTED_CURRENCIES</span> <span class="op">=</span> <span class="op">[</span><span class="str">"USD"</span><span class="op">,</span> <span class="str">"EUR"</span><span class="op">,</span> <span class="str">"JPY"</span><span class="op">,</span> <span class="str">"SGD"</span><span class="op">,</span> <span class="str">"MXN"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"><span class="key">def</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"> <span class="str">"""Load alerts from JSON file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">ALERTS_FILE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"_meta"</span><span class="op">:</span> <span class="op">{</span><span class="str">"version"</span><span class="op">:</span> <span class="num">1</span><span class="op">,</span> <span class="str">"supported_currencies"</span><span class="op">:</span> <span class="nam">SUPPORTED_CURRENCIES</span><span class="op">}</span><span class="op">,</span> <span class="str">"alerts"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="key">return</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">ALERTS_FILE</span><span class="op">.</span><span class="nam">read_text</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"><span class="key">def</span> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="str">"""Save alerts to JSON file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"_meta"</span><span class="op">]</span><span class="op">[</span><span class="str">"updated_at"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="nam">ALERTS_FILE</span><span class="op">.</span><span class="nam">write_text</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"><span class="key">def</span> <span class="nam">get_alert_by_ticker</span><span class="op">(</span><span class="nam">alerts</span><span class="op">:</span> <span class="nam">list</span><span class="op">,</span> <span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span> <span class="op">|</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="str">"""Find alert by ticker."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="key">for</span> <span class="nam">alert</span> <span class="key">in</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t"> <span class="key">if</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="key">return</span> <span class="nam">alert</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="key">return</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t"><span class="key">def</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">price</span><span class="op">:</span> <span class="nam">float</span><span class="op">,</span> <span class="nam">currency</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="str">"""Format price with currency symbol."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="nam">symbols</span> <span class="op">=</span> <span class="op">{</span><span class="str">"USD"</span><span class="op">:</span> <span class="str">"$"</span><span class="op">,</span> <span class="str">"EUR"</span><span class="op">:</span> <span class="str">"&#8364;"</span><span class="op">,</span> <span class="str">"JPY"</span><span class="op">:</span> <span class="str">"&#165;"</span><span class="op">,</span> <span class="str">"SGD"</span><span class="op">:</span> <span class="str">"S$"</span><span class="op">,</span> <span class="str">"MXN"</span><span class="op">:</span> <span class="str">"MX$"</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="nam">symbol</span> <span class="op">=</span> <span class="nam">symbols</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">currency</span><span class="op">,</span> <span class="nam">currency</span> <span class="op">+</span> <span class="str">" "</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="key">if</span> <span class="nam">currency</span> <span class="op">==</span> <span class="str">"JPY"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="key">return</span> <span class="str">f"{symbol}{price:,.0f}"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="key">return</span> <span class="str">f"{symbol}{price:,.2f}"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_list</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="str">"""List all alerts."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128237; No price alerts set"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128202; Price Alerts ({len(alerts)} total)\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="nam">now</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="nam">active</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">snoozed</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="key">for</span> <span class="nam">alert</span> <span class="key">in</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"> <span class="nam">snooze_until</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"snooze_until"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="key">if</span> <span class="nam">snooze_until</span> <span class="key">and</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">snooze_until</span><span class="op">)</span> <span class="op">></span> <span class="nam">now</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="nam">snoozed</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">alert</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="nam">active</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">alert</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="key">if</span> <span class="nam">active</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"### Active Alerts"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">active</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="nam">target</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">a</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"currency"</span><span class="op">,</span> <span class="str">"USD"</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="nam">note</span> <span class="op">=</span> <span class="str">f' &#8212; "{a["note"]}"'</span> <span class="key">if</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"note"</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="nam">user</span> <span class="op">=</span> <span class="str">f" (by {a['set_by']})"</span> <span class="key">if</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"set_by"</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#8226; {a['ticker']}: {target}{note}{user}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="key">if</span> <span class="nam">snoozed</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"### Snoozed"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">snoozed</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="nam">target</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">a</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"currency"</span><span class="op">,</span> <span class="str">"USD"</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="nam">until</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">a</span><span class="op">[</span><span class="str">"snooze_until"</span><span class="op">]</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#8226; {a['ticker']}: {target} (until {until})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_set</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="str">"""Set a new alert."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="com"># Check if alert exists</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="nam">existing</span> <span class="op">=</span> <span class="nam">get_alert_by_ticker</span><span class="op">(</span><span class="nam">alerts</span><span class="op">,</span> <span class="nam">ticker</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t"> <span class="key">if</span> <span class="nam">existing</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Alert for {ticker} already exists. Use 'update' to change target."</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="com"># Validate target price</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span> <span class="op">&lt;=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Target price must be greater than 0"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="nam">currency</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">currency</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">currency</span> <span class="key">else</span> <span class="str">"USD"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="key">if</span> <span class="nam">currency</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">SUPPORTED_CURRENCIES</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Currency {currency} not supported. Use: {', '.join(SUPPORTED_CURRENCIES)}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="com"># Warn about currency mismatch based on ticker suffix</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="nam">ticker_currency_map</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="str">".T"</span><span class="op">:</span> <span class="str">"JPY"</span><span class="op">,</span> <span class="com"># Tokyo</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="str">".SI"</span><span class="op">:</span> <span class="str">"SGD"</span><span class="op">,</span> <span class="com"># Singapore</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="str">".MX"</span><span class="op">:</span> <span class="str">"MXN"</span><span class="op">,</span> <span class="com"># Mexico</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="str">".DE"</span><span class="op">:</span> <span class="str">"EUR"</span><span class="op">,</span> <span class="str">".F"</span><span class="op">:</span> <span class="str">"EUR"</span><span class="op">,</span> <span class="str">".PA"</span><span class="op">:</span> <span class="str">"EUR"</span><span class="op">,</span> <span class="com"># Europe</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="nam">expected_currency</span> <span class="op">=</span> <span class="str">"USD"</span> <span class="com"># Default for US stocks</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="key">for</span> <span class="nam">suffix</span><span class="op">,</span> <span class="nam">curr</span> <span class="key">in</span> <span class="nam">ticker_currency_map</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="key">if</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">endswith</span><span class="op">(</span><span class="nam">suffix</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="nam">expected_currency</span> <span class="op">=</span> <span class="nam">curr</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="key">if</span> <span class="nam">currency</span> <span class="op">!=</span> <span class="nam">expected_currency</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Warning: {ticker} trades in {expected_currency}, but alert set in {currency}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="com"># Fetch current price (optional - may fail if numpy broken)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="nam">current_price</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="nam">quotes</span> <span class="op">=</span> <span class="nam">get_fetch_market_data</span><span class="op">(</span><span class="op">)</span><span class="op">(</span><span class="op">[</span><span class="nam">ticker</span><span class="op">]</span><span class="op">,</span> <span class="nam">timeout</span><span class="op">=</span><span class="num">10</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="key">if</span> <span class="nam">ticker</span> <span class="key">in</span> <span class="nam">quotes</span> <span class="key">and</span> <span class="nam">quotes</span><span class="op">[</span><span class="nam">ticker</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"price"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">current_price</span> <span class="op">=</span> <span class="nam">quotes</span><span class="op">[</span><span class="nam">ticker</span><span class="op">]</span><span class="op">[</span><span class="str">"price"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Could not fetch current price: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="nam">alert</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t"> <span class="str">"target_price"</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t"> <span class="str">"currency"</span><span class="op">:</span> <span class="nam">currency</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="str">"note"</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">note</span> <span class="key">or</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="str">"set_by"</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">user</span> <span class="key">or</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="str">"set_date"</span><span class="op">:</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"> <span class="str">"status"</span><span class="op">:</span> <span class="str">"active"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="str">"snooze_until"</span><span class="op">:</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"> <span class="str">"triggered_count"</span><span class="op">:</span> <span class="num">0</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t"> <span class="str">"last_triggered"</span><span class="op">:</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="nam">alerts</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">alert</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"alerts"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t"> <span class="nam">target_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">,</span> <span class="nam">currency</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Alert set: {ticker} under {target_str}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t"> <span class="key">if</span> <span class="nam">current_price</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="nam">pct_diff</span> <span class="op">=</span> <span class="op">(</span><span class="op">(</span><span class="nam">current_price</span> <span class="op">-</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">)</span> <span class="op">/</span> <span class="nam">current_price</span><span class="op">)</span> <span class="op">*</span> <span class="num">100</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t"> <span class="nam">current_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">current_price</span><span class="op">,</span> <span class="nam">currency</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" Current: {current_str} ({pct_diff:+.1f}% to target)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_delete</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t"> <span class="str">"""Delete an alert."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t"> <span class="nam">new_alerts</span> <span class="op">=</span> <span class="op">[</span><span class="nam">a</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">alerts</span> <span class="key">if</span> <span class="nam">a</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span> <span class="op">!=</span> <span class="nam">ticker</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">new_alerts</span><span class="op">)</span> <span class="op">==</span> <span class="nam">len</span><span class="op">(</span><span class="nam">alerts</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; No alert found for {ticker}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"alerts"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">new_alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128465;&#65039; Alert deleted: {ticker}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_snooze</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="str">"""Snooze an alert."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="nam">alert</span> <span class="op">=</span> <span class="nam">get_alert_by_ticker</span><span class="op">(</span><span class="nam">alerts</span><span class="op">,</span> <span class="nam">ticker</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">alert</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; No alert found for {ticker}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="nam">days</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">days</span> <span class="key">or</span> <span class="num">7</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t"> <span class="nam">snooze_until</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span> <span class="op">+</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">days</span><span class="op">=</span><span class="nam">days</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"snooze_until"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">snooze_until</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128564; Alert snoozed: {ticker} until {snooze_until.strftime('%Y-%m-%d')}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_update</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="str">"""Update alert target price."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t"> <span class="nam">alert</span> <span class="op">=</span> <span class="nam">get_alert_by_ticker</span><span class="op">(</span><span class="nam">alerts</span><span class="op">,</span> <span class="nam">ticker</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">alert</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; No alert found for {ticker}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="com"># Validate target price</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span> <span class="op">&lt;=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Target price must be greater than 0"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="nam">old_target</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">note</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"note"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">note</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"> <span class="nam">currency</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"currency"</span><span class="op">,</span> <span class="str">"USD"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"> <span class="nam">old_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">old_target</span><span class="op">,</span> <span class="nam">currency</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t"> <span class="nam">new_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">,</span> <span class="nam">currency</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9999;&#65039; Alert updated: {ticker} {old_str} &#8594; {new_str}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"><span class="key">def</span> <span class="nam">cmd_check</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t"> <span class="str">"""Check alerts against current prices."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128237; No alerts to check"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"> <span class="nam">now</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"> <span class="nam">active_alerts</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="key">for</span> <span class="nam">alert</span> <span class="key">in</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t"> <span class="nam">snooze_until</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"snooze_until"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t"> <span class="key">if</span> <span class="nam">snooze_until</span> <span class="key">and</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">snooze_until</span><span class="op">)</span> <span class="op">></span> <span class="nam">now</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="nam">active_alerts</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">alert</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">active_alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128237; All alerts snoozed"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t"> <span class="com"># Fetch prices for all active alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="nam">tickers</span> <span class="op">=</span> <span class="op">[</span><span class="nam">a</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">active_alerts</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t"> <span class="nam">quotes</span> <span class="op">=</span> <span class="nam">get_fetch_market_data</span><span class="op">(</span><span class="op">)</span><span class="op">(</span><span class="nam">tickers</span><span class="op">,</span> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="nam">triggered</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t"> <span class="nam">watching</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="key">for</span> <span class="nam">alert</span> <span class="key">in</span> <span class="nam">active_alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t"> <span class="nam">target</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"> <span class="nam">currency</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"currency"</span><span class="op">,</span> <span class="str">"USD"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t291" href="#t291">291</a></span><span class="t"> <span class="nam">quote</span> <span class="op">=</span> <span class="nam">quotes</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">ticker</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t292" href="#t292">292</a></span><span class="t"> <span class="nam">price</span> <span class="op">=</span> <span class="nam">quote</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t293" href="#t293">293</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t294" href="#t294">294</a></span><span class="t"> <span class="key">if</span> <span class="nam">price</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t295" href="#t295">295</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t296" href="#t296">296</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t297" href="#t297">297</a></span><span class="t"> <span class="com"># Divide-by-zero protection</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t298" href="#t298">298</a></span><span class="t"> <span class="key">if</span> <span class="nam">target</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t299" href="#t299">299</a></span><span class="t"> <span class="nam">pct_diff</span> <span class="op">=</span> <span class="num">0</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t300" href="#t300">300</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t301" href="#t301">301</a></span><span class="t"> <span class="nam">pct_diff</span> <span class="op">=</span> <span class="op">(</span><span class="op">(</span><span class="nam">price</span> <span class="op">-</span> <span class="nam">target</span><span class="op">)</span> <span class="op">/</span> <span class="nam">target</span><span class="op">)</span> <span class="op">*</span> <span class="num">100</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t302" href="#t302">302</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t303" href="#t303">303</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t304" href="#t304">304</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t305" href="#t305">305</a></span><span class="t"> <span class="str">"target_price"</span><span class="op">:</span> <span class="nam">target</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t306" href="#t306">306</a></span><span class="t"> <span class="str">"current_price"</span><span class="op">:</span> <span class="nam">price</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t307" href="#t307">307</a></span><span class="t"> <span class="str">"currency"</span><span class="op">:</span> <span class="nam">currency</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t308" href="#t308">308</a></span><span class="t"> <span class="str">"pct_from_target"</span><span class="op">:</span> <span class="nam">round</span><span class="op">(</span><span class="nam">pct_diff</span><span class="op">,</span> <span class="num">2</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t309" href="#t309">309</a></span><span class="t"> <span class="str">"note"</span><span class="op">:</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"note"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t310" href="#t310">310</a></span><span class="t"> <span class="str">"set_by"</span><span class="op">:</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"set_by"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t311" href="#t311">311</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t312" href="#t312">312</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t313" href="#t313">313</a></span><span class="t"> <span class="key">if</span> <span class="nam">price</span> <span class="op">&lt;=</span> <span class="nam">target</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t314" href="#t314">314</a></span><span class="t"> <span class="nam">triggered</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">result</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t315" href="#t315">315</a></span><span class="t"> <span class="com"># Update triggered count (only once per day to avoid inflation)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t316" href="#t316">316</a></span><span class="t"> <span class="nam">last_triggered</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"last_triggered"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t317" href="#t317">317</a></span><span class="t"> <span class="nam">today</span> <span class="op">=</span> <span class="nam">now</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t318" href="#t318">318</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">last_triggered</span> <span class="key">or</span> <span class="key">not</span> <span class="nam">last_triggered</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="nam">today</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t319" href="#t319">319</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"triggered_count"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"triggered_count"</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">+</span> <span class="num">1</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t320" href="#t320">320</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"last_triggered"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">now</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t321" href="#t321">321</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t322" href="#t322">322</a></span><span class="t"> <span class="nam">watching</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">result</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t323" href="#t323">323</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t324" href="#t324">324</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t325" href="#t325">325</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t326" href="#t326">326</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t327" href="#t327">327</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="nam">triggered</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="nam">watching</span><span class="op">}</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t328" href="#t328">328</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t329" href="#t329">329</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t330" href="#t330">330</a></span><span class="t"> <span class="com"># Translations</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t331" href="#t331">331</a></span><span class="t"> <span class="nam">lang</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'lang'</span><span class="op">,</span> <span class="str">'en'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t332" href="#t332">332</a></span><span class="t"> <span class="key">if</span> <span class="nam">lang</span> <span class="op">==</span> <span class="str">"de"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t333" href="#t333">333</a></span><span class="t"> <span class="nam">labels</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t334" href="#t334">334</a></span><span class="t"> <span class="str">"title"</span><span class="op">:</span> <span class="str">"PREISWARNUNGEN"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t335" href="#t335">335</a></span><span class="t"> <span class="str">"in_zone"</span><span class="op">:</span> <span class="str">"IN KAUFZONE"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t336" href="#t336">336</a></span><span class="t"> <span class="str">"buy"</span><span class="op">:</span> <span class="str">"KAUFEN!"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t337" href="#t337">337</a></span><span class="t"> <span class="str">"target"</span><span class="op">:</span> <span class="str">"Ziel"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t338" href="#t338">338</a></span><span class="t"> <span class="str">"watching"</span><span class="op">:</span> <span class="str">"BEOBACHTUNG"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t339" href="#t339">339</a></span><span class="t"> <span class="str">"to_target"</span><span class="op">:</span> <span class="str">"noch"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t340" href="#t340">340</a></span><span class="t"> <span class="str">"no_data"</span><span class="op">:</span> <span class="str">"Keine Preisdaten f&#252;r Alerts verf&#252;gbar"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t341" href="#t341">341</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t342" href="#t342">342</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t343" href="#t343">343</a></span><span class="t"> <span class="nam">labels</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t344" href="#t344">344</a></span><span class="t"> <span class="str">"title"</span><span class="op">:</span> <span class="str">"PRICE ALERTS"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t345" href="#t345">345</a></span><span class="t"> <span class="str">"in_zone"</span><span class="op">:</span> <span class="str">"IN BUY ZONE"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t346" href="#t346">346</a></span><span class="t"> <span class="str">"buy"</span><span class="op">:</span> <span class="str">"BUY SIGNAL"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t347" href="#t347">347</a></span><span class="t"> <span class="str">"target"</span><span class="op">:</span> <span class="str">"target"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t348" href="#t348">348</a></span><span class="t"> <span class="str">"watching"</span><span class="op">:</span> <span class="str">"WATCHING"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t349" href="#t349">349</a></span><span class="t"> <span class="str">"to_target"</span><span class="op">:</span> <span class="str">"to target"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t350" href="#t350">350</a></span><span class="t"> <span class="str">"no_data"</span><span class="op">:</span> <span class="str">"No price data available for alerts"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t351" href="#t351">351</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t352" href="#t352">352</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t353" href="#t353">353</a></span><span class="t"> <span class="com"># Date header</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t354" href="#t354">354</a></span><span class="t"> <span class="nam">date_str</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%b %d, %Y"</span><span class="op">)</span> <span class="key">if</span> <span class="nam">lang</span> <span class="op">==</span> <span class="str">"en"</span> <span class="key">else</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%d. %b %Y"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t355" href="#t355">355</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128202; {labels['title']} &#8212; {date_str}\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t356" href="#t356">356</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t357" href="#t357">357</a></span><span class="t"> <span class="com"># Human-readable output</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t358" href="#t358">358</a></span><span class="t"> <span class="key">if</span> <span class="nam">triggered</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t359" href="#t359">359</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128994; {labels['in_zone']}:\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t360" href="#t360">360</a></span><span class="t"> <span class="key">for</span> <span class="nam">t</span> <span class="key">in</span> <span class="nam">triggered</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t361" href="#t361">361</a></span><span class="t"> <span class="nam">target_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">t</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">t</span><span class="op">[</span><span class="str">"currency"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t362" href="#t362">362</a></span><span class="t"> <span class="nam">current_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">t</span><span class="op">[</span><span class="str">"current_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">t</span><span class="op">[</span><span class="str">"currency"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t363" href="#t363">363</a></span><span class="t"> <span class="nam">note</span> <span class="op">=</span> <span class="str">f'\n "{t["note"]}"'</span> <span class="key">if</span> <span class="nam">t</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"note"</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t364" href="#t364">364</a></span><span class="t"> <span class="nam">user</span> <span class="op">=</span> <span class="str">f" &#8212; {t['set_by']}"</span> <span class="key">if</span> <span class="nam">t</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"set_by"</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t365" href="#t365">365</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#8226; {t['ticker']}: {current_str} ({labels['target']}: {target_str}) &#8592; {labels['buy']}{note}{user}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t366" href="#t366">366</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t367" href="#t367">367</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t368" href="#t368">368</a></span><span class="t"> <span class="key">if</span> <span class="nam">watching</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t369" href="#t369">369</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9203; {labels['watching']}:\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t370" href="#t370">370</a></span><span class="t"> <span class="key">for</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">watching</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="str">"pct_from_target"</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t371" href="#t371">371</a></span><span class="t"> <span class="nam">target_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">w</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">w</span><span class="op">[</span><span class="str">"currency"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t372" href="#t372">372</a></span><span class="t"> <span class="nam">current_str</span> <span class="op">=</span> <span class="nam">format_price</span><span class="op">(</span><span class="nam">w</span><span class="op">[</span><span class="str">"current_price"</span><span class="op">]</span><span class="op">,</span> <span class="nam">w</span><span class="op">[</span><span class="str">"currency"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t373" href="#t373">373</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#8226; {w['ticker']}: {current_str} ({labels['target']}: {target_str}) &#8212; {labels['to_target']} {abs(w['pct_from_target']):.1f}%"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t374" href="#t374">374</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t375" href="#t375">375</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t376" href="#t376">376</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">triggered</span> <span class="key">and</span> <span class="key">not</span> <span class="nam">watching</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t377" href="#t377">377</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128237; {labels['no_data']}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t378" href="#t378">378</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t379" href="#t379">379</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t380" href="#t380">380</a></span><span class="t"><span class="key">def</span> <span class="nam">check_alerts</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t381" href="#t381">381</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t382" href="#t382">382</a></span><span class="t"><span class="str"> Check alerts and return results for briefing integration.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t383" href="#t383">383</a></span><span class="t"><span class="str"> Returns: {"triggered": [...], "watching": [...]}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t384" href="#t384">384</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t385" href="#t385">385</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_alerts</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t386" href="#t386">386</a></span><span class="t"> <span class="nam">alerts</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"alerts"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t387" href="#t387">387</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t388" href="#t388">388</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t389" href="#t389">389</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t390" href="#t390">390</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t391" href="#t391">391</a></span><span class="t"> <span class="nam">now</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t392" href="#t392">392</a></span><span class="t"> <span class="nam">active_alerts</span> <span class="op">=</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t393" href="#t393">393</a></span><span class="t"> <span class="nam">a</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t394" href="#t394">394</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"snooze_until"</span><span class="op">)</span> <span class="key">or</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">a</span><span class="op">[</span><span class="str">"snooze_until"</span><span class="op">]</span><span class="op">)</span> <span class="op">&lt;=</span> <span class="nam">now</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t395" href="#t395">395</a></span><span class="t"> <span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t396" href="#t396">396</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t397" href="#t397">397</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">active_alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t398" href="#t398">398</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t399" href="#t399">399</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t400" href="#t400">400</a></span><span class="t"> <span class="nam">tickers</span> <span class="op">=</span> <span class="op">[</span><span class="nam">a</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">active_alerts</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t401" href="#t401">401</a></span><span class="t"> <span class="nam">quotes</span> <span class="op">=</span> <span class="nam">get_fetch_market_data</span><span class="op">(</span><span class="op">)</span><span class="op">(</span><span class="nam">tickers</span><span class="op">,</span> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t402" href="#t402">402</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t403" href="#t403">403</a></span><span class="t"> <span class="nam">triggered</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t404" href="#t404">404</a></span><span class="t"> <span class="nam">watching</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t405" href="#t405">405</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t406" href="#t406">406</a></span><span class="t"> <span class="key">for</span> <span class="nam">alert</span> <span class="key">in</span> <span class="nam">active_alerts</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t407" href="#t407">407</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t408" href="#t408">408</a></span><span class="t"> <span class="nam">target</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">[</span><span class="str">"target_price"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t409" href="#t409">409</a></span><span class="t"> <span class="nam">currency</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"currency"</span><span class="op">,</span> <span class="str">"USD"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t410" href="#t410">410</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t411" href="#t411">411</a></span><span class="t"> <span class="nam">quote</span> <span class="op">=</span> <span class="nam">quotes</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">ticker</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t412" href="#t412">412</a></span><span class="t"> <span class="nam">price</span> <span class="op">=</span> <span class="nam">quote</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t413" href="#t413">413</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t414" href="#t414">414</a></span><span class="t"> <span class="key">if</span> <span class="nam">price</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t415" href="#t415">415</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t416" href="#t416">416</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t417" href="#t417">417</a></span><span class="t"> <span class="com"># Divide-by-zero protection</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t418" href="#t418">418</a></span><span class="t"> <span class="key">if</span> <span class="nam">target</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t419" href="#t419">419</a></span><span class="t"> <span class="nam">pct_diff</span> <span class="op">=</span> <span class="num">0</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t420" href="#t420">420</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t421" href="#t421">421</a></span><span class="t"> <span class="nam">pct_diff</span> <span class="op">=</span> <span class="op">(</span><span class="op">(</span><span class="nam">price</span> <span class="op">-</span> <span class="nam">target</span><span class="op">)</span> <span class="op">/</span> <span class="nam">target</span><span class="op">)</span> <span class="op">*</span> <span class="num">100</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t422" href="#t422">422</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t423" href="#t423">423</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t424" href="#t424">424</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t425" href="#t425">425</a></span><span class="t"> <span class="str">"target_price"</span><span class="op">:</span> <span class="nam">target</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t426" href="#t426">426</a></span><span class="t"> <span class="str">"current_price"</span><span class="op">:</span> <span class="nam">price</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t427" href="#t427">427</a></span><span class="t"> <span class="str">"currency"</span><span class="op">:</span> <span class="nam">currency</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t428" href="#t428">428</a></span><span class="t"> <span class="str">"pct_from_target"</span><span class="op">:</span> <span class="nam">round</span><span class="op">(</span><span class="nam">pct_diff</span><span class="op">,</span> <span class="num">2</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t429" href="#t429">429</a></span><span class="t"> <span class="str">"note"</span><span class="op">:</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"note"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t430" href="#t430">430</a></span><span class="t"> <span class="str">"set_by"</span><span class="op">:</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"set_by"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t431" href="#t431">431</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t432" href="#t432">432</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t433" href="#t433">433</a></span><span class="t"> <span class="key">if</span> <span class="nam">price</span> <span class="op">&lt;=</span> <span class="nam">target</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t434" href="#t434">434</a></span><span class="t"> <span class="nam">triggered</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">result</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t435" href="#t435">435</a></span><span class="t"> <span class="com"># Update triggered count (only once per day to avoid inflation)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t436" href="#t436">436</a></span><span class="t"> <span class="nam">last_triggered</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"last_triggered"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t437" href="#t437">437</a></span><span class="t"> <span class="nam">today</span> <span class="op">=</span> <span class="nam">now</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t438" href="#t438">438</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">last_triggered</span> <span class="key">or</span> <span class="key">not</span> <span class="nam">last_triggered</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="nam">today</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t439" href="#t439">439</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"triggered_count"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">alert</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"triggered_count"</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">+</span> <span class="num">1</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t440" href="#t440">440</a></span><span class="t"> <span class="nam">alert</span><span class="op">[</span><span class="str">"last_triggered"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">now</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t441" href="#t441">441</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t442" href="#t442">442</a></span><span class="t"> <span class="nam">watching</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">result</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t443" href="#t443">443</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t444" href="#t444">444</a></span><span class="t"> <span class="nam">save_alerts</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t445" href="#t445">445</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"triggered"</span><span class="op">:</span> <span class="nam">triggered</span><span class="op">,</span> <span class="str">"watching"</span><span class="op">:</span> <span class="nam">watching</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t446" href="#t446">446</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t447" href="#t447">447</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t448" href="#t448">448</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t449" href="#t449">449</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">"Price target alerts"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t450" href="#t450">450</a></span><span class="t"> <span class="nam">subparsers</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_subparsers</span><span class="op">(</span><span class="nam">dest</span><span class="op">=</span><span class="str">"command"</span><span class="op">,</span> <span class="nam">required</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t451" href="#t451">451</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t452" href="#t452">452</a></span><span class="t"> <span class="com"># list</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t453" href="#t453">453</a></span><span class="t"> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"list"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"List all alerts"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t454" href="#t454">454</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t455" href="#t455">455</a></span><span class="t"> <span class="com"># set</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t456" href="#t456">456</a></span><span class="t"> <span class="nam">set_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"set"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Set new alert"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t457" href="#t457">457</a></span><span class="t"> <span class="nam">set_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t458" href="#t458">458</a></span><span class="t"> <span class="nam">set_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"target"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Target price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t459" href="#t459">459</a></span><span class="t"> <span class="nam">set_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--note"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Note/reason"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t460" href="#t460">460</a></span><span class="t"> <span class="nam">set_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--user"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Who set the alert"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t461" href="#t461">461</a></span><span class="t"> <span class="nam">set_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--currency"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">"USD"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Currency (USD, EUR, JPY, SGD, MXN)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t462" href="#t462">462</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t463" href="#t463">463</a></span><span class="t"> <span class="com"># delete</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t464" href="#t464">464</a></span><span class="t"> <span class="nam">del_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"delete"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Delete alert"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t465" href="#t465">465</a></span><span class="t"> <span class="nam">del_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t466" href="#t466">466</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t467" href="#t467">467</a></span><span class="t"> <span class="com"># snooze</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t468" href="#t468">468</a></span><span class="t"> <span class="nam">snooze_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"snooze"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Snooze alert"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t469" href="#t469">469</a></span><span class="t"> <span class="nam">snooze_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t470" href="#t470">470</a></span><span class="t"> <span class="nam">snooze_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--days"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">int</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="num">7</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Days to snooze"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t471" href="#t471">471</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t472" href="#t472">472</a></span><span class="t"> <span class="com"># update</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t473" href="#t473">473</a></span><span class="t"> <span class="nam">update_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"update"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Update alert target"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t474" href="#t474">474</a></span><span class="t"> <span class="nam">update_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t475" href="#t475">475</a></span><span class="t"> <span class="nam">update_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"target"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"New target price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t476" href="#t476">476</a></span><span class="t"> <span class="nam">update_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--note"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Update note"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t477" href="#t477">477</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t478" href="#t478">478</a></span><span class="t"> <span class="com"># check</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t479" href="#t479">479</a></span><span class="t"> <span class="nam">check_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"check"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Check alerts against prices"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t480" href="#t480">480</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--json"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"JSON output"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t481" href="#t481">481</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--lang"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">"en"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Output language (en, de)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t482" href="#t482">482</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t483" href="#t483">483</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t484" href="#t484">484</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t485" href="#t485">485</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"list"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t486" href="#t486">486</a></span><span class="t"> <span class="nam">cmd_list</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t487" href="#t487">487</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"set"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t488" href="#t488">488</a></span><span class="t"> <span class="nam">cmd_set</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t489" href="#t489">489</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"delete"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t490" href="#t490">490</a></span><span class="t"> <span class="nam">cmd_delete</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t491" href="#t491">491</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"snooze"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t492" href="#t492">492</a></span><span class="t"> <span class="nam">cmd_snooze</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t493" href="#t493">493</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"update"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t494" href="#t494">494</a></span><span class="t"> <span class="nam">cmd_update</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t495" href="#t495">495</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"check"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t496" href="#t496">496</a></span><span class="t"> <span class="nam">cmd_check</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t497" href="#t497">497</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t498" href="#t498">498</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t499" href="#t499">499</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">"__main__"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t500" href="#t500">500</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="index.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_briefing_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/briefing.py: 56%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;briefing.py</b>:
<span class="pc_cov">56%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">87 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">49<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">38<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_alerts_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_earnings_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Briefing Generator - Main entry point for market briefings.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="str">Generates and optionally sends to WhatsApp group.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="key">import</span> <span class="nam">os</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="key">import</span> <span class="nam">subprocess</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="key">from</span> <span class="nam">utils</span> <span class="key">import</span> <span class="nam">ensure_venv</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="nam">SCRIPT_DIR</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"><span class="nam">ensure_venv</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="key">def</span> <span class="nam">send_to_whatsapp</span><span class="op">(</span><span class="nam">message</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">group_name</span><span class="op">:</span> <span class="nam">str</span> <span class="op">|</span> <span class="key">None</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"> <span class="str">"""Send message to WhatsApp group via openclaw message tool."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">group_name</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"> <span class="nam">group_name</span> <span class="op">=</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'FINANCE_NEWS_TARGET'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">group_name</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#10060; No target specified. Set FINANCE_NEWS_TARGET env var or use --group"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="com"># Use openclaw message tool</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t"> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"> <span class="str">'openclaw'</span><span class="op">,</span> <span class="str">'message'</span><span class="op">,</span> <span class="str">'send'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="str">'--channel'</span><span class="op">,</span> <span class="str">'whatsapp'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="str">'--target'</span><span class="op">,</span> <span class="nam">group_name</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="str">'--message'</span><span class="op">,</span> <span class="nam">message</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Sent to WhatsApp group: {group_name}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; WhatsApp send failed: {result.stderr}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; WhatsApp error: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"><span class="key">def</span> <span class="nam">generate_and_send</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="str">"""Generate briefing and optionally send to WhatsApp."""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="com"># Determine briefing type based on current time or args</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">time</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"> <span class="nam">briefing_time</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">time</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="nam">hour</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">hour</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="nam">briefing_time</span> <span class="op">=</span> <span class="str">'morning'</span> <span class="key">if</span> <span class="nam">hour</span> <span class="op">&lt;</span> <span class="num">12</span> <span class="key">else</span> <span class="str">'evening'</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="com"># Generate the briefing</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="nam">cmd</span> <span class="op">=</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">sys</span><span class="op">.</span><span class="nam">executable</span><span class="op">,</span> <span class="nam">SCRIPT_DIR</span> <span class="op">/</span> <span class="str">'summarize.py'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="str">'--time'</span><span class="op">,</span> <span class="nam">briefing_time</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t"> <span class="str">'--style'</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">style</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="str">'--lang'</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">lang</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">deadline</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">extend</span><span class="op">(</span><span class="op">[</span><span class="str">'--deadline'</span><span class="op">,</span> <span class="nam">str</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">deadline</span><span class="op">)</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">fast</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">'--fast'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">llm</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">'--llm'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">extend</span><span class="op">(</span><span class="op">[</span><span class="str">'--model'</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">model</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">debug</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">'--debug'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="com"># Always use JSON for internal processing to handle splits</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"> <span class="nam">cmd</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">'--json'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128202; Generating {briefing_time} briefing..."</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">timeout</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">deadline</span> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">deadline</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span> <span class="key">else</span> <span class="num">300</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="nam">timeout</span> <span class="op">=</span> <span class="nam">max</span><span class="op">(</span><span class="num">1</span><span class="op">,</span> <span class="nam">int</span><span class="op">(</span><span class="nam">timeout</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">deadline</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"> <span class="nam">timeout</span> <span class="op">=</span> <span class="nam">timeout</span> <span class="op">+</span> <span class="num">5</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="nam">cmd</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="nam">stdin</span><span class="op">=</span><span class="nam">subprocess</span><span class="op">.</span><span class="nam">DEVNULL</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="nam">timeout</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">!=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Briefing generation failed: {result.stderr}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="nam">sys</span><span class="op">.</span><span class="nam">exit</span><span class="op">(</span><span class="num">1</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="key">except</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="com"># Fallback if not JSON (shouldn't happen with --json)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Failed to parse briefing JSON"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"> <span class="com"># Output handling</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="com"># Print for humans</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'macro_message'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">'macro_message'</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'portfolio_message'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n"</span> <span class="op">+</span> <span class="str">"="</span><span class="op">*</span><span class="num">20</span> <span class="op">+</span> <span class="str">"\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">'portfolio_message'</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t"> <span class="com"># Send to WhatsApp if requested</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">send</span> <span class="key">and</span> <span class="nam">args</span><span class="op">.</span><span class="nam">group</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="com"># Message 1: Macro</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="nam">macro_msg</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'macro_message'</span><span class="op">)</span> <span class="key">or</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'summary'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="key">if</span> <span class="nam">macro_msg</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="nam">send_to_whatsapp</span><span class="op">(</span><span class="nam">macro_msg</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">group</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="com"># Message 2: Portfolio (if exists)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="nam">portfolio_msg</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'portfolio_message'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="key">if</span> <span class="nam">portfolio_msg</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="nam">send_to_whatsapp</span><span class="op">(</span><span class="nam">portfolio_msg</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">group</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="key">return</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'macro_message'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">'Briefing Generator'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--time'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'morning'</span><span class="op">,</span> <span class="str">'evening'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Briefing type (auto-detected if not specified)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--style'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'briefing'</span><span class="op">,</span> <span class="str">'analysis'</span><span class="op">,</span> <span class="str">'headlines'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="nam">default</span><span class="op">=</span><span class="str">'briefing'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Summary style'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--lang'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'en'</span><span class="op">,</span> <span class="str">'de'</span><span class="op">]</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">'en'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Output language'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--send'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Send to WhatsApp group'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--group'</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'FINANCE_NEWS_TARGET'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'WhatsApp group name or JID (default: FINANCE_NEWS_TARGET env var)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--json'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Output as JSON'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--deadline'</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">int</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Overall deadline in seconds'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--llm'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Use LLM summary'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--model'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'claude'</span><span class="op">,</span> <span class="str">'minimax'</span><span class="op">,</span> <span class="str">'gemini'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="nam">default</span><span class="op">=</span><span class="str">'claude'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'LLM model (only with --llm)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--fast'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Use fast mode (shorter timeouts, fewer items)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--debug'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Write debug log with sources'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="nam">generate_and_send</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">'__main__'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_alerts_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_earnings_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,711 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/earnings.py: 45%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;earnings.py</b>:
<span class="pc_cov">45%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">329 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">148<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">181<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_briefing_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_fetch_news_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Earnings Calendar - Track earnings dates for portfolio stocks.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">Features:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="str">- Fetch earnings dates from FMP API</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="str">- Show upcoming earnings in daily briefing</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="str">- Alert 24h before earnings release</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="str">- Cache results to avoid API spam</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="str">Usage:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="str"> earnings.py list # Show all upcoming earnings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="str"> earnings.py check # Check what's reporting today/this week</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="str"> earnings.py refresh # Force refresh earnings data</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"><span class="key">import</span> <span class="nam">csv</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"><span class="key">import</span> <span class="nam">os</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="key">import</span> <span class="nam">shutil</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="key">import</span> <span class="nam">subprocess</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span><span class="op">,</span> <span class="nam">timedelta</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"><span class="key">from</span> <span class="nam">urllib</span><span class="op">.</span><span class="nam">request</span> <span class="key">import</span> <span class="nam">urlopen</span><span class="op">,</span> <span class="nam">Request</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"><span class="key">from</span> <span class="nam">urllib</span><span class="op">.</span><span class="nam">error</span> <span class="key">import</span> <span class="nam">URLError</span><span class="op">,</span> <span class="nam">HTTPError</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"><span class="com"># Paths</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"><span class="nam">SCRIPT_DIR</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"><span class="nam">CONFIG_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t"><span class="nam">CACHE_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"cache"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"><span class="nam">PORTFOLIO_FILE</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"portfolio.csv"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"><span class="nam">EARNINGS_CACHE</span> <span class="op">=</span> <span class="nam">CACHE_DIR</span> <span class="op">/</span> <span class="str">"earnings_calendar.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"><span class="nam">MANUAL_EARNINGS</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"manual_earnings.json"</span> <span class="com"># For JP/other stocks not in Finnhub</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"><span class="com"># OpenBB binary path</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"><span class="nam">OPENBB_BINARY</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"><span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="nam">env_path</span> <span class="op">=</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'OPENBB_QUOTE_BIN'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="key">if</span> <span class="nam">env_path</span> <span class="key">and</span> <span class="nam">os</span><span class="op">.</span><span class="nam">path</span><span class="op">.</span><span class="nam">isfile</span><span class="op">(</span><span class="nam">env_path</span><span class="op">)</span> <span class="key">and</span> <span class="nam">os</span><span class="op">.</span><span class="nam">access</span><span class="op">(</span><span class="nam">env_path</span><span class="op">,</span> <span class="nam">os</span><span class="op">.</span><span class="nam">X_OK</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"> <span class="nam">OPENBB_BINARY</span> <span class="op">=</span> <span class="nam">env_path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="nam">OPENBB_BINARY</span> <span class="op">=</span> <span class="nam">shutil</span><span class="op">.</span><span class="nam">which</span><span class="op">(</span><span class="str">'openbb-quote'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"><span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"><span class="com"># API Keys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"><span class="key">def</span> <span class="nam">get_fmp_key</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="str">"""Get FMP API key from environment or .env file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="nam">key</span> <span class="op">=</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"FMP_API_KEY"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">key</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="nam">env_file</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">.</span><span class="nam">home</span><span class="op">(</span><span class="op">)</span> <span class="op">/</span> <span class="str">".openclaw"</span> <span class="op">/</span> <span class="str">".env"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="key">if</span> <span class="nam">env_file</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="key">for</span> <span class="nam">line</span> <span class="key">in</span> <span class="nam">env_file</span><span class="op">.</span><span class="nam">read_text</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">splitlines</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="key">if</span> <span class="nam">line</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="str">"FMP_API_KEY="</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="nam">key</span> <span class="op">=</span> <span class="nam">line</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">"="</span><span class="op">,</span> <span class="num">1</span><span class="op">)</span><span class="op">[</span><span class="num">1</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="key">return</span> <span class="nam">key</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"><span class="key">def</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="str">"""Load portfolio from CSV."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">PORTFOLIO_FILE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">PORTFOLIO_FILE</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">reader</span> <span class="op">=</span> <span class="nam">csv</span><span class="op">.</span><span class="nam">DictReader</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="key">return</span> <span class="nam">list</span><span class="op">(</span><span class="nam">reader</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"><span class="key">def</span> <span class="nam">load_earnings_cache</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="str">"""Load cached earnings data."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="key">if</span> <span class="nam">EARNINGS_CACHE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="key">return</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">EARNINGS_CACHE</span><span class="op">.</span><span class="nam">read_text</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"last_updated"</span><span class="op">:</span> <span class="key">None</span><span class="op">,</span> <span class="str">"earnings"</span><span class="op">:</span> <span class="op">{</span><span class="op">}</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t"><span class="key">def</span> <span class="nam">load_manual_earnings</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"><span class="str"> Load manually-entered earnings dates (for JP stocks not in Finnhub).</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"><span class="str"> Format: {"6857.T": {"date": "2026-01-30", "time": "amc", "note": "Q3 FY2025"}, ...}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="key">if</span> <span class="nam">MANUAL_EARNINGS</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">MANUAL_EARNINGS</span><span class="op">.</span><span class="nam">read_text</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="com"># Filter out metadata keys (starting with _)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="nam">k</span><span class="op">:</span> <span class="nam">v</span> <span class="key">for</span> <span class="nam">k</span><span class="op">,</span> <span class="nam">v</span> <span class="key">in</span> <span class="nam">data</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span> <span class="key">if</span> <span class="key">not</span> <span class="nam">k</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="str">"_"</span><span class="op">)</span> <span class="key">and</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">v</span><span class="op">,</span> <span class="nam">dict</span><span class="op">)</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"><span class="key">def</span> <span class="nam">save_earnings_cache</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="str">"""Save earnings data to cache."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="nam">CACHE_DIR</span><span class="op">.</span><span class="nam">mkdir</span><span class="op">(</span><span class="nam">exist_ok</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="nam">EARNINGS_CACHE</span><span class="op">.</span><span class="nam">write_text</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="nam">str</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"><span class="key">def</span> <span class="nam">get_finnhub_key</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="str">"""Get Finnhub API key from environment or .env file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="nam">key</span> <span class="op">=</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"FINNHUB_API_KEY"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">key</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t"> <span class="nam">env_file</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">.</span><span class="nam">home</span><span class="op">(</span><span class="op">)</span> <span class="op">/</span> <span class="str">".openclaw"</span> <span class="op">/</span> <span class="str">".env"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="key">if</span> <span class="nam">env_file</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="key">for</span> <span class="nam">line</span> <span class="key">in</span> <span class="nam">env_file</span><span class="op">.</span><span class="nam">read_text</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">splitlines</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="key">if</span> <span class="nam">line</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="str">"FINNHUB_API_KEY="</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="nam">key</span> <span class="op">=</span> <span class="nam">line</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">"="</span><span class="op">,</span> <span class="num">1</span><span class="op">)</span><span class="op">[</span><span class="num">1</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="key">return</span> <span class="nam">key</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"><span class="key">def</span> <span class="nam">fetch_all_earnings_finnhub</span><span class="op">(</span><span class="nam">days_ahead</span><span class="op">:</span> <span class="nam">int</span> <span class="op">=</span> <span class="num">60</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"><span class="str"> Fetch all earnings for the next N days from Finnhub.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"><span class="str"> Returns dict keyed by symbol: {"AAPL": {...}, ...}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t"> <span class="nam">finnhub_key</span> <span class="op">=</span> <span class="nam">get_finnhub_key</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">finnhub_key</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="nam">from_date</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="nam">to_date</span> <span class="op">=</span> <span class="op">(</span><span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span> <span class="op">+</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">days</span><span class="op">=</span><span class="nam">days_ahead</span><span class="op">)</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="nam">url</span> <span class="op">=</span> <span class="str">f"https://finnhub.io/api/v1/calendar/earnings?from={from_date}&amp;to={to_date}&amp;token={finnhub_key}"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="nam">req</span> <span class="op">=</span> <span class="nam">Request</span><span class="op">(</span><span class="nam">url</span><span class="op">,</span> <span class="nam">headers</span><span class="op">=</span><span class="op">{</span><span class="str">"User-Agent"</span><span class="op">:</span> <span class="str">"finance-news/1.0"</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="key">with</span> <span class="nam">urlopen</span><span class="op">(</span><span class="nam">req</span><span class="op">,</span> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span><span class="op">)</span> <span class="key">as</span> <span class="nam">resp</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">resp</span><span class="op">.</span><span class="nam">read</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">decode</span><span class="op">(</span><span class="str">"utf-8"</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="nam">earnings_by_symbol</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="key">for</span> <span class="nam">entry</span> <span class="key">in</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"earningsCalendar"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="nam">symbol</span> <span class="op">=</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"symbol"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="key">if</span> <span class="nam">symbol</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="nam">earnings_by_symbol</span><span class="op">[</span><span class="nam">symbol</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="str">"date"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"date"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="str">"time"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"hour"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span> <span class="com"># bmo/amc</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="str">"eps_estimate"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"epsEstimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="str">"revenue_estimate"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"revenueEstimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="str">"quarter"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"quarter"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="str">"year"</span><span class="op">:</span> <span class="nam">entry</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"year"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="key">return</span> <span class="nam">earnings_by_symbol</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Finnhub error: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"><span class="key">def</span> <span class="nam">normalize_ticker_for_lookup</span><span class="op">(</span><span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"><span class="str"> Convert portfolio ticker to possible Finnhub symbols.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"><span class="str"> Returns list of possible formats to try.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="nam">variants</span> <span class="op">=</span> <span class="op">[</span><span class="nam">ticker</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="com"># Japanese stocks: 6857.T -> try 6857</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="key">if</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">endswith</span><span class="op">(</span><span class="str">'.T'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">base</span> <span class="op">=</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">replace</span><span class="op">(</span><span class="str">'.T'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t"> <span class="nam">variants</span><span class="op">.</span><span class="nam">extend</span><span class="op">(</span><span class="op">[</span><span class="nam">base</span><span class="op">,</span> <span class="str">f"{base}.T"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"> <span class="com"># Singapore stocks: D05.SI -> try D05</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="key">elif</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">endswith</span><span class="op">(</span><span class="str">'.SI'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="nam">base</span> <span class="op">=</span> <span class="nam">ticker</span><span class="op">.</span><span class="nam">replace</span><span class="op">(</span><span class="str">'.SI'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t"> <span class="nam">variants</span><span class="op">.</span><span class="nam">extend</span><span class="op">(</span><span class="op">[</span><span class="nam">base</span><span class="op">,</span> <span class="str">f"{base}.SI"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="key">return</span> <span class="nam">variants</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"><span class="key">def</span> <span class="nam">fetch_earnings_for_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"><span class="str"> Fetch earnings dates for portfolio stocks using Finnhub bulk API.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t"><span class="str"> More efficient than per-ticker calls.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="com"># Get all earnings for next 60 days</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="nam">all_earnings</span> <span class="op">=</span> <span class="nam">fetch_all_earnings_finnhub</span><span class="op">(</span><span class="nam">days_ahead</span><span class="op">=</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">all_earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="com"># Match portfolio tickers to earnings data</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t"> <span class="nam">results</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="key">for</span> <span class="nam">stock</span> <span class="key">in</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t"> <span class="nam">ticker</span> <span class="op">=</span> <span class="nam">stock</span><span class="op">[</span><span class="str">"symbol"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="nam">variants</span> <span class="op">=</span> <span class="nam">normalize_ticker_for_lookup</span><span class="op">(</span><span class="nam">ticker</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="key">for</span> <span class="nam">variant</span> <span class="key">in</span> <span class="nam">variants</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="key">if</span> <span class="nam">variant</span> <span class="key">in</span> <span class="nam">all_earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t"> <span class="nam">results</span><span class="op">[</span><span class="nam">ticker</span><span class="op">]</span> <span class="op">=</span> <span class="nam">all_earnings</span><span class="op">[</span><span class="nam">variant</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t"> <span class="key">return</span> <span class="nam">results</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"><span class="key">def</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">force</span><span class="op">:</span> <span class="nam">bool</span> <span class="op">=</span> <span class="key">False</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="str">"""Refresh earnings data for all portfolio stocks."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"> <span class="nam">finnhub_key</span> <span class="op">=</span> <span class="nam">get_finnhub_key</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">finnhub_key</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#10060; FINNHUB_API_KEY not found"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">load_earnings_cache</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="com"># Check if cache is fresh (&lt; 6 hours old)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">force</span> <span class="key">and</span> <span class="nam">cache</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"last_updated"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="nam">last</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">cache</span><span class="op">[</span><span class="str">"last_updated"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="key">if</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span> <span class="op">-</span> <span class="nam">last</span> <span class="op">&lt;</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">hours</span><span class="op">=</span><span class="num">6</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128230; Using cached data (updated {last.strftime('%H:%M')})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="key">return</span> <span class="nam">cache</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128260; Fetching earnings calendar from Finnhub..."</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t"> <span class="com"># Use bulk fetch - much more efficient</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t"> <span class="nam">earnings</span> <span class="op">=</span> <span class="nam">fetch_earnings_for_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t"> <span class="com"># Merge manual earnings (for JP stocks not in Finnhub)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t"> <span class="nam">manual</span> <span class="op">=</span> <span class="nam">load_manual_earnings</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"> <span class="key">if</span> <span class="nam">manual</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128221; Merging {len(manual)} manual entries..."</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="key">for</span> <span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">manual</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="key">if</span> <span class="nam">ticker</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">earnings</span><span class="op">:</span> <span class="com"># Manual data fills gaps</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="nam">earnings</span><span class="op">[</span><span class="nam">ticker</span><span class="op">]</span> <span class="op">=</span> <span class="nam">data</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="nam">found</span> <span class="op">=</span> <span class="nam">len</span><span class="op">(</span><span class="nam">earnings</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t"> <span class="nam">total</span> <span class="op">=</span> <span class="nam">len</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Found earnings data for {found}/{total} stocks"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="key">if</span> <span class="nam">earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t"> <span class="key">for</span> <span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">earnings</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="num">1</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"date"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#8226; {ticker}: {data.get('date', '?')}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"> <span class="str">"last_updated"</span><span class="op">:</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t"> <span class="str">"earnings"</span><span class="op">:</span> <span class="nam">earnings</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t"> <span class="nam">save_earnings_cache</span><span class="op">(</span><span class="nam">cache</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"> <span class="key">return</span> <span class="nam">cache</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"><span class="key">def</span> <span class="nam">list_earnings</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"> <span class="str">"""List all upcoming earnings for portfolio."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128194; Portfolio empty"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">,</span> <span class="nam">force</span><span class="op">=</span><span class="nam">args</span><span class="op">.</span><span class="nam">refresh</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="nam">earnings</span> <span class="op">=</span> <span class="nam">cache</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"earnings"</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#10060; No earnings dates found"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="com"># Sort by date</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"> <span class="nam">sorted_earnings</span> <span class="op">=</span> <span class="nam">sorted</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t"> <span class="op">[</span><span class="op">(</span><span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span><span class="op">)</span> <span class="key">for</span> <span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">earnings</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span> <span class="key">if</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"date"</span><span class="op">)</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t"> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="num">1</span><span class="op">]</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#128197; Upcoming Earnings ({len(sorted_earnings)} stocks)\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t"> <span class="nam">today</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">date</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="key">for</span> <span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">sorted_earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t"> <span class="nam">date_str</span> <span class="op">=</span> <span class="nam">data</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t"> <span class="nam">ed</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">strptime</span><span class="op">(</span><span class="nam">date_str</span><span class="op">,</span> <span class="str">"%Y-%m-%d"</span><span class="op">)</span><span class="op">.</span><span class="nam">date</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t"> <span class="nam">days_until</span> <span class="op">=</span> <span class="op">(</span><span class="nam">ed</span> <span class="op">-</span> <span class="nam">today</span><span class="op">)</span><span class="op">.</span><span class="nam">days</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="com"># Emoji based on timing</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t"> <span class="key">if</span> <span class="nam">days_until</span> <span class="op">&lt;</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">"&#9989;"</span> <span class="com"># Past</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t"> <span class="nam">timing</span> <span class="op">=</span> <span class="str">f"{-days_until}d ago"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="key">elif</span> <span class="nam">days_until</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">"&#128308;"</span> <span class="com"># Today!</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"> <span class="nam">timing</span> <span class="op">=</span> <span class="str">"TODAY"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="key">elif</span> <span class="nam">days_until</span> <span class="op">==</span> <span class="num">1</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">"&#128993;"</span> <span class="com"># Tomorrow</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t"> <span class="nam">timing</span> <span class="op">=</span> <span class="str">"TOMORROW"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="key">elif</span> <span class="nam">days_until</span> <span class="op">&lt;=</span> <span class="num">7</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">"&#128992;"</span> <span class="com"># This week</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t"> <span class="nam">timing</span> <span class="op">=</span> <span class="str">f"in {days_until}d"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">"&#9898;"</span> <span class="com"># Later</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t291" href="#t291">291</a></span><span class="t"> <span class="nam">timing</span> <span class="op">=</span> <span class="str">f"in {days_until}d"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t292" href="#t292">292</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t293" href="#t293">293</a></span><span class="t"> <span class="com"># Time of day</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t294" href="#t294">294</a></span><span class="t"> <span class="nam">time_str</span> <span class="op">=</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t295" href="#t295">295</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"time"</span><span class="op">)</span> <span class="op">==</span> <span class="str">"bmo"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t296" href="#t296">296</a></span><span class="t"> <span class="nam">time_str</span> <span class="op">=</span> <span class="str">" (pre-market)"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t297" href="#t297">297</a></span><span class="t"> <span class="key">elif</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"time"</span><span class="op">)</span> <span class="op">==</span> <span class="str">"amc"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t298" href="#t298">298</a></span><span class="t"> <span class="nam">time_str</span> <span class="op">=</span> <span class="str">" (after-close)"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t299" href="#t299">299</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t300" href="#t300">300</a></span><span class="t"> <span class="com"># EPS estimate</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t301" href="#t301">301</a></span><span class="t"> <span class="nam">eps_str</span> <span class="op">=</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t302" href="#t302">302</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t303" href="#t303">303</a></span><span class="t"> <span class="nam">eps_str</span> <span class="op">=</span> <span class="str">f" | Est: ${data['eps_estimate']:.2f}"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t304" href="#t304">304</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t305" href="#t305">305</a></span><span class="t"> <span class="com"># Stock name from portfolio</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t306" href="#t306">306</a></span><span class="t"> <span class="nam">stock_name</span> <span class="op">=</span> <span class="nam">next</span><span class="op">(</span><span class="op">(</span><span class="nam">s</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">portfolio</span> <span class="key">if</span> <span class="nam">s</span><span class="op">[</span><span class="str">"symbol"</span><span class="op">]</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">)</span><span class="op">,</span> <span class="nam">ticker</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t307" href="#t307">307</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t308" href="#t308">308</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"{emoji} {date_str} ({timing}): **{ticker}** &#8212; {stock_name}{time_str}{eps_str}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t309" href="#t309">309</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t310" href="#t310">310</a></span><span class="t"> <span class="key">except</span> <span class="nam">ValueError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t311" href="#t311">311</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9898; {date_str}: {ticker}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t312" href="#t312">312</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t313" href="#t313">313</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t314" href="#t314">314</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t315" href="#t315">315</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t316" href="#t316">316</a></span><span class="t"><span class="key">def</span> <span class="nam">check_earnings</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t317" href="#t317">317</a></span><span class="t"> <span class="str">"""Check earnings for today and this week (briefing format)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t318" href="#t318">318</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t319" href="#t319">319</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t320" href="#t320">320</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t321" href="#t321">321</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t322" href="#t322">322</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">load_earnings_cache</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t323" href="#t323">323</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t324" href="#t324">324</a></span><span class="t"> <span class="com"># Auto-refresh if cache is stale</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t325" href="#t325">325</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">cache</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"last_updated"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t326" href="#t326">326</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">,</span> <span class="nam">force</span><span class="op">=</span><span class="key">False</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t327" href="#t327">327</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t328" href="#t328">328</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t329" href="#t329">329</a></span><span class="t"> <span class="nam">last</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">cache</span><span class="op">[</span><span class="str">"last_updated"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t330" href="#t330">330</a></span><span class="t"> <span class="key">if</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span> <span class="op">-</span> <span class="nam">last</span> <span class="op">></span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">hours</span><span class="op">=</span><span class="num">12</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t331" href="#t331">331</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">,</span> <span class="nam">force</span><span class="op">=</span><span class="key">False</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t332" href="#t332">332</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t333" href="#t333">333</a></span><span class="t"> <span class="nam">cache</span> <span class="op">=</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">,</span> <span class="nam">force</span><span class="op">=</span><span class="key">False</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t334" href="#t334">334</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t335" href="#t335">335</a></span><span class="t"> <span class="nam">earnings</span> <span class="op">=</span> <span class="nam">cache</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"earnings"</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t336" href="#t336">336</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">earnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t337" href="#t337">337</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t338" href="#t338">338</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t339" href="#t339">339</a></span><span class="t"> <span class="nam">today</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">date</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t340" href="#t340">340</a></span><span class="t"> <span class="nam">week_only</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'week'</span><span class="op">,</span> <span class="key">False</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t341" href="#t341">341</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t342" href="#t342">342</a></span><span class="t"> <span class="com"># For weekly mode (Sunday cron), show Mon-Fri of upcoming week</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t343" href="#t343">343</a></span><span class="t"> <span class="com"># Calculation: weekday() returns 0=Mon, 6=Sun. (7 - weekday) % 7 gives days until next Monday.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t344" href="#t344">344</a></span><span class="t"> <span class="com"># Special case: if today is Monday (result=0), we want next Monday (7 days), not today.</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t345" href="#t345">345</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_only</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t346" href="#t346">346</a></span><span class="t"> <span class="nam">days_until_monday</span> <span class="op">=</span> <span class="op">(</span><span class="num">7</span> <span class="op">-</span> <span class="nam">today</span><span class="op">.</span><span class="nam">weekday</span><span class="op">(</span><span class="op">)</span><span class="op">)</span> <span class="op">%</span> <span class="num">7</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t347" href="#t347">347</a></span><span class="t"> <span class="key">if</span> <span class="nam">days_until_monday</span> <span class="op">==</span> <span class="num">0</span> <span class="key">and</span> <span class="nam">today</span><span class="op">.</span><span class="nam">weekday</span><span class="op">(</span><span class="op">)</span> <span class="op">!=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t348" href="#t348">348</a></span><span class="t"> <span class="nam">days_until_monday</span> <span class="op">=</span> <span class="num">7</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t349" href="#t349">349</a></span><span class="t"> <span class="nam">week_start</span> <span class="op">=</span> <span class="nam">today</span> <span class="op">+</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">days</span><span class="op">=</span><span class="nam">days_until_monday</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t350" href="#t350">350</a></span><span class="t"> <span class="nam">week_end</span> <span class="op">=</span> <span class="nam">week_start</span> <span class="op">+</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">days</span><span class="op">=</span><span class="num">4</span><span class="op">)</span> <span class="com"># Mon-Fri</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t351" href="#t351">351</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t352" href="#t352">352</a></span><span class="t"> <span class="nam">week_end</span> <span class="op">=</span> <span class="nam">today</span> <span class="op">+</span> <span class="nam">timedelta</span><span class="op">(</span><span class="nam">days</span><span class="op">=</span><span class="num">7</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t353" href="#t353">353</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t354" href="#t354">354</a></span><span class="t"> <span class="nam">today_list</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t355" href="#t355">355</a></span><span class="t"> <span class="nam">week_list</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t356" href="#t356">356</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t357" href="#t357">357</a></span><span class="t"> <span class="key">for</span> <span class="nam">ticker</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">earnings</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t358" href="#t358">358</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"date"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t359" href="#t359">359</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t360" href="#t360">360</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t361" href="#t361">361</a></span><span class="t"> <span class="nam">ed</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">strptime</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">,</span> <span class="str">"%Y-%m-%d"</span><span class="op">)</span><span class="op">.</span><span class="nam">date</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t362" href="#t362">362</a></span><span class="t"> <span class="nam">stock</span> <span class="op">=</span> <span class="nam">next</span><span class="op">(</span><span class="op">(</span><span class="nam">s</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">portfolio</span> <span class="key">if</span> <span class="nam">s</span><span class="op">[</span><span class="str">"symbol"</span><span class="op">]</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">)</span><span class="op">,</span> <span class="key">None</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t363" href="#t363">363</a></span><span class="t"> <span class="nam">name</span> <span class="op">=</span> <span class="nam">stock</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span> <span class="key">if</span> <span class="nam">stock</span> <span class="key">else</span> <span class="nam">ticker</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t364" href="#t364">364</a></span><span class="t"> <span class="nam">category</span> <span class="op">=</span> <span class="nam">stock</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"category"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span> <span class="key">if</span> <span class="nam">stock</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t365" href="#t365">365</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t366" href="#t366">366</a></span><span class="t"> <span class="nam">entry</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t367" href="#t367">367</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t368" href="#t368">368</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">name</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t369" href="#t369">369</a></span><span class="t"> <span class="str">"date"</span><span class="op">:</span> <span class="nam">ed</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t370" href="#t370">370</a></span><span class="t"> <span class="str">"time"</span><span class="op">:</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"time"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t371" href="#t371">371</a></span><span class="t"> <span class="str">"eps_estimate"</span><span class="op">:</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t372" href="#t372">372</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">category</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t373" href="#t373">373</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t374" href="#t374">374</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t375" href="#t375">375</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_only</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t376" href="#t376">376</a></span><span class="t"> <span class="com"># Weekly mode: only show week range</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t377" href="#t377">377</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_start</span> <span class="op">&lt;=</span> <span class="nam">ed</span> <span class="op">&lt;=</span> <span class="nam">week_end</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t378" href="#t378">378</a></span><span class="t"> <span class="nam">week_list</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">entry</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t379" href="#t379">379</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t380" href="#t380">380</a></span><span class="t"> <span class="com"># Daily mode: today + this week</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t381" href="#t381">381</a></span><span class="t"> <span class="key">if</span> <span class="nam">ed</span> <span class="op">==</span> <span class="nam">today</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t382" href="#t382">382</a></span><span class="t"> <span class="nam">today_list</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">entry</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t383" href="#t383">383</a></span><span class="t"> <span class="key">elif</span> <span class="nam">today</span> <span class="op">&lt;</span> <span class="nam">ed</span> <span class="op">&lt;=</span> <span class="nam">week_end</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t384" href="#t384">384</a></span><span class="t"> <span class="nam">week_list</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">entry</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t385" href="#t385">385</a></span><span class="t"> <span class="key">except</span> <span class="nam">ValueError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t386" href="#t386">386</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t387" href="#t387">387</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t388" href="#t388">388</a></span><span class="t"> <span class="com"># Handle JSON output</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t389" href="#t389">389</a></span><span class="t"> <span class="key">if</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'json'</span><span class="op">,</span> <span class="key">False</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t390" href="#t390">390</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_only</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t391" href="#t391">391</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t392" href="#t392">392</a></span><span class="t"> <span class="str">"week_start"</span><span class="op">:</span> <span class="nam">week_start</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t393" href="#t393">393</a></span><span class="t"> <span class="str">"week_end"</span><span class="op">:</span> <span class="nam">week_end</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t394" href="#t394">394</a></span><span class="t"> <span class="str">"earnings"</span><span class="op">:</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t395" href="#t395">395</a></span><span class="t"> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t396" href="#t396">396</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t397" href="#t397">397</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t398" href="#t398">398</a></span><span class="t"> <span class="str">"date"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t399" href="#t399">399</a></span><span class="t"> <span class="str">"time"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t400" href="#t400">400</a></span><span class="t"> <span class="str">"eps_estimate"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t401" href="#t401">401</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"category"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t402" href="#t402">402</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t403" href="#t403">403</a></span><span class="t"> <span class="key">for</span> <span class="nam">e</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">week_list</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t404" href="#t404">404</a></span><span class="t"> <span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t405" href="#t405">405</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t406" href="#t406">406</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t407" href="#t407">407</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t408" href="#t408">408</a></span><span class="t"> <span class="str">"today"</span><span class="op">:</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t409" href="#t409">409</a></span><span class="t"> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t410" href="#t410">410</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t411" href="#t411">411</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t412" href="#t412">412</a></span><span class="t"> <span class="str">"date"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t413" href="#t413">413</a></span><span class="t"> <span class="str">"time"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t414" href="#t414">414</a></span><span class="t"> <span class="str">"eps_estimate"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t415" href="#t415">415</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"category"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t416" href="#t416">416</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t417" href="#t417">417</a></span><span class="t"> <span class="key">for</span> <span class="nam">e</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">today_list</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"time"</span><span class="op">,</span> <span class="str">"zzz"</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t418" href="#t418">418</a></span><span class="t"> <span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t419" href="#t419">419</a></span><span class="t"> <span class="str">"this_week"</span><span class="op">:</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t420" href="#t420">420</a></span><span class="t"> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t421" href="#t421">421</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"ticker"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t422" href="#t422">422</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t423" href="#t423">423</a></span><span class="t"> <span class="str">"date"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t424" href="#t424">424</a></span><span class="t"> <span class="str">"time"</span><span class="op">:</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t425" href="#t425">425</a></span><span class="t"> <span class="str">"eps_estimate"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t426" href="#t426">426</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"category"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t427" href="#t427">427</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t428" href="#t428">428</a></span><span class="t"> <span class="key">for</span> <span class="nam">e</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">week_list</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t429" href="#t429">429</a></span><span class="t"> <span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t430" href="#t430">430</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t431" href="#t431">431</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">result</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t432" href="#t432">432</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t433" href="#t433">433</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t434" href="#t434">434</a></span><span class="t"> <span class="com"># Translations</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t435" href="#t435">435</a></span><span class="t"> <span class="nam">lang</span> <span class="op">=</span> <span class="nam">getattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'lang'</span><span class="op">,</span> <span class="str">'en'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t436" href="#t436">436</a></span><span class="t"> <span class="key">if</span> <span class="nam">lang</span> <span class="op">==</span> <span class="str">"de"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t437" href="#t437">437</a></span><span class="t"> <span class="nam">labels</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t438" href="#t438">438</a></span><span class="t"> <span class="str">"today"</span><span class="op">:</span> <span class="str">"EARNINGS HEUTE"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t439" href="#t439">439</a></span><span class="t"> <span class="str">"week"</span><span class="op">:</span> <span class="str">"EARNINGS DIESE WOCHE"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t440" href="#t440">440</a></span><span class="t"> <span class="str">"week_preview"</span><span class="op">:</span> <span class="str">"EARNINGS N&#196;CHSTE WOCHE"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t441" href="#t441">441</a></span><span class="t"> <span class="str">"pre"</span><span class="op">:</span> <span class="str">"vor B&#246;rsener&#246;ffnung"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t442" href="#t442">442</a></span><span class="t"> <span class="str">"post"</span><span class="op">:</span> <span class="str">"nach B&#246;rsenschluss"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t443" href="#t443">443</a></span><span class="t"> <span class="str">"pre_short"</span><span class="op">:</span> <span class="str">"vor"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t444" href="#t444">444</a></span><span class="t"> <span class="str">"post_short"</span><span class="op">:</span> <span class="str">"nach"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t445" href="#t445">445</a></span><span class="t"> <span class="str">"est"</span><span class="op">:</span> <span class="str">"Erw"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t446" href="#t446">446</a></span><span class="t"> <span class="str">"none"</span><span class="op">:</span> <span class="str">"Keine Earnings diese Woche"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t447" href="#t447">447</a></span><span class="t"> <span class="str">"none_week"</span><span class="op">:</span> <span class="str">"Keine Earnings n&#228;chste Woche"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t448" href="#t448">448</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t449" href="#t449">449</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t450" href="#t450">450</a></span><span class="t"> <span class="nam">labels</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t451" href="#t451">451</a></span><span class="t"> <span class="str">"today"</span><span class="op">:</span> <span class="str">"EARNINGS TODAY"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t452" href="#t452">452</a></span><span class="t"> <span class="str">"week"</span><span class="op">:</span> <span class="str">"EARNINGS THIS WEEK"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t453" href="#t453">453</a></span><span class="t"> <span class="str">"week_preview"</span><span class="op">:</span> <span class="str">"EARNINGS NEXT WEEK"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t454" href="#t454">454</a></span><span class="t"> <span class="str">"pre"</span><span class="op">:</span> <span class="str">"pre-market"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t455" href="#t455">455</a></span><span class="t"> <span class="str">"post"</span><span class="op">:</span> <span class="str">"after-close"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t456" href="#t456">456</a></span><span class="t"> <span class="str">"pre_short"</span><span class="op">:</span> <span class="str">"pre"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t457" href="#t457">457</a></span><span class="t"> <span class="str">"post_short"</span><span class="op">:</span> <span class="str">"post"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t458" href="#t458">458</a></span><span class="t"> <span class="str">"est"</span><span class="op">:</span> <span class="str">"Est"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t459" href="#t459">459</a></span><span class="t"> <span class="str">"none"</span><span class="op">:</span> <span class="str">"No earnings this week"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t460" href="#t460">460</a></span><span class="t"> <span class="str">"none_week"</span><span class="op">:</span> <span class="str">"No earnings next week"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t461" href="#t461">461</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t462" href="#t462">462</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t463" href="#t463">463</a></span><span class="t"> <span class="com"># Date header</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t464" href="#t464">464</a></span><span class="t"> <span class="nam">date_str</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%b %d, %Y"</span><span class="op">)</span> <span class="key">if</span> <span class="nam">lang</span> <span class="op">==</span> <span class="str">"en"</span> <span class="key">else</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%d. %b %Y"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t465" href="#t465">465</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t466" href="#t466">466</a></span><span class="t"> <span class="com"># Output for briefing</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t467" href="#t467">467</a></span><span class="t"> <span class="nam">output</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t468" href="#t468">468</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t469" href="#t469">469</a></span><span class="t"> <span class="com"># Daily mode: show today's earnings</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t470" href="#t470">470</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">week_only</span> <span class="key">and</span> <span class="nam">today_list</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t471" href="#t471">471</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"&#128197; {labels['today']} &#8212; {date_str}\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t472" href="#t472">472</a></span><span class="t"> <span class="key">for</span> <span class="nam">e</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">today_list</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"time"</span><span class="op">,</span> <span class="str">"zzz"</span><span class="op">)</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t473" href="#t473">473</a></span><span class="t"> <span class="nam">time_str</span> <span class="op">=</span> <span class="str">f" ({labels['pre']})"</span> <span class="key">if</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span> <span class="op">==</span> <span class="str">"bmo"</span> <span class="key">else</span> <span class="str">f" ({labels['post']})"</span> <span class="key">if</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span> <span class="op">==</span> <span class="str">"amc"</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t474" href="#t474">474</a></span><span class="t"> <span class="nam">eps_str</span> <span class="op">=</span> <span class="str">f" &#8212; {labels['est']}: ${e['eps_estimate']:.2f}"</span> <span class="key">if</span> <span class="nam">e</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"eps_estimate"</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t475" href="#t475">475</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"&#8226; {e['ticker']} &#8212; {e['name']}{time_str}{eps_str}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t476" href="#t476">476</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t477" href="#t477">477</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t478" href="#t478">478</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_list</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t479" href="#t479">479</a></span><span class="t"> <span class="com"># Use different header for weekly preview mode</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t480" href="#t480">480</a></span><span class="t"> <span class="nam">week_label</span> <span class="op">=</span> <span class="nam">labels</span><span class="op">[</span><span class="str">'week_preview'</span><span class="op">]</span> <span class="key">if</span> <span class="nam">week_only</span> <span class="key">else</span> <span class="nam">labels</span><span class="op">[</span><span class="str">'week'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t481" href="#t481">481</a></span><span class="t"> <span class="key">if</span> <span class="nam">week_only</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t482" href="#t482">482</a></span><span class="t"> <span class="com"># Show date range for weekly preview</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t483" href="#t483">483</a></span><span class="t"> <span class="nam">week_range</span> <span class="op">=</span> <span class="str">f"{week_start.strftime('%b %d')} - {week_end.strftime('%b %d')}"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t484" href="#t484">484</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"&#128197; {week_label} ({week_range})\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t485" href="#t485">485</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t486" href="#t486">486</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"&#128197; {week_label}\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t487" href="#t487">487</a></span><span class="t"> <span class="key">for</span> <span class="nam">e</span> <span class="key">in</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">week_list</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t488" href="#t488">488</a></span><span class="t"> <span class="nam">day_name</span> <span class="op">=</span> <span class="nam">e</span><span class="op">[</span><span class="str">"date"</span><span class="op">]</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%a %d.%m"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t489" href="#t489">489</a></span><span class="t"> <span class="nam">time_str</span> <span class="op">=</span> <span class="str">f" ({labels['pre_short']})"</span> <span class="key">if</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span> <span class="op">==</span> <span class="str">"bmo"</span> <span class="key">else</span> <span class="str">f" ({labels['post_short']})"</span> <span class="key">if</span> <span class="nam">e</span><span class="op">[</span><span class="str">"time"</span><span class="op">]</span> <span class="op">==</span> <span class="str">"amc"</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t490" href="#t490">490</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"&#8226; {day_name}: {e['ticker']} &#8212; {e['name']}{time_str}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t491" href="#t491">491</a></span><span class="t"> <span class="nam">output</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t492" href="#t492">492</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t493" href="#t493">493</a></span><span class="t"> <span class="key">if</span> <span class="nam">output</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t494" href="#t494">494</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n"</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">output</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t495" href="#t495">495</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t496" href="#t496">496</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">verbose</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t497" href="#t497">497</a></span><span class="t"> <span class="nam">no_earnings_label</span> <span class="op">=</span> <span class="nam">labels</span><span class="op">[</span><span class="str">'none_week'</span><span class="op">]</span> <span class="key">if</span> <span class="nam">week_only</span> <span class="key">else</span> <span class="nam">labels</span><span class="op">[</span><span class="str">'none'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t498" href="#t498">498</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128197; {no_earnings_label}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t499" href="#t499">499</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t500" href="#t500">500</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t501" href="#t501">501</a></span><span class="t"><span class="key">def</span> <span class="nam">get_briefing_section</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t502" href="#t502">502</a></span><span class="t"> <span class="str">"""Get earnings section for daily briefing (called by briefing.py)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t503" href="#t503">503</a></span><span class="t"> <span class="key">from</span> <span class="nam">io</span> <span class="key">import</span> <span class="nam">StringIO</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t504" href="#t504">504</a></span><span class="t"> <span class="key">import</span> <span class="nam">contextlib</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t505" href="#t505">505</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t506" href="#t506">506</a></span><span class="t"> <span class="com"># Capture check output</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t507" href="#t507">507</a></span><span class="t"> <span class="key">class</span> <span class="nam">Args</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t508" href="#t508">508</a></span><span class="t"> <span class="nam">verbose</span> <span class="op">=</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t509" href="#t509">509</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t510" href="#t510">510</a></span><span class="t"> <span class="nam">f</span> <span class="op">=</span> <span class="nam">StringIO</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t511" href="#t511">511</a></span><span class="t"> <span class="key">with</span> <span class="nam">contextlib</span><span class="op">.</span><span class="nam">redirect_stdout</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t512" href="#t512">512</a></span><span class="t"> <span class="nam">check_earnings</span><span class="op">(</span><span class="nam">Args</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t513" href="#t513">513</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t514" href="#t514">514</a></span><span class="t"> <span class="key">return</span> <span class="nam">f</span><span class="op">.</span><span class="nam">getvalue</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t515" href="#t515">515</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t516" href="#t516">516</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t517" href="#t517">517</a></span><span class="t"><span class="key">def</span> <span class="nam">get_earnings_context</span><span class="op">(</span><span class="nam">symbols</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t518" href="#t518">518</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t519" href="#t519">519</a></span><span class="t"><span class="str"> Get recent earnings data (beats/misses) for symbols using OpenBB.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t520" href="#t520">520</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t521" href="#t521">521</a></span><span class="t"><span class="str"> Returns list of dicts with: symbol, eps_actual, eps_estimate, surprise, revenue_actual, revenue_estimate</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t522" href="#t522">522</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t523" href="#t523">523</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">OPENBB_BINARY</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t524" href="#t524">524</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t525" href="#t525">525</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t526" href="#t526">526</a></span><span class="t"> <span class="nam">results</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t527" href="#t527">527</a></span><span class="t"> <span class="key">for</span> <span class="nam">symbol</span> <span class="key">in</span> <span class="nam">symbols</span><span class="op">[</span><span class="op">:</span><span class="num">10</span><span class="op">]</span><span class="op">:</span> <span class="com"># Limit to 10 symbols</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t528" href="#t528">528</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t529" href="#t529">529</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t530" href="#t530">530</a></span><span class="t"> <span class="op">[</span><span class="nam">OPENBB_BINARY</span><span class="op">,</span> <span class="nam">symbol</span><span class="op">,</span> <span class="str">'--earnings'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t531" href="#t531">531</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t532" href="#t532">532</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t533" href="#t533">533</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t534" href="#t534">534</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t535" href="#t535">535</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t536" href="#t536">536</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t537" href="#t537">537</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t538" href="#t538">538</a></span><span class="t"> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">list</span><span class="op">)</span> <span class="key">and</span> <span class="nam">data</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t539" href="#t539">539</a></span><span class="t"> <span class="nam">results</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t540" href="#t540">540</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">symbol</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t541" href="#t541">541</a></span><span class="t"> <span class="str">'earnings'</span><span class="op">:</span> <span class="nam">data</span><span class="op">[</span><span class="num">0</span><span class="op">]</span> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="num">0</span><span class="op">]</span><span class="op">,</span> <span class="nam">dict</span><span class="op">)</span> <span class="key">else</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t542" href="#t542">542</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t543" href="#t543">543</a></span><span class="t"> <span class="key">except</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t544" href="#t544">544</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t545" href="#t545">545</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t546" href="#t546">546</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t547" href="#t547">547</a></span><span class="t"> <span class="key">return</span> <span class="nam">results</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t548" href="#t548">548</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t549" href="#t549">549</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t550" href="#t550">550</a></span><span class="t"><span class="key">def</span> <span class="nam">get_analyst_ratings</span><span class="op">(</span><span class="nam">symbols</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t551" href="#t551">551</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t552" href="#t552">552</a></span><span class="t"><span class="str"> Get analyst upgrades/downgrades for symbols using OpenBB.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t553" href="#t553">553</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t554" href="#t554">554</a></span><span class="t"><span class="str"> Returns list of dicts with: symbol, rating, target_price, firm, direction</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t555" href="#t555">555</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t556" href="#t556">556</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">OPENBB_BINARY</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t557" href="#t557">557</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t558" href="#t558">558</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t559" href="#t559">559</a></span><span class="t"> <span class="nam">results</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t560" href="#t560">560</a></span><span class="t"> <span class="key">for</span> <span class="nam">symbol</span> <span class="key">in</span> <span class="nam">symbols</span><span class="op">[</span><span class="op">:</span><span class="num">10</span><span class="op">]</span><span class="op">:</span> <span class="com"># Limit to 10 symbols</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t561" href="#t561">561</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t562" href="#t562">562</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t563" href="#t563">563</a></span><span class="t"> <span class="op">[</span><span class="nam">OPENBB_BINARY</span><span class="op">,</span> <span class="nam">symbol</span><span class="op">,</span> <span class="str">'--rating'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t564" href="#t564">564</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t565" href="#t565">565</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t566" href="#t566">566</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="num">30</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t567" href="#t567">567</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t568" href="#t568">568</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t569" href="#t569">569</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t570" href="#t570">570</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t571" href="#t571">571</a></span><span class="t"> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">list</span><span class="op">)</span> <span class="key">and</span> <span class="nam">data</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t572" href="#t572">572</a></span><span class="t"> <span class="nam">results</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t573" href="#t573">573</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">symbol</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t574" href="#t574">574</a></span><span class="t"> <span class="str">'rating'</span><span class="op">:</span> <span class="nam">data</span><span class="op">[</span><span class="num">0</span><span class="op">]</span> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="num">0</span><span class="op">]</span><span class="op">,</span> <span class="nam">dict</span><span class="op">)</span> <span class="key">else</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t575" href="#t575">575</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t576" href="#t576">576</a></span><span class="t"> <span class="key">except</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t577" href="#t577">577</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t578" href="#t578">578</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t579" href="#t579">579</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t580" href="#t580">580</a></span><span class="t"> <span class="key">return</span> <span class="nam">results</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t581" href="#t581">581</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t582" href="#t582">582</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t583" href="#t583">583</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t584" href="#t584">584</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">"Earnings Calendar Tracker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t585" href="#t585">585</a></span><span class="t"> <span class="nam">subparsers</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_subparsers</span><span class="op">(</span><span class="nam">dest</span><span class="op">=</span><span class="str">"command"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Commands"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t586" href="#t586">586</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t587" href="#t587">587</a></span><span class="t"> <span class="com"># list command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t588" href="#t588">588</a></span><span class="t"> <span class="nam">list_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"list"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"List all upcoming earnings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t589" href="#t589">589</a></span><span class="t"> <span class="nam">list_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--refresh"</span><span class="op">,</span> <span class="str">"-r"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Force refresh"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t590" href="#t590">590</a></span><span class="t"> <span class="nam">list_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">list_earnings</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t591" href="#t591">591</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t592" href="#t592">592</a></span><span class="t"> <span class="com"># check command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t593" href="#t593">593</a></span><span class="t"> <span class="nam">check_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"check"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Check today/this week"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t594" href="#t594">594</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--verbose"</span><span class="op">,</span> <span class="str">"-v"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t595" href="#t595">595</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--json"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"JSON output"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t596" href="#t596">596</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--lang"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">"en"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Output language (en, de)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t597" href="#t597">597</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--week"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Show full week preview (for weekly cron)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t598" href="#t598">598</a></span><span class="t"> <span class="nam">check_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">check_earnings</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t599" href="#t599">599</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t600" href="#t600">600</a></span><span class="t"> <span class="com"># refresh command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t601" href="#t601">601</a></span><span class="t"> <span class="nam">refresh_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"refresh"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Force refresh all data"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t602" href="#t602">602</a></span><span class="t"> <span class="nam">refresh_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">a</span><span class="op">:</span> <span class="nam">refresh_earnings</span><span class="op">(</span><span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">force</span><span class="op">=</span><span class="key">True</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t603" href="#t603">603</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t604" href="#t604">604</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t605" href="#t605">605</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t606" href="#t606">606</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t607" href="#t607">607</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">print_help</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t608" href="#t608">608</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t609" href="#t609">609</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t610" href="#t610">610</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">func</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t611" href="#t611">611</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t612" href="#t612">612</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t613" href="#t613">613</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">"__main__"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t614" href="#t614">614</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_briefing_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_fetch_news_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,414 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/portfolio.py: 32%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;portfolio.py</b>:
<span class="pc_cov">32%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">183 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">59<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">124<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_fetch_news_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_ranking_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Portfolio Manager - CRUD operations for stock watchlist.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="key">import</span> <span class="nam">csv</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="nam">PORTFOLIO_FILE</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span> <span class="op">/</span> <span class="str">"portfolio.csv"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="nam">REQUIRED_COLUMNS</span> <span class="op">=</span> <span class="op">[</span><span class="str">'symbol'</span><span class="op">,</span> <span class="str">'name'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="nam">DEFAULT_COLUMNS</span> <span class="op">=</span> <span class="op">[</span><span class="str">'symbol'</span><span class="op">,</span> <span class="str">'name'</span><span class="op">,</span> <span class="str">'category'</span><span class="op">,</span> <span class="str">'notes'</span><span class="op">,</span> <span class="str">'type'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t"><span class="key">def</span> <span class="nam">validate_portfolio_csv</span><span class="op">(</span><span class="nam">path</span><span class="op">:</span> <span class="nam">Path</span><span class="op">)</span> <span class="op">-></span> <span class="nam">tuple</span><span class="op">[</span><span class="nam">bool</span><span class="op">,</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"><span class="str"> Validate portfolio CSV file for common issues.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"><span class="str"> Returns:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="str"> Tuple of (is_valid, list of warnings)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"> <span class="nam">warnings</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">path</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span><span class="op">,</span> <span class="nam">warnings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">path</span><span class="op">,</span> <span class="str">'r'</span><span class="op">,</span> <span class="nam">encoding</span><span class="op">=</span><span class="str">'utf-8'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="com"># Check for encoding issues</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"> <span class="nam">content</span> <span class="op">=</span> <span class="nam">f</span><span class="op">.</span><span class="nam">read</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">path</span><span class="op">,</span> <span class="str">'r'</span><span class="op">,</span> <span class="nam">encoding</span><span class="op">=</span><span class="str">'utf-8'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="nam">reader</span> <span class="op">=</span> <span class="nam">csv</span><span class="op">.</span><span class="nam">DictReader</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="com"># Check required columns</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="key">if</span> <span class="nam">reader</span><span class="op">.</span><span class="nam">fieldnames</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="nam">warnings</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">"CSV appears to be empty"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span><span class="op">,</span> <span class="nam">warnings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="nam">missing_cols</span> <span class="op">=</span> <span class="nam">set</span><span class="op">(</span><span class="nam">REQUIRED_COLUMNS</span><span class="op">)</span> <span class="op">-</span> <span class="nam">set</span><span class="op">(</span><span class="nam">reader</span><span class="op">.</span><span class="nam">fieldnames</span> <span class="key">or</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"> <span class="key">if</span> <span class="nam">missing_cols</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="nam">warnings</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"Missing required columns: {', '.join(missing_cols)}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="com"># Check for duplicate symbols</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="nam">symbols</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"> <span class="key">for</span> <span class="nam">row</span> <span class="key">in</span> <span class="nam">reader</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"> <span class="nam">symbol</span> <span class="op">=</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'symbol'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"> <span class="key">if</span> <span class="nam">symbol</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="nam">symbols</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">symbol</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="nam">duplicates</span> <span class="op">=</span> <span class="op">[</span><span class="nam">s</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">set</span><span class="op">(</span><span class="nam">symbols</span><span class="op">)</span> <span class="key">if</span> <span class="nam">symbols</span><span class="op">.</span><span class="nam">count</span><span class="op">(</span><span class="nam">s</span><span class="op">)</span> <span class="op">></span> <span class="num">1</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="key">if</span> <span class="nam">duplicates</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="nam">warnings</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"Duplicate symbols found: {', '.join(duplicates)}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="key">except</span> <span class="nam">UnicodeDecodeError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="nam">warnings</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">"File encoding issue - try saving as UTF-8"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="nam">warnings</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"Error reading portfolio: {e}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span><span class="op">,</span> <span class="nam">warnings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span><span class="op">,</span> <span class="nam">warnings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"><span class="key">def</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="str">"""Load portfolio from CSV with validation."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">PORTFOLIO_FILE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="com"># Validate first</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="nam">is_valid</span><span class="op">,</span> <span class="nam">warnings</span> <span class="op">=</span> <span class="nam">validate_portfolio_csv</span><span class="op">(</span><span class="nam">PORTFOLIO_FILE</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="key">for</span> <span class="nam">warning</span> <span class="key">in</span> <span class="nam">warnings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Portfolio warning: {warning}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">is_valid</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#9888;&#65039; Portfolio has errors - returning empty"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">PORTFOLIO_FILE</span><span class="op">,</span> <span class="str">'r'</span><span class="op">,</span> <span class="nam">encoding</span><span class="op">=</span><span class="str">'utf-8'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t"> <span class="nam">reader</span> <span class="op">=</span> <span class="nam">csv</span><span class="op">.</span><span class="nam">DictReader</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"> <span class="com"># Normalize data</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="nam">seen_symbols</span> <span class="op">=</span> <span class="nam">set</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"> <span class="key">for</span> <span class="nam">row</span> <span class="key">in</span> <span class="nam">reader</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="nam">symbol</span> <span class="op">=</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'symbol'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">symbol</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="com"># Skip duplicates (keep first occurrence)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="key">if</span> <span class="nam">symbol</span> <span class="key">in</span> <span class="nam">seen_symbols</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="nam">seen_symbols</span><span class="op">.</span><span class="nam">add</span><span class="op">(</span><span class="nam">symbol</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="nam">portfolio</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">symbol</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="str">'name'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'name'</span><span class="op">,</span> <span class="nam">symbol</span><span class="op">)</span> <span class="key">or</span> <span class="nam">symbol</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="str">'category'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'category'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span> <span class="key">or</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="str">'notes'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'notes'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span> <span class="key">or</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"> <span class="str">'type'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'type'</span><span class="op">,</span> <span class="str">'Watchlist'</span><span class="op">)</span> <span class="key">or</span> <span class="str">'Watchlist'</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="key">return</span> <span class="nam">portfolio</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Error loading portfolio: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"><span class="key">def</span> <span class="nam">save_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="str">"""Save portfolio to CSV."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"> <span class="nam">PORTFOLIO_FILE</span><span class="op">.</span><span class="nam">write_text</span><span class="op">(</span><span class="str">"symbol,name,category,notes,type\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">PORTFOLIO_FILE</span><span class="op">,</span> <span class="str">'w'</span><span class="op">,</span> <span class="nam">newline</span><span class="op">=</span><span class="str">''</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="nam">writer</span> <span class="op">=</span> <span class="nam">csv</span><span class="op">.</span><span class="nam">DictWriter</span><span class="op">(</span><span class="nam">f</span><span class="op">,</span> <span class="nam">fieldnames</span><span class="op">=</span><span class="op">[</span><span class="str">'symbol'</span><span class="op">,</span> <span class="str">'name'</span><span class="op">,</span> <span class="str">'category'</span><span class="op">,</span> <span class="str">'notes'</span><span class="op">,</span> <span class="str">'type'</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t"> <span class="nam">writer</span><span class="op">.</span><span class="nam">writeheader</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="nam">writer</span><span class="op">.</span><span class="nam">writerows</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"><span class="key">def</span> <span class="nam">list_portfolio</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="str">"""List all stocks in portfolio."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128194; Portfolio is empty. Use 'portfolio add &lt;SYMBOL>' to add stocks."</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#128202; Portfolio ({len(portfolio)} stocks)\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="com"># Group by Type then Category</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="nam">by_type</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="key">for</span> <span class="nam">stock</span> <span class="key">in</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="nam">t</span> <span class="op">=</span> <span class="nam">stock</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'type'</span><span class="op">,</span> <span class="str">'Watchlist'</span><span class="op">)</span> <span class="key">or</span> <span class="str">'Watchlist'</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="key">if</span> <span class="nam">t</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">by_type</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="nam">by_type</span><span class="op">[</span><span class="nam">t</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="nam">by_type</span><span class="op">[</span><span class="nam">t</span><span class="op">]</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">stock</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="key">for</span> <span class="nam">t</span><span class="op">,</span> <span class="nam">type_stocks</span> <span class="key">in</span> <span class="nam">by_type</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"# {t}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">categories</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="key">for</span> <span class="nam">stock</span> <span class="key">in</span> <span class="nam">type_stocks</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="nam">cat</span> <span class="op">=</span> <span class="nam">stock</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'category'</span><span class="op">,</span> <span class="str">'Other'</span><span class="op">)</span> <span class="key">or</span> <span class="str">'Other'</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="key">if</span> <span class="nam">cat</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">categories</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">categories</span><span class="op">[</span><span class="nam">cat</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="nam">categories</span><span class="op">[</span><span class="nam">cat</span><span class="op">]</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">stock</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="key">for</span> <span class="nam">cat</span><span class="op">,</span> <span class="nam">stocks</span> <span class="key">in</span> <span class="nam">categories</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"### {cat}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">stocks</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="nam">notes</span> <span class="op">=</span> <span class="str">f" &#8212; {s['notes']}"</span> <span class="key">if</span> <span class="nam">s</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'notes'</span><span class="op">)</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#8226; {s['symbol']}: {s['name']}{notes}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"><span class="key">def</span> <span class="nam">add_stock</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="str">"""Add a stock to portfolio."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="com"># Check if already exists</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"> <span class="key">if</span> <span class="nam">any</span><span class="op">(</span><span class="nam">s</span><span class="op">[</span><span class="str">'symbol'</span><span class="op">]</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span> <span class="op">==</span> <span class="nam">args</span><span class="op">.</span><span class="nam">symbol</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">portfolio</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; {args.symbol.upper()} already in portfolio"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t"> <span class="nam">new_stock</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">symbol</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="str">'name'</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">name</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">symbol</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="str">'category'</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">category</span> <span class="key">or</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"> <span class="str">'notes'</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">notes</span> <span class="key">or</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="str">'type'</span><span class="op">:</span> <span class="nam">args</span><span class="op">.</span><span class="nam">type</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"> <span class="nam">portfolio</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">new_stock</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="nam">save_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Added {args.symbol.upper()} to portfolio ({args.type})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"><span class="key">def</span> <span class="nam">remove_stock</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t"> <span class="str">"""Remove a stock from portfolio."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="nam">original_len</span> <span class="op">=</span> <span class="nam">len</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="op">[</span><span class="nam">s</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">portfolio</span> <span class="key">if</span> <span class="nam">s</span><span class="op">[</span><span class="str">'symbol'</span><span class="op">]</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span> <span class="op">!=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">symbol</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span> <span class="op">==</span> <span class="nam">original_len</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; {args.symbol.upper()} not found in portfolio"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="nam">save_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Removed {args.symbol.upper()} from portfolio"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t"><span class="key">def</span> <span class="nam">import_csv</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"> <span class="str">"""Import portfolio from external CSV."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="nam">import_path</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">file</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">import_path</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; File not found: {args.file}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="nam">sys</span><span class="op">.</span><span class="nam">exit</span><span class="op">(</span><span class="num">1</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">import_path</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t"> <span class="nam">reader</span> <span class="op">=</span> <span class="nam">csv</span><span class="op">.</span><span class="nam">DictReader</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="nam">imported</span> <span class="op">=</span> <span class="nam">list</span><span class="op">(</span><span class="nam">reader</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="com"># Normalize fields</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="nam">normalized</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="key">for</span> <span class="nam">row</span> <span class="key">in</span> <span class="nam">imported</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="nam">normalized</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'symbol'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'Symbol'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'ticker'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span><span class="op">)</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="str">'name'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'name'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'Name'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'company'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="str">'category'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'category'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'Category'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'sector'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t"> <span class="str">'notes'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'notes'</span><span class="op">,</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'Notes'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t"> <span class="str">'type'</span><span class="op">:</span> <span class="nam">row</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'type'</span><span class="op">,</span> <span class="str">'Watchlist'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t"> <span class="nam">save_portfolio</span><span class="op">(</span><span class="nam">normalized</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Imported {len(normalized)} stocks from {args.file}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"><span class="key">def</span> <span class="nam">create_interactive</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t"> <span class="str">"""Interactive portfolio creation."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128202; Portfolio Creator\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"Enter stocks one per line (format: SYMBOL or SYMBOL,Name,Category)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"Type 'done' when finished.\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t"> <span class="key">while</span> <span class="key">True</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="nam">line</span> <span class="op">=</span> <span class="nam">input</span><span class="op">(</span><span class="str">"> "</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t"> <span class="key">except</span> <span class="op">(</span><span class="nam">EOFError</span><span class="op">,</span> <span class="nam">KeyboardInterrupt</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"> <span class="key">if</span> <span class="nam">line</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span> <span class="op">==</span> <span class="str">'done'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">line</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t"> <span class="key">continue</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"> <span class="nam">parts</span> <span class="op">=</span> <span class="nam">line</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">','</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"> <span class="nam">symbol</span> <span class="op">=</span> <span class="nam">parts</span><span class="op">[</span><span class="num">0</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t"> <span class="nam">name</span> <span class="op">=</span> <span class="nam">parts</span><span class="op">[</span><span class="num">1</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">parts</span><span class="op">)</span> <span class="op">></span> <span class="num">1</span> <span class="key">else</span> <span class="nam">symbol</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"> <span class="nam">category</span> <span class="op">=</span> <span class="nam">parts</span><span class="op">[</span><span class="num">2</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">parts</span><span class="op">)</span> <span class="op">></span> <span class="num">2</span> <span class="key">else</span> <span class="str">''</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t"> <span class="nam">portfolio</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="str">'symbol'</span><span class="op">:</span> <span class="nam">symbol</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="str">'name'</span><span class="op">:</span> <span class="nam">name</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t"> <span class="str">'category'</span><span class="op">:</span> <span class="nam">category</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"> <span class="str">'notes'</span><span class="op">:</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t"> <span class="str">'type'</span><span class="op">:</span> <span class="str">'Watchlist'</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" Added: {symbol}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"> <span class="key">if</span> <span class="nam">portfolio</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"> <span class="nam">save_portfolio</span><span class="op">(</span><span class="nam">portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#9989; Created portfolio with {len(portfolio)} stocks"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#9888;&#65039; No stocks added"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"><span class="key">def</span> <span class="nam">get_symbols</span><span class="op">(</span><span class="nam">args</span><span class="op">=</span><span class="key">None</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"> <span class="str">"""Get list of symbols (for other scripts to use)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="nam">portfolio</span> <span class="op">=</span> <span class="nam">load_portfolio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t"> <span class="nam">symbols</span> <span class="op">=</span> <span class="op">[</span><span class="nam">s</span><span class="op">[</span><span class="str">'symbol'</span><span class="op">]</span> <span class="key">for</span> <span class="nam">s</span> <span class="key">in</span> <span class="nam">portfolio</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span> <span class="key">and</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">symbols</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">','</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">symbols</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">'Portfolio Manager'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">subparsers</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_subparsers</span><span class="op">(</span><span class="nam">dest</span><span class="op">=</span><span class="str">'command'</span><span class="op">,</span> <span class="nam">required</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="com"># List command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t"> <span class="nam">list_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'list'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'List portfolio'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"> <span class="nam">list_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">list_portfolio</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t"> <span class="com"># Add command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t"> <span class="nam">add_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'add'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Add stock'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'symbol'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Stock symbol'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--name'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Company name'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--category'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Category (e.g., Tech, Finance)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--notes'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Notes'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--type'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'Holding'</span><span class="op">,</span> <span class="str">'Watchlist'</span><span class="op">]</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">'Watchlist'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Portfolio type'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t291" href="#t291">291</a></span><span class="t"> <span class="nam">add_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">add_stock</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t292" href="#t292">292</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t293" href="#t293">293</a></span><span class="t"> <span class="com"># Remove command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t294" href="#t294">294</a></span><span class="t"> <span class="nam">remove_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'remove'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Remove stock'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t295" href="#t295">295</a></span><span class="t"> <span class="nam">remove_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'symbol'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Stock symbol'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t296" href="#t296">296</a></span><span class="t"> <span class="nam">remove_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">remove_stock</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t297" href="#t297">297</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t298" href="#t298">298</a></span><span class="t"> <span class="com"># Import command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t299" href="#t299">299</a></span><span class="t"> <span class="nam">import_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'import'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Import from CSV'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t300" href="#t300">300</a></span><span class="t"> <span class="nam">import_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'file'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'CSV file path'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t301" href="#t301">301</a></span><span class="t"> <span class="nam">import_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">import_csv</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t302" href="#t302">302</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t303" href="#t303">303</a></span><span class="t"> <span class="com"># Create command</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t304" href="#t304">304</a></span><span class="t"> <span class="nam">create_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'create'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Interactive creation'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t305" href="#t305">305</a></span><span class="t"> <span class="nam">create_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">create_interactive</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t306" href="#t306">306</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t307" href="#t307">307</a></span><span class="t"> <span class="com"># Symbols command (for other scripts)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t308" href="#t308">308</a></span><span class="t"> <span class="nam">symbols_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'symbols'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Get symbols list'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t309" href="#t309">309</a></span><span class="t"> <span class="nam">symbols_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--json'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Output as JSON'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t310" href="#t310">310</a></span><span class="t"> <span class="nam">symbols_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">get_symbols</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t311" href="#t311">311</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t312" href="#t312">312</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t313" href="#t313">313</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">func</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t314" href="#t314">314</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t315" href="#t315">315</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t316" href="#t316">316</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">'__main__'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t317" href="#t317">317</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_fetch_news_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_ranking_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

422
htmlcov/z_de1a740d5dc98ffd_ranking_py.html generated Normal file
View File

@@ -0,0 +1,422 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/ranking.py: 86%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;ranking.py</b>:
<span class="pc_cov">86%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">147 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">126<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">21<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">9<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_portfolio_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_research_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Deterministic Headline Ranking - Impact-based ranking policy.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">Implements #53: Deterministic impact-based ranking for headline selection.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="str">Scoring Rubric (weights):</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="str">- Market Impact (40%): CB decisions, earnings, sanctions, oil spikes</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="str">- Novelty (20%): New vs recycled news</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="str">- Breadth (20%): Sector-wide vs single-stock</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="str">- Credibility (10%): Source reliability</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="str">- Diversity Bonus (10%): Underrepresented categories</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="str">Output:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="str">- MUST_READ: Top 5 stories</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t"><span class="str">- SCAN: 3-5 additional stories (if quality threshold met)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"><span class="key">import</span> <span class="nam">re</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="key">from</span> <span class="nam">difflib</span> <span class="key">import</span> <span class="nam">SequenceMatcher</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"><span class="com"># Category keywords for classification</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"><span class="nam">CATEGORY_KEYWORDS</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"> <span class="str">"macro"</span><span class="op">:</span> <span class="op">[</span><span class="str">"fed"</span><span class="op">,</span> <span class="str">"ecb"</span><span class="op">,</span> <span class="str">"boj"</span><span class="op">,</span> <span class="str">"central bank"</span><span class="op">,</span> <span class="str">"rate"</span><span class="op">,</span> <span class="str">"inflation"</span><span class="op">,</span> <span class="str">"gdp"</span><span class="op">,</span> <span class="str">"unemployment"</span><span class="op">,</span> <span class="str">"treasury"</span><span class="op">,</span> <span class="str">"yield"</span><span class="op">,</span> <span class="str">"bond"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"> <span class="str">"equities"</span><span class="op">:</span> <span class="op">[</span><span class="str">"earnings"</span><span class="op">,</span> <span class="str">"revenue"</span><span class="op">,</span> <span class="str">"profit"</span><span class="op">,</span> <span class="str">"eps"</span><span class="op">,</span> <span class="str">"guidance"</span><span class="op">,</span> <span class="str">"beat"</span><span class="op">,</span> <span class="str">"miss"</span><span class="op">,</span> <span class="str">"upgrade"</span><span class="op">,</span> <span class="str">"downgrade"</span><span class="op">,</span> <span class="str">"target"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="str">"geopolitics"</span><span class="op">:</span> <span class="op">[</span><span class="str">"sanction"</span><span class="op">,</span> <span class="str">"tariff"</span><span class="op">,</span> <span class="str">"war"</span><span class="op">,</span> <span class="str">"conflict"</span><span class="op">,</span> <span class="str">"embargo"</span><span class="op">,</span> <span class="str">"trump"</span><span class="op">,</span> <span class="str">"china"</span><span class="op">,</span> <span class="str">"russia"</span><span class="op">,</span> <span class="str">"ukraine"</span><span class="op">,</span> <span class="str">"iran"</span><span class="op">,</span> <span class="str">"trade war"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="str">"energy"</span><span class="op">:</span> <span class="op">[</span><span class="str">"oil"</span><span class="op">,</span> <span class="str">"opec"</span><span class="op">,</span> <span class="str">"crude"</span><span class="op">,</span> <span class="str">"gas"</span><span class="op">,</span> <span class="str">"energy"</span><span class="op">,</span> <span class="str">"brent"</span><span class="op">,</span> <span class="str">"wti"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="str">"tech"</span><span class="op">:</span> <span class="op">[</span><span class="str">"ai"</span><span class="op">,</span> <span class="str">"chip"</span><span class="op">,</span> <span class="str">"semiconductor"</span><span class="op">,</span> <span class="str">"nvidia"</span><span class="op">,</span> <span class="str">"apple"</span><span class="op">,</span> <span class="str">"google"</span><span class="op">,</span> <span class="str">"microsoft"</span><span class="op">,</span> <span class="str">"meta"</span><span class="op">,</span> <span class="str">"amazon"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"><span class="com"># Source credibility scores (0-1)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"><span class="nam">SOURCE_CREDIBILITY</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="str">"Wall Street Journal"</span><span class="op">:</span> <span class="num">0.95</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="str">"WSJ"</span><span class="op">:</span> <span class="num">0.95</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="str">"Bloomberg"</span><span class="op">:</span> <span class="num">0.95</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="str">"Reuters"</span><span class="op">:</span> <span class="num">0.90</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="str">"Financial Times"</span><span class="op">:</span> <span class="num">0.90</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="str">"CNBC"</span><span class="op">:</span> <span class="num">0.80</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="str">"Yahoo Finance"</span><span class="op">:</span> <span class="num">0.70</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"> <span class="str">"MarketWatch"</span><span class="op">:</span> <span class="num">0.75</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="str">"Barron's"</span><span class="op">:</span> <span class="num">0.85</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="str">"Seeking Alpha"</span><span class="op">:</span> <span class="num">0.60</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="str">"Tagesschau"</span><span class="op">:</span> <span class="num">0.85</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="str">"Handelsblatt"</span><span class="op">:</span> <span class="num">0.80</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"><span class="com"># Default config</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"><span class="nam">DEFAULT_CONFIG</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="str">"dedupe_threshold"</span><span class="op">:</span> <span class="num">0.7</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="str">"must_read_count"</span><span class="op">:</span> <span class="num">5</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="str">"scan_count"</span><span class="op">:</span> <span class="num">5</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="str">"must_read_min_score"</span><span class="op">:</span> <span class="num">0.4</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="str">"scan_min_score"</span><span class="op">:</span> <span class="num">0.25</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="str">"source_cap"</span><span class="op">:</span> <span class="num">2</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="str">"weights"</span><span class="op">:</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="str">"market_impact"</span><span class="op">:</span> <span class="num">0.40</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="str">"novelty"</span><span class="op">:</span> <span class="num">0.20</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"> <span class="str">"breadth"</span><span class="op">:</span> <span class="num">0.20</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="str">"credibility"</span><span class="op">:</span> <span class="num">0.10</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="str">"diversity"</span><span class="op">:</span> <span class="num">0.10</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t"><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"><span class="key">def</span> <span class="nam">normalize_title</span><span class="op">(</span><span class="nam">title</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="str">"""Normalize title for comparison."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">title</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="key">return</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="nam">cleaned</span> <span class="op">=</span> <span class="nam">re</span><span class="op">.</span><span class="nam">sub</span><span class="op">(</span><span class="str">r"[^a-z0-9\s]"</span><span class="op">,</span> <span class="str">" "</span><span class="op">,</span> <span class="nam">title</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="nam">tokens</span> <span class="op">=</span> <span class="nam">cleaned</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="key">return</span> <span class="str">" "</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">tokens</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"><span class="key">def</span> <span class="nam">title_similarity</span><span class="op">(</span><span class="nam">a</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">b</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="str">"""Calculate title similarity using SequenceMatcher."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">a</span> <span class="key">or</span> <span class="key">not</span> <span class="nam">b</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="key">return</span> <span class="num">0.0</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="key">return</span> <span class="nam">SequenceMatcher</span><span class="op">(</span><span class="key">None</span><span class="op">,</span> <span class="nam">normalize_title</span><span class="op">(</span><span class="nam">a</span><span class="op">)</span><span class="op">,</span> <span class="nam">normalize_title</span><span class="op">(</span><span class="nam">b</span><span class="op">)</span><span class="op">)</span><span class="op">.</span><span class="nam">ratio</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"><span class="key">def</span> <span class="nam">deduplicate_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">threshold</span><span class="op">:</span> <span class="nam">float</span> <span class="op">=</span> <span class="num">0.7</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="str">"""Remove duplicate headlines by title similarity."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">headlines</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="nam">unique</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">headlines</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="nam">title</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"title"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">is_dupe</span> <span class="op">=</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="key">for</span> <span class="nam">existing</span> <span class="key">in</span> <span class="nam">unique</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="key">if</span> <span class="nam">title_similarity</span><span class="op">(</span><span class="nam">title</span><span class="op">,</span> <span class="nam">existing</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"title"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">)</span> <span class="op">></span> <span class="nam">threshold</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"> <span class="nam">is_dupe</span> <span class="op">=</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">is_dupe</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="nam">unique</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">article</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="key">return</span> <span class="nam">unique</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"><span class="key">def</span> <span class="nam">classify_category</span><span class="op">(</span><span class="nam">title</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">description</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="str">"""Classify headline into categories based on keywords."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="nam">text</span> <span class="op">=</span> <span class="str">f"{title} {description}"</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="nam">categories</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="key">for</span> <span class="nam">category</span><span class="op">,</span> <span class="nam">keywords</span> <span class="key">in</span> <span class="nam">CATEGORY_KEYWORDS</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="key">for</span> <span class="nam">keyword</span> <span class="key">in</span> <span class="nam">keywords</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="key">if</span> <span class="nam">keyword</span> <span class="key">in</span> <span class="nam">text</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="nam">categories</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">category</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="key">return</span> <span class="nam">categories</span> <span class="key">if</span> <span class="nam">categories</span> <span class="key">else</span> <span class="op">[</span><span class="str">"general"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"><span class="key">def</span> <span class="nam">score_market_impact</span><span class="op">(</span><span class="nam">title</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">description</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"> <span class="str">"""Score market impact (0-1)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="nam">text</span> <span class="op">=</span> <span class="str">f"{title} {description}"</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="nam">score</span> <span class="op">=</span> <span class="num">0.3</span> <span class="com"># Base score</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="com"># High impact indicators</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="nam">high_impact</span> <span class="op">=</span> <span class="op">[</span><span class="str">"fed"</span><span class="op">,</span> <span class="str">"rate cut"</span><span class="op">,</span> <span class="str">"rate hike"</span><span class="op">,</span> <span class="str">"earnings"</span><span class="op">,</span> <span class="str">"guidance"</span><span class="op">,</span> <span class="str">"sanctions"</span><span class="op">,</span> <span class="str">"war"</span><span class="op">,</span> <span class="str">"oil"</span><span class="op">,</span> <span class="str">"recession"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"> <span class="key">for</span> <span class="nam">term</span> <span class="key">in</span> <span class="nam">high_impact</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="key">if</span> <span class="nam">term</span> <span class="key">in</span> <span class="nam">text</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="nam">score</span> <span class="op">+=</span> <span class="num">0.15</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="com"># Medium impact</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="nam">medium_impact</span> <span class="op">=</span> <span class="op">[</span><span class="str">"profit"</span><span class="op">,</span> <span class="str">"revenue"</span><span class="op">,</span> <span class="str">"gdp"</span><span class="op">,</span> <span class="str">"inflation"</span><span class="op">,</span> <span class="str">"tariff"</span><span class="op">,</span> <span class="str">"merger"</span><span class="op">,</span> <span class="str">"acquisition"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="key">for</span> <span class="nam">term</span> <span class="key">in</span> <span class="nam">medium_impact</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="key">if</span> <span class="nam">term</span> <span class="key">in</span> <span class="nam">text</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="nam">score</span> <span class="op">+=</span> <span class="num">0.1</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="key">return</span> <span class="nam">min</span><span class="op">(</span><span class="nam">score</span><span class="op">,</span> <span class="num">1.0</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"><span class="key">def</span> <span class="nam">score_novelty</span><span class="op">(</span><span class="nam">article</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="str">"""Score novelty based on recency (0-1)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="nam">published_at</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"published_at"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">published_at</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="key">return</span> <span class="num">0.5</span> <span class="com"># Unknown = medium</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">published_at</span><span class="op">,</span> <span class="nam">str</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">pub_time</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">fromisoformat</span><span class="op">(</span><span class="nam">published_at</span><span class="op">.</span><span class="nam">replace</span><span class="op">(</span><span class="str">"Z"</span><span class="op">,</span> <span class="str">"+00:00"</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="nam">pub_time</span> <span class="op">=</span> <span class="nam">published_at</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">hours_old</span> <span class="op">=</span> <span class="op">(</span><span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="nam">pub_time</span><span class="op">.</span><span class="nam">tzinfo</span><span class="op">)</span> <span class="op">-</span> <span class="nam">pub_time</span><span class="op">)</span><span class="op">.</span><span class="nam">total_seconds</span><span class="op">(</span><span class="op">)</span> <span class="op">/</span> <span class="num">3600</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="key">if</span> <span class="nam">hours_old</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="key">return</span> <span class="num">1.0</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="key">elif</span> <span class="nam">hours_old</span> <span class="op">&lt;</span> <span class="num">6</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="key">return</span> <span class="num">0.8</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="key">elif</span> <span class="nam">hours_old</span> <span class="op">&lt;</span> <span class="num">12</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"> <span class="key">return</span> <span class="num">0.6</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="key">elif</span> <span class="nam">hours_old</span> <span class="op">&lt;</span> <span class="num">24</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="key">return</span> <span class="num">0.4</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="key">return</span> <span class="num">0.2</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="key">except</span> <span class="nam">Exception</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="key">return</span> <span class="num">0.5</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"><span class="key">def</span> <span class="nam">score_breadth</span><span class="op">(</span><span class="nam">categories</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="str">"""Score breadth - sector-wide vs single-stock (0-1)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="com"># More categories = broader impact</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t"> <span class="key">if</span> <span class="str">"macro"</span> <span class="key">in</span> <span class="nam">categories</span> <span class="key">or</span> <span class="str">"geopolitics"</span> <span class="key">in</span> <span class="nam">categories</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t"> <span class="key">return</span> <span class="num">0.9</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="key">if</span> <span class="str">"energy"</span> <span class="key">in</span> <span class="nam">categories</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="key">return</span> <span class="num">0.7</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">categories</span><span class="op">)</span> <span class="op">></span> <span class="num">1</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"> <span class="key">return</span> <span class="num">0.6</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="key">return</span> <span class="num">0.4</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"><span class="key">def</span> <span class="nam">score_credibility</span><span class="op">(</span><span class="nam">source</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="str">"""Score source credibility (0-1)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="key">return</span> <span class="nam">SOURCE_CREDIBILITY</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">source</span><span class="op">,</span> <span class="num">0.5</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"><span class="key">def</span> <span class="nam">calculate_score</span><span class="op">(</span><span class="nam">article</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">weights</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">category_counts</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t"> <span class="str">"""Calculate overall score for a headline."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="nam">title</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"title"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t"> <span class="nam">description</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"description"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="nam">source</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"source"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t"> <span class="nam">categories</span> <span class="op">=</span> <span class="nam">classify_category</span><span class="op">(</span><span class="nam">title</span><span class="op">,</span> <span class="nam">description</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="nam">article</span><span class="op">[</span><span class="str">"_categories"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">categories</span> <span class="com"># Store for later use</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="com"># Component scores</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="nam">impact</span> <span class="op">=</span> <span class="nam">score_market_impact</span><span class="op">(</span><span class="nam">title</span><span class="op">,</span> <span class="nam">description</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t"> <span class="nam">novelty</span> <span class="op">=</span> <span class="nam">score_novelty</span><span class="op">(</span><span class="nam">article</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="nam">breadth</span> <span class="op">=</span> <span class="nam">score_breadth</span><span class="op">(</span><span class="nam">categories</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t"> <span class="nam">credibility</span> <span class="op">=</span> <span class="nam">score_credibility</span><span class="op">(</span><span class="nam">source</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t"> <span class="com"># Diversity bonus - boost underrepresented categories</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t"> <span class="nam">diversity</span> <span class="op">=</span> <span class="num">0.0</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"> <span class="key">for</span> <span class="nam">cat</span> <span class="key">in</span> <span class="nam">categories</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="key">if</span> <span class="nam">category_counts</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">cat</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">&lt;</span> <span class="num">1</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"> <span class="nam">diversity</span> <span class="op">=</span> <span class="num">0.5</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="key">elif</span> <span class="nam">category_counts</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">cat</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">&lt;</span> <span class="num">2</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="nam">diversity</span> <span class="op">=</span> <span class="num">0.3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t"> <span class="com"># Weighted sum</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t"> <span class="nam">score</span> <span class="op">=</span> <span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="nam">impact</span> <span class="op">*</span> <span class="nam">weights</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"market_impact"</span><span class="op">,</span> <span class="num">0.4</span><span class="op">)</span> <span class="op">+</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="nam">novelty</span> <span class="op">*</span> <span class="nam">weights</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"novelty"</span><span class="op">,</span> <span class="num">0.2</span><span class="op">)</span> <span class="op">+</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="nam">breadth</span> <span class="op">*</span> <span class="nam">weights</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"breadth"</span><span class="op">,</span> <span class="num">0.2</span><span class="op">)</span> <span class="op">+</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="nam">credibility</span> <span class="op">*</span> <span class="nam">weights</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"credibility"</span><span class="op">,</span> <span class="num">0.1</span><span class="op">)</span> <span class="op">+</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="nam">diversity</span> <span class="op">*</span> <span class="nam">weights</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"diversity"</span><span class="op">,</span> <span class="num">0.1</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="nam">article</span><span class="op">[</span><span class="str">"_score"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">round</span><span class="op">(</span><span class="nam">score</span><span class="op">,</span> <span class="num">3</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="nam">article</span><span class="op">[</span><span class="str">"_impact"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">round</span><span class="op">(</span><span class="nam">impact</span><span class="op">,</span> <span class="num">3</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t"> <span class="nam">article</span><span class="op">[</span><span class="str">"_novelty"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">round</span><span class="op">(</span><span class="nam">novelty</span><span class="op">,</span> <span class="num">3</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="key">return</span> <span class="nam">score</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t"><span class="key">def</span> <span class="nam">apply_source_cap</span><span class="op">(</span><span class="nam">ranked</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">cap</span><span class="op">:</span> <span class="nam">int</span> <span class="op">=</span> <span class="num">2</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t"> <span class="str">"""Apply source cap - max N items per outlet."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t"> <span class="nam">source_counts</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">ranked</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="nam">source</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"source"</span><span class="op">,</span> <span class="str">"Unknown"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="key">if</span> <span class="nam">source_counts</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">source</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">&lt;</span> <span class="nam">cap</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t"> <span class="nam">result</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">article</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="nam">source_counts</span><span class="op">[</span><span class="nam">source</span><span class="op">]</span> <span class="op">=</span> <span class="nam">source_counts</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">source</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">+</span> <span class="num">1</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t"><span class="key">def</span> <span class="nam">ensure_diversity</span><span class="op">(</span><span class="nam">selected</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">candidates</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">required</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="str">"""Ensure at least one headline from required categories if available."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">list</span><span class="op">(</span><span class="nam">selected</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"> <span class="nam">covered</span> <span class="op">=</span> <span class="nam">set</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">result</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="key">for</span> <span class="nam">cat</span> <span class="key">in</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_categories"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t"> <span class="nam">covered</span><span class="op">.</span><span class="nam">add</span><span class="op">(</span><span class="nam">cat</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"> <span class="key">for</span> <span class="nam">req_cat</span> <span class="key">in</span> <span class="nam">required</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"> <span class="key">if</span> <span class="nam">req_cat</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">covered</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t"> <span class="com"># Find candidate from this category</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"> <span class="key">for</span> <span class="nam">candidate</span> <span class="key">in</span> <span class="nam">candidates</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"> <span class="key">if</span> <span class="nam">candidate</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">result</span> <span class="key">and</span> <span class="nam">req_cat</span> <span class="key">in</span> <span class="nam">candidate</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_categories"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t"> <span class="nam">result</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">candidate</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="nam">covered</span><span class="op">.</span><span class="nam">add</span><span class="op">(</span><span class="nam">req_cat</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t"><span class="key">def</span> <span class="nam">rank_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span><span class="op">,</span> <span class="nam">config</span><span class="op">:</span> <span class="nam">dict</span> <span class="op">|</span> <span class="key">None</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t"> <span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"><span class="str"> Rank headlines deterministically.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"><span class="str"> </span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"><span class="str"> Args:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"><span class="str"> headlines: List of headline dicts with title, source, description, etc.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"><span class="str"> config: Optional config overrides</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t"><span class="str"> </span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t"><span class="str"> Returns:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"><span class="str"> {"must_read": [...], "scan": [...]}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="nam">cfg</span> <span class="op">=</span> <span class="op">{</span><span class="op">**</span><span class="nam">DEFAULT_CONFIG</span><span class="op">,</span> <span class="op">**</span><span class="op">(</span><span class="nam">config</span> <span class="key">or</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t"> <span class="nam">weights</span> <span class="op">=</span> <span class="nam">cfg</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"weights"</span><span class="op">,</span> <span class="nam">DEFAULT_CONFIG</span><span class="op">[</span><span class="str">"weights"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">headlines</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="str">"must_read"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span> <span class="str">"scan"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="com"># Step 1: Deduplicate</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t"> <span class="nam">unique</span> <span class="op">=</span> <span class="nam">deduplicate_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">,</span> <span class="nam">cfg</span><span class="op">[</span><span class="str">"dedupe_threshold"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t"> <span class="com"># Step 2: Score all headlines</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="nam">category_counts</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">unique</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">calculate_score</span><span class="op">(</span><span class="nam">article</span><span class="op">,</span> <span class="nam">weights</span><span class="op">,</span> <span class="nam">category_counts</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t"> <span class="key">for</span> <span class="nam">cat</span> <span class="key">in</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_categories"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="nam">category_counts</span><span class="op">[</span><span class="nam">cat</span><span class="op">]</span> <span class="op">=</span> <span class="nam">category_counts</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="nam">cat</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">+</span> <span class="num">1</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"> <span class="com"># Step 3: Sort by score</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="nam">ranked</span> <span class="op">=</span> <span class="nam">sorted</span><span class="op">(</span><span class="nam">unique</span><span class="op">,</span> <span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_score"</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span><span class="op">,</span> <span class="nam">reverse</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t"> <span class="com"># Step 4: Apply source cap</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="nam">capped</span> <span class="op">=</span> <span class="nam">apply_source_cap</span><span class="op">(</span><span class="nam">ranked</span><span class="op">,</span> <span class="nam">cfg</span><span class="op">[</span><span class="str">"source_cap"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t"> <span class="com"># Step 5: Select must_read with diversity quota</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"> <span class="com"># Leave room for diversity additions by taking count-1 initially</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t"> <span class="nam">must_read_candidates</span> <span class="op">=</span> <span class="op">[</span><span class="nam">a</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">capped</span> <span class="key">if</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_score"</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">>=</span> <span class="nam">cfg</span><span class="op">[</span><span class="str">"must_read_min_score"</span><span class="op">]</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t291" href="#t291">291</a></span><span class="t"> <span class="nam">must_read_count</span> <span class="op">=</span> <span class="nam">cfg</span><span class="op">[</span><span class="str">"must_read_count"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t292" href="#t292">292</a></span><span class="t"> <span class="nam">must_read</span> <span class="op">=</span> <span class="nam">must_read_candidates</span><span class="op">[</span><span class="op">:</span><span class="nam">max</span><span class="op">(</span><span class="num">1</span><span class="op">,</span> <span class="nam">must_read_count</span> <span class="op">-</span> <span class="num">2</span><span class="op">)</span><span class="op">]</span> <span class="com"># Reserve 2 slots for diversity</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t293" href="#t293">293</a></span><span class="t"> <span class="nam">must_read</span> <span class="op">=</span> <span class="nam">ensure_diversity</span><span class="op">(</span><span class="nam">must_read</span><span class="op">,</span> <span class="nam">capped</span><span class="op">,</span> <span class="op">[</span><span class="str">"macro"</span><span class="op">,</span> <span class="str">"equities"</span><span class="op">,</span> <span class="str">"geopolitics"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t294" href="#t294">294</a></span><span class="t"> <span class="nam">must_read</span> <span class="op">=</span> <span class="nam">must_read</span><span class="op">[</span><span class="op">:</span><span class="nam">must_read_count</span><span class="op">]</span> <span class="com"># Final trim to exact count</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t295" href="#t295">295</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t296" href="#t296">296</a></span><span class="t"> <span class="com"># Step 6: Select scan (additional items)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t297" href="#t297">297</a></span><span class="t"> <span class="nam">scan_candidates</span> <span class="op">=</span> <span class="op">[</span><span class="nam">a</span> <span class="key">for</span> <span class="nam">a</span> <span class="key">in</span> <span class="nam">capped</span> <span class="key">if</span> <span class="nam">a</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">must_read</span> <span class="key">and</span> <span class="nam">a</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"_score"</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span> <span class="op">>=</span> <span class="nam">cfg</span><span class="op">[</span><span class="str">"scan_min_score"</span><span class="op">]</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t298" href="#t298">298</a></span><span class="t"> <span class="nam">scan</span> <span class="op">=</span> <span class="nam">scan_candidates</span><span class="op">[</span><span class="op">:</span><span class="nam">cfg</span><span class="op">[</span><span class="str">"scan_count"</span><span class="op">]</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t299" href="#t299">299</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t300" href="#t300">300</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t301" href="#t301">301</a></span><span class="t"> <span class="str">"must_read"</span><span class="op">:</span> <span class="nam">must_read</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t302" href="#t302">302</a></span><span class="t"> <span class="str">"scan"</span><span class="op">:</span> <span class="nam">scan</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t303" href="#t303">303</a></span><span class="t"> <span class="str">"total_processed"</span><span class="op">:</span> <span class="nam">len</span><span class="op">(</span><span class="nam">headlines</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t304" href="#t304">304</a></span><span class="t"> <span class="str">"after_dedupe"</span><span class="op">:</span> <span class="nam">len</span><span class="op">(</span><span class="nam">unique</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t305" href="#t305">305</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t306" href="#t306">306</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t307" href="#t307">307</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t308" href="#t308">308</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">"__main__"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t309" href="#t309">309</a></span><span class="t"> <span class="com"># Test with sample data</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t310" href="#t310">310</a></span><span class="t"> <span class="nam">test_headlines</span> <span class="op">=</span> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t311" href="#t311">311</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"Fed signals rate cut in March"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"WSJ"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"Federal Reserve hints at policy shift"</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t312" href="#t312">312</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"Apple earnings beat expectations"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"CNBC"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"Revenue up 15%"</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t313" href="#t313">313</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"Oil prices surge on OPEC cuts"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"Reuters"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"Brent crude hits $90"</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t314" href="#t314">314</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"China-US trade tensions escalate"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"Bloomberg"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"New tariffs announced"</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t315" href="#t315">315</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"Tech stocks rally on AI optimism"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"Yahoo Finance"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"Nvidia leads gains"</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t316" href="#t316">316</a></span><span class="t"> <span class="op">{</span><span class="str">"title"</span><span class="op">:</span> <span class="str">"Fed hints at rate reduction"</span><span class="op">,</span> <span class="str">"source"</span><span class="op">:</span> <span class="str">"MarketWatch"</span><span class="op">,</span> <span class="str">"description"</span><span class="op">:</span> <span class="str">"Same story as WSJ"</span><span class="op">}</span><span class="op">,</span> <span class="com"># Dupe</span>&nbsp;</span><span class="r"></span></p>
<p class="exc exc2 show_exc"><span class="n"><a id="t317" href="#t317">317</a></span><span class="t"> <span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t318" href="#t318">318</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t319" href="#t319">319</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">rank_headlines</span><span class="op">(</span><span class="nam">test_headlines</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t320" href="#t320">320</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"MUST_READ:"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t321" href="#t321">321</a></span><span class="t"> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">result</span><span class="op">[</span><span class="str">"must_read"</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t322" href="#t322">322</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" [{h['_score']:.2f}] {h['title']} ({h['source']})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t323" href="#t323">323</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\nSCAN:"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t324" href="#t324">324</a></span><span class="t"> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">result</span><span class="op">[</span><span class="str">"scan"</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t325" href="#t325">325</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" [{h['_score']:.2f}] {h['title']} ({h['source']})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_portfolio_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_research_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,380 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/research.py: 65%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;research.py</b>:
<span class="pc_cov">65%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">130 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">85<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">45<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_ranking_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_setup_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Research Module - Deep research using Gemini CLI.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="str">Crawls articles, finds correlations, researches companies.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">Outputs research_report.md for later analysis.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="key">import</span> <span class="nam">os</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="key">import</span> <span class="nam">shutil</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="key">import</span> <span class="nam">subprocess</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="key">from</span> <span class="nam">utils</span> <span class="key">import</span> <span class="nam">ensure_venv</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"><span class="key">from</span> <span class="nam">fetch_news</span> <span class="key">import</span> <span class="nam">PortfolioError</span><span class="op">,</span> <span class="nam">get_market_news</span><span class="op">,</span> <span class="nam">get_portfolio_news</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="nam">SCRIPT_DIR</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="nam">CONFIG_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"><span class="nam">OUTPUT_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"research"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"><span class="nam">ensure_venv</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"><span class="key">def</span> <span class="nam">format_market_data</span><span class="op">(</span><span class="nam">market_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="str">"""Format market data for research prompt."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"> <span class="nam">lines</span> <span class="op">=</span> <span class="op">[</span><span class="str">"## Market Data\n"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"> <span class="key">for</span> <span class="nam">region</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">market_data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'markets'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"### {data['name']}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="key">for</span> <span class="nam">symbol</span><span class="op">,</span> <span class="nam">idx</span> <span class="key">in</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'indices'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="key">if</span> <span class="str">'data'</span> <span class="key">in</span> <span class="nam">idx</span> <span class="key">and</span> <span class="nam">idx</span><span class="op">[</span><span class="str">'data'</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="nam">price</span> <span class="op">=</span> <span class="nam">idx</span><span class="op">[</span><span class="str">'data'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'price'</span><span class="op">,</span> <span class="str">'N/A'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="nam">change_pct</span> <span class="op">=</span> <span class="nam">idx</span><span class="op">[</span><span class="str">'data'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'change_percent'</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="nam">emoji</span> <span class="op">=</span> <span class="str">'&#128200;'</span> <span class="key">if</span> <span class="nam">change_pct</span> <span class="op">>=</span> <span class="num">0</span> <span class="key">else</span> <span class="str">'&#128201;'</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"- {idx['name']}: {price} ({change_pct:+.2f}%) {emoji}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="key">return</span> <span class="str">'\n'</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">lines</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"><span class="key">def</span> <span class="nam">format_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">:</span> <span class="nam">list</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"> <span class="str">"""Format headlines for research prompt."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"> <span class="nam">lines</span> <span class="op">=</span> <span class="op">[</span><span class="str">"## Current Headlines\n"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">headlines</span><span class="op">[</span><span class="op">:</span><span class="num">20</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="nam">source</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'source'</span><span class="op">,</span> <span class="str">'Unknown'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="nam">title</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'title'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="nam">link</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'link'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"- [{source}] {title}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="key">if</span> <span class="nam">link</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f" URL: {link}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="key">return</span> <span class="str">'\n'</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">lines</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"><span class="key">def</span> <span class="nam">format_portfolio_news</span><span class="op">(</span><span class="nam">portfolio_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="str">"""Format portfolio news for research prompt."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="nam">lines</span> <span class="op">=</span> <span class="op">[</span><span class="str">"## Portfolio Analysis\n"</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="key">for</span> <span class="nam">symbol</span><span class="op">,</span> <span class="nam">data</span> <span class="key">in</span> <span class="nam">portfolio_data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'stocks'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="nam">quote</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'quote'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">price</span> <span class="op">=</span> <span class="nam">quote</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'price'</span><span class="op">,</span> <span class="str">'N/A'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="nam">change_pct</span> <span class="op">=</span> <span class="nam">quote</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'change_percent'</span><span class="op">,</span> <span class="num">0</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"### {symbol} (${price}, {change_pct:+.2f}%)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="key">for</span> <span class="nam">article</span> <span class="key">in</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'articles'</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span><span class="op">[</span><span class="op">:</span><span class="num">5</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="nam">title</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'title'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="nam">link</span> <span class="op">=</span> <span class="nam">article</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'link'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f"- {title}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="key">if</span> <span class="nam">link</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">f" URL: {link}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="nam">lines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="key">return</span> <span class="str">'\n'</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">lines</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"><span class="key">def</span> <span class="nam">gemini_available</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="key">return</span> <span class="nam">shutil</span><span class="op">.</span><span class="nam">which</span><span class="op">(</span><span class="str">'gemini'</span><span class="op">)</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"><span class="key">def</span> <span class="nam">research_with_gemini</span><span class="op">(</span><span class="nam">content</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">focus_areas</span><span class="op">:</span> <span class="nam">list</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="str">"""Perform deep research using Gemini CLI.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"><span class="str"> </span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"><span class="str"> Args:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"><span class="str"> content: Combined market/headlines/portfolio content</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"><span class="str"> focus_areas: Optional list of focus areas (e.g., ['earnings', 'macro', 'sectors'])</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"><span class="str"> </span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"><span class="str"> Returns:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"><span class="str"> Research report text</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"><span class="str"> """</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="nam">focus_prompt</span> <span class="op">=</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="key">if</span> <span class="nam">focus_areas</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="nam">focus_prompt</span> <span class="op">=</span> <span class="str">f"""</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"><span class="str">Focus areas for the research:</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"><span class="str">{', '.join(f'- {area}' for area in focus_areas)}</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"><span class="str">Go deep on each area.</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t"> <span class="nam">prompt</span> <span class="op">=</span> <span class="str">f"""You are an experienced investment research analyst.</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"><span class="str">Your task is to deliver deep research on current market developments.</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"><span class="str">{focus_prompt}</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"><span class="str">Please analyze the following market data:</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"><span class="str">{content}</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"><span class="str">## Analysis Requirements:</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"><span class="str">1. **Macro Trends**: What is driving the market today? Which economic data/decisions matter?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"><span class="str">2. **Sector Analysis**: Which sectors are performing best/worst? Why?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"><span class="str">3. **Company News**: Relevant earnings, M&amp;A, product launches?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"><span class="str">4. **Risks**: What downside risks should be noted?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"><span class="str">5. **Opportunities**: Which positive developments offer opportunities?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"><span class="str">6. **Correlations**: Are there links between different news items/asset classes?</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"><span class="str">7. **Trade Ideas**: Concrete setups based on the analysis (not financial advice!)</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"><span class="str">8. **Sources**: Original links for further research</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"><span class="str">Be analytical, objective, and opinionated where appropriate.</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"><span class="str">Deliver a substantial report (500-800 words).</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="op">[</span><span class="str">'gemini'</span><span class="op">,</span> <span class="nam">prompt</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="num">120</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">==</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="key">return</span> <span class="str">f"&#9888;&#65039; Gemini research error: {result.stderr}"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="key">except</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">TimeoutExpired</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="key">return</span> <span class="str">"&#9888;&#65039; Gemini research timeout"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="key">except</span> <span class="nam">FileNotFoundError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="key">return</span> <span class="str">"&#9888;&#65039; Gemini CLI not found. Install: brew install gemini-cli"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"><span class="key">def</span> <span class="nam">format_raw_data_report</span><span class="op">(</span><span class="nam">market_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">portfolio_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="nam">parts</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="key">if</span> <span class="nam">market_data</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="nam">parts</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">format_market_data</span><span class="op">(</span><span class="nam">market_data</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="key">if</span> <span class="nam">market_data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'headlines'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">parts</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">format_headlines</span><span class="op">(</span><span class="nam">market_data</span><span class="op">[</span><span class="str">'headlines'</span><span class="op">]</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t"> <span class="key">if</span> <span class="nam">portfolio_data</span> <span class="key">and</span> <span class="str">'error'</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">portfolio_data</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="nam">parts</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">format_portfolio_news</span><span class="op">(</span><span class="nam">portfolio_data</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"> <span class="key">return</span> <span class="str">'\n\n'</span><span class="op">.</span><span class="nam">join</span><span class="op">(</span><span class="nam">parts</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t"><span class="key">def</span> <span class="nam">generate_research_content</span><span class="op">(</span><span class="nam">market_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">portfolio_data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">focus_areas</span><span class="op">:</span> <span class="nam">list</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t"> <span class="nam">raw_report</span> <span class="op">=</span> <span class="nam">format_raw_data_report</span><span class="op">(</span><span class="nam">market_data</span><span class="op">,</span> <span class="nam">portfolio_data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">raw_report</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="str">'report'</span><span class="op">:</span> <span class="str">''</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"> <span class="str">'source'</span><span class="op">:</span> <span class="str">'none'</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"> <span class="key">if</span> <span class="nam">gemini_available</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"> <span class="str">'report'</span><span class="op">:</span> <span class="nam">research_with_gemini</span><span class="op">(</span><span class="nam">raw_report</span><span class="op">,</span> <span class="nam">focus_areas</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="str">'source'</span><span class="op">:</span> <span class="str">'gemini'</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t"> <span class="str">'report'</span><span class="op">:</span> <span class="nam">raw_report</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"> <span class="str">'source'</span><span class="op">:</span> <span class="str">'raw'</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"><span class="key">def</span> <span class="nam">generate_research_report</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t"> <span class="str">"""Generate full research report."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="nam">OUTPUT_DIR</span><span class="op">.</span><span class="nam">mkdir</span><span class="op">(</span><span class="nam">parents</span><span class="op">=</span><span class="key">True</span><span class="op">,</span> <span class="nam">exist_ok</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="nam">config_path</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"config.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">config_path</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#9888;&#65039; No config found. Run 'finance-news wizard' first."</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="nam">sys</span><span class="op">.</span><span class="nam">exit</span><span class="op">(</span><span class="num">1</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t"> <span class="com"># Fetch fresh data</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128225; Fetching market data..."</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"> <span class="com"># Get market overview</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="nam">market_data</span> <span class="op">=</span> <span class="nam">get_market_news</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">limit</span> <span class="key">if</span> <span class="nam">hasattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'limit'</span><span class="op">)</span> <span class="key">else</span> <span class="num">5</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t"> <span class="nam">regions</span><span class="op">=</span><span class="nam">args</span><span class="op">.</span><span class="nam">regions</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">','</span><span class="op">)</span> <span class="key">if</span> <span class="nam">hasattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'regions'</span><span class="op">)</span> <span class="key">else</span> <span class="op">[</span><span class="str">"us"</span><span class="op">,</span> <span class="str">"europe"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">max_indices_per_region</span><span class="op">=</span><span class="num">2</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t"> <span class="com"># Get portfolio news</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="nam">portfolio_data</span> <span class="op">=</span> <span class="nam">get_portfolio_news</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">limit</span> <span class="key">if</span> <span class="nam">hasattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'limit'</span><span class="op">)</span> <span class="key">else</span> <span class="num">5</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">max_stocks</span> <span class="key">if</span> <span class="nam">hasattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'max_stocks'</span><span class="op">)</span> <span class="key">else</span> <span class="num">10</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="key">except</span> <span class="nam">PortfolioError</span> <span class="key">as</span> <span class="nam">exc</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Skipping portfolio: {exc}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="nam">portfolio_data</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="com"># Build report</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t"> <span class="nam">focus_areas</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t"> <span class="key">if</span> <span class="nam">hasattr</span><span class="op">(</span><span class="nam">args</span><span class="op">,</span> <span class="str">'focus'</span><span class="op">)</span> <span class="key">and</span> <span class="nam">args</span><span class="op">.</span><span class="nam">focus</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="nam">focus_areas</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">focus</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">','</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t"> <span class="nam">research_result</span> <span class="op">=</span> <span class="nam">generate_research_content</span><span class="op">(</span><span class="nam">market_data</span><span class="op">,</span> <span class="nam">portfolio_data</span><span class="op">,</span> <span class="nam">focus_areas</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t"> <span class="nam">research_report</span> <span class="op">=</span> <span class="nam">research_result</span><span class="op">[</span><span class="str">'report'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t"> <span class="nam">source</span> <span class="op">=</span> <span class="nam">research_result</span><span class="op">[</span><span class="str">'source'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">research_report</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#9888;&#65039; No data available for research"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="key">if</span> <span class="nam">source</span> <span class="op">==</span> <span class="str">'gemini'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128300; Running deep research with Gemini..."</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#129534; Gemini not available; using raw data report"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t"> <span class="com"># Add metadata header</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="nam">timestamp</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">isoformat</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t"> <span class="nam">date_str</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d %H:%M"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t"> <span class="nam">full_report</span> <span class="op">=</span> <span class="str">f"""# Market Research Report</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"><span class="str">**Generiert:** {date_str}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"><span class="str">**Quelle:** Finance News Skill</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"><span class="str">---</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t"><span class="str">{research_report}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"><span class="str">---</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"><span class="str">*This report was generated automatically. Not financial advice.*</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="com"># Save to file</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="nam">output_file</span> <span class="op">=</span> <span class="nam">OUTPUT_DIR</span> <span class="op">/</span> <span class="str">f"research_{datetime.now().strftime('%Y-%m-%d')}.md"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">output_file</span><span class="op">,</span> <span class="str">'w'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"> <span class="nam">f</span><span class="op">.</span><span class="nam">write</span><span class="op">(</span><span class="nam">full_report</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Research report saved to: {output_file}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t"> <span class="com"># Also output to stdout</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">json</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"> <span class="str">'report'</span><span class="op">:</span> <span class="nam">research_report</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="str">'saved_to'</span><span class="op">:</span> <span class="nam">str</span><span class="op">(</span><span class="nam">output_file</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"> <span class="str">'timestamp'</span><span class="op">:</span> <span class="nam">timestamp</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t"> <span class="op">}</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n"</span> <span class="op">+</span> <span class="str">"="</span><span class="op">*</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"RESEARCH REPORT"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"="</span><span class="op">*</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">research_report</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">'Deep Market Research'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--limit'</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">int</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="num">5</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Max headlines per source'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--regions'</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">'us,europe'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Comma-separated regions'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--max-stocks'</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">int</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="num">10</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Max portfolio stocks'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--focus'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Focus areas (comma-separated)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--json'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Output as JSON'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t"> <span class="nam">generate_research_report</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">'__main__'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_ranking_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_setup_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

387
htmlcov/z_de1a740d5dc98ffd_setup_py.html generated Normal file
View File

@@ -0,0 +1,387 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/setup.py: 26%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;setup.py</b>:
<span class="pc_cov">26%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">168 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">44<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">124<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_research_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_stocks_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">Finance News Skill - Interactive Setup</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="str">Configures RSS feeds, WhatsApp channels, and cron jobs.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="key">import</span> <span class="nam">subprocess</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="nam">SCRIPT_DIR</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="nam">CONFIG_DIR</span> <span class="op">=</span> <span class="nam">SCRIPT_DIR</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="nam">SOURCES_FILE</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"config.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"><span class="key">def</span> <span class="nam">load_sources</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"> <span class="str">"""Load current sources configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"> <span class="key">if</span> <span class="nam">SOURCES_FILE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">SOURCES_FILE</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"> <span class="key">return</span> <span class="nam">json</span><span class="op">.</span><span class="nam">load</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"> <span class="key">return</span> <span class="nam">get_default_sources</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"><span class="key">def</span> <span class="nam">save_sources</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"> <span class="str">"""Save sources configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="nam">CONFIG_DIR</span><span class="op">.</span><span class="nam">mkdir</span><span class="op">(</span><span class="nam">parents</span><span class="op">=</span><span class="key">True</span><span class="op">,</span> <span class="nam">exist_ok</span><span class="op">=</span><span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">SOURCES_FILE</span><span class="op">,</span> <span class="str">'w'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="nam">json</span><span class="op">.</span><span class="nam">dump</span><span class="op">(</span><span class="nam">sources</span><span class="op">,</span> <span class="nam">f</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Configuration saved to {SOURCES_FILE}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"><span class="key">def</span> <span class="nam">get_default_sources</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="str">"""Return default source configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="nam">config_path</span> <span class="op">=</span> <span class="nam">CONFIG_DIR</span> <span class="op">/</span> <span class="str">"config.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="key">if</span> <span class="nam">config_path</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">config_path</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="key">return</span> <span class="nam">json</span><span class="op">.</span><span class="nam">load</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"><span class="key">def</span> <span class="nam">prompt</span><span class="op">(</span><span class="nam">message</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">default</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="str">"""Prompt for input with optional default."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="key">if</span> <span class="nam">default</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">input</span><span class="op">(</span><span class="str">f"{message} [{default}]: "</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span> <span class="key">if</span> <span class="nam">result</span> <span class="key">else</span> <span class="nam">default</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"> <span class="key">return</span> <span class="nam">input</span><span class="op">(</span><span class="str">f"{message}: "</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"><span class="key">def</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="nam">message</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">default</span><span class="op">:</span> <span class="nam">bool</span> <span class="op">=</span> <span class="key">True</span><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="str">"""Prompt for yes/no input."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="nam">default_str</span> <span class="op">=</span> <span class="str">"Y/n"</span> <span class="key">if</span> <span class="nam">default</span> <span class="key">else</span> <span class="str">"y/N"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">input</span><span class="op">(</span><span class="str">f"{message} [{default_str}]: "</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">result</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="key">return</span> <span class="nam">default</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span> <span class="key">in</span> <span class="op">(</span><span class="str">'y'</span><span class="op">,</span> <span class="str">'yes'</span><span class="op">,</span> <span class="str">'1'</span><span class="op">,</span> <span class="str">'true'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_rss_feeds</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="str">"""Interactive RSS feed configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128240; RSS Feed Configuration\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"Enable/disable news sources:\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="key">for</span> <span class="nam">feed_id</span><span class="op">,</span> <span class="nam">feed_config</span> <span class="key">in</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'rss_feeds'</span><span class="op">]</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="nam">name</span> <span class="op">=</span> <span class="nam">feed_config</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'name'</span><span class="op">,</span> <span class="nam">feed_id</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">current</span> <span class="op">=</span> <span class="nam">feed_config</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="nam">enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">f" {name}"</span><span class="op">,</span> <span class="nam">current</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'rss_feeds'</span><span class="op">]</span><span class="op">[</span><span class="nam">feed_id</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n Add custom RSS feed? (leave blank to skip)"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="nam">custom_name</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" Feed name"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="key">if</span> <span class="nam">custom_name</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="nam">custom_url</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" Feed URL"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'rss_feeds'</span><span class="op">]</span><span class="op">[</span><span class="nam">custom_name</span><span class="op">.</span><span class="nam">lower</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">replace</span><span class="op">(</span><span class="str">' '</span><span class="op">,</span> <span class="str">'_'</span><span class="op">)</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">custom_name</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"> <span class="str">"enabled"</span><span class="op">:</span> <span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="str">"main"</span><span class="op">:</span> <span class="nam">custom_url</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#9989; Added {custom_name}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_markets</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="str">"""Interactive market configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128202; Market Coverage\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"Enable/disable market regions:\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="key">for</span> <span class="nam">market_id</span><span class="op">,</span> <span class="nam">market_config</span> <span class="key">in</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'markets'</span><span class="op">]</span><span class="op">.</span><span class="nam">items</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="nam">name</span> <span class="op">=</span> <span class="nam">market_config</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'name'</span><span class="op">,</span> <span class="nam">market_id</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="nam">current</span> <span class="op">=</span> <span class="nam">market_config</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">f" {name}"</span><span class="op">,</span> <span class="nam">current</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'markets'</span><span class="op">]</span><span class="op">[</span><span class="nam">market_id</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_delivery</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="str">"""Interactive delivery channel configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128228; Delivery Channels\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="com"># Ensure delivery dict exists</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="key">if</span> <span class="str">'delivery'</span> <span class="key">not</span> <span class="key">in</span> <span class="nam">sources</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"> <span class="str">'whatsapp'</span><span class="op">:</span> <span class="op">{</span><span class="str">'enabled'</span><span class="op">:</span> <span class="key">True</span><span class="op">,</span> <span class="str">'group'</span><span class="op">:</span> <span class="str">''</span><span class="op">}</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="str">'telegram'</span><span class="op">:</span> <span class="op">{</span><span class="str">'enabled'</span><span class="op">:</span> <span class="key">False</span><span class="op">,</span> <span class="str">'group'</span><span class="op">:</span> <span class="str">''</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t"> <span class="com"># WhatsApp</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"> <span class="nam">wa_enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">"Enable WhatsApp delivery"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="nam">sources</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'delivery'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'whatsapp'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'whatsapp'</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">wa_enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="key">if</span> <span class="nam">wa_enabled</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="nam">wa_group</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" WhatsApp group name or JID"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'whatsapp'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'group'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'whatsapp'</span><span class="op">]</span><span class="op">[</span><span class="str">'group'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">wa_group</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"> <span class="com"># Telegram</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"> <span class="nam">tg_enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">"Enable Telegram delivery"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'telegram'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">False</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'telegram'</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">tg_enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="key">if</span> <span class="nam">tg_enabled</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="nam">tg_group</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" Telegram group name or ID"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'telegram'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'group'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'delivery'</span><span class="op">]</span><span class="op">[</span><span class="str">'telegram'</span><span class="op">]</span><span class="op">[</span><span class="str">'group'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">tg_group</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_language</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="str">"""Interactive language configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#127760; Language Settings\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="nam">current_lang</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'language'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'default'</span><span class="op">,</span> <span class="str">'de'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t"> <span class="nam">lang</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">"Default language (de/en)"</span><span class="op">,</span> <span class="nam">current_lang</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="key">if</span> <span class="nam">lang</span> <span class="key">in</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'language'</span><span class="op">]</span><span class="op">[</span><span class="str">'supported'</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'language'</span><span class="op">]</span><span class="op">[</span><span class="str">'default'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">lang</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#9888;&#65039; Unsupported language '{lang}', keeping '{current_lang}'"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_schedule</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="str">"""Interactive schedule configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#9200; Briefing Schedule\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="com"># Morning</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">morning</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="nam">morning_enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">f"Enable morning briefing ({morning['description']})"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="nam">morning</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">morning_enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="key">if</span> <span class="nam">morning_enabled</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="nam">morning_cron</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" Morning cron expression"</span><span class="op">,</span> <span class="nam">morning</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'cron'</span><span class="op">,</span> <span class="str">'30 6 * * 1-5'</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">[</span><span class="str">'cron'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">morning_cron</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="com"># Evening</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="nam">evening</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"> <span class="nam">evening_enabled</span> <span class="op">=</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">f"Enable evening briefing ({evening['description']})"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="nam">evening</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span><span class="op">[</span><span class="str">'enabled'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">evening_enabled</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="key">if</span> <span class="nam">evening_enabled</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="nam">evening_cron</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">" Evening cron expression"</span><span class="op">,</span> <span class="nam">evening</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'cron'</span><span class="op">,</span> <span class="str">'0 13 * * 1-5'</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span><span class="op">[</span><span class="str">'cron'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">evening_cron</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="com"># Timezone</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"> <span class="nam">tz</span> <span class="op">=</span> <span class="nam">prompt</span><span class="op">(</span><span class="str">"Timezone"</span><span class="op">,</span> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'timezone'</span><span class="op">,</span> <span class="str">'America/Los_Angeles'</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">[</span><span class="str">'timezone'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">tz</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t"> <span class="nam">sources</span><span class="op">[</span><span class="str">'schedule'</span><span class="op">]</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span><span class="op">[</span><span class="str">'timezone'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">tz</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"><span class="key">def</span> <span class="nam">setup_cron_jobs</span><span class="op">(</span><span class="nam">sources</span><span class="op">:</span> <span class="nam">dict</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="str">"""Set up OpenClaw cron jobs based on configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128197; Setting up cron jobs...\n"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="nam">schedule</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'schedule'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"> <span class="nam">delivery</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'delivery'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t"> <span class="nam">language</span> <span class="op">=</span> <span class="nam">sources</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'language'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'default'</span><span class="op">,</span> <span class="str">'de'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="com"># Determine delivery target</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="key">if</span> <span class="nam">delivery</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'whatsapp'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t"> <span class="nam">group</span> <span class="op">=</span> <span class="nam">delivery</span><span class="op">[</span><span class="str">'whatsapp'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'group'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t"> <span class="nam">send_cmd</span> <span class="op">=</span> <span class="str">f"--send --group '{group}'"</span> <span class="key">if</span> <span class="nam">group</span> <span class="key">else</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"> <span class="key">elif</span> <span class="nam">delivery</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'telegram'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t"> <span class="nam">group</span> <span class="op">=</span> <span class="nam">delivery</span><span class="op">[</span><span class="str">'telegram'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'group'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="nam">send_cmd</span> <span class="op">=</span> <span class="str">f"--send --group '{group}'"</span> <span class="com"># Would need telegram support</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="nam">send_cmd</span> <span class="op">=</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="com"># Morning job</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t"> <span class="key">if</span> <span class="nam">schedule</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'morning'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="nam">morning_cron</span> <span class="op">=</span> <span class="nam">schedule</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'cron'</span><span class="op">,</span> <span class="str">'30 6 * * 1-5'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="nam">tz</span> <span class="op">=</span> <span class="nam">schedule</span><span class="op">[</span><span class="str">'morning'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'timezone'</span><span class="op">,</span> <span class="str">'America/Los_Angeles'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" Creating morning briefing job: {morning_cron} ({tz})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t"> <span class="com"># Note: Actual cron creation would happen via openclaw cron add</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#9989; Morning briefing configured"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t"> <span class="com"># Evening job</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t"> <span class="key">if</span> <span class="nam">schedule</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'evening'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'enabled'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t"> <span class="nam">evening_cron</span> <span class="op">=</span> <span class="nam">schedule</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'cron'</span><span class="op">,</span> <span class="str">'0 13 * * 1-5'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"> <span class="nam">tz</span> <span class="op">=</span> <span class="nam">schedule</span><span class="op">[</span><span class="str">'evening'</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'timezone'</span><span class="op">,</span> <span class="str">'America/Los_Angeles'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" Creating evening briefing job: {evening_cron} ({tz})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" &#9989; Evening briefing configured"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t"><span class="key">def</span> <span class="nam">run_setup</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="str">"""Run interactive setup wizard."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n"</span> <span class="op">+</span> <span class="str">"="</span><span class="op">*</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#128200; Finance News Skill - Setup Wizard"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"="</span><span class="op">*</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="com"># Load existing or default config</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">reset</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="nam">sources</span> <span class="op">=</span> <span class="nam">get_default_sources</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#9888;&#65039; Starting with fresh configuration"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t"> <span class="nam">sources</span> <span class="op">=</span> <span class="nam">load_sources</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="key">if</span> <span class="nam">SOURCES_FILE</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#128194; Loaded existing configuration from {SOURCES_FILE}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#128194; No existing configuration found, using defaults"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t"> <span class="com"># Run through each section</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">==</span> <span class="str">'feeds'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t"> <span class="nam">setup_rss_feeds</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">==</span> <span class="str">'markets'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="nam">setup_markets</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">==</span> <span class="str">'delivery'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t"> <span class="nam">setup_delivery</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">==</span> <span class="str">'language'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="nam">setup_language</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">==</span> <span class="str">'schedule'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t"> <span class="nam">setup_schedule</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"> <span class="com"># Save configuration</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n"</span> <span class="op">+</span> <span class="str">"-"</span><span class="op">*</span><span class="num">60</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="key">if</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">"Save configuration?"</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t"> <span class="nam">save_sources</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"> <span class="com"># Set up cron jobs</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"> <span class="key">if</span> <span class="nam">prompt_bool</span><span class="op">(</span><span class="str">"Set up cron jobs now?"</span><span class="op">,</span> <span class="key">True</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t"> <span class="nam">setup_cron_jobs</span><span class="op">(</span><span class="nam">sources</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#10060; Configuration not saved"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\n&#9989; Setup complete!"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"\nNext steps:"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">" &#8226; Run 'finance-news portfolio-list' to check your watchlist"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">" &#8226; Run 'finance-news briefing --morning' to test a briefing"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">" &#8226; Run 'finance-news market' to see market overview"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"><span class="key">def</span> <span class="nam">show_config</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t"> <span class="str">"""Show current configuration."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"> <span class="nam">sources</span> <span class="op">=</span> <span class="nam">load_sources</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">sources</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">'Finance News Setup'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"> <span class="nam">subparsers</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_subparsers</span><span class="op">(</span><span class="nam">dest</span><span class="op">=</span><span class="str">'command'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t"> <span class="com"># Setup command (default)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t"> <span class="nam">setup_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'wizard'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Run setup wizard'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t"> <span class="nam">setup_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--reset'</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">'store_true'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Reset to defaults'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="nam">setup_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--section'</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">'feeds'</span><span class="op">,</span> <span class="str">'markets'</span><span class="op">,</span> <span class="str">'delivery'</span><span class="op">,</span> <span class="str">'language'</span><span class="op">,</span> <span class="str">'schedule'</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t"> <span class="nam">help</span><span class="op">=</span><span class="str">'Configure specific section only'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="nam">setup_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">run_setup</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t"> <span class="com"># Show config</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t"> <span class="nam">show_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">'show'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Show current configuration'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="nam">show_parser</span><span class="op">.</span><span class="nam">set_defaults</span><span class="op">(</span><span class="nam">func</span><span class="op">=</span><span class="nam">show_config</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">func</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="com"># Default to wizard</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">reset</span> <span class="op">=</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">section</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="nam">run_setup</span><span class="op">(</span><span class="nam">args</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">'__main__'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_research_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_stocks_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

432
htmlcov/z_de1a740d5dc98ffd_stocks_py.html generated Normal file
View File

@@ -0,0 +1,432 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/stocks.py: 53%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;stocks.py</b>:
<span class="pc_cov">53%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">184 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">97<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">87<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_setup_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_summarize_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="str">stocks.py - Unified stock management for holdings and watchlist.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="str">Single source of truth for:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="str">- Holdings (stocks you own)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="str">- Watchlist (stocks you're watching to buy)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="str">Usage:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="str"> from stocks import load_stocks, save_stocks, get_holdings, get_watchlist</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="str"> from stocks import add_to_watchlist, add_to_holdings, move_to_holdings</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="str">CLI:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="str"> stocks.py list [--holdings|--watchlist]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"><span class="str"> stocks.py add-watchlist TICKER [--target 380] [--notes "Buy zone"]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t"><span class="str"> stocks.py add-holding TICKER --name "Company" [--category "Tech"]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="str"> stocks.py move TICKER # watchlist &#8594; holdings (you bought it)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"><span class="str"> stocks.py remove TICKER [--from holdings|watchlist]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"><span class="key">from</span> <span class="nam">datetime</span> <span class="key">import</span> <span class="nam">datetime</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"><span class="key">from</span> <span class="nam">typing</span> <span class="key">import</span> <span class="nam">Optional</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"><span class="com"># Default path - can be overridden</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"><span class="nam">STOCKS_FILE</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"config"</span> <span class="op">/</span> <span class="str">"stocks.json"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t"><span class="key">def</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="nam">path</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">Path</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">dict</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"> <span class="str">"""Load the unified stocks file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="nam">path</span> <span class="op">=</span> <span class="nam">path</span> <span class="key">or</span> <span class="nam">STOCKS_FILE</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">path</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="str">"version"</span><span class="op">:</span> <span class="str">"1.0"</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t"> <span class="str">"updated"</span><span class="op">:</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="str">"holdings"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="str">"watchlist"</span><span class="op">:</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="str">"alert_definitions"</span><span class="op">:</span> <span class="op">{</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"> <span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">path</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="key">return</span> <span class="nam">json</span><span class="op">.</span><span class="nam">load</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t"><span class="key">def</span> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">dict</span><span class="op">,</span> <span class="nam">path</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">Path</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"> <span class="str">"""Save the unified stocks file."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="nam">path</span> <span class="op">=</span> <span class="nam">path</span> <span class="key">or</span> <span class="nam">STOCKS_FILE</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"updated"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">datetime</span><span class="op">.</span><span class="nam">now</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">strftime</span><span class="op">(</span><span class="str">"%Y-%m-%d"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">path</span><span class="op">,</span> <span class="str">'w'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="nam">json</span><span class="op">.</span><span class="nam">dump</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">f</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"><span class="key">def</span> <span class="nam">get_holdings</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="str">"""Get list of holdings."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="key">return</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"holdings"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t"><span class="key">def</span> <span class="nam">get_watchlist</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t"> <span class="str">"""Get list of watchlist items."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="key">if</span> <span class="nam">data</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="key">return</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"watchlist"</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"><span class="key">def</span> <span class="nam">get_holding_tickers</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">set</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="str">"""Get set of holding tickers for quick lookup."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="nam">holdings</span> <span class="op">=</span> <span class="nam">get_holdings</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="nam">h</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">holdings</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t"><span class="key">def</span> <span class="nam">get_watchlist_tickers</span><span class="op">(</span><span class="nam">data</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">dict</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">set</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="str">"""Get set of watchlist tickers for quick lookup."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="nam">watchlist</span> <span class="op">=</span> <span class="nam">get_watchlist</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="key">return</span> <span class="op">{</span><span class="nam">w</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="key">for</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">watchlist</span><span class="op">}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"><span class="key">def</span> <span class="nam">add_to_watchlist</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="nam">target</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">float</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="nam">stop</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">float</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t"> <span class="nam">notes</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="nam">alerts</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">list</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="str">"""Add a stock to the watchlist."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="com"># Check if already in watchlist</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t"> <span class="key">for</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="key">if</span> <span class="nam">w</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="com"># Update existing</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="key">if</span> <span class="nam">target</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="nam">w</span><span class="op">[</span><span class="str">"target"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">target</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="key">if</span> <span class="nam">stop</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="nam">w</span><span class="op">[</span><span class="str">"stop"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">stop</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="key">if</span> <span class="nam">notes</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t"> <span class="nam">w</span><span class="op">[</span><span class="str">"notes"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">notes</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="key">if</span> <span class="nam">alerts</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="nam">w</span><span class="op">[</span><span class="str">"alerts"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="com"># Add new</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="str">"target"</span><span class="op">:</span> <span class="nam">target</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="str">"stop"</span><span class="op">:</span> <span class="nam">stop</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="str">"alerts"</span><span class="op">:</span> <span class="nam">alerts</span> <span class="key">or</span> <span class="op">[</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t"> <span class="str">"notes"</span><span class="op">:</span> <span class="nam">notes</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">.</span><span class="nam">sort</span><span class="op">(</span><span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"><span class="key">def</span> <span class="nam">add_to_holdings</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t"> <span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"> <span class="nam">name</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="nam">category</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="nam">notes</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t"> <span class="nam">target</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">float</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="nam">stop</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">float</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="nam">alerts</span><span class="op">:</span> <span class="nam">Optional</span><span class="op">[</span><span class="nam">list</span><span class="op">]</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t"> <span class="str">"""Add a stock to holdings. Target/stop for 'buy more' alerts."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="com"># Check if already in holdings</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="key">if</span> <span class="nam">h</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t"> <span class="com"># Update existing</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="key">if</span> <span class="nam">name</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"name"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">name</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="key">if</span> <span class="nam">category</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"category"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">category</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="key">if</span> <span class="nam">notes</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"notes"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">notes</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t"> <span class="key">if</span> <span class="nam">target</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"target"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">target</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="key">if</span> <span class="nam">stop</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"stop"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">stop</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="key">if</span> <span class="nam">alerts</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"alerts"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">alerts</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="com"># Add new</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">name</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">category</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"> <span class="str">"notes"</span><span class="op">:</span> <span class="nam">notes</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="str">"target"</span><span class="op">:</span> <span class="nam">target</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t159" href="#t159">159</a></span><span class="t"> <span class="str">"stop"</span><span class="op">:</span> <span class="nam">stop</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t160" href="#t160">160</a></span><span class="t"> <span class="str">"alerts"</span><span class="op">:</span> <span class="nam">alerts</span> <span class="key">or</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t161" href="#t161">161</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t162" href="#t162">162</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">.</span><span class="nam">sort</span><span class="op">(</span><span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t163" href="#t163">163</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t164" href="#t164">164</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t165" href="#t165">165</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t166" href="#t166">166</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t167" href="#t167">167</a></span><span class="t"><span class="key">def</span> <span class="nam">move_to_holdings</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t168" href="#t168">168</a></span><span class="t"> <span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t169" href="#t169">169</a></span><span class="t"> <span class="nam">name</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t170" href="#t170">170</a></span><span class="t"> <span class="nam">category</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t171" href="#t171">171</a></span><span class="t"> <span class="nam">notes</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">""</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t172" href="#t172">172</a></span><span class="t"><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t173" href="#t173">173</a></span><span class="t"> <span class="str">"""Move a stock from watchlist to holdings (you bought it)."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t174" href="#t174">174</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t175" href="#t175">175</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t176" href="#t176">176</a></span><span class="t"> <span class="com"># Find in watchlist</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t177" href="#t177">177</a></span><span class="t"> <span class="nam">watchlist_item</span> <span class="op">=</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t178" href="#t178">178</a></span><span class="t"> <span class="key">for</span> <span class="nam">i</span><span class="op">,</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">enumerate</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t179" href="#t179">179</a></span><span class="t"> <span class="key">if</span> <span class="nam">w</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">==</span> <span class="nam">ticker</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t180" href="#t180">180</a></span><span class="t"> <span class="nam">watchlist_item</span> <span class="op">=</span> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">.</span><span class="nam">pop</span><span class="op">(</span><span class="nam">i</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t181" href="#t181">181</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t182" href="#t182">182</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t183" href="#t183">183</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">watchlist_item</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t184" href="#t184">184</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; {ticker} not found in watchlist"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t185" href="#t185">185</a></span><span class="t"> <span class="key">return</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t186" href="#t186">186</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t187" href="#t187">187</a></span><span class="t"> <span class="com"># Add to holdings</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t188" href="#t188">188</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="op">{</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t189" href="#t189">189</a></span><span class="t"> <span class="str">"ticker"</span><span class="op">:</span> <span class="nam">ticker</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t190" href="#t190">190</a></span><span class="t"> <span class="str">"name"</span><span class="op">:</span> <span class="nam">name</span> <span class="key">or</span> <span class="nam">watchlist_item</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"notes"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t191" href="#t191">191</a></span><span class="t"> <span class="str">"category"</span><span class="op">:</span> <span class="nam">category</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t192" href="#t192">192</a></span><span class="t"> <span class="str">"notes"</span><span class="op">:</span> <span class="nam">notes</span> <span class="key">or</span> <span class="str">f"Bought (was on watchlist with target ${watchlist_item.get('target', 'N/A')})"</span>&nbsp;</span><span class="r"></span></p>
<p class="run run2"><span class="n"><a id="t193" href="#t193">193</a></span><span class="t"> <span class="op">}</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t194" href="#t194">194</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">.</span><span class="nam">sort</span><span class="op">(</span><span class="nam">key</span><span class="op">=</span><span class="key">lambda</span> <span class="nam">x</span><span class="op">:</span> <span class="nam">x</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="str">""</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t195" href="#t195">195</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t196" href="#t196">196</a></span><span class="t"> <span class="key">return</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t197" href="#t197">197</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t198" href="#t198">198</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t199" href="#t199">199</a></span><span class="t"><span class="key">def</span> <span class="nam">remove_stock</span><span class="op">(</span><span class="nam">ticker</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">from_list</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">"both"</span><span class="op">)</span> <span class="op">-></span> <span class="nam">bool</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t200" href="#t200">200</a></span><span class="t"> <span class="str">"""Remove a stock from holdings, watchlist, or both."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t201" href="#t201">201</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t202" href="#t202">202</a></span><span class="t"> <span class="nam">removed</span> <span class="op">=</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t203" href="#t203">203</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t204" href="#t204">204</a></span><span class="t"> <span class="key">if</span> <span class="nam">from_list</span> <span class="key">in</span> <span class="op">(</span><span class="str">"holdings"</span><span class="op">,</span> <span class="str">"both"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t205" href="#t205">205</a></span><span class="t"> <span class="nam">original_len</span> <span class="op">=</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t206" href="#t206">206</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="nam">h</span> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span> <span class="key">if</span> <span class="nam">h</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">!=</span> <span class="nam">ticker</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t207" href="#t207">207</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">)</span> <span class="op">&lt;</span> <span class="nam">original_len</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t208" href="#t208">208</a></span><span class="t"> <span class="nam">removed</span> <span class="op">=</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t209" href="#t209">209</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t210" href="#t210">210</a></span><span class="t"> <span class="key">if</span> <span class="nam">from_list</span> <span class="key">in</span> <span class="op">(</span><span class="str">"watchlist"</span><span class="op">,</span> <span class="str">"both"</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t211" href="#t211">211</a></span><span class="t"> <span class="nam">original_len</span> <span class="op">=</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t212" href="#t212">212</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="nam">w</span> <span class="key">for</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span> <span class="key">if</span> <span class="nam">w</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">!=</span> <span class="nam">ticker</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t213" href="#t213">213</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">)</span> <span class="op">&lt;</span> <span class="nam">original_len</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t214" href="#t214">214</a></span><span class="t"> <span class="nam">removed</span> <span class="op">=</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t215" href="#t215">215</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t216" href="#t216">216</a></span><span class="t"> <span class="key">if</span> <span class="nam">removed</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t217" href="#t217">217</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t218" href="#t218">218</a></span><span class="t"> <span class="key">return</span> <span class="nam">removed</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t219" href="#t219">219</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t220" href="#t220">220</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t221" href="#t221">221</a></span><span class="t"><span class="key">def</span> <span class="nam">list_stocks</span><span class="op">(</span><span class="nam">show_holdings</span><span class="op">:</span> <span class="nam">bool</span> <span class="op">=</span> <span class="key">True</span><span class="op">,</span> <span class="nam">show_watchlist</span><span class="op">:</span> <span class="nam">bool</span> <span class="op">=</span> <span class="key">True</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t222" href="#t222">222</a></span><span class="t"> <span class="str">"""Print stocks list."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t223" href="#t223">223</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t224" href="#t224">224</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t225" href="#t225">225</a></span><span class="t"> <span class="key">if</span> <span class="nam">show_holdings</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t226" href="#t226">226</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#128202; HOLDINGS ({len(data['holdings'])})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t227" href="#t227">227</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"-"</span> <span class="op">*</span> <span class="num">50</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t228" href="#t228">228</a></span><span class="t"> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">[</span><span class="op">:</span><span class="num">20</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t229" href="#t229">229</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" {h['ticker']:10} {h.get('name', '')[:30]}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t230" href="#t230">230</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">)</span> <span class="op">></span> <span class="num">20</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t231" href="#t231">231</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" ... and {len(data['holdings']) - 20} more"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t232" href="#t232">232</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t233" href="#t233">233</a></span><span class="t"> <span class="key">if</span> <span class="nam">show_watchlist</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t234" href="#t234">234</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"\n&#128064; WATCHLIST ({len(data['watchlist'])})"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t235" href="#t235">235</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"-"</span> <span class="op">*</span> <span class="num">50</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t236" href="#t236">236</a></span><span class="t"> <span class="key">for</span> <span class="nam">w</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">[</span><span class="op">:</span><span class="num">20</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t237" href="#t237">237</a></span><span class="t"> <span class="nam">target</span> <span class="op">=</span> <span class="str">f"${w['target']}"</span> <span class="key">if</span> <span class="nam">w</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'target'</span><span class="op">)</span> <span class="key">else</span> <span class="str">"no target"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t238" href="#t238">238</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" {w['ticker']:10} {target:>10} {w.get('notes', '')[:25]}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t239" href="#t239">239</a></span><span class="t"> <span class="key">if</span> <span class="nam">len</span><span class="op">(</span><span class="nam">data</span><span class="op">[</span><span class="str">"watchlist"</span><span class="op">]</span><span class="op">)</span> <span class="op">></span> <span class="num">20</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t240" href="#t240">240</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f" ... and {len(data['watchlist']) - 20} more"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t241" href="#t241">241</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t242" href="#t242">242</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t243" href="#t243">243</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t244" href="#t244">244</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">"Unified stock management"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t245" href="#t245">245</a></span><span class="t"> <span class="nam">subparsers</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_subparsers</span><span class="op">(</span><span class="nam">dest</span><span class="op">=</span><span class="str">"command"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Commands"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t246" href="#t246">246</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t247" href="#t247">247</a></span><span class="t"> <span class="com"># list</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t248" href="#t248">248</a></span><span class="t"> <span class="nam">list_parser</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"list"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"List stocks"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t249" href="#t249">249</a></span><span class="t"> <span class="nam">list_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--holdings"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Show only holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t250" href="#t250">250</a></span><span class="t"> <span class="nam">list_parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--watchlist"</span><span class="op">,</span> <span class="nam">action</span><span class="op">=</span><span class="str">"store_true"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Show only watchlist"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t251" href="#t251">251</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t252" href="#t252">252</a></span><span class="t"> <span class="com"># add-watchlist</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t253" href="#t253">253</a></span><span class="t"> <span class="nam">add_watch</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"add-watchlist"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Add to watchlist"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t254" href="#t254">254</a></span><span class="t"> <span class="nam">add_watch</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t255" href="#t255">255</a></span><span class="t"> <span class="nam">add_watch</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--target"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Target price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t256" href="#t256">256</a></span><span class="t"> <span class="nam">add_watch</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--stop"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stop loss"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t257" href="#t257">257</a></span><span class="t"> <span class="nam">add_watch</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--notes"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Notes"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t258" href="#t258">258</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t259" href="#t259">259</a></span><span class="t"> <span class="com"># add-holding</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t260" href="#t260">260</a></span><span class="t"> <span class="nam">add_hold</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"add-holding"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Add to holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t261" href="#t261">261</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t262" href="#t262">262</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--name"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Company name"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t263" href="#t263">263</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--category"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Category"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t264" href="#t264">264</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--notes"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Notes"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t265" href="#t265">265</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--target"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Buy-more target price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t266" href="#t266">266</a></span><span class="t"> <span class="nam">add_hold</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--stop"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stop loss price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t267" href="#t267">267</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t268" href="#t268">268</a></span><span class="t"> <span class="com"># move (watchlist &#8594; holdings)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t269" href="#t269">269</a></span><span class="t"> <span class="nam">move</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"move"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Move from watchlist to holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t270" href="#t270">270</a></span><span class="t"> <span class="nam">move</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t271" href="#t271">271</a></span><span class="t"> <span class="nam">move</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--name"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Company name"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t272" href="#t272">272</a></span><span class="t"> <span class="nam">move</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--category"</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">""</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Category"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t273" href="#t273">273</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t274" href="#t274">274</a></span><span class="t"> <span class="com"># remove</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t275" href="#t275">275</a></span><span class="t"> <span class="nam">remove</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"remove"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Remove stock"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t276" href="#t276">276</a></span><span class="t"> <span class="nam">remove</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t277" href="#t277">277</a></span><span class="t"> <span class="nam">remove</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--from"</span><span class="op">,</span> <span class="nam">dest</span><span class="op">=</span><span class="str">"from_list"</span><span class="op">,</span> <span class="nam">choices</span><span class="op">=</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">,</span> <span class="str">"watchlist"</span><span class="op">,</span> <span class="str">"both"</span><span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t278" href="#t278">278</a></span><span class="t"> <span class="nam">default</span><span class="op">=</span><span class="str">"both"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Remove from which list"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t279" href="#t279">279</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t280" href="#t280">280</a></span><span class="t"> <span class="com"># set-alert (for existing holdings)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t281" href="#t281">281</a></span><span class="t"> <span class="nam">set_alert</span> <span class="op">=</span> <span class="nam">subparsers</span><span class="op">.</span><span class="nam">add_parser</span><span class="op">(</span><span class="str">"set-alert"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Set buy-more/stop alert on holding"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t282" href="#t282">282</a></span><span class="t"> <span class="nam">set_alert</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stock ticker"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t283" href="#t283">283</a></span><span class="t"> <span class="nam">set_alert</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--target"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Buy-more target price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t284" href="#t284">284</a></span><span class="t"> <span class="nam">set_alert</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">"--stop"</span><span class="op">,</span> <span class="nam">type</span><span class="op">=</span><span class="nam">float</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">"Stop loss price"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t285" href="#t285">285</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t286" href="#t286">286</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t287" href="#t287">287</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t288" href="#t288">288</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"list"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t289" href="#t289">289</a></span><span class="t"> <span class="nam">show_h</span> <span class="op">=</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">watchlist</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">holdings</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t290" href="#t290">290</a></span><span class="t"> <span class="nam">show_w</span> <span class="op">=</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">holdings</span> <span class="key">or</span> <span class="nam">args</span><span class="op">.</span><span class="nam">watchlist</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t291" href="#t291">291</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">holdings</span> <span class="key">and</span> <span class="key">not</span> <span class="nam">args</span><span class="op">.</span><span class="nam">watchlist</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t292" href="#t292">292</a></span><span class="t"> <span class="nam">show_h</span> <span class="op">=</span> <span class="nam">show_w</span> <span class="op">=</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t293" href="#t293">293</a></span><span class="t"> <span class="nam">list_stocks</span><span class="op">(</span><span class="nam">show_holdings</span><span class="op">=</span><span class="nam">show_h</span><span class="op">,</span> <span class="nam">show_watchlist</span><span class="op">=</span><span class="nam">show_w</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t294" href="#t294">294</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t295" href="#t295">295</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"add-watchlist"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t296" href="#t296">296</a></span><span class="t"> <span class="nam">add_to_watchlist</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">stop</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">notes</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t297" href="#t297">297</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Added {args.ticker.upper()} to watchlist"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t298" href="#t298">298</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t299" href="#t299">299</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"add-holding"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t300" href="#t300">300</a></span><span class="t"> <span class="nam">add_to_holdings</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">name</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">category</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">notes</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t301" href="#t301">301</a></span><span class="t"> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">stop</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t302" href="#t302">302</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Added {args.ticker.upper()} to holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t303" href="#t303">303</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t304" href="#t304">304</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"move"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t305" href="#t305">305</a></span><span class="t"> <span class="key">if</span> <span class="nam">move_to_holdings</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">name</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">category</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t306" href="#t306">306</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Moved {args.ticker.upper()} from watchlist to holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t307" href="#t307">307</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t308" href="#t308">308</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"remove"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t309" href="#t309">309</a></span><span class="t"> <span class="key">if</span> <span class="nam">remove_stock</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">from_list</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t310" href="#t310">310</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Removed {args.ticker.upper()}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t311" href="#t311">311</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t312" href="#t312">312</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; {args.ticker.upper()} not found"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t313" href="#t313">313</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t314" href="#t314">314</a></span><span class="t"> <span class="key">elif</span> <span class="nam">args</span><span class="op">.</span><span class="nam">command</span> <span class="op">==</span> <span class="str">"set-alert"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t315" href="#t315">315</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">load_stocks</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t316" href="#t316">316</a></span><span class="t"> <span class="nam">found</span> <span class="op">=</span> <span class="key">False</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t317" href="#t317">317</a></span><span class="t"> <span class="key">for</span> <span class="nam">h</span> <span class="key">in</span> <span class="nam">data</span><span class="op">[</span><span class="str">"holdings"</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t318" href="#t318">318</a></span><span class="t"> <span class="key">if</span> <span class="nam">h</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"ticker"</span><span class="op">)</span> <span class="op">==</span> <span class="nam">args</span><span class="op">.</span><span class="nam">ticker</span><span class="op">.</span><span class="nam">upper</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t319" href="#t319">319</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t320" href="#t320">320</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"target"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">target</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t321" href="#t321">321</a></span><span class="t"> <span class="key">if</span> <span class="nam">args</span><span class="op">.</span><span class="nam">stop</span> <span class="key">is</span> <span class="key">not</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t322" href="#t322">322</a></span><span class="t"> <span class="nam">h</span><span class="op">[</span><span class="str">"stop"</span><span class="op">]</span> <span class="op">=</span> <span class="nam">args</span><span class="op">.</span><span class="nam">stop</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t323" href="#t323">323</a></span><span class="t"> <span class="nam">save_stocks</span><span class="op">(</span><span class="nam">data</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t324" href="#t324">324</a></span><span class="t"> <span class="nam">found</span> <span class="op">=</span> <span class="key">True</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t325" href="#t325">325</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Set alert on {args.ticker.upper()}: target=${args.target}, stop=${args.stop}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t326" href="#t326">326</a></span><span class="t"> <span class="key">break</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t327" href="#t327">327</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">found</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t328" href="#t328">328</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; {args.ticker.upper()} not found in holdings"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t329" href="#t329">329</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t330" href="#t330">330</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t331" href="#t331">331</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">print_help</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t332" href="#t332">332</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t333" href="#t333">333</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t334" href="#t334">334</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">"__main__"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t335" href="#t335">335</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_setup_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_summarize_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/translate_portfolio.py: 0%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;translate_portfolio.py</b>:
<span class="pc_cov">0%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">88 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">0<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">88<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">2<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_summarize_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="z_de1a740d5dc98ffd_utils_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="com">#!/usr/bin/env python3</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t"><span class="str">"""Translate portfolio headlines in briefing JSON using openclaw.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="str">Usage: python3 translate_portfolio.py /path/to/briefing.json [--lang de]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="str">Reads briefing JSON, translates portfolio article headlines via openclaw,</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t"><span class="str">writes back the modified JSON.</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"><span class="key">import</span> <span class="nam">argparse</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"><span class="key">import</span> <span class="nam">json</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"><span class="key">import</span> <span class="nam">re</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"><span class="key">import</span> <span class="nam">subprocess</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="key">def</span> <span class="nam">extract_headlines</span><span class="op">(</span><span class="nam">portfolio_message</span><span class="op">:</span> <span class="nam">str</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"> <span class="str">"""Extract article headlines (lines starting with &#8226;) from portfolio message."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"> <span class="nam">headlines</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"> <span class="key">for</span> <span class="nam">line</span> <span class="key">in</span> <span class="nam">portfolio_message</span><span class="op">.</span><span class="nam">split</span><span class="op">(</span><span class="str">'\n'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"> <span class="nam">line</span> <span class="op">=</span> <span class="nam">line</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"> <span class="key">if</span> <span class="nam">line</span><span class="op">.</span><span class="nam">startswith</span><span class="op">(</span><span class="str">'&#8226;'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"> <span class="com"># Remove bullet, reference number, and clean up</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"> <span class="com"># Format: "&#8226; Headline text [1]"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"> <span class="nam">match</span> <span class="op">=</span> <span class="nam">re</span><span class="op">.</span><span class="nam">match</span><span class="op">(</span><span class="str">r'&#8226;\s*(.+?)\s*\[\d+\]$'</span><span class="op">,</span> <span class="nam">line</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"> <span class="key">if</span> <span class="nam">match</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"> <span class="nam">headlines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">match</span><span class="op">.</span><span class="nam">group</span><span class="op">(</span><span class="num">1</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="com"># No reference number</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t"> <span class="nam">headlines</span><span class="op">.</span><span class="nam">append</span><span class="op">(</span><span class="nam">line</span><span class="op">[</span><span class="num">1</span><span class="op">:</span><span class="op">]</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t"> <span class="key">return</span> <span class="nam">headlines</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"><span class="key">def</span> <span class="nam">translate_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">,</span> <span class="nam">lang</span><span class="op">:</span> <span class="nam">str</span> <span class="op">=</span> <span class="str">"de"</span><span class="op">)</span> <span class="op">-></span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="str">"""Translate headlines using openclaw agent."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">headlines</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t"> <span class="key">return</span> <span class="op">[</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"> <span class="nam">prompt</span> <span class="op">=</span> <span class="str">f"""Translate these English headlines to German.</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"><span class="str">Return ONLY a JSON array of strings in the same order.</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"><span class="str">Example: ["&#220;bersetzung 1", "&#220;bersetzung 2"]</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"><span class="str">Do not add commentary.</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"><span class="str">Headlines:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"><span class="str">"""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t46" href="#t46">46</a></span><span class="t"> <span class="key">for</span> <span class="nam">idx</span><span class="op">,</span> <span class="nam">title</span> <span class="key">in</span> <span class="nam">enumerate</span><span class="op">(</span><span class="nam">headlines</span><span class="op">,</span> <span class="nam">start</span><span class="op">=</span><span class="num">1</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t47" href="#t47">47</a></span><span class="t"> <span class="nam">prompt</span> <span class="op">+=</span> <span class="str">f"{idx}. {title}\n"</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t48" href="#t48">48</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t49" href="#t49">49</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t50" href="#t50">50</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">subprocess</span><span class="op">.</span><span class="nam">run</span><span class="op">(</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t51" href="#t51">51</a></span><span class="t"> <span class="op">[</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t52" href="#t52">52</a></span><span class="t"> <span class="str">'openclaw'</span><span class="op">,</span> <span class="str">'agent'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t53" href="#t53">53</a></span><span class="t"> <span class="str">'--session-id'</span><span class="op">,</span> <span class="str">'finance-news-translate-portfolio'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t54" href="#t54">54</a></span><span class="t"> <span class="str">'--message'</span><span class="op">,</span> <span class="nam">prompt</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t55" href="#t55">55</a></span><span class="t"> <span class="str">'--json'</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t56" href="#t56">56</a></span><span class="t"> <span class="str">'--timeout'</span><span class="op">,</span> <span class="str">'60'</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t57" href="#t57">57</a></span><span class="t"> <span class="op">]</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t58" href="#t58">58</a></span><span class="t"> <span class="nam">capture_output</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t59" href="#t59">59</a></span><span class="t"> <span class="nam">text</span><span class="op">=</span><span class="key">True</span><span class="op">,</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t60" href="#t60">60</a></span><span class="t"> <span class="nam">timeout</span><span class="op">=</span><span class="num">90</span>&nbsp;</span><span class="r"></span></p>
<p class="mis mis2 show_mis"><span class="n"><a id="t61" href="#t61">61</a></span><span class="t"> <span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t62" href="#t62">62</a></span><span class="t"> <span class="key">except</span> <span class="op">(</span><span class="nam">subprocess</span><span class="op">.</span><span class="nam">TimeoutExpired</span><span class="op">,</span> <span class="nam">FileNotFoundError</span><span class="op">,</span> <span class="nam">OSError</span><span class="op">)</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t63" href="#t63">63</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Translation failed: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t64" href="#t64">64</a></span><span class="t"> <span class="key">return</span> <span class="nam">headlines</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t65" href="#t65">65</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t66" href="#t66">66</a></span><span class="t"> <span class="key">if</span> <span class="nam">result</span><span class="op">.</span><span class="nam">returncode</span> <span class="op">!=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t67" href="#t67">67</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; openclaw error: {result.stderr}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t68" href="#t68">68</a></span><span class="t"> <span class="key">return</span> <span class="nam">headlines</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t69" href="#t69">69</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t70" href="#t70">70</a></span><span class="t"> <span class="com"># Extract reply from openclaw JSON output</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t71" href="#t71">71</a></span><span class="t"> <span class="com"># Format: {"result": {"payloads": [{"text": "..."}]}}</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t72" href="#t72">72</a></span><span class="t"> <span class="com"># Note: openclaw may print plugin loading messages before JSON, so find the JSON start</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t73" href="#t73">73</a></span><span class="t"> <span class="nam">stdout</span> <span class="op">=</span> <span class="nam">result</span><span class="op">.</span><span class="nam">stdout</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t74" href="#t74">74</a></span><span class="t"> <span class="nam">json_start</span> <span class="op">=</span> <span class="nam">stdout</span><span class="op">.</span><span class="nam">find</span><span class="op">(</span><span class="str">'{'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t75" href="#t75">75</a></span><span class="t"> <span class="key">if</span> <span class="nam">json_start</span> <span class="op">></span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t76" href="#t76">76</a></span><span class="t"> <span class="nam">stdout</span> <span class="op">=</span> <span class="nam">stdout</span><span class="op">[</span><span class="nam">json_start</span><span class="op">:</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t77" href="#t77">77</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t78" href="#t78">78</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t79" href="#t79">79</a></span><span class="t"> <span class="nam">output</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">stdout</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t80" href="#t80">80</a></span><span class="t"> <span class="nam">payloads</span> <span class="op">=</span> <span class="nam">output</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'result'</span><span class="op">,</span> <span class="op">{</span><span class="op">}</span><span class="op">)</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'payloads'</span><span class="op">,</span> <span class="op">[</span><span class="op">]</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t81" href="#t81">81</a></span><span class="t"> <span class="key">if</span> <span class="nam">payloads</span> <span class="key">and</span> <span class="nam">payloads</span><span class="op">[</span><span class="num">0</span><span class="op">]</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'text'</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t82" href="#t82">82</a></span><span class="t"> <span class="nam">reply</span> <span class="op">=</span> <span class="nam">payloads</span><span class="op">[</span><span class="num">0</span><span class="op">]</span><span class="op">[</span><span class="str">'text'</span><span class="op">]</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t83" href="#t83">83</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t84" href="#t84">84</a></span><span class="t"> <span class="nam">reply</span> <span class="op">=</span> <span class="nam">output</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'reply'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span> <span class="key">or</span> <span class="nam">output</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'message'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span> <span class="key">or</span> <span class="nam">stdout</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t85" href="#t85">85</a></span><span class="t"> <span class="key">except</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t86" href="#t86">86</a></span><span class="t"> <span class="nam">reply</span> <span class="op">=</span> <span class="nam">stdout</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t87" href="#t87">87</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t88" href="#t88">88</a></span><span class="t"> <span class="com"># Parse JSON array from reply</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t89" href="#t89">89</a></span><span class="t"> <span class="nam">json_text</span> <span class="op">=</span> <span class="nam">reply</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t90" href="#t90">90</a></span><span class="t"> <span class="key">if</span> <span class="str">"```"</span> <span class="key">in</span> <span class="nam">json_text</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t91" href="#t91">91</a></span><span class="t"> <span class="nam">match</span> <span class="op">=</span> <span class="nam">re</span><span class="op">.</span><span class="nam">search</span><span class="op">(</span><span class="str">r'```(?:json)?\s*(.*?)```'</span><span class="op">,</span> <span class="nam">json_text</span><span class="op">,</span> <span class="nam">re</span><span class="op">.</span><span class="nam">DOTALL</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t92" href="#t92">92</a></span><span class="t"> <span class="key">if</span> <span class="nam">match</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t93" href="#t93">93</a></span><span class="t"> <span class="nam">json_text</span> <span class="op">=</span> <span class="nam">match</span><span class="op">.</span><span class="nam">group</span><span class="op">(</span><span class="num">1</span><span class="op">)</span><span class="op">.</span><span class="nam">strip</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t94" href="#t94">94</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t95" href="#t95">95</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t96" href="#t96">96</a></span><span class="t"> <span class="nam">translated</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">loads</span><span class="op">(</span><span class="nam">json_text</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t97" href="#t97">97</a></span><span class="t"> <span class="key">if</span> <span class="nam">isinstance</span><span class="op">(</span><span class="nam">translated</span><span class="op">,</span> <span class="nam">list</span><span class="op">)</span> <span class="key">and</span> <span class="nam">len</span><span class="op">(</span><span class="nam">translated</span><span class="op">)</span> <span class="op">==</span> <span class="nam">len</span><span class="op">(</span><span class="nam">headlines</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t98" href="#t98">98</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Translated {len(headlines)} portfolio headlines"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t99" href="#t99">99</a></span><span class="t"> <span class="key">return</span> <span class="nam">translated</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t100" href="#t100">100</a></span><span class="t"> <span class="key">except</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t101" href="#t101">101</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; JSON parse error: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t102" href="#t102">102</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t103" href="#t103">103</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9888;&#65039; Translation failed, using original headlines"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t104" href="#t104">104</a></span><span class="t"> <span class="key">return</span> <span class="nam">headlines</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t105" href="#t105">105</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t106" href="#t106">106</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t107" href="#t107">107</a></span><span class="t"><span class="key">def</span> <span class="nam">replace_headlines</span><span class="op">(</span><span class="nam">portfolio_message</span><span class="op">:</span> <span class="nam">str</span><span class="op">,</span> <span class="nam">original</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">,</span> <span class="nam">translated</span><span class="op">:</span> <span class="nam">list</span><span class="op">[</span><span class="nam">str</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="nam">str</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t108" href="#t108">108</a></span><span class="t"> <span class="str">"""Replace original headlines with translated ones in portfolio message."""</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t109" href="#t109">109</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">portfolio_message</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t110" href="#t110">110</a></span><span class="t"> <span class="key">for</span> <span class="nam">orig</span><span class="op">,</span> <span class="nam">trans</span> <span class="key">in</span> <span class="nam">zip</span><span class="op">(</span><span class="nam">original</span><span class="op">,</span> <span class="nam">translated</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t111" href="#t111">111</a></span><span class="t"> <span class="key">if</span> <span class="nam">orig</span> <span class="op">!=</span> <span class="nam">trans</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t112" href="#t112">112</a></span><span class="t"> <span class="com"># Replace the headline text, preserving bullet and reference</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t113" href="#t113">113</a></span><span class="t"> <span class="nam">result</span> <span class="op">=</span> <span class="nam">result</span><span class="op">.</span><span class="nam">replace</span><span class="op">(</span><span class="str">f"&#8226; {orig}"</span><span class="op">,</span> <span class="str">f"&#8226; {trans}"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t114" href="#t114">114</a></span><span class="t"> <span class="key">return</span> <span class="nam">result</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t115" href="#t115">115</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t116" href="#t116">116</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t117" href="#t117">117</a></span><span class="t"><span class="key">def</span> <span class="nam">main</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t118" href="#t118">118</a></span><span class="t"> <span class="nam">parser</span> <span class="op">=</span> <span class="nam">argparse</span><span class="op">.</span><span class="nam">ArgumentParser</span><span class="op">(</span><span class="nam">description</span><span class="op">=</span><span class="str">'Translate portfolio headlines'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t119" href="#t119">119</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'json_file'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Path to briefing JSON file'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t120" href="#t120">120</a></span><span class="t"> <span class="nam">parser</span><span class="op">.</span><span class="nam">add_argument</span><span class="op">(</span><span class="str">'--lang'</span><span class="op">,</span> <span class="nam">default</span><span class="op">=</span><span class="str">'de'</span><span class="op">,</span> <span class="nam">help</span><span class="op">=</span><span class="str">'Target language (default: de)'</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t121" href="#t121">121</a></span><span class="t"> <span class="nam">args</span> <span class="op">=</span> <span class="nam">parser</span><span class="op">.</span><span class="nam">parse_args</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t122" href="#t122">122</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t123" href="#t123">123</a></span><span class="t"> <span class="com"># Read JSON</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t124" href="#t124">124</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t125" href="#t125">125</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">json_file</span><span class="op">,</span> <span class="str">'r'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t126" href="#t126">126</a></span><span class="t"> <span class="nam">data</span> <span class="op">=</span> <span class="nam">json</span><span class="op">.</span><span class="nam">load</span><span class="op">(</span><span class="nam">f</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t127" href="#t127">127</a></span><span class="t"> <span class="key">except</span> <span class="op">(</span><span class="nam">FileNotFoundError</span><span class="op">,</span> <span class="nam">json</span><span class="op">.</span><span class="nam">JSONDecodeError</span><span class="op">)</span> <span class="key">as</span> <span class="nam">e</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t128" href="#t128">128</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#10060; Error reading {args.json_file}: {e}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t129" href="#t129">129</a></span><span class="t"> <span class="nam">sys</span><span class="op">.</span><span class="nam">exit</span><span class="op">(</span><span class="num">1</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t130" href="#t130">130</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t131" href="#t131">131</a></span><span class="t"> <span class="nam">portfolio_message</span> <span class="op">=</span> <span class="nam">data</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">'portfolio_message'</span><span class="op">,</span> <span class="str">''</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t132" href="#t132">132</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">portfolio_message</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t133" href="#t133">133</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"No portfolio_message to translate"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t134" href="#t134">134</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">ensure_ascii</span><span class="op">=</span><span class="key">False</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t135" href="#t135">135</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t136" href="#t136">136</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t137" href="#t137">137</a></span><span class="t"> <span class="com"># Extract, translate, replace</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t138" href="#t138">138</a></span><span class="t"> <span class="nam">headlines</span> <span class="op">=</span> <span class="nam">extract_headlines</span><span class="op">(</span><span class="nam">portfolio_message</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t139" href="#t139">139</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">headlines</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t140" href="#t140">140</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"No headlines found in portfolio_message"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t141" href="#t141">141</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="nam">json</span><span class="op">.</span><span class="nam">dumps</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">ensure_ascii</span><span class="op">=</span><span class="key">False</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t142" href="#t142">142</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t143" href="#t143">143</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t144" href="#t144">144</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#128221; Found {len(headlines)} headlines to translate"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t145" href="#t145">145</a></span><span class="t"> <span class="nam">translated</span> <span class="op">=</span> <span class="nam">translate_headlines</span><span class="op">(</span><span class="nam">headlines</span><span class="op">,</span> <span class="nam">args</span><span class="op">.</span><span class="nam">lang</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t146" href="#t146">146</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t147" href="#t147">147</a></span><span class="t"> <span class="com"># Update portfolio message</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t148" href="#t148">148</a></span><span class="t"> <span class="nam">data</span><span class="op">[</span><span class="str">'portfolio_message'</span><span class="op">]</span> <span class="op">=</span> <span class="nam">replace_headlines</span><span class="op">(</span><span class="nam">portfolio_message</span><span class="op">,</span> <span class="nam">headlines</span><span class="op">,</span> <span class="nam">translated</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t149" href="#t149">149</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t150" href="#t150">150</a></span><span class="t"> <span class="com"># Write back</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t151" href="#t151">151</a></span><span class="t"> <span class="key">with</span> <span class="nam">open</span><span class="op">(</span><span class="nam">args</span><span class="op">.</span><span class="nam">json_file</span><span class="op">,</span> <span class="str">'w'</span><span class="op">)</span> <span class="key">as</span> <span class="nam">f</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t152" href="#t152">152</a></span><span class="t"> <span class="nam">json</span><span class="op">.</span><span class="nam">dump</span><span class="op">(</span><span class="nam">data</span><span class="op">,</span> <span class="nam">f</span><span class="op">,</span> <span class="nam">ensure_ascii</span><span class="op">=</span><span class="key">False</span><span class="op">,</span> <span class="nam">indent</span><span class="op">=</span><span class="num">2</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t153" href="#t153">153</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t154" href="#t154">154</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">f"&#9989; Updated {args.json_file}"</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t155" href="#t155">155</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t156" href="#t156">156</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t157" href="#t157">157</a></span><span class="t"><span class="key">if</span> <span class="nam">__name__</span> <span class="op">==</span> <span class="str">'__main__'</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="exc show_exc"><span class="n"><a id="t158" href="#t158">158</a></span><span class="t"> <span class="nam">main</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_summarize_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="z_de1a740d5dc98ffd_utils_py.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>

142
htmlcov/z_de1a740d5dc98ffd_utils_py.html generated Normal file
View File

@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage for scripts/utils.py: 71%</title>
<link rel="icon" sizes="32x32" href="favicon_32_cb_c827f16f.png">
<link rel="stylesheet" href="style_cb_9ff733b0.css" type="text/css">
<script src="coverage_html_cb_dd2e7eb5.js" defer></script>
</head>
<body class="pyfile">
<header>
<div class="content">
<h1>
<span class="text">Coverage for </span><b>scripts&#8201;/&#8201;utils.py</b>:
<span class="pc_cov">71%</span>
</h1>
<aside id="help_panel_wrapper">
<input id="help_panel_state" type="checkbox">
<label for="help_panel_state">
<img id="keyboard_icon" src="keybd_closed_cb_900cfef5.png" alt="Show/hide keyboard shortcuts">
</label>
<div id="help_panel">
<p class="legend">Shortcuts on this page</p>
<div class="keyhelp">
<p>
<kbd>r</kbd>
<kbd>m</kbd>
<kbd>x</kbd>
&nbsp; toggle line displays
</p>
<p>
<kbd>j</kbd>
<kbd>k</kbd>
&nbsp; next/prev highlighted chunk
</p>
<p>
<kbd>0</kbd> &nbsp; (zero) top of page
</p>
<p>
<kbd>1</kbd> &nbsp; (one) first highlighted chunk
</p>
<p>
<kbd>[</kbd>
<kbd>]</kbd>
&nbsp; prev/next file
</p>
<p>
<kbd>u</kbd> &nbsp; up to the index
</p>
<p>
<kbd>?</kbd> &nbsp; show/hide this help
</p>
</div>
</div>
</aside>
<h2>
<span class="text">34 statements &nbsp;</span>
<button type="button" class="run button_toggle_run" value="run" data-shortcut="r" title="Toggle lines run">24<span class="text"> run</span></button>
<button type="button" class="mis show_mis button_toggle_mis" value="mis" data-shortcut="m" title="Toggle lines missing">10<span class="text"> missing</span></button>
<button type="button" class="exc show_exc button_toggle_exc" value="exc" data-shortcut="x" title="Toggle lines excluded">0<span class="text"> excluded</span></button>
</h2>
<p class="text">
<a id="prevFileLink" class="nav" href="z_de1a740d5dc98ffd_translate_portfolio_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a id="nextFileLink" class="nav" href="index.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
<aside class="hidden">
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
<button type="button" class="button_prev_chunk" data-shortcut="k"></button>
<button type="button" class="button_top_of_page" data-shortcut="0"></button>
<button type="button" class="button_first_chunk" data-shortcut="1"></button>
<button type="button" class="button_prev_file" data-shortcut="["></button>
<button type="button" class="button_next_file" data-shortcut="]"></button>
<button type="button" class="button_to_index" data-shortcut="u"></button>
<button type="button" class="button_show_hide_help" data-shortcut="?"></button>
</aside>
</div>
</header>
<main id="source">
<p class="pln"><span class="n"><a id="t1" href="#t1">1</a></span><span class="t"><span class="str">"""Shared helpers."""</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t2" href="#t2">2</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t3" href="#t3">3</a></span><span class="t"><span class="key">import</span> <span class="nam">os</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t4" href="#t4">4</a></span><span class="t"><span class="key">import</span> <span class="nam">sys</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t5" href="#t5">5</a></span><span class="t"><span class="key">import</span> <span class="nam">time</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t6" href="#t6">6</a></span><span class="t"><span class="key">from</span> <span class="nam">pathlib</span> <span class="key">import</span> <span class="nam">Path</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t7" href="#t7">7</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t8" href="#t8">8</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t9" href="#t9">9</a></span><span class="t"><span class="key">def</span> <span class="nam">ensure_venv</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t10" href="#t10">10</a></span><span class="t"> <span class="str">"""Re-exec inside local venv if available and not already active."""</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t11" href="#t11">11</a></span><span class="t"> <span class="key">if</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">get</span><span class="op">(</span><span class="str">"FINANCE_NEWS_VENV_BOOTSTRAPPED"</span><span class="op">)</span> <span class="op">==</span> <span class="str">"1"</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t12" href="#t12">12</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t13" href="#t13">13</a></span><span class="t"> <span class="key">if</span> <span class="nam">sys</span><span class="op">.</span><span class="nam">prefix</span> <span class="op">!=</span> <span class="nam">sys</span><span class="op">.</span><span class="nam">base_prefix</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t14" href="#t14">14</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t15" href="#t15">15</a></span><span class="t"> <span class="nam">venv_python</span> <span class="op">=</span> <span class="nam">Path</span><span class="op">(</span><span class="nam">__file__</span><span class="op">)</span><span class="op">.</span><span class="nam">resolve</span><span class="op">(</span><span class="op">)</span><span class="op">.</span><span class="nam">parent</span><span class="op">.</span><span class="nam">parent</span> <span class="op">/</span> <span class="str">"venv"</span> <span class="op">/</span> <span class="str">"bin"</span> <span class="op">/</span> <span class="str">"python3"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t16" href="#t16">16</a></span><span class="t"> <span class="key">if</span> <span class="key">not</span> <span class="nam">venv_python</span><span class="op">.</span><span class="nam">exists</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"> <span class="nam">print</span><span class="op">(</span><span class="str">"&#9888;&#65039; finance-news venv missing; run scripts from the repo venv to avoid dependency errors."</span><span class="op">,</span> <span class="nam">file</span><span class="op">=</span><span class="nam">sys</span><span class="op">.</span><span class="nam">stderr</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"> <span class="key">return</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"> <span class="nam">env</span> <span class="op">=</span> <span class="nam">os</span><span class="op">.</span><span class="nam">environ</span><span class="op">.</span><span class="nam">copy</span><span class="op">(</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"> <span class="nam">env</span><span class="op">[</span><span class="str">"FINANCE_NEWS_VENV_BOOTSTRAPPED"</span><span class="op">]</span> <span class="op">=</span> <span class="str">"1"</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"> <span class="nam">os</span><span class="op">.</span><span class="nam">execvpe</span><span class="op">(</span><span class="nam">str</span><span class="op">(</span><span class="nam">venv_python</span><span class="op">)</span><span class="op">,</span> <span class="op">[</span><span class="nam">str</span><span class="op">(</span><span class="nam">venv_python</span><span class="op">)</span><span class="op">]</span> <span class="op">+</span> <span class="nam">sys</span><span class="op">.</span><span class="nam">argv</span><span class="op">,</span> <span class="nam">env</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t24" href="#t24">24</a></span><span class="t"><span class="key">def</span> <span class="nam">compute_deadline</span><span class="op">(</span><span class="nam">deadline_sec</span><span class="op">:</span> <span class="nam">int</span> <span class="op">|</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">float</span> <span class="op">|</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t25" href="#t25">25</a></span><span class="t"> <span class="key">if</span> <span class="nam">deadline_sec</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t26" href="#t26">26</a></span><span class="t"> <span class="key">return</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t27" href="#t27">27</a></span><span class="t"> <span class="key">if</span> <span class="nam">deadline_sec</span> <span class="op">&lt;=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="mis show_mis"><span class="n"><a id="t28" href="#t28">28</a></span><span class="t"> <span class="key">return</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t29" href="#t29">29</a></span><span class="t"> <span class="key">return</span> <span class="nam">time</span><span class="op">.</span><span class="nam">monotonic</span><span class="op">(</span><span class="op">)</span> <span class="op">+</span> <span class="nam">deadline_sec</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t30" href="#t30">30</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t31" href="#t31">31</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t32" href="#t32">32</a></span><span class="t"><span class="key">def</span> <span class="nam">time_left</span><span class="op">(</span><span class="nam">deadline</span><span class="op">:</span> <span class="nam">float</span> <span class="op">|</span> <span class="key">None</span><span class="op">)</span> <span class="op">-></span> <span class="nam">int</span> <span class="op">|</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t33" href="#t33">33</a></span><span class="t"> <span class="key">if</span> <span class="nam">deadline</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t34" href="#t34">34</a></span><span class="t"> <span class="key">return</span> <span class="key">None</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t35" href="#t35">35</a></span><span class="t"> <span class="nam">remaining</span> <span class="op">=</span> <span class="nam">int</span><span class="op">(</span><span class="nam">deadline</span> <span class="op">-</span> <span class="nam">time</span><span class="op">.</span><span class="nam">monotonic</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t36" href="#t36">36</a></span><span class="t"> <span class="key">return</span> <span class="nam">remaining</span>&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t37" href="#t37">37</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="pln"><span class="n"><a id="t38" href="#t38">38</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t39" href="#t39">39</a></span><span class="t"><span class="key">def</span> <span class="nam">clamp_timeout</span><span class="op">(</span><span class="nam">default_timeout</span><span class="op">:</span> <span class="nam">int</span><span class="op">,</span> <span class="nam">deadline</span><span class="op">:</span> <span class="nam">float</span> <span class="op">|</span> <span class="key">None</span><span class="op">,</span> <span class="nam">minimum</span><span class="op">:</span> <span class="nam">int</span> <span class="op">=</span> <span class="num">1</span><span class="op">)</span> <span class="op">-></span> <span class="nam">int</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t40" href="#t40">40</a></span><span class="t"> <span class="nam">remaining</span> <span class="op">=</span> <span class="nam">time_left</span><span class="op">(</span><span class="nam">deadline</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t41" href="#t41">41</a></span><span class="t"> <span class="key">if</span> <span class="nam">remaining</span> <span class="key">is</span> <span class="key">None</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t42" href="#t42">42</a></span><span class="t"> <span class="key">return</span> <span class="nam">default_timeout</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t43" href="#t43">43</a></span><span class="t"> <span class="key">if</span> <span class="nam">remaining</span> <span class="op">&lt;=</span> <span class="num">0</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t44" href="#t44">44</a></span><span class="t"> <span class="key">raise</span> <span class="nam">TimeoutError</span><span class="op">(</span><span class="str">"Deadline exceeded"</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
<p class="run"><span class="n"><a id="t45" href="#t45">45</a></span><span class="t"> <span class="key">return</span> <span class="nam">max</span><span class="op">(</span><span class="nam">min</span><span class="op">(</span><span class="nam">default_timeout</span><span class="op">,</span> <span class="nam">remaining</span><span class="op">)</span><span class="op">,</span> <span class="nam">minimum</span><span class="op">)</span>&nbsp;</span><span class="r"></span></p>
</main>
<footer>
<div class="content">
<p>
<a class="nav" href="z_de1a740d5dc98ffd_translate_portfolio_py.html">&#xab; prev</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
<a class="nav" href="index.html">&#xbb; next</a>
&nbsp; &nbsp; &nbsp;
<a class="nav" href="https://coverage.readthedocs.io/en/7.13.2">coverage.py v7.13.2</a>,
created at 2026-02-01 16:34 -0800
</p>
</div>
</footer>
</body>
</html>