diff --git a/README.md b/README.md index ed2e3af..6c5d752 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,49 @@ Fork of the original [lightweight-charts](louisnw01/lightweight-charts-python) with enhancements and supporting vectorbtpro workflow -* legends cookie matching line colors -* automatic colors picks if not provided -* support for left and mid price scales +* legend color matching each line color +* automatic color assignment if not provided +* support for multiple scales (right, left, middle1, middle2, histogram) * accepts df,pd.series or vectorbtpro indicator object (including unpacking multi outputs) * new markers_set method allowing to set pd.series or dataframe as markers input -* supports simple df/sr accessors `close.lw.plot()` for quick visualization of single panel chart -* supports `ch = chart([pane1, pane2], sync=True, title="Title", size="m")` to quickly display chart with N panes (`Panels`). Also supports syncing the Panels `sync=True` or using xloc. +* for quick display supports simple df/sr accessors `close.lw.plot()` for quick visualization of single panel chart +* df accessor unpacks dataframe columns and display them accordingly (ohlcv as ohlcv, vwap on the right, rsi on the left scale etc.) +* multipanes support `ch = chart([pane1, pane2], sync=True, title="Title", size="m")` to quickly display chart with N panes (`Panels`). Also supports syncing the Panels `sync=True` or using xloc. image -It should be installed directly from this repository. +## Instalation +Directly from the repository + +```pip install git+https://github.com/drew2323/lightweight-charts-python.git``` ## Examples ```python -from lightweight_charts import chart, Panel +from lightweight_charts import chart, Panel, PlotSRAccessor, PlotDFAccessor #one liner, displays close series as line on single Panel -close.lw.plot() - +close_sr.lw.plot() +``` +![alt text](image-2.png) +```python +close_sr.lw.plot(size="m") #on medium panesize +close_se.lw.plot(histogram=(trade_series, "trades")) #number of trades as histogram is displayed on top of that +``` +![alt text](image-1.png) +```python +#one liner to display OHLCV +ohlcv_df.lw.plot() #if dataframe contains other columns they are displayed too - based on standard settings (rsi-on the left, vwap - right etc.) +``` +![alt text](image-3.png) +```python +ohlcv_df.lw.plot(left=[(angle_series, "angle_momentum")]) #Another line is displayed on top of OHLCV on left scale +``` +![alt text](image-4.png) +```python +ohlcv_complex_df.lw.plot() #df containing ohlcv and other columns +``` +![alt text](image-5.png) +```python #quick few liner, displays close series with label "close" on right pricescale and rsi on left price scale, all on single Panel pane1 = Panel( right=[(close, "close")], @@ -53,7 +77,7 @@ pane2 = Panel( ) #display both Panels, sync them and pick size, use xloc -ch = chart([pane1, pane2], sync=True, title="Title", size="l", xloc=slice("1-1-2024","1-2-2024") +ch = chart([pane1, pane2], sync=True, title="Title", size="l", xloc=slice("1-1-2024","1-2-2024")) ``` @@ -88,7 +112,7 @@ ch = chart([pane1], title="Chart with EntryShort/ExitShort (yellow) and EntryLon
-# lightweight-charts-python +# lightweight-charts-python - forked from [![PyPi Release](https://img.shields.io/pypi/v/lightweight-charts?color=32a852&label=PyPi)](https://pypi.org/project/lightweight-charts/) [![Made with Python](https://img.shields.io/badge/Python-3.8+-c7a002?logo=python&logoColor=white)](https://python.org "Go to Python homepage") diff --git a/image-1.png b/image-1.png new file mode 100644 index 0000000..13a3346 Binary files /dev/null and b/image-1.png differ diff --git a/image-2.png b/image-2.png new file mode 100644 index 0000000..4b77e2c Binary files /dev/null and b/image-2.png differ diff --git a/image-3.png b/image-3.png new file mode 100644 index 0000000..0a6c246 Binary files /dev/null and b/image-3.png differ diff --git a/image-4.png b/image-4.png new file mode 100644 index 0000000..83e8625 Binary files /dev/null and b/image-4.png differ diff --git a/image-5.png b/image-5.png new file mode 100644 index 0000000..1ad3fc2 Binary files /dev/null and b/image-5.png differ diff --git a/image.png b/image.png new file mode 100644 index 0000000..13a3346 Binary files /dev/null and b/image.png differ diff --git a/lightweight_charts/__init__.py b/lightweight_charts/__init__.py index 156b719..69c97ec 100644 --- a/lightweight_charts/__init__.py +++ b/lightweight_charts/__init__.py @@ -2,4 +2,4 @@ from .abstract import AbstractChart, Window from .chart import Chart from .widgets import JupyterChart from .polygon import PolygonChart -from .helpers import chart, Panel, PlotAccessor \ No newline at end of file +from .helpers import chart, Panel, PlotSRAccessor, PlotDFAccessor \ No newline at end of file diff --git a/lightweight_charts/helpers.py b/lightweight_charts/helpers.py index 1fbca38..1ad22da 100644 --- a/lightweight_charts/helpers.py +++ b/lightweight_charts/helpers.py @@ -4,15 +4,54 @@ from .util import ( ) import pandas as pd +def append_or_extend(target_list, value): + if isinstance(value, list): + target_list.extend(value) # Extend if it's a list + else: + target_list.append(value) # Append if it's a single value + +def extend_kwargs(ohlcv, right, left, middle1, middle2, histogram, kwargs): + """ + Mutate lists based on kwargs for accessor. + Used when user added additional series to kwargs when using accessor. + """ + if 'ohlcv' in kwargs: + ohlcv = kwargs['ohlcv'] #ohlcv is only a tuple + if 'left' in kwargs: + append_or_extend(left, kwargs['left']) + if 'right' in kwargs: + append_or_extend(right, kwargs['right']) + if 'histogram' in kwargs: + append_or_extend(histogram, kwargs['histogram']) + if 'middle1' in kwargs: + append_or_extend(middle1, kwargs['middle1']) + if 'middle2' in kwargs: + append_or_extend(middle1, kwargs['middle2']) + + return ohlcv #as tuple is immutable + # Register the custom accessor @pd.api.extensions.register_series_accessor("lw") -class PlotAccessor: +class PlotSRAccessor: """ - Custom plot accessor for pandas series. + Custom plot accessor for pandas series. Quickly displays series values as line on the single pane. + + Also additional priceseries can be added on top of them. They can be added + for each scale in the correct format - either as tuple(OHLCV) or as list of tuple (others) + + # input parameter / expected format: + # ohlcv=(), #(series, entries, exits, other_markers) + # histogram=[], # [(series, name, "rgba(53, 94, 59, 0.6)", opacity)] + # right=[], + # left=[], #[(series, name, entries, exits, other_markers)] + # middle1=[], + # middle2=[], + Usage: s - series.lw.plot() - series.lw.plot(size="m") + series.lw.plot() #plot series as line + series.lw.plot(size="m") #on medium panesize + series.lw.plot(histogram=(trade_series, "trades")) #plot histogram with trades on top of that """ def __init__(self, pandas_obj): self._obj = pandas_obj @@ -20,11 +59,123 @@ class PlotAccessor: def plot(self, **kwargs): if "size" not in kwargs: kwargs["size"] = "xs" + + ohlcv = () + right = [] + left = [] + middle1 = [] + middle2 = [] + histogram = [] + + #if there are additional series in kwargs add them too + #ohlcv is returned as it is tuple thus immutable + ohlcv = extend_kwargs(ohlcv, right, left, middle1, middle2, histogram, kwargs) + + right.append((self._obj,"line")) + pane1 = Panel( - right=[(self._obj, "line")], - ) + ohlcv=ohlcv, + histogram=histogram, + right=right, + left=left, + middle1=middle1, + middle2=middle2 + ) + ch = chart([pane1], **kwargs) +@pd.api.extensions.register_dataframe_accessor("lw") +class PlotDFAccessor: + """ + Custom plot accessor for dataframe. Quickly displays all columns on the single pane. + + Series type is automatically extracted for each column based on following setting: + scale / columns + ohlcv = ['close', 'volume', 'open', 'high', 'low'] + right = ['vwap'] + left = ['rsi'] + middle1 = [] + middle2 = [] + histogram = ['buyvolume', 'sellvolume', 'trades'] + + Also additional priceseries can be added on top of them as parameters. They can be added + for each scale in the correct format - either as tuple(OHLCV) or as list of tuple (others) + + # input parameter / expected format: + # ohlcv=(), #(series, entries, exits, other_markers) + # histogram=[], # [(series, name, "rgba(53, 94, 59, 0.6)", opacity)] + # right=[], + # left=[], #[(series, name, entries, exits, other_markers)] + # middle1=[], + # middle2=[], + + + Usage: + ohlcv_df.lw.plot() + ohlcv_df.lw.plot(size="m") + ohlcv_df.lw.plot(right=(rsi_series, "rsi")) + ohlcv_df.lw.plot(right=[(rsi_series, "rsi"), (angle_series, "angle")]) + basic_data.data[SYMBOL].lw.plot(histogram=(basic_data.data[SYMBOL].close, "close"), size="m") + """ + def __init__(self, pandas_obj): + self._obj = pandas_obj + + def plot(self, **kwargs): + if "size" not in kwargs: + kwargs["size"] = "xs" + + #default settings for each pricescale + ohlcv_cols = ['close', 'volume', 'open', 'high', 'low'] + right_cols = ['vwap'] + left_cols = ['rsi'] + middle1_cols = [] + middle2_cols = [] + histogram_cols = ['buyvolume', 'sellvolume', 'trades'] + + ohlcv = () + right = [] + left = [] + middle1 = [] + middle2 = [] + histogram = [] + + for col in self._obj.columns: + if col in right_cols: + right.append((self._obj[col],col,)) + if col in histogram_cols: + histogram.append((self._obj[col],col,)) + if col in left_cols: + left.append((self._obj[col],col,)) + if col in middle1_cols: + middle1_cols.append((self._obj[col],col,)) + if col in middle2_cols: + middle2_cols.append((self._obj[col],col,)) + + ohlcv = (self._obj[ohlcv_cols],) + + #if there are additional series in kwargs add them too + ohlcv = extend_kwargs(ohlcv, right, left, middle1, middle2, histogram, kwargs) + + pane1 = Panel( + ohlcv=ohlcv, + histogram=histogram, + right=right, + left=left, + middle1=middle1, + middle2=middle2 + ) + + ch = chart([pane1], **kwargs) + + # pane1 = Panel( + # ohlcv=(), #(series, entries, exits, other_markers) + # histogram=[], # [(series, name, "rgba(53, 94, 59, 0.6)", opacity)] + # right=[], + # left=[], #[(series, name, entries, exits, other_markers)] + # middle1=[], + # middle2=[], + # ) + class Panel: """ A class to represent a panel in a chart. @@ -118,7 +269,7 @@ class Panel: self.precision = precision -def chart(panes: list[Panel], sync=False, title='', size="m", xloc=None, session: str="9:30:00, 09:30:05", precision=None): +def chart(panes: list[Panel], sync=False, title='', size="m", xloc=None, session: str="9:30:00, 09:30:05", precision=None, **kwargs): """ Function to fast render a chart with multiple panes. This function manipulates graphical output or interfaces with an external framework to display charts with synchronized diff --git a/setup.py b/setup.py index 09be3b8..e3244c0 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r', encoding='utf-8') as f: setup( name='lightweight_charts', - version='2.1.7', + version='2.2.0', packages=find_packages(), python_requires='>=3.8', install_requires=[