Release v1.0.3
This commit is contained in:
@@ -6,6 +6,13 @@ All notable changes to this project are documented in this file.
|
|||||||
|
|
||||||
No unreleased changes yet.
|
No unreleased changes yet.
|
||||||
|
|
||||||
|
## 2026-07-29 v1.0.3
|
||||||
|
|
||||||
|
- Removed invalid fixed sizing mode from Panel Tabulator grids to avoid Bokeh
|
||||||
|
layout warnings while preserving compact table layout.
|
||||||
|
- Changed the Panel Calculate action to refresh the result-file list before
|
||||||
|
loading data and removed the standalone Panel Refresh button.
|
||||||
|
|
||||||
## 2026-07-29 v1.0.2
|
## 2026-07-29 v1.0.2
|
||||||
|
|
||||||
- Added a Panel application for single-day SPBT result analysis with result-file
|
- Added a Panel application for single-day SPBT result analysis with result-file
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class SpbtDayPanelApp:
|
|||||||
value=str(self.repo_root / "data"),
|
value=str(self.repo_root / "data"),
|
||||||
)
|
)
|
||||||
self.show_all_files = pn.widgets.Checkbox(label="Show all files", value=False)
|
self.show_all_files = pn.widgets.Checkbox(label="Show all files", value=False)
|
||||||
self.refresh_button = pn.widgets.Button(label="Refresh")
|
|
||||||
self.file_select = pn.widgets.Select(
|
self.file_select = pn.widgets.Select(
|
||||||
label="SQLite result file",
|
label="SQLite result file",
|
||||||
options={},
|
options={},
|
||||||
@@ -85,7 +84,6 @@ class SpbtDayPanelApp:
|
|||||||
sizing_mode="stretch_width",
|
sizing_mode="stretch_width",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.refresh_button.on_click(self.refresh_files)
|
|
||||||
self.calculate_button.on_click(self.calculate)
|
self.calculate_button.on_click(self.calculate)
|
||||||
self.directory_input.param.watch(self.refresh_files, "value")
|
self.directory_input.param.watch(self.refresh_files, "value")
|
||||||
self.show_all_files.param.watch(self.refresh_files, "value")
|
self.show_all_files.param.watch(self.refresh_files, "value")
|
||||||
@@ -111,7 +109,7 @@ class SpbtDayPanelApp:
|
|||||||
raise ValueError(f"Selected database path is not a file: {db_path}")
|
raise ValueError(f"Selected database path is not a file: {db_path}")
|
||||||
return db_path
|
return db_path
|
||||||
|
|
||||||
def refresh_files(self, *_events: Any) -> None:
|
def refresh_files(self, *_events: Any) -> bool:
|
||||||
"""Refresh selectable SQLite files from the configured directory."""
|
"""Refresh selectable SQLite files from the configured directory."""
|
||||||
try:
|
try:
|
||||||
directory = spbt_day.normalize_directory(
|
directory = spbt_day.normalize_directory(
|
||||||
@@ -126,7 +124,7 @@ class SpbtDayPanelApp:
|
|||||||
self.file_select.options = {}
|
self.file_select.options = {}
|
||||||
self.file_select.value = None
|
self.file_select.value = None
|
||||||
self.set_status(str(exc), error=True)
|
self.set_status(str(exc), error=True)
|
||||||
return
|
return False
|
||||||
|
|
||||||
options = {path.name: str(path) for path in candidates}
|
options = {path.name: str(path) for path in candidates}
|
||||||
previous_value = self.file_select.value
|
previous_value = self.file_select.value
|
||||||
@@ -142,11 +140,14 @@ class SpbtDayPanelApp:
|
|||||||
self.set_status(f"Found {len(options):,} file(s) in {directory}.")
|
self.set_status(f"Found {len(options):,} file(s) in {directory}.")
|
||||||
else:
|
else:
|
||||||
self.set_status(f"No selectable files found in {directory}.")
|
self.set_status(f"No selectable files found in {directory}.")
|
||||||
|
return True
|
||||||
|
|
||||||
def calculate(self, *_events: Any) -> None:
|
def calculate(self, *_events: Any) -> None:
|
||||||
"""Load selected data and calculate all-pair TheoRet."""
|
"""Load selected data and calculate all-pair TheoRet."""
|
||||||
self.calculate_button.loading = True
|
self.calculate_button.loading = True
|
||||||
try:
|
try:
|
||||||
|
if not self.refresh_files():
|
||||||
|
return
|
||||||
db_path = self.selected_database_path()
|
db_path = self.selected_database_path()
|
||||||
self.min_pctg_change = float(self.min_pctg_change_input.value)
|
self.min_pctg_change = float(self.min_pctg_change_input.value)
|
||||||
|
|
||||||
@@ -260,7 +261,7 @@ class SpbtDayPanelApp:
|
|||||||
"""Return the app layout."""
|
"""Return the app layout."""
|
||||||
controls = pn.Column(
|
controls = pn.Column(
|
||||||
"## Inputs",
|
"## Inputs",
|
||||||
pn.Row(self.directory_input, self.refresh_button),
|
self.directory_input,
|
||||||
self.show_all_files,
|
self.show_all_files,
|
||||||
self.file_select,
|
self.file_select,
|
||||||
self.min_pctg_change_input,
|
self.min_pctg_change_input,
|
||||||
|
|||||||
@@ -1137,7 +1137,6 @@ def create_pair_theo_ret_analyze_grid(
|
|||||||
pagination=None,
|
pagination=None,
|
||||||
layout="fit_data_table",
|
layout="fit_data_table",
|
||||||
height=height,
|
height=height,
|
||||||
sizing_mode="fixed",
|
|
||||||
selectable=False,
|
selectable=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1161,7 +1160,6 @@ def create_selected_pair_executions_grid(
|
|||||||
pagination=None,
|
pagination=None,
|
||||||
layout="fit_data_table",
|
layout="fit_data_table",
|
||||||
height=height,
|
height=height,
|
||||||
sizing_mode="fixed",
|
|
||||||
selectable=False,
|
selectable=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1023,7 +1023,7 @@ def test_create_panel_grids_use_analyze_button_and_hidden_pair_column():
|
|||||||
assert pair_grid.disabled is False
|
assert pair_grid.disabled is False
|
||||||
assert pair_grid.pagination is None
|
assert pair_grid.pagination is None
|
||||||
assert pair_grid.layout == "fit_data_table"
|
assert pair_grid.layout == "fit_data_table"
|
||||||
assert pair_grid.sizing_mode == "fixed"
|
assert pair_grid.sizing_mode is None
|
||||||
assert executions_grid.value.columns.tolist() == [
|
assert executions_grid.value.columns.tolist() == [
|
||||||
"time",
|
"time",
|
||||||
"asset",
|
"asset",
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ def test_panel_app_uses_fast_list_template(tmp_path):
|
|||||||
app = module.SpbtDayPanelApp(repo_root=tmp_path)
|
app = module.SpbtDayPanelApp(repo_root=tmp_path)
|
||||||
view = app.view
|
view = app.view
|
||||||
|
|
||||||
|
assert not hasattr(app, "refresh_button")
|
||||||
assert isinstance(view, module.pn.template.FastListTemplate)
|
assert isinstance(view, module.pn.template.FastListTemplate)
|
||||||
assert view.title == module.APP_TITLE
|
assert view.title == module.APP_TITLE
|
||||||
assert view.sidebar_width == 430
|
assert view.sidebar_width == 430
|
||||||
@@ -216,3 +217,22 @@ def test_panel_app_calculates_pairs_and_selected_pair_outputs(tmp_path):
|
|||||||
"CLOSE",
|
"CLOSE",
|
||||||
]
|
]
|
||||||
assert app.selected_pair_market_plot.object is not None
|
assert app.selected_pair_market_plot.object is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_calculate_refreshes_file_list_before_loading(tmp_path):
|
||||||
|
module = load_panel_app_module()
|
||||||
|
data_dir = tmp_path / "data"
|
||||||
|
data_dir.mkdir()
|
||||||
|
|
||||||
|
app = module.SpbtDayPanelApp(repo_root=tmp_path)
|
||||||
|
app.directory_input.value = str(data_dir)
|
||||||
|
app.refresh_files()
|
||||||
|
assert app.file_select.value is None
|
||||||
|
|
||||||
|
db_path = data_dir / "20260617.spbt_results.db"
|
||||||
|
create_panel_fixture_db(db_path)
|
||||||
|
|
||||||
|
app.calculate()
|
||||||
|
|
||||||
|
assert app.file_select.value == str(db_path)
|
||||||
|
assert app.pair_theo_ret_table.value["pair_name"].tolist() == ["AAA-BBB"]
|
||||||
|
|||||||
Reference in New Issue
Block a user