-
Notifications
You must be signed in to change notification settings - Fork 15
/
ticker.py
executable file
·160 lines (126 loc) · 5.07 KB
/
ticker.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
import itertools
import os
import time
from frame import Frame
from rgbmatrix import graphics
from price_apis import get_api_cls, logger
class Ticker(Frame):
def __init__(self, *args, **kwargs):
"""Initialize the Ticker class
Gather the users settings from environment variables, then initialize the
LED Panel Frame class.
"""
# initialize variables used for price data cache
self._cached_price_data = None
self._last_fetch_time = 0
# Set up the API
api_cls = get_api_cls(os.environ.get('API', 'coingecko'))
self.api = api_cls(symbols=self.get_symbols(), currency=self.get_currency())
# Get user settings
self.refresh_rate = int(os.environ.get('REFRESH_RATE', 300)) # 300s or 5m
self.sleep = int(os.environ.get('SLEEP', 3)) # 3s
super().__init__(*args, **kwargs)
def get_symbols(self):
"""Get the symbols to include"""
symbols = os.environ.get('SYMBOLS', 'btc,eth')
if not symbols:
return 'btc,eth'
return symbols
def get_currency(self):
"""Get the currency to use"""
currency = os.environ.get('CURRENCY', 'usd')
if not currency:
return 'usd'
return currency
@property
def price_data(self):
"""Price data for the requested assets, update automatically.
This function will return a cached copy of the asset prices unless its time to
fetch fresh data. We'll use the REFRESH_RATE environment variable to determine
if it's time to refresh data.
Returns:
Updated price data. See self.api.fetch_price_data.
"""
# Determine if the cache is stale
cache_is_stale = (time.time() - self._last_fetch_time) > self.refresh_rate
# See if we should return the cached price data
if self._cached_price_data and not cache_is_stale:
logger.info('Using cached price data.')
return self._cached_price_data
# Otherwise fetch new data and set the _last_fetch_time
price_data = self.api.fetch_price_data()
self._last_fetch_time = time.time()
self._cached_price_data = price_data
return price_data
def get_ticker_canvas(self, asset):
"""Build the ticker canvas given an asset
Returns:
A canvas object with the symbol, change, and price drawn.
"""
# Generate a fresh canvas
canvas = self.matrix.CreateFrameCanvas()
canvas.Clear()
# Create fonts for displaying prices
font_symbol = graphics.Font()
font_symbol.LoadFont('fonts/7x13.bdf')
font_price = graphics.Font()
font_price.LoadFont('fonts/6x12.bdf')
font_change = graphics.Font()
font_change.LoadFont('fonts/6x10.bdf')
# To right align, we have to calculate the width of the text
change_width = sum(
[font_change.CharacterWidth(ord(c)) for c in asset['change_24h']]
)
change_x = 62 - change_width
# Get colors
main_color = graphics.Color(255, 255, 0)
change_color = (
graphics.Color(194, 24, 7)
if asset['change_24h'].startswith('-')
else graphics.Color(46, 139, 87)
)
# Load a smaller font to andle 6-figure asset prices
if len(asset['price']) > 10:
font_price.LoadFont('fonts/5x8.bdf')
# Draw the elements on the canvas
graphics.DrawText(canvas, font_symbol, 3, 12, main_color, asset['symbol'])
graphics.DrawText(canvas, font_price, 3, 28, main_color, asset['price'])
graphics.DrawText(
canvas, font_change, change_x, 10, change_color, asset['change_24h']
)
return canvas
def get_error_canvas(self):
"""Build an error canvas to show on errors"""
canvas = self.matrix.CreateFrameCanvas()
canvas.Clear()
font = graphics.Font()
font.LoadFont('../rpi-rgb-led-matrix/fonts/7x13.bdf')
color = graphics.Color(194, 24, 7)
graphics.DrawText(canvas, font, 15, 20, color, 'ERROR')
return canvas
def get_assets(self):
"""Generator method that yields assets infinitely.
Since it uses `self.price_data` it will always return the latest prices,
respecting the REFRESH_RATE.
"""
# The size of the price_data list should not change, even when updated
price_data_length = len(self.price_data)
for index in itertools.cycle(range(price_data_length)):
try:
yield self.price_data[index]
except IndexError:
yield None
def run(self):
"""Run the loop and display ticker prices.
This is called by process.
"""
for asset in self.get_assets():
if asset:
canvas = self.get_ticker_canvas(asset)
else:
canvas = self.get_error_canvas()
self.matrix.SwapOnVSync(canvas)
time.sleep(self.sleep)
if __name__ == '__main__':
Ticker().process()