Skip to content

Commit

Permalink
...
Browse files Browse the repository at this point in the history
  • Loading branch information
furechan committed Sep 23, 2024
1 parent 96c02ac commit 874052f
Show file tree
Hide file tree
Showing 10 changed files with 1,121 additions and 1,467 deletions.
98 changes: 49 additions & 49 deletions examples/candlesticks-chart.ipynb

Large diffs are not rendered by default.

536 changes: 97 additions & 439 deletions examples/multiple-plots.ipynb

Large diffs are not rendered by default.

233 changes: 72 additions & 161 deletions examples/ohlc-chart.ipynb

Large diffs are not rendered by default.

207 changes: 101 additions & 106 deletions examples/typical-usage.ipynb

Large diffs are not rendered by default.

992 changes: 360 additions & 632 deletions misc/date-mapper.ipynb

Large diffs are not rendered by default.

390 changes: 390 additions & 0 deletions misc/mapping-proto.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ classifiers = [
]

[project.optional-dependencies]
dev = ["yfinance", "pytest", "ipykernel", "ta-lib", "ruff"]
extras = ["yfinance", "pytest", "ipykernel", "ta-lib", "ruff"]

[tool.hatch.version]
path = "src/mplchart/__init__.py"
Expand Down
56 changes: 28 additions & 28 deletions src/mplchart/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def valid_target(target):
"""whether the target bname is valid"""
return target in ("main", "samex", "twinx", "above", "below")

def inspect_data(self, data):
def init_mapper(self, data):
"""initalizes chart from data"""

if self.source_data is None:
Expand All @@ -114,28 +114,48 @@ def inspect_data(self, data):
if not self.mapper_done:
self.config_mapper(data=data)

def config_mapper(self, *, data=None):
"""Configures the date mapper from the original data"""

self.mapper_done = True

if self.use_calendar:
self.mapper = RawDateMapper(
index=data.index, start=self.start, end=self.end, max_bars=self.max_bars
)
elif data is not None:
self.mapper = DateIndexMapper(
index=data.index, start=self.start, end=self.end, max_bars=self.max_bars
)
else:
raise ValueError("Cannot create mapper. data is None!")

if self.mapper:
ax = self.root_axes()
self.mapper.config_axes(ax)

def rebase_data(self, data):
if self.source_data is None:
warnings.warn("No source data to rebase to!")
return data

source_data = self.mapper.slice(self.source_data)
index = data.index.intersection(source_data.index)
source_data = self.mapper.extract_df(self.source_data)
mapped_data = self.mapper.extract_df(data)

if not len(index):
if not len(data) or not len(source_data):
warnings.warn("No intersection of data!")
return data

dp = data.loc[index[0]]
sp = source_data.loc[index[0]]
factor = sp / dp
sp = source_data.loc[0].close
mp = mapped_data.loc[0].close
factor = sp / mp

return data.filter(["open", "high", "low", "close"]) * factor

def extract_df(self, data):
"""extract dataframe subset"""

self.inspect_data(data)
self.init_mapper(data)

if self.mapper:
return self.mapper.extract_df(data)
Expand Down Expand Up @@ -164,26 +184,6 @@ def set_title(self, title):
ax = self.root_axes()
ax.set_title(title)

def config_mapper(self, *, data=None):
"""Configures the date mapper from the original data"""

self.mapper_done = True

if self.use_calendar:
self.mapper = RawDateMapper(
index=data.index, start=self.start, end=self.end, max_bars=self.max_bars
)
elif data is not None:
self.mapper = DateIndexMapper(
index=data.index, start=self.start, end=self.end, max_bars=self.max_bars
)
else:
raise ValueError("Cannot create mapper. data is None!")

if self.mapper:
ax = self.root_axes()
self.mapper.config_axes(ax)

def config_axes(self, ax, root=False):
"""configures axes"""

Expand Down
68 changes: 17 additions & 51 deletions src/mplchart/mapper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" date mapper """
"""date mapper"""

import numpy as np
import pandas as pd
Expand All @@ -7,56 +7,29 @@
from .formatter import DateIndexFormatter


def get_boundaries(index, max_bars=None, start=None, end=None):
if index is None:
raise ValueError("Index is None!")

if max_bars and (start or end):
raise ValueError(
"Cannot specify `max_bars` together with `start` or `end`!"
)

if start and end and start >= end:
raise ValueError("Argument `start` value must be less than `end` value!")

if max_bars and len(index) > max_bars:
start = index[-max_bars]

if start is None and len(index):
start = index[0]

if end is None and len(index):
end = index[-1]

return start, end


class RawDateMapper:
"""RawDate mapper between date and number using matplotlib.dates"""

def __init__(self, index, max_bars=None, start=None, end=None):
start, end = get_boundaries(index, max_bars=max_bars, start=start, end=end)
if start or end:
locs = index.slice_indexer(start=start, end=end)
index = index[locs]

if max_bars > 0:
index = index[-max_bars:]

self.start = start
self.end = end
self.start = index[0]
self.end = index[-1]

def map_date(self, date): # needed for plot_vline
"""returns date as number/coordinate"""
return date

def slice(self, data):
"""slice data on dates"""

if self.start or self.end:
data = data.loc[self.start: self.end]

return data

def extract_df(self, data):
"""extracts dataframe by mapping date to number/coordinate"""

if self.start or self.end:
data = data.loc[self.start: self.end]
data = data.loc[self.start:self.end]

return data

Expand All @@ -73,15 +46,19 @@ def config_axes(self, ax):
pass



class DateIndexMapper:
"""DateIndex mapper between date and position/coordinate"""

def __init__(self, index, *, max_bars=None, start=None, end=None):
start, end = get_boundaries(index, max_bars=max_bars, start=start, end=end)
if start or end:
locs = index.slice_indexer(start=start, end=end)
index = index[locs]

if max_bars > 0:
index = index[-max_bars:]

self.index = index
self.start = start
self.end = end

def map_date(self, date): # nedded for plot_vline
"""location of date in index"""
Expand All @@ -90,22 +67,11 @@ def map_date(self, date): # nedded for plot_vline

return result[0]

def slice(self, data):
"""slice data on dates"""

if self.start or self.end:
data = data.loc[self.start: self.end]

return data

def extract_df(self, data):
"""extracts dataframe by mapping date to position/coordinate"""

xloc = pd.Series(np.arange(len(self.index)), index=self.index)

if self.start or self.end:
xloc = xloc[self.start: self.end]

xloc, data = xloc.align(data, join="inner")

return data.set_axis(xloc)
Expand Down
6 changes: 6 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
FOLDER = Path(__file__).parent


@task
def install(c):
"""Check package versions"""
c.run("pip install -e \".[extras]\"")


@task
def info(c):
"""Check package versions"""
Expand Down

0 comments on commit 874052f

Please sign in to comment.