class MainWindowController(QObject): ''' ''' def __init__(self, zeromq_context, window): ''' Constructor ''' self.zeromq_context = zeromq_context self.window = window self.timeSeries = TimeSeries() self.window.view.setData(self.timeSeries) self.presentation = None def incomingBar(self, ticker, bar): self.timeSeries.addPoint(bar.timestamp, bar) self.window.view.update() def incomingTick(self, ticker, tick): self.timeSeries.addPoint(tick.timestamp.toDatetime(), tick) def incomingEvent(self): self.window.view.update() def startStream(self, streamParameters): if streamParameters['type'] == 'attach': self.client = QuotesourceClient(self.zeromq_context, streamParameters['control-ep']) self.client.setSummaryCallback(self.incomingBar) self.client.setTickCallback(self.incomingTick) self.client.setEventCallback(self.incomingEvent) try: from_time = streamParameters['from'] except KeyError: from_time = None try: to_time = streamParameters['to'] except KeyError: to_time = None if self.presentation is None: self.presentation = ClusterChartPresentation(streamParameters['cluster-chart/cluster-size'], streamParameters['cluster-chart/bar-length'] * 60) self.window.view.setPresentation(self.presentation) self.client.startStream(streamParameters['source'], streamParameters['ticker-selectors'], from_time, to_time, streamParameters['delay']) def stopAllStreams(self): self.client.stopStream()
class ClusterChartPresentation(): ''' ''' def __init__(self, cluster_size, bar_length): ''' Constructor ''' self.offset = 0 self.candles_on_screen = 40 self.volume_chart_height = 100 self.cluster_size = cluster_size self.bar_length = bar_length self.y_min = 0 self.y_max = 0 self.cached_data = None def getMinMax(self): return (self.y_min, self.y_max) def getNumberOfItems(self): if self.cached_data is not None: return len(self.cached_data) else: return 0 def getNumberOfVisibleItems(self): return self.candles_on_screen def setOffset(self, offset): self.offset = offset def setData(self, data): self.data = data self.cached_data = TimeSeries() self.current_bar = ClusterBar(self.cluster_size, self.bar_length) self.current_bar_number = 0 self.bar_end_time = None self.data.registerNewPointListener(self.incomingTick) def incomingTick(self, time, data): if self.current_bar_number == 0: self.current_bar_number = int(time.timestamp() // self.bar_length) self.bar_end_time = datetime.datetime.fromtimestamp((self.current_bar_number + 1) * self.bar_length) self.current_bar = ClusterBar(self.cluster_size, self.bar_length) self.cached_data.addPoint(time, self.current_bar) bar_number = int(time.timestamp() // self.bar_length) if bar_number != self.current_bar_number: self.cached_data.updateLastPointTime(self.bar_end_time) self.current_bar = ClusterBar(self.cluster_size, self.bar_length) self.current_bar_number = bar_number self.bar_end_time = datetime.datetime.fromtimestamp((self.current_bar_number + 1) * self.bar_length) self.cached_data.addPoint(time, self.current_bar) self.current_bar.addPoint(float(data.value), data.volume) self.cached_data.updateLastPointTime(time) def paint(self, painter, rect): if len(self.cached_data) == 0: return print("paint start", len(self.data)) painter.setFont(QtGui.QFont("serif", 6)) start_index = max(0, len(self.cached_data) - self.offset - self.candles_on_screen - 1) end_index = max(0, len(self.cached_data) - self.offset) (abs_min, abs_max, min_volume, max_volume, min_bar_volume, max_bar_volume) = self._get_minmax(self.cached_data, start_index, end_index) self.y_min = abs_min self.y_max = abs_max cluster_chart_rect = rect.adjusted(0, 0, 0, -self.volume_chart_height - 20) volume_chart_rect = rect.adjusted(0, cluster_chart_rect.height() + 20, 0, 0) if abs_max > abs_min: y_scale = cluster_chart_rect.height() / (abs_max - abs_min) y_offset = abs_min else: y_scale = 0 y_offset = 0 bar_width = cluster_chart_rect.width() / (self.candles_on_screen + 1) cluster_height = cluster_chart_rect.height() / self.cluster_size bar_x = 1 painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0))) for i in range(start_index, end_index): bar = self.cached_data[i] self._paint_bar(painter, cluster_chart_rect, bar, bar_x, bar_width, cluster_height, y_scale, y_offset, min_volume, max_volume) self._paint_bar_volume(painter, volume_chart_rect, bar, bar_x, bar_width, min_bar_volume, max_bar_volume) bar_x += bar_width print("paint end") def _paint_bar(self, painter, rect, bar, bar_x, bar_width, cluster_height, y_scale, y_offset, min_volume, max_volume): cluster_start = bar.start_price cluster_end = bar.start_price + bar.cluster_size for cluster in bar.clusters: if max_volume > min_volume: relative_volume = (cluster.total() - min_volume) / (max_volume - min_volume) else: relative_volume = 0 painter.setPen(QtGui.QColor(relative_volume * 255, 0, (1 - relative_volume) * 255)) painter.setBrush(QtGui.QColor(255, 255, 255)) low_y = (cluster_start - y_offset) * y_scale high_y = (cluster_end - y_offset) * y_scale cluster_rect = QtCore.QRect(bar_x, rect.height() - high_y, bar_width, high_y - low_y) painter.drawRect(cluster_rect) if cluster.minus() == 0: painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, str(cluster.total())) else: cluster_rect.adjust(0, 0, 0, -cluster_rect.height() / 2) painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, str(cluster.total())) cluster_rect.translate(0, cluster_rect.height()) painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, "+" + str(cluster.plus()) + "\n-" + str(cluster.minus())) delta_bar_rect = cluster_rect.adjusted(0, cluster_rect.height() * 0.8, 0, 0) plus_bar_rect = QtCore.QRect(delta_bar_rect.left(), delta_bar_rect.top(), delta_bar_rect.width() * (cluster.plus() / cluster.total()), delta_bar_rect.height()) minus_bar_rect = delta_bar_rect.adjusted(plus_bar_rect.width(), 0, 0, 0) painter.setBrush(QtGui.QColor(0, 192, 0)) painter.drawRect(plus_bar_rect) painter.setBrush(QtGui.QColor(192, 0, 0)) painter.drawRect(minus_bar_rect) cluster_start = cluster_end cluster_end = cluster_start + bar.cluster_size first_price_y = rect.height() - (bar.first_price - y_offset) * y_scale last_price_y = rect.height() - (bar.last_price - y_offset) * y_scale painter.setPen(QtGui.QColor(0, 0, 0)) painter.setBrush(QtGui.QColor(0, 0, 0)) painter.drawLine(bar_x, first_price_y, bar_x + 5, first_price_y) painter.drawLine(bar_x + bar_width, last_price_y, bar_x + bar_width - 5, last_price_y) def _paint_bar_volume(self, painter, volume_chart_rect, bar, bar_x, bar_width, min_bar_volume, max_bar_volume): total_minus = 0 total_plus = 0 for cluster in bar.clusters: total_minus += cluster.minus() total_plus += cluster.plus() delta_num_height = 20 if max_bar_volume > 0: y_scale = (volume_chart_rect.height() - delta_num_height) / max_bar_volume else: y_scale = 0 painter.setPen(QtGui.QColor(0, 0, 0)) minus_bar_height = y_scale * total_minus plus_bar_height = y_scale * total_plus bar_rect = QtCore.QRect(bar_x, volume_chart_rect.top(), bar_width, volume_chart_rect.height() - delta_num_height) painter.setBrush(QtGui.QColor(0, 192, 0)) plus_bar_rect = bar_rect.adjusted(0, (volume_chart_rect.height() - delta_num_height - plus_bar_height - minus_bar_height), 0, -minus_bar_height) painter.drawRect(plus_bar_rect) painter.setBrush(QtGui.QColor(192, 0, 0)) minus_bar_rect = bar_rect.adjusted(0, (volume_chart_rect.height() - delta_num_height - minus_bar_height), 0, 0) painter.drawRect(minus_bar_rect) delta_num_rect = QtCore.QRect(bar_x, volume_chart_rect.bottom() - delta_num_height, bar_width, delta_num_height) delta = total_plus - total_minus if total_plus + total_minus == 0: painter.setPen(QtGui.QColor(128, 128, 128)) else: if delta / (total_plus + total_minus) > 0.1: painter.setPen(QtGui.QColor(0, 192, 0)) elif delta / (total_plus + total_minus) < -0.1: painter.setPen(QtGui.QColor(192, 0, 0)) else: painter.setPen(QtGui.QColor(128, 128, 128)) painter.setBrush(QtGui.QColor(255, 255, 255)) painter.drawRect(delta_num_rect) painter.drawText(delta_num_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, str(delta)) def _get_minmax(self, data, start_index, end_index): abs_min = float(data[start_index].start_price) abs_max = float(data[start_index].start_price) min_volume = data[start_index].clusters[0].total() max_volume = data[start_index].clusters[0].total() min_bar_volume = data[start_index].clusters[0].total() max_bar_volume = data[start_index].clusters[0].total() for i in range(start_index, end_index): bar = data[i] if abs_min > bar.start_price: abs_min = bar.start_price if abs_max < bar.start_price + bar.cluster_size * len(bar.clusters): abs_max = bar.start_price + bar.cluster_size * len(bar.clusters) bar_volume = 0 for cluster in bar.clusters: if min_volume > cluster.total(): min_volume = cluster.total() if max_volume < cluster.total(): max_volume = cluster.total() bar_volume += cluster.total() if min_bar_volume > bar_volume: min_bar_volume = bar_volume if max_bar_volume < bar_volume: max_bar_volume = bar_volume return (abs_min, abs_max, min_volume, max_volume, min_bar_volume, max_bar_volume)