Improved divergence graph.
This commit is contained in:
+5
-5
@@ -8,7 +8,7 @@ calculate:
|
||||
overlap_pct: 90
|
||||
max_p_value: 0.05
|
||||
monitor:
|
||||
interval: 20
|
||||
interval: 10
|
||||
calculations:
|
||||
long:
|
||||
from: 30
|
||||
@@ -74,10 +74,10 @@ charts:
|
||||
developer:
|
||||
inspection: true
|
||||
window:
|
||||
x: -8
|
||||
y: -8
|
||||
width: 1636
|
||||
height: 1056
|
||||
x: -4
|
||||
y: 1
|
||||
width: 1626
|
||||
height: 1043
|
||||
style: 541072960
|
||||
settings_window:
|
||||
x: 354
|
||||
|
||||
@@ -93,6 +93,10 @@ monitor:
|
||||
autosave:
|
||||
__label: Auto Save
|
||||
__helptext: Whether to auto save after every monitoring event. If a file was opened or has been saved, then the data will be saved to this file, otherwise the data will be saved to a file named autosave.cpd.
|
||||
charts:
|
||||
colormap:
|
||||
__label: Color Map
|
||||
__helptext: The matplotlib color pallet to use for plotting graphs. A list of pallets is available at https://matplotlib.org/stable/tutorials/colors/colormaps.html
|
||||
developer:
|
||||
inspection:
|
||||
__label: Inspection
|
||||
|
||||
@@ -5,6 +5,7 @@ import logging
|
||||
import pytz
|
||||
import wx
|
||||
import wx.lib.inspection as ins
|
||||
import wxconfig
|
||||
import wxconfig as cfg
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
@@ -289,7 +290,8 @@ class CorrelationMDIFrame(wx.MDIParentFrame):
|
||||
for child in children:
|
||||
if isinstance(child, CorrelationMDIChild):
|
||||
child.refresh()
|
||||
elif isinstance(child, wx.StatusBar) or isinstance(child, ins.InspectionFrame):
|
||||
elif isinstance(child, wx.StatusBar) or isinstance(child, ins.InspectionFrame) or \
|
||||
isinstance(child, wxconfig.SettingsDialog):
|
||||
# Ignore
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import logging
|
||||
import matplotlib.dates
|
||||
import matplotlib.pylab as pl
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as mticker
|
||||
import numpy as mp
|
||||
import numpy as np
|
||||
import wx
|
||||
import wx.lib.scrolledpanel as scrolled
|
||||
import wxconfig as cfg
|
||||
|
||||
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
|
||||
from mpl_toolkits.axes_grid1 import host_subplot
|
||||
from mpl_toolkits import axisartist
|
||||
|
||||
import mt5_correlation.gui.mdi as mdi
|
||||
from mt5_correlation import correlation as cor
|
||||
@@ -22,20 +21,23 @@ class MDIChildDivergedGraph(mdi.CorrelationMDIChild):
|
||||
|
||||
symbol = None # Symbol to chart divergence for. Public as we use to check if window for the symbol is already open.
|
||||
|
||||
# Logger
|
||||
__log = None
|
||||
|
||||
# Date formats for graphs
|
||||
__tick_fmt_date = matplotlib.dates.DateFormatter('%d-%b')
|
||||
__tick_fmt_time = matplotlib.dates.DateFormatter('%H:%M:%S')
|
||||
|
||||
# Panel
|
||||
__panel = None
|
||||
|
||||
# Graph Canvas and Figure
|
||||
# Graph Canvas
|
||||
__canvas = None
|
||||
__fig = None
|
||||
|
||||
# Colors for graph lines
|
||||
__colours = matplotlib.cm.get_cmap(cfg.Config().get("charts.colormap")).colors
|
||||
|
||||
# We will store the other symbols last plotted. This will save us rebuilding teh figure if teh symbols haven't
|
||||
# changed.
|
||||
__other_symbols = None
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
# Super
|
||||
wx.MDIChildFrame.__init__(self, parent=parent, id=wx.ID_ANY,
|
||||
@@ -48,17 +50,14 @@ class MDIChildDivergedGraph(mdi.CorrelationMDIChild):
|
||||
self.symbol = kwargs['symbol']
|
||||
|
||||
# Create panel and sizer
|
||||
self.__panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
|
||||
panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
|
||||
sizer = wx.BoxSizer()
|
||||
self.__panel.SetSizer(sizer)
|
||||
panel.SetSizer(sizer)
|
||||
|
||||
# Create figure and canvas. Add canvas to sizer
|
||||
self.__fig = plt.Figure()
|
||||
self.__canvas = FigureCanvas(self.__panel, wx.ID_ANY, self.__fig)
|
||||
self.__panel.GetSizer().Add(self.__canvas, 1, wx.ALL | wx.EXPAND)
|
||||
|
||||
# Setup scrolling
|
||||
self.__panel.SetupScrolling()
|
||||
self.__canvas = FigureCanvas(panel, wx.ID_ANY, plt.figure())
|
||||
panel.GetSizer().Add(self.__canvas, 1, wx.ALL | wx.EXPAND)
|
||||
panel.SetupScrolling()
|
||||
|
||||
# Refresh to show content
|
||||
self.refresh()
|
||||
@@ -69,6 +68,41 @@ class MDIChildDivergedGraph(mdi.CorrelationMDIChild):
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Get tick data for base symbol
|
||||
symbol_tick_data = self.GetMDIParent().cor.get_ticks(self.symbol, cache_only=True)
|
||||
|
||||
# Get the other symbols and their tick data
|
||||
other_symbols_data = self.__get_other_symbols_data()
|
||||
|
||||
# Delete all axes from the figure. They will need to be recreated as the symbols may be different
|
||||
for axes in self.__canvas.figure.axes:
|
||||
axes.remove()
|
||||
|
||||
# Plot for all other symbols
|
||||
num_subplots = 1 if len(other_symbols_data.keys()) == 0 else len(other_symbols_data.keys())
|
||||
plotnum = 1
|
||||
axs = [] # Store the axes for sharing x axis
|
||||
for other_symbol in other_symbols_data:
|
||||
axs.append(self.__canvas.figure.add_subplot(num_subplots, 1, plotnum))
|
||||
self.__plot(axes=axs[-1], base_symbol=self.symbol, other_symbol=other_symbol,
|
||||
base_symbol_data=symbol_tick_data, other_symbol_data=other_symbols_data[other_symbol])
|
||||
|
||||
# Next plot
|
||||
plotnum += 1
|
||||
|
||||
# Share x axis of the last axes with all the others
|
||||
self.__share_xaxis(axs)
|
||||
|
||||
# Redraw canvas
|
||||
self.__canvas.figure.tight_layout(pad=0.5)
|
||||
self.__canvas.draw()
|
||||
|
||||
def __get_other_symbols_data(self):
|
||||
"""
|
||||
Gets the data required for the graphs
|
||||
:param self:
|
||||
:return: dict of other symbols their tick data
|
||||
"""
|
||||
# Get the symbols that this one has diverged against.
|
||||
data = self.GetMDIParent().cor.filtered_coefficient_data
|
||||
filtered_data = data.loc[(
|
||||
@@ -77,90 +111,83 @@ class MDIChildDivergedGraph(mdi.CorrelationMDIChild):
|
||||
(data['Status'] == cor.STATUS_CONVERGING)
|
||||
) &
|
||||
(
|
||||
(data['Symbol 1'] == self.symbol) |
|
||||
(data['Symbol 2'] == self.symbol)
|
||||
)]
|
||||
(data['Symbol 1'] == self.symbol) |
|
||||
(data['Symbol 2'] == self.symbol)
|
||||
)]
|
||||
|
||||
# Get all symbols and remove the base one. We will need to ensure that this is first in the list
|
||||
other_symbols = list(filtered_data['Symbol 1'].append(filtered_data['Symbol 2']).drop_duplicates())
|
||||
if self.symbol in other_symbols:
|
||||
other_symbols.remove(self.symbol)
|
||||
|
||||
# Get the tick data for base symbols and other symbols
|
||||
symbol_tick_data = self.GetMDIParent().cor.get_ticks(self.symbol, cache_only=True)
|
||||
other_tick_data = []
|
||||
# Get the tick data other symbols and add to dict
|
||||
other_tick_data = {}
|
||||
for symbol in other_symbols:
|
||||
tick_data = self.GetMDIParent().cor.get_ticks(symbol, cache_only=True)
|
||||
other_tick_data.append(tick_data)
|
||||
other_tick_data[symbol] = tick_data
|
||||
|
||||
# We will clear the figure and create new axis on every refresh as the shape will change on refresh if symbols
|
||||
# change
|
||||
self.__fig.clear()
|
||||
axs = self.__fig.add_subplot(111) # Axis to fill canvas
|
||||
return other_tick_data
|
||||
|
||||
# Set title
|
||||
axs.set_title(f"Price Data for {self.symbol}:{other_symbols}".replace("'", ""))
|
||||
def __plot(self, axes, base_symbol, other_symbol, base_symbol_data, other_symbol_data):
|
||||
"""
|
||||
Plots the data on the axes
|
||||
:param axes: The subplot to plot onto
|
||||
:param base_symbol
|
||||
:param other_symbol:
|
||||
"""
|
||||
# Create the other axes. Will need an axes for the base symbol data and another for the other symbol data
|
||||
other_axes = axes.twinx()
|
||||
|
||||
# Set Y Labels and tick colours for symbol. This will be set for other symbols on plot
|
||||
for i in range(0, 2):
|
||||
axs.set_ylabel(f"{self.symbol}", color=self.__colours[0], labelpad=10)
|
||||
axs.tick_params(axis='y', labelcolor=self.__colours[0])
|
||||
# Set plot title and axis labels
|
||||
axes.set_title(f"Tick Data for {base_symbol}:{other_symbol}")
|
||||
axes.set_ylabel(base_symbol, color=self.__colours[0], labelpad=10)
|
||||
other_axes.set_ylabel(other_symbol, color=self.__colours[1], labelpad=10)
|
||||
|
||||
# Store the lines for the legend
|
||||
lines = []
|
||||
# Set the tick and axis colors
|
||||
self.__set_axes_color(axes, self.__colours[0], 'left')
|
||||
self.__set_axes_color(other_axes, self.__colours[1], 'right')
|
||||
|
||||
# Plot tick data for the base symbol. Store line and label.
|
||||
line = axs.plot(symbol_tick_data['time'], symbol_tick_data['ask'], color=self.__colours[0],
|
||||
label=self.symbol)[0]
|
||||
lines.append(line)
|
||||
# Plot both lines
|
||||
axes.plot(base_symbol_data['time'], base_symbol_data['ask'], color=self.__colours[0])
|
||||
other_axes.plot(other_symbol_data['time'], other_symbol_data['ask'], color=self.__colours[1])
|
||||
|
||||
# Plot for the other symbols on new axes.
|
||||
spinepos = 0 # Position of spine. Starting at 2nd plot, will be increased for every plot.
|
||||
for i in range(0, len(other_tick_data)):
|
||||
other_axis = axs.twinx()
|
||||
@staticmethod
|
||||
def __set_axes_color(axes, color, axis_loc='right'):
|
||||
"""
|
||||
Set the color for the axes, including axis line, ticks and tick labels
|
||||
:param axes: The axes to set color for.
|
||||
:param color: The color to set to
|
||||
:param axis_loc: The location of the axis, label and ticks. Either left for base symbol or right for others
|
||||
:return:
|
||||
"""
|
||||
# Change the color for the axis line, ticks and tick labels
|
||||
axes.spines[axis_loc].set_color(color)
|
||||
axes.tick_params(axis='y', colors=color)
|
||||
|
||||
# Move spine if we are not on first plot
|
||||
if i > 0:
|
||||
spinepos += 1.15
|
||||
other_axis.spines['right'].set_position(("axes", spinepos))
|
||||
|
||||
# The frame is off, so line of detached spine is invisible. Activate frame and then make patch and
|
||||
# spines invisible
|
||||
other_axis.set_frame_on(True)
|
||||
other_axis.patch.set_visible(False)
|
||||
for sp in other_axis.spines.values():
|
||||
sp.set_visible(False)
|
||||
|
||||
# Show the right spline
|
||||
other_axis.spines['right'].set_visible(True)
|
||||
|
||||
# Plot. We will use a lighter line for the other symbols. Store line.
|
||||
line = other_axis.plot(other_tick_data[i]['time'], other_tick_data[i]['ask'], color=self.__colours[i + 1],
|
||||
label=other_symbols[i], linestyle='solid', linewidth=0.5)[0]
|
||||
lines.append(line)
|
||||
|
||||
# Set y label, label colour and tick colour
|
||||
other_axis.set_ylabel(f"{other_symbols[i]}", color=self.__colours[i + 1], labelpad=10)
|
||||
other_axis.tick_params(axis='y', labelcolor=self.__colours[i + 1])
|
||||
|
||||
# Ticks, labels and formats. Fixing xticks with FixedLocator but also using MaxNLocator to avoid
|
||||
# cramped x-labels
|
||||
if len(symbol_tick_data['time']) > 0:
|
||||
axs.xaxis.set_major_locator(mticker.MaxNLocator(10))
|
||||
ticks_loc = axs.get_xticks().tolist()
|
||||
axs.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
|
||||
axs.set_xticklabels(ticks_loc)
|
||||
axs.xaxis.set_major_formatter(self.__tick_fmt_time)
|
||||
plt.setp(axs.xaxis.get_majorticklabels(), rotation=45)
|
||||
|
||||
# Legend
|
||||
# axs.legend(lines, [line.get_label() for line in lines], loc=0)
|
||||
|
||||
# Tight layout
|
||||
self.__fig.tight_layout()
|
||||
|
||||
# Redraw canvas
|
||||
self.__canvas.draw()
|
||||
def __share_xaxis(self, axs):
|
||||
"""
|
||||
Share the xaxis of the last axes with all other axes. Remove axis tick labels for all but the last. Format axis
|
||||
tick labels for the last.
|
||||
:param axs:
|
||||
:return:
|
||||
"""
|
||||
if len(axs) > 0:
|
||||
last_ax = axs[-1]
|
||||
for ax in axs:
|
||||
# If we are not on the last one, share and hide tick labels. If we are on the last one, format tick
|
||||
# labels.
|
||||
if ax != last_ax:
|
||||
ax.sharex(last_ax)
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), visible=False)
|
||||
else:
|
||||
# Ticks, labels and formats. Fixing xticks with FixedLocator but also using MaxNLocator to avoid
|
||||
# cramped x-labels
|
||||
ax.xaxis.set_major_locator(mticker.MaxNLocator(10))
|
||||
ticks_loc = ax.get_xticks().tolist()
|
||||
ax.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
|
||||
ax.set_xticklabels(ticks_loc)
|
||||
ax.xaxis.set_major_formatter(self.__tick_fmt_time)
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)
|
||||
|
||||
def __del__(self):
|
||||
# Close all plots
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
pandas==1.2.1
|
||||
matplotlib==3.3.4
|
||||
matplotlib==3.4.2
|
||||
MetaTrader5==5.0.34
|
||||
pytz==2021.1
|
||||
scipy==1.6.0
|
||||
|
||||
Reference in New Issue
Block a user