markers finished, subsecond precision supported

This commit is contained in:
David Brazda
2024-06-11 09:06:56 +02:00
parent 23fbf3a23d
commit 9bd4d089b5
2 changed files with 73 additions and 25 deletions

View File

@ -13,7 +13,7 @@ from .drawings import Box, HorizontalLine, RayLine, TrendLine, TwoPointDrawing,
from .topbar import TopBar
from .util import (
BulkRunScript, Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT,
LINE_STYLE, MARKER_POSITION, MARKER_SHAPE, CROSSHAIR_MODE,
LINE_STYLE, MARKER_POSITION, MARKER_SHAPE, CROSSHAIR_MODE, MARKER_TYPE,
PRICE_SCALE_MODE, marker_position, marker_shape, js_data,
)
@ -274,12 +274,12 @@ class SeriesCommon(Pane):
self._set_interval(df)
if not pd.api.types.is_datetime64_any_dtype(df['time']):
df['time'] = pd.to_datetime(df['time'])
df['time'] = df['time'].astype('int64') // 10 ** 9
df['time'] = df['time'].astype('int64') / 10 ** 9 #removed integer divison // 10 ** 9 to keep subseconds precision
return df
def _series_datetime_format(self, series: pd.Series, exclude_lowercase=None):
series = series.copy()
series.index = self._format_labels(series, series.index, series.name, exclude_lowercase)
series.index = self._format_labels(series, series.name, series.index, exclude_lowercase)
series['time'] = self._single_datetime_format(series['time'])
return series
@ -325,26 +325,71 @@ class SeriesCommon(Pane):
def _update_markers(self):
self.run_script(f'{self.id}.series.setMarkers({json.dumps(list(self.markers.values()))})')
def markers_set(self, markers_series: pd.Series,
def markers_set(self, markers: Union[pd.Series, pd.DataFrame],
type: MARKER_TYPE = None,
col_name: Optional[str] = None,
position: MARKER_POSITION = 'below',
shape: MARKER_SHAPE = 'arrow_up',
color: str = '#2196F3', text: str = ''):
"""
Creates multiple markers from pd series.
:param markers: A pandas Series with DateTimeIndex and boolean values.
Adds multiple markers from pd series or Dataframe
:param markers: A pandas Series or Dataframe with DateTimeIndex and boolean values.
The index should be DateTimeIndex and values should be True/False.
:param type: The type of the marker to quickly style entries or exits
:param col_name: The name of the column to use in case of DataFrame
:param position: The position of the marker.
:param shape: The shape of the marker.
:param color: The color of the marker.
:return: a list of marker ids.
It refreshes the markers, deleting the current.
It adds new markers to the chart. Existing markers will remain.
To delete markers use remove_marker() or clear_markers()
"""
if not isinstance(markers_series, pd.Series):
#raise exception
raise TypeError('Markers must be a pd.Series')
series = self._series_datetime_format(markers_series, exclude_lowercase=self.name)
if type is not None:
match type:
case "entries":
position = "below"
shape = "arrow_up"
color = "blue"
case "exits":
position = "above"
shape = "arrow_down"
color = "red"
if isinstance(markers, pd.Series):
markers = markers.to_frame(name="markers")
markers = self._df_datetime_format(markers)
# Get the list of columns in the DataFrame - must be datetimeindex
#either with one column, or col_name is set
columns = markers.columns.tolist()
if "time" not in columns:
raise ValueError("Time column required in the dataframe.")
# If there are exactly two columns
if len(columns) == 2:
# Rename the non-"time" column to "value"
other_col = [col for col in columns if col != "time"][0]
markers.rename(columns={other_col: "value"}, inplace=True)
# If there are more than two columns
elif len(columns) > 2:
if col_name in columns:
# Keep "time" and col_name column and rename it to "value"
markers = markers[["time", col_name]]
markers.rename(columns={col_name: "value"}, inplace=True)
else:
raise ValueError("Specify a column name in the dataframe.")
else:
raise ValueError("No matching columns in the dataframe.")
marker_ids = []
self.markers = {}
for timestamp, value in series.iteritems():
if value:
#self.markers = {}
valid_rows = markers[markers['value']] # Filter rows where value is True
for timestamp in valid_rows['time']:
marker_id = self.win._id_gen.generate()
self.markers[marker_id] = {
"time": timestamp,
@ -355,6 +400,8 @@ class SeriesCommon(Pane):
}
marker_ids.append(marker_id)
# Sort markers by time
self.markers = dict(sorted(self.markers.items(), key=lambda item: item[1]["time"]))
self._update_markers()
return marker_ids

View File

@ -63,6 +63,7 @@ def js_json(d: dict):
def jbool(b: bool): return 'true' if b is True else 'false' if b is False else None
MARKER_TYPE = Literal['entries', 'exits']
LINE_STYLE = Literal['solid', 'dotted', 'dashed', 'large_dashed', 'sparse_dotted']