marketGraph.py (18760B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\market\marketGraph.py 2 from carbon.common.script.util.format import FmtDate, FmtAmt 3 import carbonui.const as uiconst 4 from carbonui import ButtonVariant, Density 5 from carbonui.primitives.container import Container 6 from carbonui.control.button import Button 7 from carbonui.graphs.minimap import MiniMap 8 from carbonui.graphs.pointgraph import PointGraph 9 from carbonui.graphs.donchianchannel import DonchianChannel 10 from carbonui.graphs import axis, axislabels, graph 11 from carbonui.graphs.lowhighvaluegraph import LowHighValueGraph 12 from carbonui.graphs.graphsutil import MovingAvg 13 from carbonui.graphs.linegraph import LineGraph 14 from carbonui.graphs.bargraph import BarGraph, DynamicHint 15 from carbonui.graphs.areagraph import AreaGraph 16 from carbonui.graphs.grid import Grid 17 from carbonui.graphs.legend import Legend 18 from carbonui.graphs import animations 19 import localization 20 from eve.client.script.ui.control.utilMenu import UtilMenu 21 _cachedRange = () 22 graphColors = ((0.0, 0.247, 0.31, 1.0), 23 (0.098, 0.4, 0.479, 1.0), 24 (0.082, 0.663, 0.812, 1.0), 25 (0.569, 0.275, 0.0, 1.0), 26 (0.812, 0.435, 0.082, 1.0)) 27 TOP_AXIS_HEIGHT = 30 28 LEFT_AXIS_WIDTH = 70 29 RIGHT_AXIS_WIDTH = 50 30 31 def _GetSetting(name, default): 32 return settings.user.ui.Get('market_setting_%s' % name, default) 33 34 35 def _SetSetting(name, value): 36 settings.user.ui.Set('market_setting_%s' % name, value) 37 38 39 def _OnAnimationCheckbox(): 40 curr = _GetSetting('animations', 1) 41 _SetSetting('animations', not curr) 42 43 44 def _FormatAmount(x): 45 if abs(x) < 100: 46 return FmtAmt(x, 'ln', 1) 47 try: 48 return FmtAmt(x, 'sn') 49 except: 50 return '' 51 52 53 class MarketGraph(Container): 54 default_state = uiconst.UI_ACTIVE 55 default_zoom = 30 56 57 def ApplyAttributes(self, attributes): 58 global _cachedRange 59 Container.ApplyAttributes(self, attributes) 60 self.typeID = attributes.get('typeID', None) 61 self.data = attributes.get('data', None) 62 count = len(self.data) 63 self.zoom = attributes.get('zoom', None) 64 if self.zoom is None: 65 self.zoom = _GetSetting('dateRange', self.default_zoom) 66 self.zoom = min(self.zoom, count) 67 if _cachedRange: 68 timeRange = _cachedRange 69 if timeRange[1] > count: 70 timeRange = (count - self.zoom - 0.5, count - 0.5) 71 else: 72 timeRange = (count - self.zoom - 0.5, count - 0.5) 73 self.resetZoomBtn = Button(name='resetZoom', label=localization.GetByLabel('UI/Market/PriceHistory/ResetAxis'), parent=self, align=uiconst.CENTERTOP, func=self._ResetAutoZoom, density=Density.COMPACT, variant=ButtonVariant.GHOST) 74 self.topAxisContainer = Container(parent=self, name='TopAxisContainer', align=uiconst.TOTOP, height=TOP_AXIS_HEIGHT) 75 self.miniMapContainer = Container(parent=self, name='MinimapContainer', align=uiconst.TOBOTTOM, height=60) 76 self.leftAxisContainer = Container(parent=self, name='LeftAxisContainer', align=uiconst.TOLEFT, width=LEFT_AXIS_WIDTH) 77 self.rightAxisContainer = Container(parent=self, name='RightAxisContainer', align=uiconst.TORIGHT, width=RIGHT_AXIS_WIDTH) 78 self.leftAxis = axis.AutoTicksAxis(self._GetLeftAxisData(), tickCount=10, margins=(0.4, 0.15), tickFilter=lambda t: t >= 0, labelFormat=_FormatAmount) 79 self.rightAxis = axis.AutoTicksAxis(self._GetRightAxisData(), behavior=axis.AXIS_FROM_ZERO, tickCount=4, labelFormat=_FormatAmount) 80 self.topAxis = axis.MonthlyTimeAxis([ x[0] for x in self.data ], visibleRange=timeRange, behavior=axis.AXIS_CUSTOM, interactionLimit=axis.InteractionLimit.DATA_RANGE) 81 self.graphArea = graph.GraphArea(parent=self, name='GraphArea') 82 self.graphArea.AddAxis(axis.AxisOrientation.HORIZONTAL, self.topAxis) 83 self.graphArea.AddAxis(axis.AxisOrientation.VERTICAL, self.leftAxis, 1.0, 0.0) 84 self.graphArea.AddAxis(axis.AxisOrientation.VERTICAL, self.rightAxis, 1.0, 0.7) 85 self.graphArea.AddZoomAxis(self.leftAxis) 86 self.graphArea.AddPanAxis(self.leftAxis) 87 self.graphArea.AddPanAxis(self.topAxis) 88 if _GetSetting('scale_to_data', 1) and self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM: 89 self.leftAxis.ZoomOn(self._GetLeftAxisData(True)) 90 self.rightAxis.ZoomOn(self._GetRightAxisData(True)) 91 self.median = None 92 self.donchian = None 93 self.lowHigh = None 94 self.movingAvg5 = None 95 self.movingAvg20 = None 96 self.barGraph = None 97 self._isPanning = False 98 self._prevPanPosition = (0, 0) 99 self._ajustZoomToVisible = _GetSetting('scale_to_data', 1) 100 if len(self.data) < 1: 101 return 102 self.Build(_GetSetting('animations', 1)) 103 self.leftAxis.onChange.connect(self._LeftAxisChanged) 104 self.topAxis.onChange.connect(self._TopAxisChanged) 105 106 def _OnScaleToDataCheckbox(self): 107 curr = _GetSetting('scale_to_data', 1) 108 _SetSetting('scale_to_data', not curr) 109 if _GetSetting('scale_to_data', 1): 110 if self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM: 111 self.leftAxis.ZoomOn(self._GetLeftAxisData(True)) 112 self.rightAxis.ZoomOn(self._GetRightAxisData(True)) 113 else: 114 self.leftAxis.ZoomOn(None) 115 self.rightAxis.ZoomOn(None) 116 117 def _OnLegendCheckbox(self): 118 curr = _GetSetting('legend', 1) 119 _SetSetting('legend', not curr) 120 if _GetSetting('legend', 1): 121 if not self.legend: 122 self.legend = Legend(parent=self, align=uiconst.TOTOP, autoHeight=True, centerContent=True, contentSpacing=(6, 6)) 123 self._RebuildLegend() 124 elif self.legend: 125 self.legend.Close() 126 self.legend = None 127 128 def _OnGraphCheckbox(self, name): 129 curr = _GetSetting(name, 1) 130 _SetSetting(name, not curr) 131 self.leftAxis.SetDataRange(self._GetLeftAxisData(False)) 132 self.rightAxis.SetDataRange(self._GetRightAxisData(False)) 133 if name == 'movingavg5': 134 self.AddMovingAverage5() 135 elif name == 'movingavg20': 136 self.AddMovingAverage20() 137 elif name == 'donchian': 138 self.AddDonchianChannel() 139 elif name == 'mediandayprice': 140 self.AddMedianPoints() 141 elif name == 'minmax': 142 self.AddMinMaxGraph() 143 elif name == 'volume': 144 self.AddBarGraph() 145 self._RebuildLegend() 146 147 def GetGraphMenu(self, menuParent): 148 avg5 = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=5) 149 avg20 = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=20) 150 GetLabel = localization.GetByLabel 151 PRICE_FILTERS = [('movingavg5', GetLabel('UI/Market/PriceHistory/ShowMovingAvg', numDays=avg5)), 152 ('movingavg20', GetLabel('UI/Market/PriceHistory/ShowMovingAvg', numDays=avg20)), 153 ('donchian', GetLabel('UI/Market/PriceHistory/ShowDonchianChannel')), 154 ('mediandayprice', GetLabel('UI/Market/PriceHistory/ShowMedianDayPrice')), 155 ('minmax', GetLabel('UI/Market/PriceHistory/ShowMinMax')), 156 ('volume', GetLabel('UI/Market/PriceHistory/ShowVolume'))] 157 menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ShowAnimations'), checked=_GetSetting('animations', 1), callback=_OnAnimationCheckbox) 158 menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ScaleToData'), checked=_GetSetting('scale_to_data', 1), callback=self._OnScaleToDataCheckbox) 159 menuParent.AddSpace() 160 for settingName, label in PRICE_FILTERS: 161 menuParent.AddCheckBox(text=label, checked=_GetSetting(settingName, 1), callback=(self._OnGraphCheckbox, settingName)) 162 163 menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ShowLegend'), checked=_GetSetting('legend', 1), callback=self._OnLegendCheckbox) 164 165 def Build(self, animate = False): 166 self.topAxisLabels = axislabels.AxisLabels(parent=self.topAxisContainer, align=uiconst.TOTOP, axis=self.topAxis, orientation=axis.AxisOrientation.HORIZONTAL, padding=(LEFT_AXIS_WIDTH, 167 0, 168 RIGHT_AXIS_WIDTH, 169 0), height=TOP_AXIS_HEIGHT, textAlignment=uiconst.CENTERBOTTOM) 170 self.leftAxisLabels = axislabels.AxisLabels(parent=self.leftAxisContainer, axis=self.leftAxis, orientation=axis.AxisOrientation.VERTICAL, align=uiconst.TOLEFT, width=LEFT_AXIS_WIDTH, minFactor=1.0, maxFactor=0.0, hints=True) 171 self.rightAxisLabels = axislabels.AxisLabels(parent=self.rightAxisContainer, axis=self.rightAxis, orientation=axis.AxisOrientation.VERTICAL, textAlignment=uiconst.BOTTOMLEFT, align=uiconst.TOBOTTOM_PROP, height=0.3, width=self.rightAxisContainer.width, minFactor=1.0, maxFactor=0.0, hints=True) 172 if _GetSetting('legend', 1): 173 self.legend = Legend(parent=self, align=uiconst.TOTOP, autoHeight=True, centerContent=True, contentSpacing=(6, 6)) 174 else: 175 self.legend = None 176 self.AddMiniMap() 177 self.AddMedianPoints(animate) 178 self.AddMinMaxGraph() 179 self.AddMovingAverage5() 180 self.AddMovingAverage20() 181 self.AddDonchianChannel() 182 self.AddGrid() 183 self.AddBarGraph(animate) 184 if self.legend: 185 self._RebuildLegend() 186 self.utilMenu = UtilMenu(menuAlign=uiconst.TOPLEFT, parent=self, align=uiconst.TOPRIGHT, GetUtilMenu=self.GetGraphMenu, texturePath='res:/UI/Texture/SettingsCogwheel.png', top=4, width=18, height=18, iconSize=18) 187 self.resetZoomBtn.display = self.leftAxis.GetBehavior() == axis.AXIS_CUSTOM 188 189 def _ResetAutoZoom(self, *_): 190 self.resetZoomBtn.display = False 191 self.leftAxis.SetBehavior(axis.AXIS_TIGHT) 192 if _GetSetting('scale_to_data', 1): 193 self.leftAxis.ZoomOn(self._GetLeftAxisData(True)) 194 195 def _LeftAxisChanged(self, _): 196 self.resetZoomBtn.display = self.leftAxis.GetBehavior() == axis.AXIS_CUSTOM 197 198 def _TopAxisChanged(self, _): 199 global _cachedRange 200 _cachedRange = self.topAxis.GetVisibleRange() 201 _SetSetting('dateRange', int(_cachedRange[1] - _cachedRange[0])) 202 if _GetSetting('scale_to_data', 1): 203 if self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM: 204 self.leftAxis.ZoomOn(self._GetLeftAxisData(True)) 205 self.rightAxis.ZoomOn(self._GetRightAxisData(True)) 206 207 def _GetLeftAxisData(self, visibleRange = False): 208 if visibleRange: 209 inputData = self.data[max(0, int(self.topAxis.GetVisibleRange()[0])):int(self.topAxis.GetVisibleRange()[1]) + 1] 210 else: 211 inputData = self.data 212 data = [] 213 if _GetSetting('mediandayprice', True): 214 data.extend((x[3] for x in inputData)) 215 if _GetSetting('minmax', True): 216 data.extend((x[1] for x in inputData)) 217 data.extend((x[2] for x in inputData)) 218 closing = [ x[3] for x in inputData ] 219 if _GetSetting('movingavg5', True): 220 data.extend(MovingAvg(closing, 5)) 221 if _GetSetting('movingavg20', True): 222 data.extend(MovingAvg(closing, 20)) 223 if _GetSetting('donchian', True): 224 data.extend((x[1] for x in inputData)) 225 data.extend((y[2] for y in inputData)) 226 if not data: 227 return (0, 1) 228 return (min(data), max(data)) 229 230 def _GetRightAxisData(self, visibleRange = False): 231 data = [] 232 if _GetSetting('volume', True): 233 if visibleRange: 234 inputData = self.data[max(0, int(self.topAxis.GetVisibleRange()[0])):int(self.topAxis.GetVisibleRange()[1]) + 1] 235 else: 236 inputData = self.data 237 data.extend((x[4] for x in inputData)) 238 return axis.GetRangeFromSequences([0], data) 239 240 def AddGrid(self): 241 Grid(parent=self.graphArea, axis=self.leftAxis, orientation=axis.AxisOrientation.VERTICAL, minFactor=1.0, maxFactor=0.0, color=(1.0, 1.0, 1.0, 0.1), zeroColor=(1, 1, 1, 1)) 242 Grid(parent=self.graphArea, axis=self.topAxis, orientation=axis.AxisOrientation.HORIZONTAL, color=(1.0, 1.0, 1.0, 0.1)) 243 244 def AddBarGraph(self, animate = False): 245 if _GetSetting('volume', True): 246 self.rightAxisLabels.display = True 247 if _GetSetting('scale_to_data', 1): 248 self.rightAxis.ZoomOn(self._GetRightAxisData(True)) 249 if self.barGraph: 250 return 251 data = [ x[4] for x in self.data ] 252 253 def getHint(index): 254 point = self.data[index] 255 return localization.GetByLabel('UI/PriceHistory/VolumeHint', date=FmtDate(point[0], 'ln'), orders=FmtAmt(point[5], 'sn'), quantity=FmtAmt(point[4], 'sn')) 256 257 self.barGraph = BarGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.rightAxis, barSizeMinMax=(2, 20), values=data, hint=DynamicHint(getHint), color=graphColors[0], hoverColor=graphColors[0][0:3] + (2,)) 258 if animate: 259 self.barGraph.Animate(animations.AnimationType.FADE, animations.AnimationDynamics.SIMULTANEOUS, 1.0) 260 else: 261 if self.barGraph: 262 self.barGraph.Close() 263 self.barGraph = None 264 self.rightAxisLabels.display = False 265 266 def AddMovingAverage5(self): 267 if _GetSetting('movingavg5', True): 268 if self.movingAvg5: 269 return 270 closing = [ x[3] for x in self.data ] 271 avg5Data = MovingAvg(closing, 5) 272 idx = 0 273 if self.median: 274 idx += 1 275 if self.lowHigh: 276 idx += 1 277 self.movingAvg5 = LineGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=avg5Data, lineColor=graphColors[2], idx=idx) 278 elif self.movingAvg5: 279 self.movingAvg5.Close() 280 self.movingAvg5 = None 281 282 def AddMovingAverage20(self): 283 if _GetSetting('movingavg20', True): 284 if self.movingAvg20: 285 return 286 closing = [ x[3] for x in self.data ] 287 avg20Data = MovingAvg(closing, 20) 288 idx = 0 289 if self.median: 290 idx += 1 291 if self.lowHigh: 292 idx += 1 293 if self.movingAvg5: 294 idx += 1 295 self.movingAvg20 = LineGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=avg20Data, lineColor=graphColors[4], idx=idx) 296 elif self.movingAvg20: 297 self.movingAvg20.Close() 298 self.movingAvg20 = None 299 300 def AddMinMaxGraph(self): 301 if _GetSetting('minmax', True): 302 if self.lowHigh: 303 return 304 data = [ (x[1], x[2]) for x in self.data ] 305 idx = 1 if self.median else 0 306 self.lowHigh = LowHighValueGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, idx=idx) 307 elif self.lowHigh: 308 self.lowHigh.Close() 309 self.lowHigh = None 310 311 def AddDonchianChannel(self): 312 if _GetSetting('donchian', True): 313 if self.donchian: 314 return 315 data = ([ x[1] for x in self.data ], [ y[2] for y in self.data ]) 316 idx = 0 317 if self.median: 318 idx += 1 319 if self.lowHigh: 320 idx += 1 321 if self.movingAvg5: 322 idx += 1 323 if self.movingAvg20: 324 idx += 1 325 self.donchian = DonchianChannel(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, graphColor=(0.8, 326 0.8, 327 1.0, 328 0.12249999999999998), state=uiconst.UI_DISABLED, idx=idx) 329 elif self.donchian: 330 self.donchian.Close() 331 self.donchian = None 332 333 def AddMiniMap(self): 334 data = [ x[3] for x in self.data ] 335 categoryAxis = axis.MonthlyTimeAxis([ x[0] for x in self.data ]) 336 valueAxis = axis.AutoTicksAxis((min(data), max(data)), behavior=axis.AXIS_FROM_ZERO) 337 miniMap = MiniMap(name='MiniMap', parent=self.miniMapContainer, align=uiconst.TOALL, controlAxis=self.topAxis, padTop=4) 338 miniMap.AddAxis(axis.AxisOrientation.HORIZONTAL, categoryAxis) 339 miniMap.AddAxis(axis.AxisOrientation.VERTICAL, valueAxis, 1.0, 0.0) 340 Grid(parent=miniMap, axis=categoryAxis, orientation=axis.AxisOrientation.HORIZONTAL, color=(1.0, 1.0, 1.0, 0.1)) 341 AreaGraph(parent=miniMap, categoryAxis=categoryAxis, valueAxis=valueAxis, values=data, graphColor=(1.0, 342 1.0, 343 1.0, 344 0.09), state=uiconst.UI_DISABLED) 345 346 def AddMedianPoints(self, animate = False): 347 if _GetSetting('mediandayprice', True): 348 if self.median: 349 return 350 351 def getHint(index): 352 point = self.data[index] 353 return '%s\n%s' % (FmtDate(point[0], 'ln'), FmtAmt(point[3], 'sn')) 354 355 data = [ x[3] for x in self.data ] 356 self.median = PointGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, hint=DynamicHint(getHint), pointSizeMinMax=(5, 10), pointColor=graphColors[3], idx=0) 357 if animate: 358 self.median.Animate(animations.AnimationType.GROW, animations.AnimationDynamics.ALONG_AXIS, 1.0) 359 elif self.median: 360 self.median.Close() 361 self.median = None 362 363 def _RebuildLegend(self): 364 if self.legend is None: 365 return 366 self.legend.Flush() 367 if self.median: 368 self.legend.AddItem(self.median.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MedianDayPrice')) 369 if self.lowHigh: 370 self.legend.AddItem(self.lowHigh.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MinMax')) 371 if self.movingAvg5: 372 numDays = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=5) 373 self.legend.AddItem(self.movingAvg5.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MovingAvg', numDays=numDays)) 374 if self.movingAvg20: 375 numDays = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=20) 376 self.legend.AddItem(self.movingAvg20.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MovingAvg', numDays=numDays)) 377 if self.donchian: 378 self.legend.AddItem(self.donchian.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/DonchianChannel')) 379 if self.barGraph: 380 self.legend.AddItem(self.barGraph.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/Volume'))