Added diverged symbols chart
This commit is contained in:
+57
-24
@@ -1,11 +1,15 @@
|
||||
import abc
|
||||
import importlib
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pytz
|
||||
import wx
|
||||
import wxconfig as cfg
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import mt5_correlation.gui as gui
|
||||
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)
|
||||
|
||||
def __on_view_status(self, evt):
|
||||
from mt5_correlation.gui.mdi_child_status import MDIChildStatus
|
||||
|
||||
# Only open if not already open. If already open then raise to top.
|
||||
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()
|
||||
FrameManager.open_frame(parent=self, frame_module='mt5_correlation.gui.mdi_child_status',
|
||||
frame_class='MDIChildStatus',
|
||||
raise_if_open=True)
|
||||
|
||||
def __on_view_diverged(self, evt):
|
||||
from mt5_correlation.gui.mdi_child_diverged_symbols import MDIChildDivergedSymbols
|
||||
|
||||
# Only open if not already open. If already open then raise to top.
|
||||
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()
|
||||
FrameManager.open_frame(parent=self, frame_module='mt5_correlation.gui.mdi_child_diverged_symbols',
|
||||
frame_class='MDIChildDivergedSymbols',
|
||||
raise_if_open=True)
|
||||
|
||||
def __refresh(self):
|
||||
"""
|
||||
@@ -305,3 +291,50 @@ class CorrelationMDIChild(wx.MDIChildFrame):
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
__canvas = None
|
||||
|
||||
def __init__(self, parent, symbol1, symbol2):
|
||||
def __init__(self, parent, **kwargs):
|
||||
# Super
|
||||
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
|
||||
self.__log = logging.getLogger(__name__)
|
||||
|
||||
# Store the symbols
|
||||
self.symbols = [symbol1, symbol2]
|
||||
self.symbols = kwargs['symbols']
|
||||
|
||||
# We will freeze this frame and thaw once constructed to avoid flicker.
|
||||
self.Freeze()
|
||||
|
||||
@@ -56,6 +56,9 @@ class MDIChildDivergedSymbols(mdi.CorrelationMDIChild):
|
||||
self.__grid.SetMinSize((220, 100))
|
||||
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
|
||||
self.refresh()
|
||||
|
||||
@@ -94,6 +97,21 @@ class MDIChildDivergedSymbols(mdi.CorrelationMDIChild):
|
||||
# Update row count
|
||||
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):
|
||||
"""
|
||||
|
||||
@@ -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')
|
||||
@@ -125,20 +125,11 @@ class MDIChildStatus(mdi.CorrelationMDIChild):
|
||||
symbol1 = self.__grid.GetCellValue(row, COLUMN_SYMBOL1)
|
||||
symbol2 = self.__grid.GetCellValue(row, COLUMN_SYMBOL2)
|
||||
|
||||
from mt5_correlation.gui.mdi_child_correlationgraph import MDIChildCorrelationGraph
|
||||
|
||||
# Check if already open
|
||||
instance = None
|
||||
for child in self.GetMDIParent().GetChildren():
|
||||
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()
|
||||
mdi.FrameManager.open_frame(parent=self.GetMDIParent(),
|
||||
frame_module='mt5_correlation.gui.mdi_child_correlationgraph',
|
||||
frame_class='MDIChildCorrelationGraph',
|
||||
raise_if_open=True,
|
||||
symbols=[symbol1, symbol2])
|
||||
|
||||
|
||||
class _DataTable(wx.grid.GridTableBase):
|
||||
|
||||
Reference in New Issue
Block a user