Added diverged symbols chart

This commit is contained in:
Jamie Cash
2021-06-01 17:45:11 +01:00
parent b947b928e7
commit 66cdf47406
5 changed files with 224 additions and 41 deletions
+57 -24
View File
@@ -1,11 +1,15 @@
import abc import abc
import importlib
import logging import logging
import sys
import pytz import pytz
import wx import wx
import wxconfig as cfg import wxconfig as cfg
from datetime import datetime, timedelta from datetime import datetime, timedelta
import mt5_correlation.gui as gui
from mt5_correlation import correlation as cor from mt5_correlation import correlation as cor
@@ -249,32 +253,14 @@ class CorrelationMDIFrame(wx.MDIParentFrame):
self.SetStatusText(f"Status updated at {self.cor.get_last_calculation():%d-%b %H:%M:%S}.", 1) self.SetStatusText(f"Status updated at {self.cor.get_last_calculation():%d-%b %H:%M:%S}.", 1)
def __on_view_status(self, evt): def __on_view_status(self, evt):
from mt5_correlation.gui.mdi_child_status import MDIChildStatus FrameManager.open_frame(parent=self, frame_module='mt5_correlation.gui.mdi_child_status',
frame_class='MDIChildStatus',
# Only open if not already open. If already open then raise to top. raise_if_open=True)
opened_instance = None
for child in self.GetChildren():
if isinstance(child, MDIChildStatus):
opened_instance = child
if opened_instance is None:
MDIChildStatus(parent=self).Show(True)
else:
opened_instance.Raise()
def __on_view_diverged(self, evt): def __on_view_diverged(self, evt):
from mt5_correlation.gui.mdi_child_diverged_symbols import MDIChildDivergedSymbols FrameManager.open_frame(parent=self, frame_module='mt5_correlation.gui.mdi_child_diverged_symbols',
frame_class='MDIChildDivergedSymbols',
# Only open if not already open. If already open then raise to top. raise_if_open=True)
opened_instance = None
for child in self.GetChildren():
if isinstance(child, MDIChildDivergedSymbols):
opened_instance = child
if opened_instance is None:
MDIChildDivergedSymbols(parent=self).Show(True)
else:
opened_instance.Raise()
def __refresh(self): def __refresh(self):
""" """
@@ -305,3 +291,50 @@ class CorrelationMDIChild(wx.MDIChildFrame):
:return: :return:
""" """
raise NotImplementedError raise NotImplementedError
class FrameManager:
"""
Manages the opening and raising of MDIChild frames
"""
@staticmethod
def open_frame(parent, frame_module, frame_class, raise_if_open=True, **kwargs):
"""
Opens the frame specified by the frame class
:param parent: The MDIParentFrame to open the child frame into
:param frame_module: A string specifying the module containing the frame class to open or raise
:param frame_class: A string specifying the frame class to open or raise
:param raise_if_open: Whether the frame should raise rather than open if an instance is already open.
:param kwargs: A dict of parameters to pass to frame constructor. These will also be checked in raise_if_open to
determine uniqueness (i.e. If a frame of the same class is already open but its params are different, then
the frame will be opened again with the new params instead of being raised.)
:return:
"""
# Load the module and class
module = importlib.import_module(frame_module)
clazz = getattr(module, frame_class)
# Do we have an opened instance
opened_instance = None
for child in parent.GetChildren():
if isinstance(child, clazz):
# do the args match
match = True
for key in kwargs:
if kwargs[key] != getattr(child, key):
match = False
# Only open existing instance if args matched
if match:
opened_instance = child
# If we dont have an opened instance or raise_on_open is False then open new frame, otherwise raise it
if opened_instance is None or raise_if_open is False:
if len(kwargs) == 0:
clazz(parent=parent).Show(True)
else:
clazz(parent=parent, **kwargs).Show(True)
else:
opened_instance.Raise()
@@ -30,16 +30,16 @@ class MDIChildCorrelationGraph(mdi.CorrelationMDIChild):
__axs = None __axs = None
__canvas = None __canvas = None
def __init__(self, parent, symbol1, symbol2): def __init__(self, parent, **kwargs):
# Super # Super
wx.MDIChildFrame.__init__(self, parent=parent, id=wx.ID_ANY, wx.MDIChildFrame.__init__(self, parent=parent, id=wx.ID_ANY,
title=f"Correlation Status for {symbol1}:{symbol2}") title=f"Correlation Status for {kwargs['symbols'][0]}:{kwargs['symbols'][1]}")
# Create logger # Create logger
self.__log = logging.getLogger(__name__) self.__log = logging.getLogger(__name__)
# Store the symbols # Store the symbols
self.symbols = [symbol1, symbol2] self.symbols = kwargs['symbols']
# We will freeze this frame and thaw once constructed to avoid flicker. # We will freeze this frame and thaw once constructed to avoid flicker.
self.Freeze() self.Freeze()
@@ -56,6 +56,9 @@ class MDIChildDivergedSymbols(mdi.CorrelationMDIChild):
self.__grid.SetMinSize((220, 100)) self.__grid.SetMinSize((220, 100))
sizer.Add(self.__grid, 1, wx.ALL | wx.EXPAND) sizer.Add(self.__grid, 1, wx.ALL | wx.EXPAND)
# Bind row doubleclick
self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_doubleckick_row, self.__grid)
# Refresh to populate # Refresh to populate
self.refresh() self.refresh()
@@ -94,6 +97,21 @@ class MDIChildDivergedSymbols(mdi.CorrelationMDIChild):
# Update row count # Update row count
self.__rows = cur_rows self.__rows = cur_rows
def __on_doubleckick_row(self, evt):
"""
Open the graphs when a row is doubleclicked.
:param evt:
:return:
"""
row = evt.GetRow()
symbol = self.__grid.GetCellValue(row, COLUMN_SYMBOL)
mdi.FrameManager.open_frame(parent=self.GetMDIParent(),
frame_module='mt5_correlation.gui.mdi_child_divergedgraph',
frame_class='MDIChildDivergedGraph',
raise_if_open=True,
symbol=symbol)
class _DataTable(wx.grid.GridTableBase): class _DataTable(wx.grid.GridTableBase):
""" """
@@ -0,0 +1,141 @@
import logging
import matplotlib.dates
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import wx
import wxconfig as cfg
import wx.lib.scrolledpanel as scrolled
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import mt5_correlation.gui.mdi as mdi
from mt5_correlation import correlation as cor
class MDIChildDivergedGraph(mdi.CorrelationMDIChild):
"""
Shows the graphs for the specified correlation
"""
symbol = None # Symbol to chart divergence for. Public as we use to check if window for the symbol is already open.
# Date formats for graphs
__tick_fmt_date = matplotlib.dates.DateFormatter('%d-%b')
__tick_fmt_time = matplotlib.dates.DateFormatter('%H:%M:%S')
# Fig, axes and canvas
__fig = None
__axs = None
__canvas = None
# Colors for graph lines
__colours = ['red', 'green', 'blue', 'pink', 'purple', 'black', 'yellow', 'lightblue']
def __init__(self, parent, **kwargs):
# Super
wx.MDIChildFrame.__init__(self, parent=parent, id=wx.ID_ANY,
title=f"Divergence Graph for {kwargs['symbol']}")
# Create logger
self.__log = logging.getLogger(__name__)
# Store the symbol
self.symbol = kwargs['symbol']
# We will freeze this frame and thaw once constructed to avoid flicker.
self.Freeze()
# Draw the empty graph. We will populate with data in refresh.
# Create fig and 1 axes.
self.__fig, self.__axs = plt.subplots(1)
# Set title
self.__axs.set_title(f"Price Data for {self.symbol} vs Previously Correlated Symbols")
# Set Y Labels and tick colours for symbol. This will be set for other symbols on plot
for i in range(0, 2):
self.__axs.set_ylabel(f"{self.symbol}", color=self.__colours[0], labelpad=10)
self.__axs.tick_params(axis='y', labelcolor=self.__colours[0])
# Hack to stop xaxis dropping outside of window
self.__axs.set_xlabel(" ", labelpad=10)
# Layout with padding between charts
self.__fig.tight_layout(pad=0.5)
# Create panel and sizer. This will provide scrollbar
panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
sizer = wx.BoxSizer()
panel.SetSizer(sizer)
# Add fig to canvas and canvas to sizer. Thaw window to update
self.__canvas = FigureCanvas(panel, wx.ID_ANY, self.__fig)
sizer.Add(self.__canvas, 1, wx.ALL | wx.EXPAND)
self.Thaw()
# Setup scrolling
panel.SetupScrolling()
# Refresh to show content
self.refresh()
def refresh(self):
"""
Refresh the graph
:return:
"""
# Get the symbols that this one has diverged against.
data = self.GetMDIParent().cor.filtered_coefficient_data
filtered_data = data.loc[(
(data['Status'] == cor.STATUS_DIVERGED) |
(data['Status'] == cor.STATUS_DIVERGING) |
(data['Status'] == cor.STATUS_CONVERGING)
) &
(
(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())
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 = []
for symbol in other_symbols:
tick_data = self.GetMDIParent().cor.get_ticks(symbol, cache_only=True)
other_tick_data.append(tick_data)
# Plot tick data for the base symbol
self.__axs.plot(symbol_tick_data['time'], symbol_tick_data['ask'], color=self.__colours[0], label=self.symbol)
# Plot for the other symbols on new axes
for i in range(0, len(other_tick_data)):
new_ax = self.__axs.twinx()
new_ax.plot(other_tick_data[i]['time'], other_tick_data[i]['ask'], label=other_symbols[i],
color=self.__colours[i+1])
new_ax.set_ylabel(f"{other_symbols[i]}", color=self.__colours[i+1], labelpad=10)
new_ax.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:
self.__axs.xaxis.set_major_locator(mticker.MaxNLocator(10))
ticks_loc = self.__axs.get_xticks().tolist()
self.__axs.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
self.__axs.set_xticklabels(ticks_loc)
self.__axs.xaxis.set_major_formatter(self.__tick_fmt_time)
plt.setp(self.__axs.xaxis.get_majorticklabels(), rotation=45)
# Legend
#self.__axs.legend([self.symbol, ] + other_symbols)
# Redraw canvas
self.__canvas.draw()
def __del__(self):
# Close all plots
plt.close('all')
+5 -14
View File
@@ -125,20 +125,11 @@ class MDIChildStatus(mdi.CorrelationMDIChild):
symbol1 = self.__grid.GetCellValue(row, COLUMN_SYMBOL1) symbol1 = self.__grid.GetCellValue(row, COLUMN_SYMBOL1)
symbol2 = self.__grid.GetCellValue(row, COLUMN_SYMBOL2) symbol2 = self.__grid.GetCellValue(row, COLUMN_SYMBOL2)
from mt5_correlation.gui.mdi_child_correlationgraph import MDIChildCorrelationGraph mdi.FrameManager.open_frame(parent=self.GetMDIParent(),
frame_module='mt5_correlation.gui.mdi_child_correlationgraph',
# Check if already open frame_class='MDIChildCorrelationGraph',
instance = None raise_if_open=True,
for child in self.GetMDIParent().GetChildren(): symbols=[symbol1, symbol2])
if isinstance(child, MDIChildCorrelationGraph):
if child.symbols[0] == symbol1 and child.symbols[1] == symbol2:
instance = child
# If already open, raise to top. Otherwise open
if instance is None:
MDIChildCorrelationGraph(parent=self.GetMDIParent(), symbol1=symbol1, symbol2=symbol2).Show(True)
else:
instance.Raise()
class _DataTable(wx.grid.GridTableBase): class _DataTable(wx.grid.GridTableBase):