From 7d101cb02db277916a17a785971bf4e4e1adad36 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Wed, 9 Feb 2022 09:01:18 +0000 Subject: [PATCH] Finish implementation and tests for the cheat system --- handler/agora.py | 125 +++++++--------------------------- handler/commands.py | 10 +-- handler/markets.py | 75 ++++++++++++++++++-- handler/tests/common.py | 110 ++++++++++++++++-------------- handler/tests/test_agora.py | 50 +++++++++++++- handler/tests/test_markets.py | 38 +++++++++++ 6 files changed, 239 insertions(+), 169 deletions(-) create mode 100644 handler/tests/test_markets.py diff --git a/handler/agora.py b/handler/agora.py index 5910d0c..b425e70 100644 --- a/handler/agora.py +++ b/handler/agora.py @@ -386,14 +386,15 @@ class Agora(object): for ad in ads: price = float(ad[2]) rate = round(price / base_currency_price, 2) + ad.append(asset) ad.append(rate) return sorted(ads, key=lambda x: x[2]) - def _update_prices(self, xmr=None, btc=None): + def run_cheat_in_thread(self, assets=None): """ Update prices in another thread. """ - if xmr is None and btc is None: + if not assets: all_assets = loads(settings.Agora.AssetList) assets_not_run = set(all_assets) ^ set(self.cheat_run_on) if not assets_not_run: @@ -403,16 +404,22 @@ class Agora(object): else: asset = assets_not_run.pop() self.cheat_run_on.append(asset) - if asset == "XMR": - deferToThread(self.update_prices, True, False) # XMR, BTC - elif asset == "BTC": - deferToThread(self.update_prices, False, True) # XMR, BTC + deferToThread(self.update_prices, [asset]) return asset else: - deferToThread(self.update_prices, xmr, btc) + deferToThread(self.update_prices, assets) @handle_exceptions - def get_all_public_ads(self): + def update_prices(self, assets=None): + # Get all public ads for the given assets + public_ads = self.get_all_public_ads(assets) + + # Get the ads to update + to_update = self.markets.get_new_ad_equations(public_ads, assets) + self.slow_ad_update(to_update) + + @handle_exceptions + def get_all_public_ads(self, assets=None): """ Get all public ads for our listed currencies. :return: dict of public ads keyed by currency @@ -424,13 +431,15 @@ class Agora(object): "BTC": "bitcoin", } + if not assets: + assets = self.markets.get_all_assets() # Get all currencies we have ads for, deduplicated - currencies = list(set([x[0] for x in loads(settings.Agora.DistList)])) - providers = loads(settings.Agora.ProviderList) + currencies = self.markets.get_all_currencies() + providers = self.markets.get_all_providers() # We want to get the ads for each of these currencies and return the result - for asset in loads(settings.Agora.AssetList): - rates_crypto = self.cg.get_price(ids=["monero", "bitcoin"], vs_currencies=currencies) + rates_crypto = self.cg.get_price(ids=["monero", "bitcoin"], vs_currencies=currencies) + for asset in assets: for currency in currencies: cg_asset_name = crypto_map[asset] try: @@ -442,99 +451,13 @@ class Agora(object): if not ads: continue if currency in public_ads: - for ad in list(ads): # Copy the list so we don't mutate and get stuck in an infinite loop - public_ads[currency].append(ad) + for ad in list(ads): + if ad not in public_ads[currency]: + public_ads[currency].append(ad) else: public_ads[currency] = ads - return public_ads - @handle_exceptions - def update_prices(self, xmr=True, btc=True): - if xmr and btc: - our_ads = self.enum_ads() - elif xmr and not btc: - our_ads = self.enum_ads("XMR") - elif not xmr and btc: - our_ads = self.enum_ads("BTC") - - self.log.debug("Our ads: {ads}", ads=our_ads) - to_update = [] - if our_ads is None: - return False - if our_ads is False: - return False - currencies = [x[3].lower() for x in our_ads] - # Sets deduplicate by default - providers = list(set([x[4] for x in our_ads])) - public_ad_dict_xmr = {} - public_ad_dict_btc = {} - rates_crypto = self.cg.get_price(ids=["monero", "bitcoin"], vs_currencies=currencies) - - # NOTES: - # Get all ads for each currency, with all the payment methods. - # Create a function to, in turn, filter these so it contains only one payment method. Run autoprice on this. - # Append all results to to_update. Repeat for remaining payment methods, then call slow update. - - for currency in currencies: - try: - rates_xmr = rates_crypto["monero"][currency] - rates_btc = rates_crypto["bitcoin"][currency] - if xmr: - public_ad_dict_xmr[currency] = self.wrap_public_ads("XMR", currency, providers=providers, rates=rates_xmr) - if public_ad_dict_xmr[currency] is False: - return False - if btc: - public_ad_dict_btc[currency] = self.wrap_public_ads("BTC", currency, providers=providers, rates=rates_btc) - if public_ad_dict_btc[currency] is False: - return False - except KeyError: - self.log.error("Error getting public ads for currency {currency}", currency=currency) - continue - for asset, ad_id, country, currency, provider in our_ads: - # Get the ads for this country/currency pair - try: - if asset == "XMR": - public_ads = public_ad_dict_xmr[currency.lower()] - elif asset == "BTC": - public_ads = public_ad_dict_btc[currency.lower()] - except KeyError: - continue - if not public_ads: - continue - if xmr: - if asset == "XMR": - new_margin = self.markets.autoprice(public_ads, currency) - new_formula = f"coingeckoxmrusd*usd{currency.lower()}*{new_margin}" - if btc: - if asset == "BTC": - new_margin = self.markets.autoprice(public_ads, currency) - new_formula = f"coingeckobtcusd*usd{currency.lower()}*{new_margin}" - # Get all of our ads - our_ads_list = [ad for ad in public_ads if ad[1] == settings.Agora.Username] - if not len(our_ads_list) == 1: - if not len(set([x[3] for x in our_ads_list])) == 1: - self.log.error("Our ads have different margins: {ads}", ads=our_ads_list) - # Get one from the list, they're probably the same, if not we will deal with the other - # ones in a later pass - - # Get one - our_ad = our_ads_list.pop() - - # Take the 4th argument, the margin - our_margin = our_ad[3] - - # Don't waste API rate limits on setting the same margin as before - if new_margin != our_margin: - # rtrn = self.agora.ad_equation(ad_id, new_formula) - to_update.append([ad_id, new_formula, asset, currency, False]) - # if not rtrn["success"]: - # self.log.error("Error updating ad {ad_id}: {response}", ad_id=ad_id, response=rtrn["response"]) - self.log.info("Rate for {currency}: {margin}", currency=currency, margin=new_margin) - else: - self.log.info("Not changed rate for {currency}, keeping old margin of {margin}", currency=currency, margin=our_margin) - self.slow_ad_update(to_update) - def slow_ad_update(self, ads): """ Slow ad equation update utilising exponential backoff in order to guarantee all ads are updated. diff --git a/handler/commands.py b/handler/commands.py index d476e80..9f3d803 100644 --- a/handler/commands.py +++ b/handler/commands.py @@ -353,20 +353,14 @@ class IRCCommands(object): @staticmethod def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify): if length == 1: - agora._update_prices() + agora.run_cheat_in_thread() msg("Running cheat in thread.") elif length == 2: asset = spl[1] if asset not in loads(settings.Agora.AssetList): msg(f"Not a valid asset: {spl[1]}") return - if asset == "XMR": - xmr = True - btc = False - elif asset == "BTC": - xmr = False - btc = True - agora._update_prices(xmr=xmr, btc=btc) + agora.run_cheat_in_thread([asset]) msg(f"Running cheat in thread for {asset}.") class cheatnext(object): diff --git a/handler/markets.py b/handler/markets.py index 016da35..a3f026f 100644 --- a/handler/markets.py +++ b/handler/markets.py @@ -1,6 +1,9 @@ # Twisted/Klein imports from twisted.logger import Logger +# Other library imports +from json import loads + # Project imports from settings import settings @@ -13,6 +16,68 @@ class Markets(object): def __init__(self): self.log = Logger("markets") + def get_all_assets(self): + assets = loads(settings.Agora.AssetList) + return assets + + def get_all_providers(self): + providers = loads(settings.Agora.ProviderList) + return providers + + def get_all_currencies(self): + currencies = list(set([x[0] for x in loads(settings.Agora.DistList)])) + return currencies + + def get_new_ad_equations(self, public_ads, assets=None): + """ + Update all our prices. + :param public_ads: dictionary of public ads keyed by currency + :type public_ads: dict + :return: list of ads to modify + :rtype: list + """ + to_update = [] + + # NOTES: + # Get all ads for each currency, with all the payment methods. + # Create a function to, in turn, filter these so it contains only one payment method. Run autoprice on this. + # Append all results to to_update. Repeat for remaining payment methods, then call slow update. + + # (asset, currency, provider) + if not assets: + assets = self.get_all_assets() + currencies = self.get_all_currencies() + providers = self.get_all_providers() + + brute = [(asset, currency, provider) for asset in assets for currency in currencies for provider in providers] + for asset, currency, provider in brute: + # Filter currency + try: + public_ads_currency = public_ads[currency] + except KeyError: + self.log.error("Error getting public ads for currency {currency}", currency=currency) + continue + + # Filter asset + public_ads_filtered = [ad for ad in public_ads_currency if ad[4] == asset] + + # Filter provider + public_ads_filtered = [ad for ad in public_ads_filtered if ad[3] == provider] + + our_ads = [ad for ad in public_ads_filtered if ad[1] == settings.Agora.Username] + if not our_ads: + continue + new_margin = self.autoprice(public_ads_filtered, currency) + new_formula = f"coingecko{asset.lower()}usd*usd{currency.lower()}*{new_margin}" + for ad in our_ads: + ad_id = ad[0] + asset = ad[4] + our_margin = ad[5] + if new_margin != our_margin: + to_update.append([ad_id, new_formula, asset, currency, False]) + + return to_update + def autoprice(self, ads, currency): """ Helper function to automatically adjust the price up/down in certain markets @@ -27,7 +92,7 @@ class Markets(object): self.log.debug("Autoprice starting for {x}", x=currency) # Find cheapest ad # Filter by 3rd index on each ad list to find the cheapest - min_margin_ad = min(ads, key=lambda x: x[4]) + min_margin_ad = min(ads, key=lambda x: x[5]) self.log.debug("Minimum margin ad: {x}", x=min_margin_ad) # Find second cheapest that is not us @@ -35,15 +100,15 @@ class Markets(object): ads_without_us = [ad for ad in ads if not ad[1] == settings.Agora.Username] self.log.debug("Ads without us: {x}", x=ads_without_us) # Find ads above our min that are not us - ads_above_our_min_not_us = [ad for ad in ads_without_us if ad[4] > float(settings.Agora.MinMargin)] + ads_above_our_min_not_us = [ad for ad in ads_without_us if ad[5] > float(settings.Agora.MinMargin)] self.log.debug("Ads above our min not us: {x}", x=ads_above_our_min_not_us) # Check that this list without us is not empty if ads_without_us: # Find the cheapest from these - min_margin_ad_not_us = min(ads_without_us, key=lambda x: x[4]) + min_margin_ad_not_us = min(ads_without_us, key=lambda x: x[5]) self.log.debug("Min margin ad not us: {x}", x=min_margin_ad_not_us) # Lowball the lowest ad that is not ours - lowball_lowest_not_ours = min_margin_ad_not_us[4] # - 0.005 + lowball_lowest_not_ours = min_margin_ad_not_us[5] # - 0.005 self.log.debug("Lowball lowest not ours: {x}", x=lowball_lowest_not_ours) # Check if the username field of the cheapest ad matches ours @@ -79,7 +144,7 @@ class Markets(object): return float(settings.Agora.MaxMargin) # Find cheapest ad above our min that is not us cheapest_ad = min(ads_above_our_min_not_us, key=lambda x: x[4]) - cheapest_ad_margin = cheapest_ad[4] # - 0.005 + cheapest_ad_margin = cheapest_ad[5] # - 0.005 if cheapest_ad_margin > float(settings.Agora.MaxMargin): self.log.debug("Cheapest ad not ours more than MaxMargin") return float(settings.Agora.MaxMargin) diff --git a/handler/tests/common.py b/handler/tests/common.py index 7d50e59..6e33de2 100644 --- a/handler/tests/common.py +++ b/handler/tests/common.py @@ -1,63 +1,69 @@ fake_public_ads = { - "NOK": [ - ["9c3d9fb6-c74c-4a35-bd9f-b6c74c7a3504", "topmonero", "2023.02", "REVOLUT", 1.3], - ["9c3d9fb6-c74c-4a35-bd9f-b6c74c7a3504", "topmonero", "2023.02", "REVOLUT", 1.3], + "JPY": [["b048bad8-3aaa-4727-88ba-d83aaa1727b6", "topmonero", "26978.66", "REVOLUT", "XMR", 1.32]], + "DKK": [["ef1455dc-4629-4827-9455-dc46291827f0", "topmonero", "1521.50", "REVOLUT", "XMR", 1.32]], + "CHF": [["fb04a8e2-3c69-45f3-84a8-e23c6975f380", "topmonero", "215.74", "REVOLUT", "XMR", 1.32]], + "SEK": [ + ["f0e840b9-29ab-4a6f-a840-b929ab7a6fde", "topmonero", "2053.68", "REVOLUT", "XMR", 1.27], + ["2252a3f7-6d6b-400b-92a3-f76d6bb00b50", "SwishaMonero", "2053.68", "REVOLUT", "XMR", 1.27], ], + "CZK": [["80aa52ef-a5d3-462c-aa52-efa5d3862cbe", "topmonero", "4960.76", "REVOLUT", "XMR", 1.32]], + "PLN": [["b5be2881-4491-4a60-be28-814491ca606a", "topmonero", "926.48", "REVOLUT", "XMR", 1.32]], "EUR": [ - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["87af6467-be02-476e-af64-67be02676e9a", "topmonero", "184.28", "REVOLUT", 1.19], - ["d2c6645c-6d56-4094-8664-5c6d5640941b", "topmonero", "184.28", "REVOLUT", 1.19], - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["87af6467-be02-476e-af64-67be02676e9a", "topmonero", "184.28", "REVOLUT", 1.19], - ["d2c6645c-6d56-4094-8664-5c6d5640941b", "topmonero", "184.28", "REVOLUT", 1.19], + ["87af6467-be02-476e-af64-67be02676e9a", "topmonero", "187.11", "REVOLUT", "XMR", 1.21], + ["57e3e8d6-45fe-40da-a3e8-d645fe20da46", "SecureMole", "187.11", "REVOLUT", "XMR", 1.21], + ["d2c6645c-6d56-4094-8664-5c6d5640941b", "topmonero", "187.11", "REVOLUT", "XMR", 1.21], + ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "187.11", "REVOLUT", "XMR", 1.21], + ], + "NOK": [["9c3d9fb6-c74c-4a35-bd9f-b6c74c7a3504", "topmonero", "2058.76", "REVOLUT", "XMR", 1.32]], + "THB": [["9a7bd726-6229-4fda-bbd7-2662295fda98", "topmonero", "7668.74", "REVOLUT", "XMR", 1.31]], + "ZAR": [["92877e53-823e-4ac5-877e-53823e2ac545", "topmonero", "3583.09", "REVOLUT", "XMR", 1.3]], + "MXN": [["bf12756b-0138-49ca-9275-6b0138f9caf1", "topmonero", "4815.61", "REVOLUT", "XMR", 1.31]], + "TRY": [["3c5068ce-fcf9-40cc-9068-cefcf920cc02", "topmonero", "3166.12", "REVOLUT", "XMR", 1.31]], + "RUB": [["24a9cad1-0bce-46f3-a9ca-d10bce86f34f", "topmonero", "17513.23", "REVOLUT", "XMR", 1.31]], + "SGD": [["ac9eb9c8-88c7-4add-9eb9-c888c72addb2", "topmonero", "313.83", "REVOLUT", "XMR", 1.32]], + "HKD": [["b23aa3b3-4c91-42e4-baa3-b34c9162e4e0", "topmonero", "1818.76", "REVOLUT", "XMR", 1.32]], + "AUD": [["68b50d95-91d2-4f21-b50d-9591d22f218d", "topmonero", "327.16", "REVOLUT", "XMR", 1.31]], + "HUF": [["f886768f-b9c9-4cf7-8676-8fb9c9ccf7ee", "topmonero", "72350.54", "REVOLUT", "XMR", 1.32]], + "USD": [ + ["24871dd9-54e8-4962-871d-d954e82962b1", "topmonero", "224.36", "REVOLUT", "XMR", 1.27], + ["b5f80385-73cb-4f98-b803-8573cb6f9846", "SecureMole", "224.36", "REVOLUT", "XMR", 1.27], + ["92add2b6-ccd5-4351-add2-b6ccd53351e4", "topmonero", "224.36", "REVOLUT", "XMR", 1.27], + ["04be2471-72a5-4ab6-be24-7172a57ab64a", "EmmanuelMuema", "232.98", "REVOLUT", "XMR", 1.31], + ["bb0d817d-5d7d-4e76-8d81-7d5d7d9e76fd", "EmmanuelMuema", "233.34", "REVOLUT", "XMR", 1.32], + ["a53a9770-69ba-43fc-ba97-7069bad3fc1a", "EmmanuelMuema", "235.13", "REVOLUT", "XMR", 1.33], + ["9c7e4b21-359c-4953-be4b-21359c095370", "EmmanuelMuema", "236.93", "REVOLUT", "XMR", 1.34], + ["88c72c50-3162-4c91-872c-503162dc9176", "Alphabay", "358.80", "REVOLUT", "XMR", 2.02], ], "GBP": [ - ["15e821b8-e570-4b0f-a821-b8e5709b0ffc", "SecureMole", "166.08", "REVOLUT", 1.27], - ["071ab272-ba37-4a14-9ab2-72ba37fa1484", "Boozymad89", "166.08", "REVOLUT", 1.27], - ["850f28eb-ce63-4ca7-8f28-ebce63dca707", "topmonero", "166.34", "REVOLUT", 1.27], - ["6727d9e5-c038-43f5-a7d9-e5c038c3f5be", "topmonero", "166.34", "REVOLUT", 1.27], - ["ca4feeb9-22d5-456d-8fee-b922d5c56d27", "Boozymad89", "175.51", "REVOLUT", 1.34], - ["15e821b8-e570-4b0f-a821-b8e5709b0ffc", "SecureMole", "166.08", "REVOLUT", 1.27], - ["071ab272-ba37-4a14-9ab2-72ba37fa1484", "Boozymad89", "166.08", "REVOLUT", 1.27], - ["850f28eb-ce63-4ca7-8f28-ebce63dca707", "topmonero", "166.34", "REVOLUT", 1.27], - ["6727d9e5-c038-43f5-a7d9-e5c038c3f5be", "topmonero", "166.34", "REVOLUT", 1.27], - ["ca4feeb9-22d5-456d-8fee-b922d5c56d27", "Boozymad89", "175.51", "REVOLUT", 1.34], - ], - "SEK": [ - ["f0e840b9-29ab-4a6f-a840-b929ab7a6fde", "topmonero", "2020.45", "REVOLUT", 1.25], - ["2252a3f7-6d6b-400b-92a3-f76d6bb00b50", "SwishaMonero", "2020.45", "REVOLUT", 1.25], - ["f0e840b9-29ab-4a6f-a840-b929ab7a6fde", "topmonero", "2020.45", "REVOLUT", 1.25], - ["2252a3f7-6d6b-400b-92a3-f76d6bb00b50", "SwishaMonero", "2020.45", "REVOLUT", 1.25], - ], - "CZK": [ - ["80aa52ef-a5d3-462c-aa52-efa5d3862cbe", "topmonero", "4872.73", "REVOLUT", 1.3], - ["80aa52ef-a5d3-462c-aa52-efa5d3862cbe", "topmonero", "4872.73", "REVOLUT", 1.3], + ["15e821b8-e570-4b0f-a821-b8e5709b0ffc", "SecureMole", "167.95", "REVOLUT", "XMR", 1.28], + ["071ab272-ba37-4a14-9ab2-72ba37fa1484", "Boozymad89", "167.95", "REVOLUT", "XMR", 1.28], + ["6727d9e5-c038-43f5-a7d9-e5c038c3f5be", "topmonero", "168.22", "REVOLUT", "XMR", 1.28], + ["850f28eb-ce63-4ca7-8f28-ebce63dca707", "topmonero", "168.22", "REVOLUT", "XMR", 1.28], + ["ca4feeb9-22d5-456d-8fee-b922d5c56d27", "Boozymad89", "177.49", "REVOLUT", "XMR", 1.36], + ["78db95b7-e090-48e2-9b95-b7e09098e2d9", "Crypto_Hood", "38150.99", "REVOLUT", "BTC", 1.18], ], + "NZD": [["0addd244-c5cb-40bf-9dd2-44c5cbd0bff4", "topmonero", "351.40", "REVOLUT", "XMR", 1.31]], + "CAD": [["388442b4-0cb7-48f3-8442-b40cb7f8f39d", "topmonero", "296.50", "REVOLUT", "XMR", 1.32]], } -currency_map = { - "CZK": [["80aa52ef-a5d3-462c-aa52-efa5d3862cbe", "topmonero", "4872.73", "REVOLUT", 1.3]], - "GBP": [ - ["15e821b8-e570-4b0f-a821-b8e5709b0ffc", "SecureMole", "166.08", "REVOLUT", 1.27], - ["071ab272-ba37-4a14-9ab2-72ba37fa1484", "Boozymad89", "166.08", "REVOLUT", 1.27], - ["850f28eb-ce63-4ca7-8f28-ebce63dca707", "topmonero", "166.34", "REVOLUT", 1.27], - ["6727d9e5-c038-43f5-a7d9-e5c038c3f5be", "topmonero", "166.34", "REVOLUT", 1.27], - ["ca4feeb9-22d5-456d-8fee-b922d5c56d27", "Boozymad89", "175.51", "REVOLUT", 1.34], - ], - "NOK": [["9c3d9fb6-c74c-4a35-bd9f-b6c74c7a3504", "topmonero", "2023.02", "REVOLUT", 1.3]], - "SEK": [ - ["f0e840b9-29ab-4a6f-a840-b929ab7a6fde", "topmonero", "2020.45", "REVOLUT", 1.25], - ["2252a3f7-6d6b-400b-92a3-f76d6bb00b50", "SwishaMonero", "2020.45", "REVOLUT", 1.25], - ], - "EUR": [ - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "184.28", "REVOLUT", 1.19], - ["87af6467-be02-476e-af64-67be02676e9a", "topmonero", "184.28", "REVOLUT", 1.19], - ["d2c6645c-6d56-4094-8664-5c6d5640941b", "topmonero", "184.28", "REVOLUT", 1.19], - ], -} +expected_to_update = [ + ["fb04a8e2-3c69-45f3-84a8-e23c6975f380", "coingeckoxmrusd*usdchf*1.3", "XMR", "CHF", False], + ["bf12756b-0138-49ca-9275-6b0138f9caf1", "coingeckoxmrusd*usdmxn*1.3", "XMR", "MXN", False], + ["ef1455dc-4629-4827-9455-dc46291827f0", "coingeckoxmrusd*usddkk*1.3", "XMR", "DKK", False], + ["388442b4-0cb7-48f3-8442-b40cb7f8f39d", "coingeckoxmrusd*usdcad*1.3", "XMR", "CAD", False], + ["b5be2881-4491-4a60-be28-814491ca606a", "coingeckoxmrusd*usdpln*1.3", "XMR", "PLN", False], + ["3c5068ce-fcf9-40cc-9068-cefcf920cc02", "coingeckoxmrusd*usdtry*1.3", "XMR", "TRY", False], + ["b23aa3b3-4c91-42e4-baa3-b34c9162e4e0", "coingeckoxmrusd*usdhkd*1.3", "XMR", "HKD", False], + ["f886768f-b9c9-4cf7-8676-8fb9c9ccf7ee", "coingeckoxmrusd*usdhuf*1.3", "XMR", "HUF", False], + ["0addd244-c5cb-40bf-9dd2-44c5cbd0bff4", "coingeckoxmrusd*usdnzd*1.3", "XMR", "NZD", False], + ["24a9cad1-0bce-46f3-a9ca-d10bce86f34f", "coingeckoxmrusd*usdrub*1.3", "XMR", "RUB", False], + ["80aa52ef-a5d3-462c-aa52-efa5d3862cbe", "coingeckoxmrusd*usdczk*1.3", "XMR", "CZK", False], + ["ac9eb9c8-88c7-4add-9eb9-c888c72addb2", "coingeckoxmrusd*usdsgd*1.3", "XMR", "SGD", False], + ["68b50d95-91d2-4f21-b50d-9591d22f218d", "coingeckoxmrusd*usdaud*1.3", "XMR", "AUD", False], + ["b048bad8-3aaa-4727-88ba-d83aaa1727b6", "coingeckoxmrusd*usdjpy*1.3", "XMR", "JPY", False], + ["9c3d9fb6-c74c-4a35-bd9f-b6c74c7a3504", "coingeckoxmrusd*usdnok*1.3", "XMR", "NOK", False], + ["9a7bd726-6229-4fda-bbd7-2662295fda98", "coingeckoxmrusd*usdthb*1.3", "XMR", "THB", False], +] cg_prices = { "bitcoin": { diff --git a/handler/tests/test_agora.py b/handler/tests/test_agora.py index 1e729c6..9c304f7 100644 --- a/handler/tests/test_agora.py +++ b/handler/tests/test_agora.py @@ -1,17 +1,20 @@ from unittest import TestCase -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch -from tests.common import fake_public_ads, currency_map, cg_prices +from tests.common import fake_public_ads, cg_prices, expected_to_update from agora import Agora +from markets import Markets class TestAgora(TestCase): def setUp(self): + self.markets = Markets() self.agora = Agora() + setattr(self.agora, "markets", self.markets) def mock_wrap_public_ads(self, asset, currency, providers, rates): try: - return currency_map[currency] + return fake_public_ads[currency] except KeyError: return @@ -21,3 +24,44 @@ class TestAgora(TestCase): self.agora.wrap_public_ads = self.mock_wrap_public_ads public_ads = self.agora.get_all_public_ads() self.assertDictEqual(public_ads, fake_public_ads) + + def test_get_all_public_ads_only_one(self): + self.agora.cg.get_price = MagicMock() + self.agora.cg.get_price.return_value = cg_prices + self.agora.wrap_public_ads = self.mock_wrap_public_ads + public_ads = self.agora.get_all_public_ads() + for currency, ads in public_ads.items(): + ad_ids = [ad[0] for ad in ads] + len_ad_ids = len(ad_ids) + + ad_ids_dedup = set(ad_ids) + + len_ad_ids_dedup = len(ad_ids_dedup) + + self.assertEqual(len_ad_ids, len_ad_ids_dedup) + + @patch("twisted.internet.threads.deferToThread") + def test_run_cheat_in_thread(self, defer): + asset1 = self.agora.run_cheat_in_thread() + + asset2 = self.agora.run_cheat_in_thread() + self.assertEqual(set([asset1, asset2]), set(["XMR", "BTC"])) + + asset3 = self.agora.run_cheat_in_thread() + + asset4 = self.agora.run_cheat_in_thread() + + self.assertEqual(set([asset3, asset4]), set(["XMR", "BTC"])) + + self.assertNotEqual(asset1, asset2) + self.assertNotEqual(asset3, asset4) + + def test_update_prices(self): + self.agora.cg.get_price = MagicMock() + self.agora.cg.get_price.return_value = cg_prices + self.agora.wrap_public_ads = self.mock_wrap_public_ads + + self.agora.slow_ad_update = MagicMock() + self.agora.update_prices() + call_args = self.agora.slow_ad_update.call_args_list[0][0][0] + self.assertCountEqual(call_args, expected_to_update) diff --git a/handler/tests/test_markets.py b/handler/tests/test_markets.py new file mode 100644 index 0000000..a75656e --- /dev/null +++ b/handler/tests/test_markets.py @@ -0,0 +1,38 @@ +from unittest import TestCase +from tests.common import fake_public_ads, expected_to_update +from markets import Markets +from agora import Agora + + +class TestMarkets(TestCase): + def setUp(self): + self.markets = Markets() + self.agora = Agora() + + def test_autoprice(self): + ads = [ + ["2b6dba4d-c9db-48f2-adba-4dc9dba8f2a0", "Xpoterlolipop", "182.80", "REVOLUT", "XMR", 1.18], + ["57e3e8d6-45fe-40da-a3e8-d645fe20da46", "SecureMole", "183.26", "REVOLUT", "XMR", 1.19], + ["87af6467-be02-476e-af64-67be02676e9a", "topmonero", "183.42", "REVOLUT", "XMR", 1.19], + ["65b452e3-a29f-4233-b452-e3a29fe23369", "topmonero", "183.42", "REVOLUT", "XMR", 1.19], + ["d2c6645c-6d56-4094-8664-5c6d5640941b", "topmonero", "183.42", "REVOLUT", "XMR", 1.19], + ] + currency = "EUR" + margin = self.markets.autoprice(ads, currency) + expected_margin = 1.18 + self.assertEqual(margin, expected_margin) + + def test_get_new_ad_equation(self): + to_update = self.markets.get_new_ad_equations(fake_public_ads) + self.assertCountEqual(to_update, expected_to_update) + + res_xmr = self.markets.get_new_ad_equations(fake_public_ads, ["XMR"]) + expected_xmr_to_update = [x for x in expected_to_update if x[2] == "XMR"] + self.assertCountEqual(res_xmr, expected_xmr_to_update) + + res_btc = self.markets.get_new_ad_equations(fake_public_ads, ["BTC"]) + expected_btc_to_update = [x for x in expected_to_update if x[2] == "BTC"] + self.assertCountEqual(res_btc, expected_btc_to_update) + + res_both = self.markets.get_new_ad_equations(fake_public_ads, ["XMR", "BTC"]) + self.assertCountEqual(res_both, expected_to_update)