diff --git a/config.yaml b/config.yaml index d2868ec..929a926 100644 --- a/config.yaml +++ b/config.yaml @@ -13,14 +13,14 @@ monitor: long: from: 60 min_prices: 2000 - max_set_size_diff_pct: 90 - overlap_pct: 90 + max_set_size_diff_pct: 50 + overlap_pct: 50 max_p_value: 0.05 medium: from: 30 min_prices: 1000 - max_set_size_diff_pct: 90 - overlap_pct: 90 + max_set_size_diff_pct: 50 + overlap_pct: 50 max_p_value: 0.05 short: from: 10 @@ -71,8 +71,8 @@ logging: developer: inspection: false window: - x: 37 - y: 42 + x: 24 + y: 38 width: 1510 height: 956 style: 541072960 diff --git a/mt5_correlation/correlation.py b/mt5_correlation/correlation.py index c73e083..d246bb8 100644 --- a/mt5_correlation/correlation.py +++ b/mt5_correlation/correlation.py @@ -59,10 +59,10 @@ class CorrelationStatus: # All status's for symbol pair from monitoring. Status set from assessing coefficient for all timeframes from last run. STATUS_NOT_CALCULATED = CorrelationStatus(-1, 'NOT CALC', 'Coefficient could not be calculated') -STATUS_ABOVE_MONITORING_THRESHOLD = CorrelationStatus(1, 'ABOVE', 'All coefficients equal to or above the monitoring ' +STATUS_ABOVE_DIVERGENCE_THRESHOLD = CorrelationStatus(1, 'ABOVE', 'All coefficients equal to or above the divergence ' 'threshold') -STATUS_BELOW_MONITORING_THRESHOLD = CorrelationStatus(2, 'BELOW', 'All coefficients below the monitoring threshold') -STATUS_INCONSISTENT = CorrelationStatus(3, 'INCONSISTENT', 'Coefficients not consistently above or below monitoring ' +STATUS_BELOW_DIVERGENCE_THRESHOLD = CorrelationStatus(2, 'BELOW', 'All coefficients below the divergence threshold') +STATUS_INCONSISTENT = CorrelationStatus(3, 'INCONSISTENT', 'Coefficients not consistently above or below divergence ' 'threshold') @@ -78,6 +78,10 @@ class Correlation: # coefficient than ths won't be monitored. monitoring_threshold = 0.9 + # Threshold for divergence. Correlation coefficients that were previously above the monitoring_threshold and fall + # below this threshold will be considered as having diverged + divergence_threshold = 0.8 + # Toggle on whether we are monitoring or not. Set through start_monitor and stop_monitor __monitoring = False @@ -102,7 +106,14 @@ class Correlation: # Dict: {Symbol: [retrieved datetime, ticks dataframe]} __monitor_tick_data = {} - def __init__(self): + def __init__(self, monitoring_threshold=0.9, divergence_threshold=0.8): + """ + Initialises the Correlation class. + :param monitoring_threshold: Only correlations that are greater than or equal to this threshold will be + monitored. + :param divergence_threshold: Correlations that are being monitored and fall below this threshold are considered + to have diverged. + """ # Logger self.__log = logging.getLogger(__name__) @@ -115,6 +126,10 @@ class Correlation: # Create timer for continuous monitoring self.__scheduler = sched.scheduler(time.time, time.sleep) + # Set thresholds + self.monitoring_threshold = monitoring_threshold + self.divergence_threshold = divergence_threshold + @property def filtered_coefficient_data(self): """ @@ -672,10 +687,10 @@ class Correlation: if None in values: status = STATUS_NOT_CALCULATED - elif all(i >= self.monitoring_threshold for i in values): - status = STATUS_ABOVE_MONITORING_THRESHOLD - elif all(i < self.monitoring_threshold for i in values): - status = STATUS_BELOW_MONITORING_THRESHOLD + elif all(i >= self.divergence_threshold for i in values): + status = STATUS_ABOVE_DIVERGENCE_THRESHOLD + elif all(i < self.divergence_threshold for i in values): + status = STATUS_BELOW_DIVERGENCE_THRESHOLD else: status = STATUS_INCONSISTENT diff --git a/mt5_correlation/gui.py b/mt5_correlation/gui.py index ff692d0..718c589 100644 --- a/mt5_correlation/gui.py +++ b/mt5_correlation/gui.py @@ -6,8 +6,7 @@ import matplotlib.dates import matplotlib import matplotlib.ticker as mticker -from mt5_correlation import correlation -from mt5_correlation.correlation import Correlation +from mt5_correlation import correlation as cor from mt5_correlation.config import Config, SettingsDialog from datetime import datetime, timedelta import pytz @@ -52,8 +51,8 @@ class MonitorFrame(wx.Frame): self.__config = Config() # Create correlation instance to maintain state of calculated coefficients. Set min coefficient from config - self.__cor = Correlation() - self.__cor.monitoring_threshold = self.__config.get("monitor.monitoring_threshold") + self.__cor = cor.Correlation(monitoring_threshold=self.__config.get("monitor.monitoring_threshold"), + divergence_threshold=self.__config.get("monitor.divergence_threshold")) # Status bar. 2 fields, one for monitoring status and one for general status. On open, monitoring status is not # monitoring. SetBackgroundColour will change colour of both. Couldn't find a way to set on single field only. @@ -512,7 +511,7 @@ class DataTable(wx.grid.GridTableBase): # Is status one of interest value = self.GetValue(row, col) if value != "": - if value in [correlation.STATUS_BELOW_MONITORING_THRESHOLD]: + if value in [cor.STATUS_BELOW_DIVERGENCE_THRESHOLD]: attr.SetBackgroundColour(wx.YELLOW) else: attr.SetBackgroundColour(wx.WHITE) @@ -565,8 +564,9 @@ class GraphPanel(wx.Panel): # Check what data we have available price_data_available = prices is not None and len(prices) == 2 and \ - prices[0] is not None and prices[1] is not None - tick_data_available = ticks is not None and len(ticks) == 2 and ticks[0] is not None and ticks[1] is not None + prices[0] is not None and prices[1] is not None and len(prices[0]) > 0 and len(prices[1]) > 0 + tick_data_available = ticks is not None and len(ticks) == 2 and ticks[0] is not None and ticks[1] is not None \ + and len(ticks[0]) > 0 and len(ticks[1]) > 0 history_data_available = history is not None and len(history) > 0 symbols_selected = symbols is not None and len(symbols) == 2 diff --git a/test/test_correlation.py b/test/test_correlation.py index d85f9fb..ac00b58 100644 --- a/test/test_correlation.py +++ b/test/test_correlation.py @@ -173,8 +173,8 @@ class TestCorrelation(unittest.TestCase): # Mock symbol return values mock.symbols_get.return_value = self.mock_symbols - # Create correlation class - cor = correlation.Correlation() + # Create correlation class. We will set a divergence threshold so that we can test status. + cor = correlation.Correlation(divergence_threshold=0.8) # Calculate for price data. We should have 100% matching dates in sets. Get prices should be called 3 times. # We dont have a SYMBOL2 as this is set as not visible. All pairs should be correlated for the purpose of this @@ -185,9 +185,6 @@ class TestCorrelation(unittest.TestCase): cor.calculate(date_from=self.start_date, date_to=self.end_date, timeframe=5, min_prices=100, max_set_size_diff_pct=100, overlap_pct=100, max_p_value=1) - # Set the monitoring threshold - cor.monitoring_threshold = 0.9 - # We will build some tick data for each symbol and patch it in. Tick data will be from 10 seconds ago to now. # We only need to patch in one set of tick data for each symbol as it will be cached. columns = ['time', 'ask'] @@ -232,10 +229,11 @@ class TestCorrelation(unittest.TestCase): 'Timeframe': 0.66})), 2, "We should have 2 history records for SYMBOL1:SYMBOL2 using the 0.66 min timeframe.") - # The status should be BELOW for SYMBOL1:SYMBOL2 and SYMBOL1:SYMBOL4. It should be ABOVE for SYMBOL2:SYMBOL4. - self.assertTrue(cor.get_last_status('SYMBOL1', 'SYMBOL2') == correlation.STATUS_BELOW_MONITORING_THRESHOLD) - self.assertTrue(cor.get_last_status('SYMBOL1', 'SYMBOL4') == correlation.STATUS_BELOW_MONITORING_THRESHOLD) - self.assertTrue(cor.get_last_status('SYMBOL2', 'SYMBOL4') == correlation.STATUS_ABOVE_MONITORING_THRESHOLD) + # The status should be BELOW for SYMBOL1:SYMBOL2, and should be ABOVE for and SYMBOL1:SYMBOL4 and + # SYMBOL2:SYMBOL4. + self.assertTrue(cor.get_last_status('SYMBOL1', 'SYMBOL2') == correlation.STATUS_BELOW_DIVERGENCE_THRESHOLD) + self.assertTrue(cor.get_last_status('SYMBOL1', 'SYMBOL4') == correlation.STATUS_ABOVE_DIVERGENCE_THRESHOLD) + self.assertTrue(cor.get_last_status('SYMBOL2', 'SYMBOL4') == correlation.STATUS_ABOVE_DIVERGENCE_THRESHOLD) @patch('mt5_correlation.mt5.MetaTrader5') def test_load_and_save(self, mock):