# @name Currency Market # @desc Global currency exchange rate display # @author Avemer # @version 1.1 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 PREVIOUS_DAY_UPDATE_TICKS var GAP # INTERNAL VARIABLES var result var oldPrice var previousDayPrice var currentPriceTicks var previousDayTicks 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 # Examples: # PRICE_DECIMALS = 0 # USD 1 # PRICE_DECIMALS = 1 # USD 0.9 # PRICE_DECIMALS = 2 # USD 0.86 # PRICE_DECIMALS = 3 # USD 0.865 # PRICE_DECIMALS = 4 # USD 0.8650 self.PRICE_DECIMALS = 3 # TICKER COLOR # Format: 0xRRGGBB self.TICKER_COLOR = 0xFFFFFF # PRICE COLOR MODE # 0 = white # 1 = custom PRICE_COLOR # 2 = automatic green/red comparison # Automatic mode compares the current rate # with the previous calendar day's rate. self.PRICE_COLOR_MODE = 2 # CUSTOM PRICE COLOR # Used when PRICE_COLOR_MODE = 1. self.PRICE_COLOR = 0xFFFFFF # UPDATE INTERVALS # CURRENT PRICE UPDATE # Number of loop ticks between requests # for the current exchange rate. # The first request is made immediately # when the script starts. self.CURRENT_PRICE_UPDATE_TICKS = 600 # PREVIOUS DAY PRICE UPDATE # Number of loop ticks between requests # for the previous calendar day's exchange rate. # This request is used only when # PRICE_COLOR_MODE = 2. self.PREVIOUS_DAY_UPDATE_TICKS = 610 # DISTANCE BETWEEN TICKER AND PRICE self.GAP = 2 # ================ END OF USER SETTINGS ================ # INITIAL VALUES self.result = "..." self.oldPrice = 0 self.previousDayPrice = 0 # Start counters at zero. # Because the loop decreases the counter # before checking it, the first requests # are made immediately. self.currentPriceTicks = 0 self.previousDayTicks = 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 PREVIOUS CALENDAR DATE def get_previous_date() var y = year() var m = month() var d = day() 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 # LEAP YEAR CORRECTION if m == 2 if d == 28 if y % 4 == 0 d = 29 end end 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 if self.previousDayPrice <= 0 return end if self.oldPrice > self.previousDayPrice self.color = 0x00FF00 elif self.oldPrice < self.previousDayPrice self.color = 0xFF0000 else self.color = 0xFFFFFF end end # FORMAT CURRENT PRICE # The script uses regular expressions to # extract exactly the requested number of # decimal places from the API response. # This avoids unsupported string functions # such as len() and split(). def format_price(w) var p # 0 DECIMAL PLACES if self.PRICE_DECIMALS <= 0 p = re.search( "[0-9]+", w ) if p == nil return "..." end return p[0] end # 1 DECIMAL PLACE if self.PRICE_DECIMALS == 1 p = re.search( "[0-9]+[.][0-9]", w ) if p == nil return "..." end return p[0] end # 2 DECIMAL PLACES if self.PRICE_DECIMALS == 2 p = re.search( "[0-9]+[.][0-9][0-9]", w ) if p == nil return "..." end return p[0] end # 3 DECIMAL PLACES if self.PRICE_DECIMALS == 3 p = re.search( "[0-9]+[.][0-9][0-9][0-9]", w ) if p == nil return "..." end return p[0] end # 4 DECIMAL PLACES if self.PRICE_DECIMALS == 4 p = re.search( "[0-9]+[.][0-9][0-9][0-9][0-9]", w ) if p == nil return "..." end return p[0] end # 5 OR MORE DECIMAL PLACES # Keep the original API value. p = re.search( "[0-9]+[.][0-9]+", w ) if p == nil return "..." end return p[0] end # CURRENT PRICE RESPONSE def on_price(w, st) if w == nil return end # Find the word "rate" in the response. var m = re.search( "rate", w ) if m == nil return end # Extract the complete numeric rate # for calculations and color comparison. var p = re.search( "[0-9]+[.][0-9]+", w ) if p == nil return end var newPrice = num(p[0]) if newPrice == nil return end # Store full numeric value internally. self.oldPrice = newPrice # Format only the displayed value. self.result = self.format_price(w) # Update price color. self.update_color() end # PREVIOUS CALENDAR DAY RESPONSE def on_previous_day(w, st) if w == nil return end # Find the word "rate" in the response. var m = re.search( "rate", w ) if m == nil return end # Extract the numeric rate. var p = re.search( "[0-9]+[.][0-9]+", w ) if p == nil return end var newPrice = num(p[0]) if newPrice == nil return end self.previousDayPrice = newPrice self.update_color() end # MAIN LOOP def loop() #CURRENT PRICE REQUEST self.currentPriceTicks -= 1 if self.currentPriceTicks <= 0 self.currentPriceTicks = self.CURRENT_PRICE_UPDATE_TICKS http.get( "https://api.frankfurter.dev/v2/rate/" + self.BASE_CURRENCY + "/" + self.QUOTE_CURRENCY, / b, st -> self.on_price(b, st) ) end #PREVIOUS DAY REQUEST if self.PRICE_COLOR_MODE == 2 self.previousDayTicks -= 1 if self.previousDayTicks <= 0 self.previousDayTicks = self.PREVIOUS_DAY_UPDATE_TICKS var previousDate = self.get_previous_date() http.get( "https://api.frankfurter.dev/v2/rate/" + self.BASE_CURRENCY + "/" + self.QUOTE_CURRENCY + "?date=" + previousDate, / b, st -> self.on_previous_day(b, st) ) end 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()