Enhancements:
- Hotkeys can now use any character, and modifier keys are not required. - Refactored the colors of the topbar, searchbox, toolbox, and widgets for consistency. - Toolbox/interval refactoring and simplification. - Histograms now show up in the legend, and will use shorthand notation by default (e.g 34k rather than 34000).
This commit is contained in:
@ -3,7 +3,7 @@ import os.path
|
|||||||
project = 'lightweight-charts-python'
|
project = 'lightweight-charts-python'
|
||||||
copyright = '2023, louisnw'
|
copyright = '2023, louisnw'
|
||||||
author = 'louisnw'
|
author = 'louisnw'
|
||||||
release = '1.0.17.2'
|
release = '1.0.17.6'
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
"myst_parser",
|
"myst_parser",
|
||||||
|
|||||||
@ -27,6 +27,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
```
|
```
|
||||||
___
|
___
|
||||||
|
|
||||||
## Synced Line Chart
|
## Synced Line Chart
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -48,3 +49,47 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
chart.show(block=True)
|
chart.show(block=True)
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
## Grid of 4 with maximize buttons
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pandas as pd
|
||||||
|
from lightweight_charts import Chart
|
||||||
|
|
||||||
|
# ascii symbols
|
||||||
|
FULLSCREEN = '■'
|
||||||
|
CLOSE = '×'
|
||||||
|
|
||||||
|
|
||||||
|
def on_max(target_chart):
|
||||||
|
button = target_chart.topbar['max']
|
||||||
|
if button.value == CLOSE:
|
||||||
|
[c.resize(0.5, 0.5) for c in charts]
|
||||||
|
button.set(FULLSCREEN)
|
||||||
|
else:
|
||||||
|
for chart in charts:
|
||||||
|
width, height = (1, 1) if chart == target_chart else (0, 0)
|
||||||
|
chart.resize(width, height)
|
||||||
|
button.set(CLOSE)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main_chart = Chart(inner_width=0.5, inner_height=0.5)
|
||||||
|
charts = [
|
||||||
|
main_chart,
|
||||||
|
main_chart.create_subchart(position='top', width=0.5, height=0.5),
|
||||||
|
main_chart.create_subchart(position='left', width=0.5, height=0.5),
|
||||||
|
main_chart.create_subchart(position='right', width=0.5, height=0.5),
|
||||||
|
]
|
||||||
|
|
||||||
|
df = pd.read_csv('examples/1_setting_data/ohlcv.csv')
|
||||||
|
for i, c in enumerate(charts):
|
||||||
|
chart_number = str(i+1)
|
||||||
|
c.watermark(chart_number)
|
||||||
|
c.topbar.textbox('number', chart_number)
|
||||||
|
c.topbar.button('max', FULLSCREEN, False, align='right', func=on_max)
|
||||||
|
c.set(df)
|
||||||
|
|
||||||
|
charts[0].show(block=True)
|
||||||
|
```
|
||||||
|
|||||||
@ -105,6 +105,23 @@ class Window:
|
|||||||
''', run_last=True)
|
''', run_last=True)
|
||||||
return subchart
|
return subchart
|
||||||
|
|
||||||
|
def style(self, background_color: str = '#0c0d0f', hover_background_color: str = '#3c434c',
|
||||||
|
click_background_color: str = '#50565E',
|
||||||
|
active_background_color: str = 'rgba(0, 122, 255, 0.7)',
|
||||||
|
muted_background_color: str = 'rgba(0, 122, 255, 0.3)',
|
||||||
|
border_color: str = '#3C434C', color: str = '#d8d9db', active_color: str = '#ececed'):
|
||||||
|
self.run_script(f'''
|
||||||
|
window.pane = {{
|
||||||
|
backgroundColor: '{background_color}',
|
||||||
|
hoverBackgroundColor: '{hover_background_color}',
|
||||||
|
clickBackgroundColor: '{click_background_color}',
|
||||||
|
activeBackgroundColor: '{active_background_color}',
|
||||||
|
mutedBackgroundColor: '{muted_background_color}',
|
||||||
|
borderColor: '{border_color}',
|
||||||
|
color: '{color}',
|
||||||
|
activeColor: '{active_color}',
|
||||||
|
}}''')
|
||||||
|
|
||||||
|
|
||||||
class SeriesCommon(Pane):
|
class SeriesCommon(Pane):
|
||||||
def __init__(self, chart: 'AbstractChart', name: str = None):
|
def __init__(self, chart: 'AbstractChart', name: str = None):
|
||||||
@ -113,7 +130,7 @@ class SeriesCommon(Pane):
|
|||||||
if hasattr(chart, '_interval'):
|
if hasattr(chart, '_interval'):
|
||||||
self._interval = chart._interval
|
self._interval = chart._interval
|
||||||
else:
|
else:
|
||||||
self._interval = pd.Timedelta(seconds=1)
|
self._interval = 1
|
||||||
self._last_bar = None
|
self._last_bar = None
|
||||||
self.name = name
|
self.name = name
|
||||||
self.num_decimals = 2
|
self.num_decimals = 2
|
||||||
@ -124,11 +141,36 @@ class SeriesCommon(Pane):
|
|||||||
common_interval = df['time'].diff().value_counts()
|
common_interval = df['time'].diff().value_counts()
|
||||||
if common_interval.empty:
|
if common_interval.empty:
|
||||||
return
|
return
|
||||||
self._interval = common_interval.index[0]
|
self._interval = common_interval.index[0].total_seconds()
|
||||||
|
|
||||||
|
units = [
|
||||||
|
pd.Timedelta(microseconds=df['time'].dt.microsecond.value_counts().index[0]),
|
||||||
|
pd.Timedelta(seconds=df['time'].dt.second.value_counts().index[0]),
|
||||||
|
pd.Timedelta(minutes=df['time'].dt.minute.value_counts().index[0]),
|
||||||
|
pd.Timedelta(hours=df['time'].dt.hour.value_counts().index[0]),
|
||||||
|
pd.Timedelta(days=df['time'].dt.day.value_counts().index[0]),
|
||||||
|
]
|
||||||
|
self.offset = 0
|
||||||
|
for value in units:
|
||||||
|
value = value.total_seconds()
|
||||||
|
if value == 0:
|
||||||
|
continue
|
||||||
|
elif value > self._interval:
|
||||||
|
break
|
||||||
|
self.offset = value
|
||||||
|
break
|
||||||
|
|
||||||
self.run_script(
|
self.run_script(
|
||||||
f'if ({self.id}.toolBox) {self.id}.interval = {self._interval.total_seconds() * 1000}'
|
f'if ({self.id}.toolBox) {self.id}.interval = {self._interval}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _push_to_legend(self):
|
||||||
|
self.run_script(f'''
|
||||||
|
{self._chart.id}.lines.push({self.id})
|
||||||
|
if ('legend' in {self._chart.id}) {{
|
||||||
|
{self._chart.id}.legend.lines.push({self._chart.id}.legend.makeLineRow({self.id}))
|
||||||
|
}}''')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_labels(data, labels, index, exclude_lowercase):
|
def _format_labels(data, labels, index, exclude_lowercase):
|
||||||
def rename(la, mapper):
|
def rename(la, mapper):
|
||||||
@ -162,8 +204,7 @@ class SeriesCommon(Pane):
|
|||||||
def _single_datetime_format(self, arg):
|
def _single_datetime_format(self, arg):
|
||||||
if isinstance(arg, (str, int, float)) or not pd.api.types.is_datetime64_any_dtype(arg):
|
if isinstance(arg, (str, int, float)) or not pd.api.types.is_datetime64_any_dtype(arg):
|
||||||
arg = pd.to_datetime(arg)
|
arg = pd.to_datetime(arg)
|
||||||
interval_seconds = self._interval.total_seconds()
|
arg = self._interval * (arg.timestamp() // self._interval)+self.offset
|
||||||
arg = interval_seconds * (arg.timestamp() // interval_seconds)
|
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
def set(self, df: pd.DataFrame = None, format_cols: bool = True):
|
def set(self, df: pd.DataFrame = None, format_cols: bool = True):
|
||||||
@ -186,7 +227,6 @@ class SeriesCommon(Pane):
|
|||||||
self._last_bar = series
|
self._last_bar = series
|
||||||
self.run_script(f'{self.id}.series.update({js_data(series)})')
|
self.run_script(f'{self.id}.series.update({js_data(series)})')
|
||||||
|
|
||||||
|
|
||||||
def marker(self, time: datetime = None, position: MARKER_POSITION = 'below',
|
def marker(self, time: datetime = None, position: MARKER_POSITION = 'below',
|
||||||
shape: MARKER_SHAPE = 'arrow_up', color: str = '#2196F3', text: str = ''
|
shape: MARKER_SHAPE = 'arrow_up', color: str = '#2196F3', text: str = ''
|
||||||
) -> str:
|
) -> str:
|
||||||
@ -362,8 +402,7 @@ class VerticalSpan(Pane):
|
|||||||
else:
|
else:
|
||||||
self.run_script(f'''
|
self.run_script(f'''
|
||||||
{self.id}.setData(calculateTrendLine(
|
{self.id}.setData(calculateTrendLine(
|
||||||
{start_time.timestamp()}, 1, {end_time.timestamp()}, 1,
|
{start_time.timestamp()}, 1, {end_time.timestamp()}, 1, {chart.id}))
|
||||||
{chart._interval.total_seconds() * 1000}, {chart.id}))
|
|
||||||
''')
|
''')
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
@ -401,13 +440,6 @@ class Line(SeriesCommon):
|
|||||||
}}
|
}}
|
||||||
null''')
|
null''')
|
||||||
|
|
||||||
def _push_to_legend(self):
|
|
||||||
self.run_script(f'''
|
|
||||||
{self._chart.id}.lines.push({self.id})
|
|
||||||
if ('legend' in {self._chart.id}) {{
|
|
||||||
{self._chart.id}.legend.lines.push({self._chart.id}.legend.makeLineRow({self.id}))
|
|
||||||
}}''')
|
|
||||||
|
|
||||||
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:
|
||||||
start_time = self._single_datetime_format(start_time)
|
start_time = self._single_datetime_format(start_time)
|
||||||
@ -418,9 +450,7 @@ class Line(SeriesCommon):
|
|||||||
self.run_script(f'''
|
self.run_script(f'''
|
||||||
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: false}})
|
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: false}})
|
||||||
{self.id}.series.setData(
|
{self.id}.series.setData(
|
||||||
calculateTrendLine({start_time}, {start_value},
|
calculateTrendLine({start_time}, {start_value}, {end_time}, {end_value},
|
||||||
{end_time}, {end_value},
|
|
||||||
{self._chart._interval.total_seconds() * 1000},
|
|
||||||
{self._chart.id}, {jbool(ray)}))
|
{self._chart.id}, {jbool(ray)}))
|
||||||
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}})
|
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}})
|
||||||
''')
|
''')
|
||||||
@ -451,7 +481,8 @@ class Histogram(SeriesCommon):
|
|||||||
color: '{color}',
|
color: '{color}',
|
||||||
lastValueVisible: {jbool(price_label)},
|
lastValueVisible: {jbool(price_label)},
|
||||||
priceLineVisible: {jbool(price_line)},
|
priceLineVisible: {jbool(price_line)},
|
||||||
priceScaleId: '{self.id}'
|
priceScaleId: '{self.id}',
|
||||||
|
priceFormat: {{type: "volume"}},
|
||||||
}}),
|
}}),
|
||||||
markers: [],
|
markers: [],
|
||||||
horizontal_lines: [],
|
horizontal_lines: [],
|
||||||
@ -681,7 +712,11 @@ class AbstractChart(Candlestick, Pane):
|
|||||||
"""
|
"""
|
||||||
Creates and returns a Histogram object.
|
Creates and returns a Histogram object.
|
||||||
"""
|
"""
|
||||||
return Histogram(self, name, color, price_line, price_label, scale_margin_top, scale_margin_bottom)
|
histogram = Histogram(
|
||||||
|
self, name, color, price_line, price_label,
|
||||||
|
scale_margin_top, scale_margin_bottom)
|
||||||
|
histogram._push_to_legend()
|
||||||
|
return histogram
|
||||||
|
|
||||||
def lines(self) -> List[Line]:
|
def lines(self) -> List[Line]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,16 +1,10 @@
|
|||||||
if (!window.TopBar) {
|
if (!window.TopBar) {
|
||||||
class TopBar {
|
class TopBar {
|
||||||
constructor(chart, hoverBackgroundColor, clickBackgroundColor, activeBackgroundColor, textColor, activeTextColor) {
|
constructor(chart) {
|
||||||
this.makeSwitcher = this.makeSwitcher.bind(this)
|
this.makeSwitcher = this.makeSwitcher.bind(this)
|
||||||
this.hoverBackgroundColor = hoverBackgroundColor
|
|
||||||
this.clickBackgroundColor = clickBackgroundColor
|
|
||||||
this.activeBackgroundColor = activeBackgroundColor
|
|
||||||
this.textColor = textColor
|
|
||||||
this.activeTextColor = activeTextColor
|
|
||||||
|
|
||||||
this.topBar = document.createElement('div')
|
this.topBar = document.createElement('div')
|
||||||
this.topBar.style.backgroundColor = '#0c0d0f'
|
this.topBar.style.backgroundColor = pane.backgroundColor
|
||||||
this.topBar.style.borderBottom = '2px solid #3C434C'
|
this.topBar.style.borderBottom = '2px solid '+pane.borderColor
|
||||||
this.topBar.style.display = 'flex'
|
this.topBar.style.display = 'flex'
|
||||||
this.topBar.style.alignItems = 'center'
|
this.topBar.style.alignItems = 'center'
|
||||||
|
|
||||||
@ -45,17 +39,17 @@ if (!window.TopBar) {
|
|||||||
itemEl.style.margin = '0px 2px'
|
itemEl.style.margin = '0px 2px'
|
||||||
itemEl.style.fontSize = '13px'
|
itemEl.style.fontSize = '13px'
|
||||||
itemEl.style.borderRadius = '4px'
|
itemEl.style.borderRadius = '4px'
|
||||||
itemEl.style.backgroundColor = item === activeItem ? this.activeBackgroundColor : 'transparent'
|
itemEl.style.backgroundColor = item === activeItem ? pane.activeBackgroundColor : 'transparent'
|
||||||
itemEl.style.color = item === activeItem ? this.activeTextColor : this.textColor
|
itemEl.style.color = item === activeItem ? pane.activeColor : pane.color
|
||||||
itemEl.innerText = item;
|
itemEl.innerText = item;
|
||||||
document.body.appendChild(itemEl)
|
document.body.appendChild(itemEl)
|
||||||
itemEl.style.minWidth = itemEl.clientWidth + 1 + 'px'
|
itemEl.style.minWidth = itemEl.clientWidth + 1 + 'px'
|
||||||
document.body.removeChild(itemEl)
|
document.body.removeChild(itemEl)
|
||||||
|
|
||||||
itemEl.addEventListener('mouseenter', () => itemEl.style.backgroundColor = item === activeItem ? this.activeBackgroundColor : this.hoverBackgroundColor)
|
itemEl.addEventListener('mouseenter', () => itemEl.style.backgroundColor = item === activeItem ? pane.activeBackgroundColor : pane.hoverBackgroundColor)
|
||||||
itemEl.addEventListener('mouseleave', () => itemEl.style.backgroundColor = item === activeItem ? this.activeBackgroundColor : 'transparent')
|
itemEl.addEventListener('mouseleave', () => itemEl.style.backgroundColor = item === activeItem ? pane.activeBackgroundColor : 'transparent')
|
||||||
itemEl.addEventListener('mousedown', () => itemEl.style.backgroundColor = item === activeItem ? this.activeBackgroundColor : this.clickBackgroundColor)
|
itemEl.addEventListener('mousedown', () => itemEl.style.backgroundColor = item === activeItem ? pane.activeBackgroundColor : pane.clickBackgroundColor)
|
||||||
itemEl.addEventListener('mouseup', () => itemEl.style.backgroundColor = item === activeItem ? this.activeBackgroundColor : this.hoverBackgroundColor)
|
itemEl.addEventListener('mouseup', () => itemEl.style.backgroundColor = item === activeItem ? pane.activeBackgroundColor : pane.hoverBackgroundColor)
|
||||||
itemEl.addEventListener('click', () => onItemClicked(item))
|
itemEl.addEventListener('click', () => onItemClicked(item))
|
||||||
|
|
||||||
switcherElement.appendChild(itemEl);
|
switcherElement.appendChild(itemEl);
|
||||||
@ -66,8 +60,8 @@ if (!window.TopBar) {
|
|||||||
let onItemClicked = (item)=> {
|
let onItemClicked = (item)=> {
|
||||||
if (item === activeItem) return
|
if (item === activeItem) return
|
||||||
intervalElements.forEach((element, index) => {
|
intervalElements.forEach((element, index) => {
|
||||||
element.style.backgroundColor = items[index] === item ? this.activeBackgroundColor : 'transparent'
|
element.style.backgroundColor = items[index] === item ? pane.activeBackgroundColor : 'transparent'
|
||||||
element.style.color = items[index] === item ? this.activeTextColor : this.textColor
|
element.style.color = items[index] === item ? pane.activeColor : pane.color
|
||||||
element.style.fontWeight = items[index] === item ? '500' : 'normal'
|
element.style.fontWeight = items[index] === item ? '500' : 'normal'
|
||||||
})
|
})
|
||||||
activeItem = item;
|
activeItem = item;
|
||||||
@ -80,7 +74,7 @@ if (!window.TopBar) {
|
|||||||
let textBox = document.createElement('div')
|
let textBox = document.createElement('div')
|
||||||
textBox.style.margin = '0px 18px'
|
textBox.style.margin = '0px 18px'
|
||||||
textBox.style.fontSize = '16px'
|
textBox.style.fontSize = '16px'
|
||||||
textBox.style.color = 'rgb(220, 220, 220)'
|
textBox.style.color = pane.color
|
||||||
textBox.innerText = text
|
textBox.innerText = text
|
||||||
this.appendWidget(textBox, align, true)
|
this.appendWidget(textBox, align, true)
|
||||||
return textBox
|
return textBox
|
||||||
@ -91,9 +85,9 @@ if (!window.TopBar) {
|
|||||||
menu.style.position = 'absolute'
|
menu.style.position = 'absolute'
|
||||||
menu.style.display = 'none'
|
menu.style.display = 'none'
|
||||||
menu.style.zIndex = '100000'
|
menu.style.zIndex = '100000'
|
||||||
menu.style.backgroundColor = '#0c0d0f'
|
menu.style.backgroundColor = pane.backgroundColor
|
||||||
menu.style.borderRadius = '2px'
|
menu.style.borderRadius = '2px'
|
||||||
menu.style.border = '2px solid #3C434C'
|
menu.style.border = '2px solid '+pane.borderColor
|
||||||
menu.style.borderTop = 'none'
|
menu.style.borderTop = 'none'
|
||||||
menu.style.alignItems = 'flex-start'
|
menu.style.alignItems = 'flex-start'
|
||||||
|
|
||||||
@ -134,7 +128,7 @@ if (!window.TopBar) {
|
|||||||
button.style.margin = '4px 10px'
|
button.style.margin = '4px 10px'
|
||||||
button.style.fontSize = '13px'
|
button.style.fontSize = '13px'
|
||||||
button.style.backgroundColor = 'transparent'
|
button.style.backgroundColor = 'transparent'
|
||||||
button.style.color = this.textColor
|
button.style.color = pane.color
|
||||||
button.style.borderRadius = '4px'
|
button.style.borderRadius = '4px'
|
||||||
button.innerText = defaultText;
|
button.innerText = defaultText;
|
||||||
document.body.appendChild(button)
|
document.body.appendChild(button)
|
||||||
@ -146,19 +140,19 @@ if (!window.TopBar) {
|
|||||||
callbackName: callbackName
|
callbackName: callbackName
|
||||||
}
|
}
|
||||||
|
|
||||||
button.addEventListener('mouseenter', () => button.style.backgroundColor = this.hoverBackgroundColor)
|
button.addEventListener('mouseenter', () => button.style.backgroundColor = pane.hoverBackgroundColor)
|
||||||
button.addEventListener('mouseleave', () => button.style.backgroundColor = 'transparent')
|
button.addEventListener('mouseleave', () => button.style.backgroundColor = 'transparent')
|
||||||
if (callbackName) {
|
if (callbackName) {
|
||||||
button.addEventListener('click', () => window.callbackFunction(`${widget.callbackName}_~_${button.innerText}`));
|
button.addEventListener('click', () => window.callbackFunction(`${widget.callbackName}_~_${button.innerText}`));
|
||||||
}
|
}
|
||||||
button.addEventListener('mousedown', () => {
|
button.addEventListener('mousedown', () => {
|
||||||
button.style.backgroundColor = this.activeBackgroundColor
|
button.style.backgroundColor = pane.activeBackgroundColor
|
||||||
button.style.color = this.activeTextColor
|
button.style.color = pane.activeColor
|
||||||
button.style.fontWeight = '500'
|
button.style.fontWeight = '500'
|
||||||
})
|
})
|
||||||
button.addEventListener('mouseup', () => {
|
button.addEventListener('mouseup', () => {
|
||||||
button.style.backgroundColor = this.hoverBackgroundColor
|
button.style.backgroundColor = pane.hoverBackgroundColor
|
||||||
button.style.color = this.textColor
|
button.style.color = pane.color
|
||||||
button.style.fontWeight = 'normal'
|
button.style.fontWeight = 'normal'
|
||||||
})
|
})
|
||||||
if (append) this.appendWidget(button, align, separator)
|
if (append) this.appendWidget(button, align, separator)
|
||||||
@ -169,7 +163,7 @@ if (!window.TopBar) {
|
|||||||
let seperator = document.createElement('div')
|
let seperator = document.createElement('div')
|
||||||
seperator.style.width = '1px'
|
seperator.style.width = '1px'
|
||||||
seperator.style.height = '20px'
|
seperator.style.height = '20px'
|
||||||
seperator.style.backgroundColor = '#3C434C'
|
seperator.style.backgroundColor = pane.borderColor
|
||||||
let div = align === 'left' ? this.left : this.right
|
let div = align === 'left' ? this.left : this.right
|
||||||
div.appendChild(seperator)
|
div.appendChild(seperator)
|
||||||
}
|
}
|
||||||
@ -201,7 +195,7 @@ function makeSearchBox(chart) {
|
|||||||
searchWindow.style.zIndex = '1000'
|
searchWindow.style.zIndex = '1000'
|
||||||
searchWindow.style.alignItems = 'center'
|
searchWindow.style.alignItems = 'center'
|
||||||
searchWindow.style.backgroundColor = 'rgba(30, 30, 30, 0.9)'
|
searchWindow.style.backgroundColor = 'rgba(30, 30, 30, 0.9)'
|
||||||
searchWindow.style.border = '2px solid #3C434C'
|
searchWindow.style.border = '2px solid '+pane.borderColor
|
||||||
searchWindow.style.borderRadius = '5px'
|
searchWindow.style.borderRadius = '5px'
|
||||||
searchWindow.style.display = 'none'
|
searchWindow.style.display = 'none'
|
||||||
|
|
||||||
@ -213,8 +207,8 @@ function makeSearchBox(chart) {
|
|||||||
sBox.style.textAlign = 'center'
|
sBox.style.textAlign = 'center'
|
||||||
sBox.style.width = '100px'
|
sBox.style.width = '100px'
|
||||||
sBox.style.marginLeft = '10px'
|
sBox.style.marginLeft = '10px'
|
||||||
sBox.style.backgroundColor = 'rgba(0, 122, 255, 0.3)'
|
sBox.style.backgroundColor = pane.mutedBackgroundColor
|
||||||
sBox.style.color = 'rgb(240,240,240)'
|
sBox.style.color = pane.activeColor
|
||||||
sBox.style.fontSize = '20px'
|
sBox.style.fontSize = '20px'
|
||||||
sBox.style.border = 'none'
|
sBox.style.border = 'none'
|
||||||
sBox.style.outline = 'none'
|
sBox.style.outline = 'none'
|
||||||
@ -260,7 +254,7 @@ function makeSpinner(chart) {
|
|||||||
chart.spinner.style.width = '30px'
|
chart.spinner.style.width = '30px'
|
||||||
chart.spinner.style.height = '30px'
|
chart.spinner.style.height = '30px'
|
||||||
chart.spinner.style.border = '4px solid rgba(255, 255, 255, 0.6)'
|
chart.spinner.style.border = '4px solid rgba(255, 255, 255, 0.6)'
|
||||||
chart.spinner.style.borderTop = '4px solid rgba(0, 122, 255, 0.8)'
|
chart.spinner.style.borderTop = '4px solid '+pane.activeBackgroundColor
|
||||||
chart.spinner.style.borderRadius = '50%'
|
chart.spinner.style.borderRadius = '50%'
|
||||||
chart.spinner.style.position = 'absolute'
|
chart.spinner.style.position = 'absolute'
|
||||||
chart.spinner.style.top = '50%'
|
chart.spinner.style.top = '50%'
|
||||||
|
|||||||
@ -1,4 +1,15 @@
|
|||||||
if (!window.Chart) {
|
if (!window.Chart) {
|
||||||
|
window.pane = {
|
||||||
|
backgroundColor: '#0c0d0f',
|
||||||
|
hoverBackgroundColor: '#3c434c',
|
||||||
|
clickBackgroundColor: '#50565E',
|
||||||
|
activeBackgroundColor: 'rgba(0, 122, 255, 0.7)',
|
||||||
|
mutedBackgroundColor: 'rgba(0, 122, 255, 0.3)',
|
||||||
|
borderColor: '#3C434C',
|
||||||
|
color: '#d8d9db',
|
||||||
|
activeColor: '#ececed',
|
||||||
|
}
|
||||||
|
|
||||||
class Chart {
|
class Chart {
|
||||||
constructor(chartId, innerWidth, innerHeight, position, autoSize) {
|
constructor(chartId, innerWidth, innerHeight, position, autoSize) {
|
||||||
this.makeCandlestickSeries = this.makeCandlestickSeries.bind(this)
|
this.makeCandlestickSeries = this.makeCandlestickSeries.bind(this)
|
||||||
@ -17,7 +28,7 @@ if (!window.Chart) {
|
|||||||
width: window.innerWidth * innerWidth,
|
width: window.innerWidth * innerWidth,
|
||||||
height: window.innerHeight * innerHeight,
|
height: window.innerHeight * innerHeight,
|
||||||
layout: {
|
layout: {
|
||||||
textColor: '#d1d4dc',
|
textColor: pane.color,
|
||||||
background: {
|
background: {
|
||||||
color: '#000000',
|
color: '#000000',
|
||||||
type: LightweightCharts.ColorType.Solid,
|
type: LightweightCharts.ColorType.Solid,
|
||||||
@ -48,7 +59,7 @@ if (!window.Chart) {
|
|||||||
this.wrapper.style.position = 'relative'
|
this.wrapper.style.position = 'relative'
|
||||||
this.wrapper.style.float = position
|
this.wrapper.style.float = position
|
||||||
this.div.style.position = 'relative'
|
this.div.style.position = 'relative'
|
||||||
this.div.style.display = 'flex'
|
// this.div.style.display = 'flex'
|
||||||
this.reSize()
|
this.reSize()
|
||||||
this.wrapper.appendChild(this.div)
|
this.wrapper.appendChild(this.div)
|
||||||
document.getElementById('wrapper').append(this.wrapper)
|
document.getElementById('wrapper').append(this.wrapper)
|
||||||
@ -196,9 +207,10 @@ if (!window.Chart) {
|
|||||||
let legendItemFormat = (num, decimal) => num.toFixed(decimal).toString().padStart(8, ' ')
|
let legendItemFormat = (num, decimal) => num.toFixed(decimal).toString().padStart(8, ' ')
|
||||||
|
|
||||||
let shorthandFormat = (num) => {
|
let shorthandFormat = (num) => {
|
||||||
if (num >= 1000000) {
|
const absNum = Math.abs(num)
|
||||||
|
if (absNum >= 1000000) {
|
||||||
return (num / 1000000).toFixed(1) + 'M';
|
return (num / 1000000).toFixed(1) + 'M';
|
||||||
} else if (num >= 1000) {
|
} else if (absNum >= 1000) {
|
||||||
return (num / 1000).toFixed(1) + 'K';
|
return (num / 1000).toFixed(1) + 'K';
|
||||||
}
|
}
|
||||||
return num.toString().padStart(8, ' ');
|
return num.toString().padStart(8, ' ');
|
||||||
@ -225,7 +237,14 @@ if (!window.Chart) {
|
|||||||
this.candle.innerHTML = finalString + '</span>'
|
this.candle.innerHTML = finalString + '</span>'
|
||||||
this.lines.forEach((line) => {
|
this.lines.forEach((line) => {
|
||||||
if (!param.seriesData.get(line.line.series)) return
|
if (!param.seriesData.get(line.line.series)) return
|
||||||
let price = legendItemFormat(param.seriesData.get(line.line.series).value, line.line.precision)
|
let price = param.seriesData.get(line.line.series).value
|
||||||
|
|
||||||
|
if (line.line.series._series._seriesType === 'Histogram') {
|
||||||
|
price = shorthandFormat(price)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
price = legendItemFormat(price, line.line.precision)
|
||||||
|
}
|
||||||
line.div.innerHTML = `<span style="color: ${line.solid};">▨</span> ${line.line.name} : ${price}`
|
line.div.innerHTML = `<span style="color: ${line.solid};">▨</span> ${line.line.name} : ${price}`
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -381,7 +400,7 @@ function lastBar(obj) {
|
|||||||
return obj[obj.length-1]
|
return obj[obj.length-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateTrendLine(startDate, startValue, endDate, endValue, interval, chart, ray=false) {
|
function calculateTrendLine(startDate, startValue, endDate, endValue, chart, ray=false) {
|
||||||
let reversed = false
|
let reversed = false
|
||||||
if (stampToDate(endDate).getTime() < stampToDate(startDate).getTime()) {
|
if (stampToDate(endDate).getTime() < stampToDate(startDate).getTime()) {
|
||||||
reversed = true;
|
reversed = true;
|
||||||
@ -406,7 +425,7 @@ function calculateTrendLine(startDate, startValue, endDate, endValue, interval,
|
|||||||
else {
|
else {
|
||||||
endIndex = chart.candleData.findIndex(item => stampToDate(item.time).getTime() === stampToDate(endDate).getTime())
|
endIndex = chart.candleData.findIndex(item => stampToDate(item.time).getTime() === stampToDate(endDate).getTime())
|
||||||
if (endIndex === -1) {
|
if (endIndex === -1) {
|
||||||
let barsBetween = (stampToDate(endDate)-stampToDate(chart.candleData[chart.candleData.length-1].time))/interval
|
let barsBetween = (endDate-lastBar(chart.candleData).time)/chart.interval
|
||||||
endIndex = chart.candleData.length-1+barsBetween
|
endIndex = chart.candleData.length-1+barsBetween
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,7 +441,7 @@ function calculateTrendLine(startDate, startValue, endDate, endValue, interval,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
iPastData ++
|
iPastData ++
|
||||||
currentDate = dateToStamp(new Date(stampToDate(chart.candleData[chart.candleData.length-1].time).getTime()+(iPastData*interval)))
|
currentDate = lastBar(chart.candleData).time+(iPastData*chart.interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentValue = reversed ? startValue + rate_of_change * (numBars - i) : startValue + rate_of_change * i;
|
const currentValue = reversed ? startValue + rate_of_change * (numBars - i) : startValue + rate_of_change * i;
|
||||||
@ -439,7 +458,7 @@ if (!window.ContextMenu) {
|
|||||||
this.menu.style.position = 'absolute'
|
this.menu.style.position = 'absolute'
|
||||||
this.menu.style.zIndex = '10000'
|
this.menu.style.zIndex = '10000'
|
||||||
this.menu.style.background = 'rgb(50, 50, 50)'
|
this.menu.style.background = 'rgb(50, 50, 50)'
|
||||||
this.menu.style.color = '#ececed'
|
this.menu.style.color = pane.activeColor
|
||||||
this.menu.style.display = 'none'
|
this.menu.style.display = 'none'
|
||||||
this.menu.style.borderRadius = '5px'
|
this.menu.style.borderRadius = '5px'
|
||||||
this.menu.style.padding = '3px 3px'
|
this.menu.style.padding = '3px 3px'
|
||||||
@ -508,7 +527,7 @@ if (!window.ContextMenu) {
|
|||||||
separator.style.width = '90%'
|
separator.style.width = '90%'
|
||||||
separator.style.height = '1px'
|
separator.style.height = '1px'
|
||||||
separator.style.margin = '3px 0px'
|
separator.style.margin = '3px 0px'
|
||||||
separator.style.backgroundColor = '#3C434C'
|
separator.style.backgroundColor = pane.borderColor
|
||||||
this.menu.appendChild(separator)
|
this.menu.appendChild(separator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,11 +11,7 @@ if (!window.ToolBox) {
|
|||||||
this.chart.cursor = 'default'
|
this.chart.cursor = 'default'
|
||||||
this.makingDrawing = false
|
this.makingDrawing = false
|
||||||
|
|
||||||
this.activeBackgroundColor = 'rgba(0, 122, 255, 0.7)'
|
this.hoverBackgroundColor = 'rgba(80, 86, 94, 0.7)'
|
||||||
this.activeIconColor = 'rgb(240, 240, 240)'
|
|
||||||
this.iconColor = 'lightgrey'
|
|
||||||
this.backgroundColor = 'transparent'
|
|
||||||
this.hoverColor = 'rgba(80, 86, 94, 0.7)'
|
|
||||||
this.clickBackgroundColor = 'rgba(90, 106, 104, 0.7)'
|
this.clickBackgroundColor = 'rgba(90, 106, 104, 0.7)'
|
||||||
|
|
||||||
this.elem = this.makeToolBox()
|
this.elem = this.makeToolBox()
|
||||||
@ -35,9 +31,8 @@ if (!window.ToolBox) {
|
|||||||
toolBoxElem.style.display = 'flex'
|
toolBoxElem.style.display = 'flex'
|
||||||
toolBoxElem.style.alignItems = 'center'
|
toolBoxElem.style.alignItems = 'center'
|
||||||
toolBoxElem.style.top = '25%'
|
toolBoxElem.style.top = '25%'
|
||||||
toolBoxElem.style.borderRight = '2px solid #3C434C'
|
toolBoxElem.style.border = '2px solid '+pane.borderColor
|
||||||
toolBoxElem.style.borderTop = '2px solid #3C434C'
|
toolBoxElem.style.borderLeft = 'none'
|
||||||
toolBoxElem.style.borderBottom = '2px solid #3C434C'
|
|
||||||
toolBoxElem.style.borderTopRightRadius = '4px'
|
toolBoxElem.style.borderTopRightRadius = '4px'
|
||||||
toolBoxElem.style.borderBottomRightRadius = '4px'
|
toolBoxElem.style.borderBottomRightRadius = '4px'
|
||||||
toolBoxElem.style.backgroundColor = 'rgba(25, 27, 30, 0.5)'
|
toolBoxElem.style.backgroundColor = 'rgba(25, 27, 30, 0.5)'
|
||||||
@ -48,11 +43,11 @@ if (!window.ToolBox) {
|
|||||||
let trend = this.makeToolBoxElement(this.onTrendSelect, 'KeyT', `<rect x="3.84" y="13.67" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -5.9847 14.4482)" width="21.21" height="1.56"/><path d="M23,3.17L20.17,6L23,8.83L25.83,6L23,3.17z M23,7.41L21.59,6L23,4.59L24.41,6L23,7.41z"/><path d="M6,20.17L3.17,23L6,25.83L8.83,23L6,20.17z M6,24.41L4.59,23L6,21.59L7.41,23L6,24.41z"/>`)
|
let trend = this.makeToolBoxElement(this.onTrendSelect, 'KeyT', `<rect x="3.84" y="13.67" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -5.9847 14.4482)" width="21.21" height="1.56"/><path d="M23,3.17L20.17,6L23,8.83L25.83,6L23,3.17z M23,7.41L21.59,6L23,4.59L24.41,6L23,7.41z"/><path d="M6,20.17L3.17,23L6,25.83L8.83,23L6,20.17z M6,24.41L4.59,23L6,21.59L7.41,23L6,24.41z"/>`)
|
||||||
let horz = this.makeToolBoxElement(this.onHorzSelect, 'KeyH', `<rect x="4" y="14" width="9" height="1"/><rect x="16" y="14" width="9" height="1"/><path d="M11.67,14.5l2.83,2.83l2.83-2.83l-2.83-2.83L11.67,14.5z M15.91,14.5l-1.41,1.41l-1.41-1.41l1.41-1.41L15.91,14.5z"/>`)
|
let horz = this.makeToolBoxElement(this.onHorzSelect, 'KeyH', `<rect x="4" y="14" width="9" height="1"/><rect x="16" y="14" width="9" height="1"/><path d="M11.67,14.5l2.83,2.83l2.83-2.83l-2.83-2.83L11.67,14.5z M15.91,14.5l-1.41,1.41l-1.41-1.41l1.41-1.41L15.91,14.5z"/>`)
|
||||||
let ray = this.makeToolBoxElement(this.onRaySelect, 'KeyR', `<rect x="8" y="14" width="17" height="1"/><path d="M3.67,14.5l2.83,2.83l2.83-2.83L6.5,11.67L3.67,14.5z M7.91,14.5L6.5,15.91L5.09,14.5l1.41-1.41L7.91,14.5z"/>`)
|
let ray = this.makeToolBoxElement(this.onRaySelect, 'KeyR', `<rect x="8" y="14" width="17" height="1"/><path d="M3.67,14.5l2.83,2.83l2.83-2.83L6.5,11.67L3.67,14.5z M7.91,14.5L6.5,15.91L5.09,14.5l1.41-1.41L7.91,14.5z"/>`)
|
||||||
//let testB = this.makeToolBoxElement(this.onTrendSelect, `<rect x="8" y="6" width="12" height="1"/><rect x="9" y="22" width="11" height="1"/><path d="M3.67,6.5L6.5,9.33L9.33,6.5L6.5,3.67L3.67,6.5z M7.91,6.5L6.5,7.91L5.09,6.5L6.5,5.09L7.91,6.5z"/><path d="M19.67,6.5l2.83,2.83l2.83-2.83L22.5,3.67L19.67,6.5z M23.91,6.5L22.5,7.91L21.09,6.5l1.41-1.41L23.91,6.5z"/><path d="M19.67,22.5l2.83,2.83l2.83-2.83l-2.83-2.83L19.67,22.5z M23.91,22.5l-1.41,1.41l-1.41-1.41l1.41-1.41L23.91,22.5z"/><path d="M3.67,22.5l2.83,2.83l2.83-2.83L6.5,19.67L3.67,22.5z M7.91,22.5L6.5,23.91L5.09,22.5l1.41-1.41L7.91,22.5z"/><rect x="22" y="9" width="1" height="11"/><rect x="6" y="9" width="1" height="11"/>`)
|
// let testB = this.makeToolBoxElement(this.onTrendSelect, 'KeyB', `<rect x="8" y="6" width="12" height="1"/><rect x="9" y="22" width="11" height="1"/><path d="M3.67,6.5L6.5,9.33L9.33,6.5L6.5,3.67L3.67,6.5z M7.91,6.5L6.5,7.91L5.09,6.5L6.5,5.09L7.91,6.5z"/><path d="M19.67,6.5l2.83,2.83l2.83-2.83L22.5,3.67L19.67,6.5z M23.91,6.5L22.5,7.91L21.09,6.5l1.41-1.41L23.91,6.5z"/><path d="M19.67,22.5l2.83,2.83l2.83-2.83l-2.83-2.83L19.67,22.5z M23.91,22.5l-1.41,1.41l-1.41-1.41l1.41-1.41L23.91,22.5z"/><path d="M3.67,22.5l2.83,2.83l2.83-2.83L6.5,19.67L3.67,22.5z M7.91,22.5L6.5,23.91L5.09,22.5l1.41-1.41L7.91,22.5z"/><rect x="22" y="9" width="1" height="11"/><rect x="6" y="9" width="1" height="11"/>`)
|
||||||
toolBoxElem.appendChild(trend)
|
toolBoxElem.appendChild(trend)
|
||||||
toolBoxElem.appendChild(horz)
|
toolBoxElem.appendChild(horz)
|
||||||
toolBoxElem.appendChild(ray)
|
toolBoxElem.appendChild(ray)
|
||||||
//toolBoxElem.appendChild(testB)
|
// toolBoxElem.appendChild(testB)
|
||||||
|
|
||||||
this.chart.div.append(toolBoxElem)
|
this.chart.div.append(toolBoxElem)
|
||||||
|
|
||||||
@ -63,7 +58,7 @@ if (!window.ToolBox) {
|
|||||||
}
|
}
|
||||||
this.chart.commandFunctions.push((event) => {
|
this.chart.commandFunctions.push((event) => {
|
||||||
if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') {
|
if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') {
|
||||||
commandZHandler(this.drawings[this.drawings.length - 1])
|
commandZHandler(lastBar(this.drawings))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -72,13 +67,10 @@ if (!window.ToolBox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeToolBoxElement(action, keyCmd, paths) {
|
makeToolBoxElement(action, keyCmd, paths) {
|
||||||
let icon = {
|
let elem = document.createElement('div')
|
||||||
elem: document.createElement('div'),
|
elem.style.margin = '3px'
|
||||||
action: action,
|
elem.style.borderRadius = '4px'
|
||||||
}
|
elem.style.display = 'flex'
|
||||||
icon.elem.style.margin = '3px'
|
|
||||||
icon.elem.style.borderRadius = '4px'
|
|
||||||
icon.elem.style.display = 'flex'
|
|
||||||
|
|
||||||
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
svg.setAttribute("width", "29");
|
svg.setAttribute("width", "29");
|
||||||
@ -86,27 +78,29 @@ if (!window.ToolBox) {
|
|||||||
|
|
||||||
let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
||||||
group.innerHTML = paths
|
group.innerHTML = paths
|
||||||
group.setAttribute("fill", this.iconColor)
|
group.setAttribute("fill", pane.color)
|
||||||
|
|
||||||
svg.appendChild(group)
|
svg.appendChild(group)
|
||||||
icon.elem.appendChild(svg);
|
elem.appendChild(svg);
|
||||||
|
|
||||||
icon.elem.addEventListener('mouseenter', () => {
|
let icon = {elem: elem, action: action}
|
||||||
icon.elem.style.backgroundColor = icon === this.chart.activeIcon ? this.activeBackgroundColor : this.hoverColor
|
|
||||||
|
elem.addEventListener('mouseenter', () => {
|
||||||
|
elem.style.backgroundColor = icon === this.chart.activeIcon ? pane.activeBackgroundColor : this.hoverBackgroundColor
|
||||||
})
|
})
|
||||||
icon.elem.addEventListener('mouseleave', () => {
|
elem.addEventListener('mouseleave', () => {
|
||||||
icon.elem.style.backgroundColor = icon === this.chart.activeIcon ? this.activeBackgroundColor : this.backgroundColor
|
elem.style.backgroundColor = icon === this.chart.activeIcon ? pane.activeBackgroundColor : 'transparent'
|
||||||
})
|
})
|
||||||
icon.elem.addEventListener('mousedown', () => {
|
elem.addEventListener('mousedown', () => {
|
||||||
icon.elem.style.backgroundColor = icon === this.chart.activeIcon ? this.activeBackgroundColor : this.clickBackgroundColor
|
elem.style.backgroundColor = icon === this.chart.activeIcon ? pane.activeBackgroundColor : this.clickBackgroundColor
|
||||||
})
|
})
|
||||||
icon.elem.addEventListener('mouseup', () => {
|
elem.addEventListener('mouseup', () => {
|
||||||
icon.elem.style.backgroundColor = icon === this.chart.activeIcon ? this.activeBackgroundColor : 'transparent'
|
elem.style.backgroundColor = icon === this.chart.activeIcon ? pane.activeBackgroundColor : 'transparent'
|
||||||
})
|
})
|
||||||
icon.elem.addEventListener('click', () => {
|
elem.addEventListener('click', () => {
|
||||||
if (this.chart.activeIcon) {
|
if (this.chart.activeIcon) {
|
||||||
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
|
this.chart.activeIcon.elem.style.backgroundColor = 'transparent'
|
||||||
group.setAttribute("fill", this.iconColor)
|
group.setAttribute("fill", pane.color)
|
||||||
document.body.style.cursor = 'crosshair'
|
document.body.style.cursor = 'crosshair'
|
||||||
this.chart.cursor = 'crosshair'
|
this.chart.cursor = 'crosshair'
|
||||||
this.chart.activeIcon.action(false)
|
this.chart.activeIcon.action(false)
|
||||||
@ -115,8 +109,8 @@ if (!window.ToolBox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.chart.activeIcon = icon
|
this.chart.activeIcon = icon
|
||||||
group.setAttribute("fill", this.activeIconColor)
|
group.setAttribute("fill", pane.activeColor)
|
||||||
icon.elem.style.backgroundColor = this.activeBackgroundColor
|
elem.style.backgroundColor = pane.activeBackgroundColor
|
||||||
document.body.style.cursor = 'crosshair'
|
document.body.style.cursor = 'crosshair'
|
||||||
this.chart.cursor = 'crosshair'
|
this.chart.cursor = 'crosshair'
|
||||||
this.chart.activeIcon.action(true)
|
this.chart.activeIcon.action(true)
|
||||||
@ -125,76 +119,56 @@ if (!window.ToolBox) {
|
|||||||
if (event.altKey && event.code === keyCmd) {
|
if (event.altKey && event.code === keyCmd) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (this.chart.activeIcon) {
|
if (this.chart.activeIcon) {
|
||||||
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
|
this.chart.activeIcon.elem.style.backgroundColor = 'transparent'
|
||||||
group.setAttribute("fill", this.iconColor)
|
group.setAttribute("fill", pane.color)
|
||||||
document.body.style.cursor = 'crosshair'
|
document.body.style.cursor = 'crosshair'
|
||||||
this.chart.cursor = 'crosshair'
|
this.chart.cursor = 'crosshair'
|
||||||
this.chart.activeIcon.action(false)
|
this.chart.activeIcon.action(false)
|
||||||
}
|
}
|
||||||
this.chart.activeIcon = icon
|
this.chart.activeIcon = icon
|
||||||
group.setAttribute("fill", this.activeIconColor)
|
group.setAttribute("fill", pane.activeColor)
|
||||||
icon.elem.style.backgroundColor = this.activeBackgroundColor
|
elem.style.backgroundColor = pane.activeBackgroundColor
|
||||||
document.body.style.cursor = 'crosshair'
|
document.body.style.cursor = 'crosshair'
|
||||||
this.chart.cursor = 'crosshair'
|
this.chart.cursor = 'crosshair'
|
||||||
this.chart.activeIcon.action(true)
|
this.chart.activeIcon.action(true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return icon.elem
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
removeActiveAndSave() {
|
||||||
|
document.body.style.cursor = 'default'
|
||||||
|
this.chart.cursor = 'default'
|
||||||
|
this.chart.activeIcon.elem.style.backgroundColor = 'transparent'
|
||||||
|
this.chart.activeIcon = null
|
||||||
|
this.saveDrawings()
|
||||||
}
|
}
|
||||||
|
|
||||||
onTrendSelect(toggle, ray = false) {
|
onTrendSelect(toggle, ray = false) {
|
||||||
let trendLine = {
|
let trendLine = null
|
||||||
line: null,
|
|
||||||
color: 'rgb(15, 139, 237)',
|
|
||||||
markers: null,
|
|
||||||
data: null,
|
|
||||||
from: null,
|
|
||||||
to: null,
|
|
||||||
ray: ray,
|
|
||||||
}
|
|
||||||
let firstTime = null
|
let firstTime = null
|
||||||
let firstPrice = null
|
let firstPrice = null
|
||||||
let currentTime = null
|
let currentTime = null
|
||||||
|
|
||||||
|
|
||||||
if (!toggle) {
|
if (!toggle) {
|
||||||
this.chart.chart.unsubscribeClick(this.clickHandler)
|
return this.chart.chart.unsubscribeClick(this.clickHandler)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
let crosshairHandlerTrend = (param) => {
|
let crosshairHandlerTrend = (param) => {
|
||||||
this.chart.chart.unsubscribeCrosshairMove(crosshairHandlerTrend)
|
this.chart.chart.unsubscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
|
|
||||||
if (!this.makingDrawing) return
|
if (!this.makingDrawing) return
|
||||||
|
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: false})
|
|
||||||
let logical = this.chart.chart.timeScale().getVisibleLogicalRange()
|
|
||||||
let lastCandleTime = this.chart.candleData[this.chart.candleData.length - 1].time
|
|
||||||
currentTime = this.chart.chart.timeScale().coordinateToTime(param.point.x)
|
currentTime = this.chart.chart.timeScale().coordinateToTime(param.point.x)
|
||||||
if (!currentTime) {
|
if (!currentTime) {
|
||||||
let barsToMove = param.logical - this.chart.chart.timeScale().coordinateToLogical(this.chart.chart.timeScale().timeToCoordinate(lastCandleTime))
|
let barsToMove = param.logical - this.chart.candleData.length-1
|
||||||
currentTime = dateToStamp(new Date(stampToDate(this.chart.candleData[this.chart.candleData.length - 1].time).getTime() + (barsToMove * this.chart.interval)))
|
currentTime = lastBar(this.chart.candleData).time+(barsToMove*this.chart.interval)
|
||||||
}
|
}
|
||||||
let currentPrice = this.chart.series.coordinateToPrice(param.point.y)
|
let currentPrice = this.chart.series.coordinateToPrice(param.point.y)
|
||||||
|
|
||||||
|
|
||||||
if (!currentTime) return this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
if (!currentTime) return this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
let data = calculateTrendLine(firstTime, firstPrice, currentTime, currentPrice, this.chart.interval, this.chart, ray)
|
trendLine.calculateAndSet(firstTime, firstPrice, currentTime, currentPrice)
|
||||||
trendLine.from = [data[0].time, data[0].value]
|
|
||||||
trendLine.to = [data[data.length - 1].time, data[data.length-1].value]
|
|
||||||
|
|
||||||
trendLine.line.setData(data)
|
|
||||||
|
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: true})
|
|
||||||
this.chart.chart.timeScale().setVisibleLogicalRange(logical)
|
|
||||||
|
|
||||||
if (!ray) {
|
|
||||||
trendLine.markers = [
|
|
||||||
{time: trendLine.from[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1},
|
|
||||||
{time: trendLine.to[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1}
|
|
||||||
]
|
|
||||||
trendLine.line.setMarkers(trendLine.markers)
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
}, 10);
|
}, 10);
|
||||||
@ -203,21 +177,9 @@ if (!window.ToolBox) {
|
|||||||
this.clickHandler = (param) => {
|
this.clickHandler = (param) => {
|
||||||
if (!this.makingDrawing) {
|
if (!this.makingDrawing) {
|
||||||
this.makingDrawing = true
|
this.makingDrawing = true
|
||||||
trendLine.line = this.chart.chart.addLineSeries({
|
trendLine = new TrendLine(this.chart, 'rgb(15, 139, 237)', ray)
|
||||||
color: 'rgb(15, 139, 237)',
|
|
||||||
lineWidth: 2,
|
|
||||||
lastValueVisible: false,
|
|
||||||
priceLineVisible: false,
|
|
||||||
crosshairMarkerVisible: false,
|
|
||||||
autoscaleInfoProvider: () => ({
|
|
||||||
priceRange: {
|
|
||||||
minValue: 1_000_000_000,
|
|
||||||
maxValue: 0,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
firstPrice = this.chart.series.coordinateToPrice(param.point.y)
|
firstPrice = this.chart.series.coordinateToPrice(param.point.y)
|
||||||
firstTime = !ray ? this.chart.chart.timeScale().coordinateToTime(param.point.x) : this.chart.candleData[this.chart.candleData.length - 1].time
|
firstTime = !ray ? this.chart.chart.timeScale().coordinateToTime(param.point.x) : lastBar(this.chart.candleData).time
|
||||||
this.chart.chart.applyOptions({handleScroll: false})
|
this.chart.chart.applyOptions({handleScroll: false})
|
||||||
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
}
|
}
|
||||||
@ -228,30 +190,23 @@ if (!window.ToolBox) {
|
|||||||
this.drawings.push(trendLine)
|
this.drawings.push(trendLine)
|
||||||
this.chart.chart.unsubscribeCrosshairMove(crosshairHandlerTrend)
|
this.chart.chart.unsubscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
this.chart.chart.unsubscribeClick(this.clickHandler)
|
this.chart.chart.unsubscribeClick(this.clickHandler)
|
||||||
document.body.style.cursor = 'default'
|
this.removeActiveAndSave()
|
||||||
this.chart.cursor = 'default'
|
|
||||||
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
|
|
||||||
this.chart.activeIcon = null
|
|
||||||
this.saveDrawings()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.chart.chart.subscribeClick(this.clickHandler)
|
this.chart.chart.subscribeClick(this.clickHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
clickHandlerHorz = (param) => {
|
onHorzSelect(toggle) {
|
||||||
|
let clickHandlerHorz = (param) => {
|
||||||
let price = this.chart.series.coordinateToPrice(param.point.y)
|
let price = this.chart.series.coordinateToPrice(param.point.y)
|
||||||
let lineStyle = LightweightCharts.LineStyle.Solid
|
let lineStyle = LightweightCharts.LineStyle.Solid
|
||||||
let line = new HorizontalLine(this.chart, 'toolBox', price,'red', 2, lineStyle, true)
|
let line = new HorizontalLine(this.chart, 'toolBox', price,'red', 2, lineStyle, true)
|
||||||
this.drawings.push(line)
|
this.drawings.push(line)
|
||||||
this.chart.chart.unsubscribeClick(this.clickHandlerHorz)
|
this.chart.chart.unsubscribeClick(clickHandlerHorz)
|
||||||
document.body.style.cursor = 'default'
|
this.removeActiveAndSave()
|
||||||
this.chart.cursor = 'default'
|
|
||||||
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
|
|
||||||
this.chart.activeIcon = null
|
|
||||||
this.saveDrawings()
|
|
||||||
}
|
}
|
||||||
onHorzSelect(toggle) {
|
if (toggle) this.chart.chart.subscribeClick(clickHandlerHorz)
|
||||||
!toggle ? this.chart.chart.unsubscribeClick(this.clickHandlerHorz) : this.chart.chart.subscribeClick(this.clickHandlerHorz)
|
else this.chart.chart.unsubscribeClick(clickHandlerHorz)
|
||||||
}
|
}
|
||||||
|
|
||||||
onRaySelect(toggle) {
|
onRaySelect(toggle) {
|
||||||
@ -382,8 +337,8 @@ if (!window.ToolBox) {
|
|||||||
let priceDiff = priceAtCursor - originalPrice
|
let priceDiff = priceAtCursor - originalPrice
|
||||||
let barsToMove = param.logical - originalIndex
|
let barsToMove = param.logical - originalIndex
|
||||||
|
|
||||||
let startBarIndex = this.chart.candleData.findIndex(item => stampToDate(item.time).getTime() === stampToDate(hoveringOver.from[0]).getTime())
|
let startBarIndex = this.chart.candleData.findIndex(item => item.time === hoveringOver.from[0])
|
||||||
let endBarIndex = this.chart.candleData.findIndex(item => stampToDate(item.time).getTime() === stampToDate(hoveringOver.to[0]).getTime())
|
let endBarIndex = this.chart.candleData.findIndex(item => item.time === hoveringOver.to[0])
|
||||||
|
|
||||||
let startDate
|
let startDate
|
||||||
let endBar
|
let endBar
|
||||||
@ -395,27 +350,11 @@ if (!window.ToolBox) {
|
|||||||
endBar = endBarIndex === -1 ? null : this.chart.candleData[endBarIndex + barsToMove]
|
endBar = endBarIndex === -1 ? null : this.chart.candleData[endBarIndex + barsToMove]
|
||||||
}
|
}
|
||||||
|
|
||||||
let endDate = endBar ? endBar.time : dateToStamp(new Date(stampToDate(hoveringOver.to[0]).getTime() + (barsToMove * this.chart.interval)))
|
let endDate = endBar ? endBar.time : hoveringOver.to[0] + (barsToMove * this.chart.interval)
|
||||||
let startValue = hoveringOver.from[1] + priceDiff
|
let startValue = hoveringOver.from[1] + priceDiff
|
||||||
let endValue = hoveringOver.to[1] + priceDiff
|
let endValue = hoveringOver.to[1] + priceDiff
|
||||||
let data = calculateTrendLine(startDate, startValue, endDate, endValue, this.chart.interval, this.chart, hoveringOver.ray)
|
|
||||||
|
|
||||||
|
hoveringOver.calculateAndSet(startDate, startValue, endDate, endValue)
|
||||||
let logical = this.chart.chart.timeScale().getVisibleLogicalRange()
|
|
||||||
|
|
||||||
hoveringOver.from = [data[0].time, data[0].value]
|
|
||||||
hoveringOver.to = [data[data.length - 1].time, data[data.length - 1].value]
|
|
||||||
hoveringOver.line.setData(data)
|
|
||||||
|
|
||||||
this.chart.chart.timeScale().setVisibleLogicalRange(logical)
|
|
||||||
|
|
||||||
if (!hoveringOver.ray) {
|
|
||||||
hoveringOver.markers = [
|
|
||||||
{time: hoveringOver.from[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1},
|
|
||||||
{time: hoveringOver.to[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1}
|
|
||||||
]
|
|
||||||
hoveringOver.line.setMarkers(hoveringOver.markers)
|
|
||||||
}
|
|
||||||
|
|
||||||
originalIndex = param.logical
|
originalIndex = param.logical
|
||||||
originalPrice = priceAtCursor
|
originalPrice = priceAtCursor
|
||||||
@ -438,29 +377,12 @@ if (!window.ToolBox) {
|
|||||||
firstPrice = hoveringOver.to[1]
|
firstPrice = hoveringOver.to[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: false})
|
|
||||||
let logical = this.chart.chart.timeScale().getVisibleLogicalRange()
|
|
||||||
|
|
||||||
let lastCandleTime = this.chart.candleData[this.chart.candleData.length - 1].time
|
|
||||||
if (!currentTime) {
|
if (!currentTime) {
|
||||||
let barsToMove = param.logical - this.chart.chart.timeScale().coordinateToLogical(this.chart.chart.timeScale().timeToCoordinate(lastCandleTime))
|
let barsToMove = param.logical - this.chart.candleData.length-1
|
||||||
currentTime = dateToStamp(new Date(stampToDate(this.chart.candleData[this.chart.candleData.length - 1].time).getTime() + (barsToMove * this.chart.interval)))
|
currentTime = lastBar(this.chart.candleData).time + (barsToMove*this.chart.interval)
|
||||||
}
|
}
|
||||||
let data = calculateTrendLine(firstTime, firstPrice, currentTime, currentPrice, this.chart.interval, this.chart)
|
|
||||||
hoveringOver.line.setData(data)
|
|
||||||
|
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: true})
|
hoveringOver.calculateAndSet(firstTime, firstPrice, currentTime, currentPrice)
|
||||||
this.chart.chart.timeScale().setVisibleLogicalRange(logical)
|
|
||||||
|
|
||||||
hoveringOver.from = [data[0].time, data[0].value]
|
|
||||||
hoveringOver.to = [data[data.length - 1].time, data[data.length - 1].value]
|
|
||||||
|
|
||||||
|
|
||||||
hoveringOver.markers = [
|
|
||||||
{time: hoveringOver.from[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1},
|
|
||||||
{time: hoveringOver.to[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1}
|
|
||||||
]
|
|
||||||
hoveringOver.line.setMarkers(hoveringOver.markers)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
this.chart.chart.subscribeCrosshairMove(crosshairHandlerTrend)
|
||||||
@ -481,12 +403,10 @@ if (!window.ToolBox) {
|
|||||||
renderDrawings() {
|
renderDrawings() {
|
||||||
this.drawings.forEach((item) => {
|
this.drawings.forEach((item) => {
|
||||||
if ('price' in item) return
|
if ('price' in item) return
|
||||||
let startDate = dateToStamp(new Date(Math.round(stampToDate(item.from[0]).getTime() / this.chart.interval) * this.chart.interval))
|
console.log('rendering')
|
||||||
let endDate = dateToStamp(new Date(Math.round(stampToDate(item.to[0]).getTime() / this.chart.interval) * this.chart.interval))
|
let startDate = Math.round(item.from[0]/this.chart.interval)*this.chart.interval
|
||||||
let data = calculateTrendLine(startDate, item.from[1], endDate, item.to[1], this.chart.interval, this.chart, item.ray)
|
let endDate = Math.round(item.to[0]/this.chart.interval)*this.chart.interval
|
||||||
item.from = [data[0].time, data[0].value]
|
item.calculateAndSet(startDate, item.from[1], endDate, item.to[1])
|
||||||
item.to = [data[data.length - 1].time, data[data.length-1].value]
|
|
||||||
item.line.setData(data)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,55 +444,97 @@ if (!window.ToolBox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadDrawings(drawings) {
|
loadDrawings(drawings) {
|
||||||
this.drawings = drawings
|
this.drawings = []
|
||||||
this.chart.chart.applyOptions({handleScroll: false})
|
drawings.forEach((item) => {
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: false})
|
let drawing = null
|
||||||
this.drawings.forEach((item) => {
|
|
||||||
let idx = this.drawings.indexOf(item)
|
|
||||||
if ('price' in item) {
|
if ('price' in item) {
|
||||||
this.drawings[idx] = new HorizontalLine(this.chart, 'toolBox', item.priceLine.price, item.priceLine.color, 2, item.priceLine.lineStyle, item.priceLine.axisLabelVisible)
|
drawing = new HorizontalLine(this.chart, 'toolBox',
|
||||||
|
item.priceLine.price, item.priceLine.color, 2,
|
||||||
|
item.priceLine.lineStyle, item.priceLine.axisLabelVisible)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.drawings[idx].line = this.chart.chart.addLineSeries({
|
let startDate = Math.round(item.from[0]/this.chart.interval)*this.chart.interval
|
||||||
lineWidth: 2,
|
let endDate = Math.round(item.to[0]/this.chart.interval)*this.chart.interval
|
||||||
color: this.drawings[idx].color,
|
|
||||||
lastValueVisible: false,
|
drawing = new TrendLine(this.chart, item.color, item.ray)
|
||||||
priceLineVisible: false,
|
drawing.calculateAndSet(startDate, item.from[1], endDate, item.to[1])
|
||||||
crosshairMarkerVisible: false,
|
|
||||||
autoscaleInfoProvider: () => ({
|
|
||||||
priceRange: {
|
|
||||||
minValue: 1_000_000_000,
|
|
||||||
maxValue: 0,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
let startDate = dateToStamp(new Date(Math.round(stampToDate(item.from[0]).getTime() / this.chart.interval) * this.chart.interval))
|
|
||||||
let endDate = dateToStamp(new Date(Math.round(stampToDate(item.to[0]).getTime() / this.chart.interval) * this.chart.interval))
|
|
||||||
let data = calculateTrendLine(startDate, item.from[1], endDate, item.to[1], this.chart.interval, this.chart, item.ray)
|
|
||||||
item.from = [data[0].time, data[0].value]
|
|
||||||
item.to = [data[data.length - 1].time, data[data.length-1].value]
|
|
||||||
item.line.setData(data)
|
|
||||||
}
|
}
|
||||||
|
this.drawings.push(drawing)
|
||||||
})
|
})
|
||||||
this.chart.chart.applyOptions({handleScroll: true})
|
|
||||||
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: true})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.ToolBox = ToolBox
|
window.ToolBox = ToolBox
|
||||||
|
|
||||||
|
|
||||||
|
class TrendLine {
|
||||||
|
constructor(chart, color, ray) {
|
||||||
|
this.calculateAndSet = this.calculateAndSet.bind(this)
|
||||||
|
|
||||||
|
this.line = chart.chart.addLineSeries({
|
||||||
|
color: color,
|
||||||
|
lineWidth: 2,
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
crosshairMarkerVisible: false,
|
||||||
|
autoscaleInfoProvider: () => ({
|
||||||
|
priceRange: {
|
||||||
|
minValue: 1_000_000_000,
|
||||||
|
maxValue: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
this.color = color
|
||||||
|
this.markers = null
|
||||||
|
this.data = null
|
||||||
|
this.from = null
|
||||||
|
this.to = null
|
||||||
|
this.ray = ray
|
||||||
|
this.chart = chart
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
// Exclude the chart attribute from serialization
|
||||||
|
const {chart, ...serialized} = this;
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateAndSet(firstTime, firstPrice, currentTime, currentPrice) {
|
||||||
|
let data = calculateTrendLine(firstTime, firstPrice, currentTime, currentPrice, this.chart, this.ray)
|
||||||
|
this.from = [data[0].time, data[0].value]
|
||||||
|
this.to = [data[data.length - 1].time, data[data.length-1].value]
|
||||||
|
|
||||||
|
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: false})
|
||||||
|
let logical = this.chart.chart.timeScale().getVisibleLogicalRange()
|
||||||
|
|
||||||
|
this.line.setData(data)
|
||||||
|
|
||||||
|
this.chart.chart.timeScale().applyOptions({shiftVisibleRangeOnNewBar: true})
|
||||||
|
this.chart.chart.timeScale().setVisibleLogicalRange(logical)
|
||||||
|
|
||||||
|
if (!this.ray) {
|
||||||
|
this.markers = [
|
||||||
|
{time: this.from[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1},
|
||||||
|
{time: this.to[0], position: 'inBar', color: '#1E80F0', shape: 'circle', size: 0.1}
|
||||||
|
]
|
||||||
|
this.line.setMarkers(this.markers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.TrendLine = TrendLine
|
||||||
|
|
||||||
class ColorPicker {
|
class ColorPicker {
|
||||||
constructor(saveDrawings) {
|
constructor(saveDrawings) {
|
||||||
this.saveDrawings = saveDrawings
|
this.saveDrawings = saveDrawings
|
||||||
|
|
||||||
this.container = document.createElement('div')
|
this.container = document.createElement('div')
|
||||||
this.container.style.maxWidth = '170px'
|
this.container.style.maxWidth = '170px'
|
||||||
this.container.style.backgroundColor = '#191B1E'
|
this.container.style.backgroundColor = pane.backgroundColor
|
||||||
this.container.style.position = 'absolute'
|
this.container.style.position = 'absolute'
|
||||||
this.container.style.zIndex = '10000'
|
this.container.style.zIndex = '10000'
|
||||||
this.container.style.display = 'none'
|
this.container.style.display = 'none'
|
||||||
this.container.style.flexDirection = 'column'
|
this.container.style.flexDirection = 'column'
|
||||||
this.container.style.alignItems = 'center'
|
this.container.style.alignItems = 'center'
|
||||||
this.container.style.border = '2px solid #3C434C'
|
this.container.style.border = '2px solid '+pane.borderColor
|
||||||
this.container.style.borderRadius = '8px'
|
this.container.style.borderRadius = '8px'
|
||||||
this.container.style.cursor = 'default'
|
this.container.style.cursor = 'default'
|
||||||
|
|
||||||
@ -593,7 +555,7 @@ if (!window.ToolBox) {
|
|||||||
colors.forEach((color) => colorPicker.appendChild(this.makeColorBox(color)))
|
colors.forEach((color) => colorPicker.appendChild(this.makeColorBox(color)))
|
||||||
|
|
||||||
let separator = document.createElement('div')
|
let separator = document.createElement('div')
|
||||||
separator.style.backgroundColor = '#3C434C'
|
separator.style.backgroundColor = pane.borderColor
|
||||||
separator.style.height = '1px'
|
separator.style.height = '1px'
|
||||||
separator.style.width = '130px'
|
separator.style.width = '130px'
|
||||||
|
|
||||||
@ -696,7 +658,7 @@ if (!window.ToolBox) {
|
|||||||
this.container.style.position = 'absolute'
|
this.container.style.position = 'absolute'
|
||||||
this.container.style.zIndex = '10000'
|
this.container.style.zIndex = '10000'
|
||||||
this.container.style.background = 'rgb(50, 50, 50)'
|
this.container.style.background = 'rgb(50, 50, 50)'
|
||||||
this.container.style.color = '#ececed'
|
this.container.style.color = pane.activeColor
|
||||||
this.container.style.display = 'none'
|
this.container.style.display = 'none'
|
||||||
this.container.style.borderRadius = '5px'
|
this.container.style.borderRadius = '5px'
|
||||||
this.container.style.padding = '3px 3px'
|
this.container.style.padding = '3px 3px'
|
||||||
@ -727,7 +689,7 @@ if (!window.ToolBox) {
|
|||||||
item.style.borderRadius = '3px'
|
item.style.borderRadius = '3px'
|
||||||
item.innerText = text
|
item.innerText = text
|
||||||
|
|
||||||
item.addEventListener('mouseover', (event) => item.style.backgroundColor = 'rgba(0, 122, 255, 0.3)')
|
item.addEventListener('mouseover', (event) => item.style.backgroundColor = pane.mutedBackgroundColor)
|
||||||
item.addEventListener('mouseout', (event) => item.style.backgroundColor = 'transparent')
|
item.addEventListener('mouseout', (event) => item.style.backgroundColor = 'transparent')
|
||||||
|
|
||||||
item.addEventListener('click', (event) => {
|
item.addEventListener('click', (event) => {
|
||||||
@ -760,5 +722,5 @@ if (!window.ToolBox) {
|
|||||||
this.container.style.display = 'none'
|
this.container.style.display = 'none'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.ColorPicker = ColorPicker
|
window.StylePicker = StylePicker
|
||||||
}
|
}
|
||||||
|
|||||||
@ -411,21 +411,21 @@ class PolygonChart(Chart):
|
|||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
self.live = live
|
self.live = live
|
||||||
|
self.win.style(
|
||||||
|
active_background_color='rgba(91, 98, 246, 0.8)',
|
||||||
|
muted_background_color='rgba(91, 98, 246, 0.5)'
|
||||||
|
)
|
||||||
self.polygon.api_key(api_key)
|
self.polygon.api_key(api_key)
|
||||||
self.events.search += self.on_search
|
self.events.search += self.on_search
|
||||||
self.legend(True)
|
self.legend(True)
|
||||||
self.grid(False, False)
|
self.grid(False, False)
|
||||||
self.crosshair(vert_visible=False, horz_visible=False)
|
self.crosshair(vert_visible=False, horz_visible=False)
|
||||||
|
|
||||||
self.topbar.active_background_color = 'rgb(91, 98, 246)'
|
|
||||||
self.topbar.textbox('symbol')
|
self.topbar.textbox('symbol')
|
||||||
self.topbar.switcher('timeframe', timeframe_options, func=self._on_timeframe_selection)
|
self.topbar.switcher('timeframe', timeframe_options, func=self._on_timeframe_selection)
|
||||||
self.topbar.switcher('security', security_options, func=self._on_security_selection)
|
self.topbar.switcher('security', security_options, func=self._on_security_selection)
|
||||||
|
|
||||||
self.run_script(f'''
|
self.run_script(f'''
|
||||||
{self.id}.search.box.style.backgroundColor = 'rgba(91, 98, 246, 0.5)'
|
|
||||||
{self.id}.spinner.style.borderTop = '4px solid rgba(91, 98, 246, 0.8)'
|
|
||||||
{self.id}.search.window.style.display = "flex"
|
{self.id}.search.window.style.display = "flex"
|
||||||
{self.id}.search.box.focus()
|
{self.id}.search.box.focus()
|
||||||
''')
|
''')
|
||||||
|
|||||||
@ -50,7 +50,8 @@ class MenuWidget(Widget):
|
|||||||
class ButtonWidget(Widget):
|
class ButtonWidget(Widget):
|
||||||
def __init__(self, topbar, button, separator, align, func):
|
def __init__(self, topbar, button, separator, align, func):
|
||||||
super().__init__(topbar, value=button, func=func)
|
super().__init__(topbar, value=button, func=func)
|
||||||
self.run_script(f'{self.id} = {topbar.id}.makeButton("{button}", "{self.id}", {jbool(separator)}, "{align}")')
|
self.run_script(
|
||||||
|
f'{self.id} = {topbar.id}.makeButton("{button}", "{self.id}", {jbool(separator)}, true, "{align}")')
|
||||||
|
|
||||||
def set(self, string):
|
def set(self, string):
|
||||||
self.value = string
|
self.value = string
|
||||||
@ -62,12 +63,6 @@ class TopBar(Pane):
|
|||||||
super().__init__(chart.win)
|
super().__init__(chart.win)
|
||||||
self._chart = chart
|
self._chart = chart
|
||||||
self._widgets: Dict[str, Widget] = {}
|
self._widgets: Dict[str, Widget] = {}
|
||||||
|
|
||||||
self.click_bg_color = '#50565E'
|
|
||||||
self.hover_bg_color = '#3c434c'
|
|
||||||
self.active_bg_color = 'rgba(0, 122, 255, 0.7)'
|
|
||||||
self.active_text_color = '#ececed'
|
|
||||||
self.text_color = '#d8d9db'
|
|
||||||
self._created = False
|
self._created = False
|
||||||
|
|
||||||
def _create(self):
|
def _create(self):
|
||||||
@ -76,10 +71,7 @@ class TopBar(Pane):
|
|||||||
from lightweight_charts.abstract import JS
|
from lightweight_charts.abstract import JS
|
||||||
self._created = True
|
self._created = True
|
||||||
self.run_script(JS['callback'])
|
self.run_script(JS['callback'])
|
||||||
self.run_script(f'''
|
self.run_script(f'{self.id} = new TopBar({self._chart.id})')
|
||||||
{self.id} = new TopBar( {self._chart.id}, '{self.hover_bg_color}', '{self.click_bg_color}',
|
|
||||||
'{self.active_bg_color}', '{self.text_color}', '{self.active_text_color}')
|
|
||||||
''')
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
if widget := self._widgets.get(item):
|
if widget := self._widgets.get(item):
|
||||||
|
|||||||
2
setup.py
2
setup.py
@ -5,7 +5,7 @@ with open('README.md', 'r', encoding='utf-8') as f:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='lightweight_charts',
|
name='lightweight_charts',
|
||||||
version='1.0.17.5',
|
version='1.0.17.6',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
python_requires='>=3.8',
|
python_requires='>=3.8',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
|||||||
Reference in New Issue
Block a user