# @name Currency Market # @desc Global currency exchange rate display # @author Avemer # @version 1.2 class CurrencyMarket # USER VARIABLES var BASE_CURRENCY var QUOTE_CURRENCY var TICKER_LETTERS var PRICE_DECIMALS var TICKER_COLOR var PRICE_COLOR_MODE var PRICE_COLOR var CURRENT_PRICE_UPDATE_TICKS var GAP # INTERNAL VARIABLES var result var oldPrice var comparisonPrice var currentPriceTicks var color var displayTICKER # ================ START OF USER SETTINGS ================ def init() # BASE CURRENCY # Use a 3-letter ISO currency code. # Examples: # EUR, USD, GBP, JPY, CNY, CHF, RUB self.BASE_CURRENCY = "EUR" # QUOTE CURRENCY # The currency in which the BASE currency is priced. # Examples: # EUR + USD = EUR/USD # GBP + USD = GBP/USD self.QUOTE_CURRENCY = "USD" # NUMBER OF TICKER LETTERS DISPLAYED # 0 = hide ticker # 1 = first letter of BASE currency # 2 = first two letters of BASE currency # 3 = full BASE currency # 4+ = full BASE/QUOTE pair self.TICKER_LETTERS = 3 # NUMBER OF PRICE DECIMAL PLACES # Controls how many digits are displayed after the decimal point. # 0 = no decimal places # 1 = one decimal place # 2 = two decimal places # 3 = three decimal places # 4 = four decimal places self.PRICE_DECIMALS = 3 # TICKER COLOR # Format: 0xRRGGBB self.TICKER_COLOR = 0xFFFFFF # PRICE COLOR MODE # 0 = always white # 1 = custom PRICE_COLOR # 2 = automatic green/red comparison # Automatic mode compares the newest rate # with the most recent different rate found # in the 5-day history. self.PRICE_COLOR_MODE = 2 # CUSTOM PRICE COLOR # Used when PRICE_COLOR_MODE = 1. self.PRICE_COLOR = 0xFFFFFF # CURRENT PRICE UPDATE # Number of loop ticks between history requests. # The first request is made immediately. self.CURRENT_PRICE_UPDATE_TICKS = 600 # DISTANCE BETWEEN TICKER AND PRICE self.GAP = 2 # ================ END OF USER SETTINGS ================ # INITIAL VALUES self.result = "..." # Newest rate from the API. self.oldPrice = 0 # Previous rate that is different from the newest rate. self.comparisonPrice = 0 # Countdown until the next API request. self.currentPriceTicks = 0 self.color = 0xFFFFFF self.displayTICKER = "" # CREATE DISPLAYED TICKER if self.TICKER_LETTERS <= 0 self.displayTICKER = "" elif self.TICKER_LETTERS == 1 self.displayTICKER = self.BASE_CURRENCY[0] elif self.TICKER_LETTERS == 2 self.displayTICKER = self.BASE_CURRENCY[0] + self.BASE_CURRENCY[1] elif self.TICKER_LETTERS == 3 self.displayTICKER = self.BASE_CURRENCY else self.displayTICKER = self.BASE_CURRENCY + "/" + self.QUOTE_CURRENCY end end # GET DATE WITH OFFSET # offset = 0 -> today # offset = 1 -> yesterday # offset = 5 -> five days ago def get_previous_date(offset) var y = year() var m = month() var d = day() var i = 0 while i < offset d -= 1 if d <= 0 m -= 1 if m <= 0 m = 12 y -= 1 end if m == 1 d = 31 elif m == 2 d = 28 elif m == 3 d = 31 elif m == 4 d = 30 elif m == 5 d = 31 elif m == 6 d = 30 elif m == 7 d = 31 elif m == 8 d = 31 elif m == 9 d = 30 elif m == 10 d = 31 elif m == 11 d = 30 else d = 31 end end i += 1 end var ms = str(m) var ds = str(d) if m < 10 ms = "0" + ms end if d < 10 ds = "0" + ds end return str(y) + "-" + ms + "-" + ds end # APPLY PRICE COLOR def update_color() if self.PRICE_COLOR_MODE == 0 self.color = 0xFFFFFF return end if self.PRICE_COLOR_MODE == 1 self.color = self.PRICE_COLOR return end # No different historical price was found. if self.comparisonPrice <= 0 self.color = 0xFFFFFF return end # Current price is higher than the previous # different price -> green. if self.oldPrice > self.comparisonPrice self.color = 0x00FF00 # Current price is lower than the previous # different price -> red. elif self.oldPrice < self.comparisonPrice self.color = 0xFF0000 else self.color = 0xFFFFFF end end # FORMAT PRICE def format_price(value) var s = str(value) # 0 DECIMAL PLACES if self.PRICE_DECIMALS <= 0 var p = re.search( "[0-9]+", s ) if p == nil return "..." end return p[0] end # 1 DECIMAL PLACE if self.PRICE_DECIMALS == 1 var p = re.search( "[0-9]+[.][0-9]", s ) if p == nil return "..." end return p[0] end # 2 DECIMAL PLACES if self.PRICE_DECIMALS == 2 var p = re.search( "[0-9]+[.][0-9][0-9]", s ) if p == nil return "..." end return p[0] end # 3 DECIMAL PLACES if self.PRICE_DECIMALS == 3 var p = re.search( "[0-9]+[.][0-9][0-9][0-9]", s ) if p == nil return "..." end return p[0] end # 4 DECIMAL PLACES if self.PRICE_DECIMALS == 4 var p = re.search( "[0-9]+[.][0-9][0-9][0-9][0-9]", s ) if p == nil return "..." end return p[0] end # 5 OR MORE DECIMAL PLACES return s end # PROCESS HISTORY RESPONSE def on_rates(w, st) if w == nil return end # FIND ALL RATE VALUES # The API response contains several dates. # Example: # 82.18 # 82.44 # 82.44 # The newest value is used as the current price. # Then the history is searched for the newest value # that is actually different from the current price. var search = w var firstPrice = 0 var lastPrice = 0 var foundCount = 0 var i = 0 while i < 10 var p = re.search( "rate[^0-9]*([0-9]+[.][0-9]+)", search ) if p == nil break end var value = num(p[1]) if value == nil break end if foundCount == 0 firstPrice = value end lastPrice = value foundCount += 1 if foundCount >= 10 break end # Search for the next rate. var second = re.search( "rate[^0-9]*[0-9]+[.][0-9]+.*rate[^0-9]*([0-9]+[.][0-9]+)", search ) if second == nil break end var nextValue = num(second[1]) if nextValue == nil break end lastPrice = nextValue foundCount += 1 if foundCount >= 10 break end search = second[0] i += 1 end # NO VALID DATA if foundCount <= 0 return end # CURRENT PRICE self.oldPrice = lastPrice self.result = self.format_price(lastPrice) # FIND LAST DIFFERENT PRICE # The newest value may occur on several consecutive days. # Example: # 2026-08-10 -> 82.18 # 2026-08-11 -> 82.44 # 2026-08-12 -> 82.44 # Current price = 82.44 # Comparison price = 82.18 # This prevents the current price from being compared # with an identical value from the immediately previous day. self.comparisonPrice = 0 var currentSearch = w var attempts = 0 while attempts < 10 var q = re.search( "rate[^0-9]*([0-9]+[.][0-9]+)", currentSearch ) if q == nil break end var candidate = num(q[1]) if candidate == nil break end # We found the newest rate that differs # from the current rate. if candidate != lastPrice self.comparisonPrice = candidate break end # Continue searching for an older rate. var nextSearch = re.search( "rate[^0-9]*[0-9]+[.][0-9]+.*rate[^0-9]*([0-9]+[.][0-9]+)", currentSearch ) if nextSearch == nil break end currentSearch = nextSearch[0] attempts += 1 end # APPLY COLOR self.update_color() end # MAIN LOOP def loop() self.currentPriceTicks -= 1 if self.currentPriceTicks <= 0 self.currentPriceTicks = self.CURRENT_PRICE_UPDATE_TICKS # Request five days of historical rates at once. # The API returns the rates chronologically. # This allows the script to find the latest # price that differs from the current one. var fromDate = self.get_previous_date(5) var toDate = self.get_previous_date(0) http.get( "https://api.frankfurter.dev/v2/rates?base=" + self.BASE_CURRENCY + ""es=" + self.QUOTE_CURRENCY + "&from=" + fromDate + "&to=" + toDate, / b, st -> self.on_rates(b, st) ) end end # DRAW def draw() clear() var tickerWidth = text_ink_width(self.displayTICKER) var priceWidth = text_ink_width(self.result) var totalWidth = tickerWidth + self.GAP + priceWidth var x = int((width() - totalWidth) / 2) if self.TICKER_LETTERS > 0 text( x, 7, self.displayTICKER, self.TICKER_COLOR ) end text( x + tickerWidth + self.GAP, 7, self.result, self.color ) end def on_button(btn) end end return CurrencyMarket()