# Twisted/Klein imports from twisted.logger import Logger # Other library imports from json import loads # Project imports from settings import settings class Markets(object): """ " Markets handler for generic market functions. """ 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) if currency == "USD": self.log.error("Error getting public ads for currency USD, aborting") break 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) self.log.info("New rate for {currency}: {rate}", currency=currency, rate=new_margin) 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 in order to gain the most profits and sales. :param ads: list of ads :type ads: list of lists :param currency: currency of the ads :type currency: string :return: the rate we should use for this currency :rtype: float """ # 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[6]) # self.log.debug("Minimum margin ad: {x}", x=min_margin_ad) # Find second cheapest that is not us # Remove results from ads that are us 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[6] > 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[6]) # 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[6] # - 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 if min_margin_ad[1] == settings.Agora.Username: # self.log.debug("We are the cheapest for: {x}", x=currency) # We are the cheapest! # Are all of the ads ours? all_ads_ours = all([ad[1] == settings.Agora.Username for ad in ads]) if all_ads_ours: # self.log.debug("All ads are ours for: {x}", x=currency) # Now we know it's safe to return the maximum value return float(settings.Agora.MaxMargin) else: # self.log.debug("All ads are NOT ours for: {x}", x=currency) # All the ads are not ours, but we are first... # Check if the lowballed, lowest (that is not ours) ad's margin # is less than our minimum if lowball_lowest_not_ours < float(settings.Agora.MinMargin): # self.log.debug("Lowball lowest not ours less than MinMargin") return float(settings.Agora.MinMargin) elif lowball_lowest_not_ours > float(settings.Agora.MaxMargin): # self.log.debug("Lowball lowest not ours more than MaxMargin") return float(settings.Agora.MaxMargin) else: # self.log.debug("Returning lowballed figure: {x}", x=lowball_lowest_not_ours) return lowball_lowest_not_ours else: # self.log.debug("We are NOT the cheapest for: {x}", x=currency) # We are not the cheapest :( # Check if this list is empty if not ads_above_our_min_not_us: # Return the maximum margin? 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[6] # - 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) # self.log.debug("Cheapest ad above our min that is not us: {x}", x=cheapest_ad) return cheapest_ad_margin