Added diverged symbols chart
This commit is contained in:
+57
-24
@@ -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')
|
||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user