custom changes
This commit is contained in:
@ -5,7 +5,8 @@ from base64 import b64decode
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Callable, Union, Literal, List, Optional
|
from typing import Callable, Union, Literal, List, Optional
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import random
|
||||||
|
from vectorbtpro.indicators import IndicatorFactory
|
||||||
from .table import Table
|
from .table import Table
|
||||||
from .toolbox import ToolBox
|
from .toolbox import ToolBox
|
||||||
from .drawings import Box, HorizontalLine, RayLine, TrendLine, TwoPointDrawing, VerticalLine, VerticalSpan
|
from .drawings import Box, HorizontalLine, RayLine, TrendLine, TwoPointDrawing, VerticalLine, VerticalSpan
|
||||||
@ -19,6 +20,77 @@ from .util import (
|
|||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
INDEX = os.path.join(current_dir, 'js', 'index.html')
|
INDEX = os.path.join(current_dir, 'js', 'index.html')
|
||||||
|
|
||||||
|
def is_indicator(variable):
|
||||||
|
# Get the module path of the variable's type
|
||||||
|
module_path = variable.__class__.__module__
|
||||||
|
# Check if it starts with 'vectorbtpro.indicators'
|
||||||
|
return module_path.startswith('vectorbtpro.indicators')
|
||||||
|
|
||||||
|
# # Predefined colors that stand out well on dark backgrounds
|
||||||
|
# COLORS = [
|
||||||
|
# 'rgba(255, 0, 0, 0.6)', # Red
|
||||||
|
# 'rgba(0, 255, 0, 0.6)', # Green
|
||||||
|
# 'rgba(0, 0, 255, 0.6)', # Blue
|
||||||
|
# 'rgba(255, 255, 0, 0.6)', # Yellow
|
||||||
|
# 'rgba(255, 165, 0, 0.6)', # Orange
|
||||||
|
# 'rgba(75, 0, 130, 0.6)', # Indigo
|
||||||
|
# 'rgba(238, 130, 238, 0.6)', # Violet
|
||||||
|
# 'rgba(0, 255, 255, 0.6)', # Cyan
|
||||||
|
# 'rgba(255, 192, 203, 0.6)', # Pink
|
||||||
|
# 'rgba(0, 128, 128, 0.6)', # Teal
|
||||||
|
# 'rgba(128, 0, 128, 0.6)', # Purple
|
||||||
|
# 'rgba(255, 215, 0, 0.6)', # Gold
|
||||||
|
# 'rgba(173, 255, 47, 0.6)', # Green Yellow
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# def get_next_color():
|
||||||
|
# return random.choice(COLORS)
|
||||||
|
|
||||||
|
# Predefined pool of colors
|
||||||
|
COLORS = [
|
||||||
|
"#63AA57", "#8F8AB0", "#4CAA4E", "#E24AEE", "#D06AA6", "#7891BA", "#A39A34", "#8A94A2", "#61BB2F",
|
||||||
|
"#FD569D", "#1EB6E1", "#379AC9", "#FD6F2E", "#8C9858", "#39A4A3", "#6D97F4", "#1ECB01", "#FA5B16", "#A6891C",
|
||||||
|
"#48CF10", "#D27B26", "#D56B55", "#FE3AB8", "#E35C51", "#EC4FE6", "#E250A3", "#BA618E", "#1BC074", "#C57784",
|
||||||
|
"#888BC5", "#4FA452", "#80885C", "#B97272", "#33BF98", "#B7961D", "#A07284", "#02E54E", "#AF7F35", "#F852EF",
|
||||||
|
"#6D955B", "#E0676E", "#F73DEC", "#CE53FD", "#9773D3", "#649E81", "#D062CE", "#AB73E7", "#A4729C", "#E76A07",
|
||||||
|
"#E85CCB", "#A16FB1", "#4BB859", "#B25EE2", "#8580CE", "#A275EF", "#AC9245", "#4D988D", "#B672C9", "#4CA96E",
|
||||||
|
"#C9873E", "#5BB147", "#10C783", "#D7647D", "#CB893A", "#A586BA", "#28C0A2", "#61A755", "#0EB7C5", "#2DADBC",
|
||||||
|
"#17BB71", "#2BC733", "#2BB890", "#F04EF8", "#699580", "#A88809", "#EB3FF6", "#A75ED3", "#859171", "#BB6285",
|
||||||
|
"#81A147", "#AD7CD2", "#65B630", "#C9616C", "#BD5EFA", "#7A9F30", "#2AB6AB", "#FC496A", "#687FC7", "#DB40E7",
|
||||||
|
"#07BCE9", "#509F63", "#EC4FDD", "#A079BE", "#C17297", "#E447C2", "#E95AD9", "#9FA01E", "#7E86CF", "#21E316",
|
||||||
|
"#1CABF9", "#17C24F", "#9C9254", "#C97994", "#4BA9DA", "#0DD595", "#13BEA8", "#C2855D", "#DF6C13", "#60B370",
|
||||||
|
"#0FC3F6", "#C1830E", "#3AC917", "#0EBBB0", "#CC50B4", "#B768EC", "#D47F49", "#B47BC5", "#38ADBD", "#05DC53",
|
||||||
|
"#44CD4E", "#838E65", "#49D70F", "#2DADBE", "#2CB0C9", "#DA703E", "#06B5CA", "#7BAF3E", "#918E79", "#2AA5E5",
|
||||||
|
"#C37F5E", "#07B8C9", "#4CBA27", "#E752C6", "#7F93B2", "#4798CD", "#45AA4C", "#4DB666", "#7683A7", "#758685",
|
||||||
|
"#4B9FAD", "#9280FD", "#6682DD", "#42ACBE", "#C1609F", "#D850DB", "#649A62", "#54CC22", "#AD81C1", "#BF7A43",
|
||||||
|
"#0FCEA5", "#D06DAF", "#87799B", "#4DA94E", "#2FD654", "#07D587", "#21CF0C", "#03CF34", "#42C771", "#D563CD",
|
||||||
|
"#6D9E9A", "#C76C59", "#68B368", "#11BCE5", "#0DCFB3", "#9266D8", "#BF67F6", "#88A04E", "#73BE17", "#67B437",
|
||||||
|
"#8586E4", "#9F8749", "#479CA5", "#CC777E", "#4FAF46", "#9D9836", "#918DAF", "#D167B8", "#6F9DA5", "#2BB167",
|
||||||
|
"#16B8BC", "#B4861F", "#A08487", "#67B357", "#5CAA5C", "#20CA49", "#D18813", "#15D63F", "#C8618F", "#887E92",
|
||||||
|
"#21C457", "#4EA8CE", "#53BE49", "#5A86D5", "#BD7E4E", "#27B0A1", "#33CF42", "#709083", "#38A8DE", "#4CA762",
|
||||||
|
"#1EA4FF", "#DE3EE4", "#70A860", "#39A3C8", "#6BBB39", "#F053F4", "#8C7FB5", "#969F21", "#B19841", "#E57148",
|
||||||
|
"#C25DA7", "#6DA979", "#B27D73", "#7F9786", "#41AC99", "#C58848", "#948F9E", "#6BB620", "#81AB3B", "#09DE44",
|
||||||
|
"#43A9D2", "#41B0D7", "#20ACAA", "#649FCB", "#CD8345", "#A88669", "#3EA5E7", "#F36A19", "#E06B48", "#8388BD",
|
||||||
|
"#EC6153", "#639082", "#52CA32", "#878BAA", "#02BCDB", "#828FD9", "#3DC07F", "#29D46A", "#9C7CC1", "#EB7713",
|
||||||
|
"#F95F6A", "#E25F4C", "#589994", "#D45AB7", "#DE66AB", "#B8715F", "#E850F4", "#FB6420", "#C2832C", "#6383C5",
|
||||||
|
"#D57A58", "#EF652C", "#02D71A", "#ED664D", "#60A526"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Iterator to keep track of the current color index
|
||||||
|
color_index = 0
|
||||||
|
|
||||||
|
def get_next_color():
|
||||||
|
global color_index
|
||||||
|
# Get the next color from the list
|
||||||
|
color = COLORS[color_index]
|
||||||
|
# Convert the color from HEX to RGBA format
|
||||||
|
color_index = (color_index + 1) % len(COLORS)
|
||||||
|
return hex_to_rgba(color)
|
||||||
|
|
||||||
|
def hex_to_rgba(hex_color, alpha=0.8):
|
||||||
|
hex_color = hex_color.lstrip('#')
|
||||||
|
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
|
||||||
|
return f'rgba({r}, {g}, {b}, {alpha})'
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
_id_gen = IDGen()
|
_id_gen = IDGen()
|
||||||
@ -218,11 +290,16 @@ class SeriesCommon(Pane):
|
|||||||
arg = self._interval * (arg.timestamp() // self._interval)+self.offset
|
arg = self._interval * (arg.timestamp() // self._interval)+self.offset
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
def set(self, df: Optional[pd.DataFrame] = None, format_cols: bool = True):
|
def set(self, df: Optional[Union[pd.DataFrame, pd.Series]] = None, format_cols: bool = True):
|
||||||
if df is None or df.empty:
|
if df is None or (isinstance(df, pd.DataFrame) and df.empty):
|
||||||
self.run_script(f'{self.id}.series.setData([])')
|
self.run_script(f'{self.id}.series.setData([])')
|
||||||
self.data = pd.DataFrame()
|
self.data = pd.DataFrame()
|
||||||
return
|
return
|
||||||
|
if is_indicator(df):
|
||||||
|
df = df.real
|
||||||
|
#if df is pd.Series then convert to df
|
||||||
|
if isinstance(df, pd.Series):
|
||||||
|
df = df.to_frame(name=self.name)
|
||||||
if format_cols:
|
if format_cols:
|
||||||
df = self._df_datetime_format(df, exclude_lowercase=self.name)
|
df = self._df_datetime_format(df, exclude_lowercase=self.name)
|
||||||
if self.name:
|
if self.name:
|
||||||
@ -424,12 +501,14 @@ class SeriesCommon(Pane):
|
|||||||
|
|
||||||
|
|
||||||
class Line(SeriesCommon):
|
class Line(SeriesCommon):
|
||||||
def __init__(self, chart, name, color, style, width, price_line, price_label, crosshair_marker=True):
|
def __init__(self, chart, name, color, style, width, price_line, price_label, crosshair_marker=True, priceScaleId="right"):
|
||||||
|
|
||||||
super().__init__(chart, name)
|
super().__init__(chart, name)
|
||||||
self.color = color
|
self.color = color
|
||||||
|
|
||||||
self.run_script(f'''
|
#priceScaleId_line = f"priceScaleId: '{priceScaleId}'," if priceScaleId is not None else ''
|
||||||
|
|
||||||
|
script = f'''
|
||||||
{self.id} = {self._chart.id}.createLineSeries(
|
{self.id} = {self._chart.id}.createLineSeries(
|
||||||
"{name}",
|
"{name}",
|
||||||
{{
|
{{
|
||||||
@ -437,6 +516,7 @@ class Line(SeriesCommon):
|
|||||||
lineStyle: {as_enum(style, LINE_STYLE)},
|
lineStyle: {as_enum(style, LINE_STYLE)},
|
||||||
lineWidth: {width},
|
lineWidth: {width},
|
||||||
lastValueVisible: {jbool(price_label)},
|
lastValueVisible: {jbool(price_label)},
|
||||||
|
priceScaleId: '{priceScaleId}',
|
||||||
priceLineVisible: {jbool(price_line)},
|
priceLineVisible: {jbool(price_line)},
|
||||||
crosshairMarkerVisible: {jbool(crosshair_marker)},
|
crosshairMarkerVisible: {jbool(crosshair_marker)},
|
||||||
{"""autoscaleInfoProvider: () => ({
|
{"""autoscaleInfoProvider: () => ({
|
||||||
@ -448,7 +528,11 @@ class Line(SeriesCommon):
|
|||||||
""" if chart._scale_candles_only else ''}
|
""" if chart._scale_candles_only else ''}
|
||||||
}}
|
}}
|
||||||
)
|
)
|
||||||
null''')
|
null'''
|
||||||
|
|
||||||
|
#print(script)
|
||||||
|
|
||||||
|
self.run_script(script)
|
||||||
|
|
||||||
# def _set_trend(self, start_time, start_value, end_time, end_value, ray=False, round=False):
|
# def _set_trend(self, start_time, start_value, end_time, end_value, ray=False, round=False):
|
||||||
# if round:
|
# if round:
|
||||||
@ -465,6 +549,12 @@ class Line(SeriesCommon):
|
|||||||
# {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}})
|
# {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}})
|
||||||
# ''')
|
# ''')
|
||||||
|
|
||||||
|
def scale(self, scale_margin_top: float = 0.0, scale_margin_bottom: float = 0.0):
|
||||||
|
self.run_script(f'''
|
||||||
|
{self.id}.series.priceScale().applyOptions({{
|
||||||
|
scaleMargins: {{top: {scale_margin_top}, bottom: {scale_margin_bottom}}}
|
||||||
|
}})''')
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Irreversibly deletes the line, as well as the object that contains the line.
|
Irreversibly deletes the line, as well as the object that contains the line.
|
||||||
@ -719,24 +809,28 @@ class AbstractChart(Candlestick, Pane):
|
|||||||
self.run_script(f'{self.id}.chart.timeScale().fitContent()')
|
self.run_script(f'{self.id}.chart.timeScale().fitContent()')
|
||||||
|
|
||||||
def create_line(
|
def create_line(
|
||||||
self, name: str = '', color: str = 'rgba(214, 237, 255, 0.6)',
|
self, name: str = '', color: str = None,
|
||||||
style: LINE_STYLE = 'solid', width: int = 2,
|
style: LINE_STYLE = 'solid', width: int = 2,
|
||||||
price_line: bool = True, price_label: bool = True
|
price_line: bool = False, price_label: bool = False, priceScaleId: str = "right"
|
||||||
) -> Line:
|
) -> Line:
|
||||||
"""
|
"""
|
||||||
Creates and returns a Line object.
|
Creates and returns a Line object.
|
||||||
"""
|
"""
|
||||||
self._lines.append(Line(self, name, color, style, width, price_line, price_label))
|
if color is None:
|
||||||
|
color = get_next_color()
|
||||||
|
self._lines.append(Line(self, name, color, style, width, price_line, price_label, True, priceScaleId))
|
||||||
return self._lines[-1]
|
return self._lines[-1]
|
||||||
|
|
||||||
def create_histogram(
|
def create_histogram(
|
||||||
self, name: str = '', color: str = 'rgba(214, 237, 255, 0.6)',
|
self, name: str = '', color: str = None,
|
||||||
price_line: bool = True, price_label: bool = True,
|
price_line: bool = False, price_label: bool = True,
|
||||||
scale_margin_top: float = 0.0, scale_margin_bottom: float = 0.0
|
scale_margin_top: float = 0.0, scale_margin_bottom: float = 0.0
|
||||||
) -> Histogram:
|
) -> Histogram:
|
||||||
"""
|
"""
|
||||||
Creates and returns a Histogram object.
|
Creates and returns a Histogram object.
|
||||||
"""
|
"""
|
||||||
|
if color is None:
|
||||||
|
color = get_next_color()
|
||||||
return Histogram(
|
return Histogram(
|
||||||
self, name, color, price_line, price_label,
|
self, name, color, price_line, price_label,
|
||||||
scale_margin_top, scale_margin_bottom)
|
scale_margin_top, scale_margin_bottom)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -219,7 +219,7 @@ body {
|
|||||||
z-index: 3000;
|
z-index: 3000;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
left: 10px;
|
left: 70px;
|
||||||
display: none;
|
display: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -134,6 +134,9 @@ export class Handler {
|
|||||||
rightPriceScale: {
|
rightPriceScale: {
|
||||||
scaleMargins: {top: 0.3, bottom: 0.25},
|
scaleMargins: {top: 0.3, bottom: 0.25},
|
||||||
},
|
},
|
||||||
|
leftPriceScale: {
|
||||||
|
visible:true,
|
||||||
|
scaleMargins:{top:.3,bottom:.25}},
|
||||||
timeScale: {timeVisible: true, secondsVisible: false},
|
timeScale: {timeVisible: true, secondsVisible: false},
|
||||||
crosshair: {
|
crosshair: {
|
||||||
mode: CrosshairMode.Normal,
|
mode: CrosshairMode.Normal,
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export class Legend {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
makeSeriesRow(name: string, series: ISeriesApi<SeriesType>) {
|
makeSeriesRow(name: string, series: ISeriesApi<SeriesType>) {
|
||||||
const strokeColor = '#FFF';
|
const strokeColor = series.options().color;
|
||||||
let openEye = `
|
let openEye = `
|
||||||
<path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 21.998437 12 C 21.998437 12 18.998437 18 12 18 C 5.001562 18 2.001562 12 2.001562 12 C 2.001562 12 5.001562 6 12 6 C 18.998437 6 21.998437 12 21.998437 12 Z M 21.998437 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>
|
<path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 21.998437 12 C 21.998437 12 18.998437 18 12 18 C 5.001562 18 2.001562 12 2.001562 12 C 2.001562 12 5.001562 6 12 6 C 18.998437 6 21.998437 12 21.998437 12 Z M 21.998437 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>
|
||||||
<path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 15 12 C 15 13.654687 13.654687 15 12 15 C 10.345312 15 9 13.654687 9 12 C 9 10.345312 10.345312 9 12 9 C 13.654687 9 15 10.345312 15 12 Z M 15 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>\`
|
<path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 15 12 C 15 13.654687 13.654687 15 12 15 C 10.345312 15 9 13.654687 9 12 C 9 10.345312 10.345312 9 12 9 C 13.654687 9 15 10.345312 15 12 Z M 15 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>\`
|
||||||
@ -110,7 +110,7 @@ export class Legend {
|
|||||||
row.appendChild(toggle)
|
row.appendChild(toggle)
|
||||||
this.div.appendChild(row)
|
this.div.appendChild(row)
|
||||||
|
|
||||||
const color = series.options().baseLineColor;
|
const color = series.options().color;
|
||||||
this._lines.push({
|
this._lines.push({
|
||||||
name: name,
|
name: name,
|
||||||
div: div,
|
div: div,
|
||||||
@ -217,7 +217,7 @@ export class Legend {
|
|||||||
const format = e.series.options().priceFormat as PriceFormatBuiltIn
|
const format = e.series.options().priceFormat as PriceFormatBuiltIn
|
||||||
price = this.legendItemFormat(data.value, format.precision) // couldn't this just be line.options().precision?
|
price = this.legendItemFormat(data.value, format.precision) // couldn't this just be line.options().precision?
|
||||||
}
|
}
|
||||||
e.div.innerHTML = `<span style="color: ${e.solid};">▨</span> ${e.name} : ${price}`
|
e.div.innerHTML = `<span style="color: ${e.solid};">▨ ${e.name} : ${price}</span>`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user