From ec73d1b2e1a801c168f5b1d4f9af1b462a54df00 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sun, 9 Jan 2022 14:34:37 +0000 Subject: [PATCH] Implement cheating the price market --- handler/agora.py | 93 +++++++++++++++++++++++++++++++++++++++++ handler/agoradesk_py.py | 49 ++++++---------------- handler/commands.py | 35 ++++++++++++++++ 3 files changed, 141 insertions(+), 36 deletions(-) diff --git a/handler/agora.py b/handler/agora.py index d5cd27a..8cffcd7 100644 --- a/handler/agora.py +++ b/handler/agora.py @@ -7,6 +7,7 @@ from json import loads from forex_python.converter import CurrencyRates from agoradesk_py import AgoraDesk from httpx import ReadTimeout +from pycoingecko import CoinGeckoAPI # Project imports from settings import settings @@ -25,6 +26,7 @@ class Agora(object): self.log = Logger("agora") self.agora = AgoraDesk(settings.Agora.Token) self.cr = CurrencyRates() + self.cg = CoinGeckoAPI() # Cache for detecting new trades self.last_dash = set() @@ -213,6 +215,97 @@ class Agora(object): ads_total.append([ad[0], ad[1], ad[2]]) return ads_total + def enum_public_ads(self, country, currency, page=0): + ads = self.agora._api_call(api_method=f"buy-monero-online/{currency}/{country}/REVOLUT", query_values={"page": page}) + if not ads["success"]: + return False + for ad in ads["response"]["data"]["ad_list"]: + if ad["data"]["online_provider"] == "REVOLUT": + yield [ad["data"]["ad_id"], ad["data"]["profile"]["username"], ad["data"]["temp_price"]] + if "pagination" in ads["response"]: + if "next" in ads["response"]["pagination"]: + page += 1 + for ad in self.enum_public_ads(country, currency, page): + yield [ad[0], ad[1], ad[2]] + + def wrap_public_ads(self, country, currency, rates=None): + """ + Wrapper to sort public ads. + """ + ads = list(self.enum_public_ads(country, currency)) + if not rates: + base_monero_price = self.cg.get_price(ids="monero", vs_currencies=currency)["monero"][currency.lower()] + else: + base_monero_price = rates + for ad in ads: + price = float(ad[2]) + rate = round(price / base_monero_price, 2) + ad.append(rate) + ads.sort(key=lambda x: float(x[2])) + return ads + + def sort_ads_annotate_place(self, ads): + ads.sort(key=lambda x: float(x[2])) + for index, ad in enumerate(ads): + if ad[1] == settings.Agora.Username: + place = index + break + return (ads, place) + + def update_prices(self): + our_ads = self.enum_ads() + currencies = [x[2].lower() for x in our_ads] + rates_xmr = self.cg.get_price(ids="monero", vs_currencies=currencies) + for ad_id, country, currency in our_ads: + # Get the ads for this country/currency pair + try: + public_ads = self.wrap_public_ads(country, currency, rates=rates_xmr["monero"][currency.lower()]) + except KeyError: + self.log.error("Error getting public ads for currency {currency}", currency=currency) + continue + new_margin = self.autoprice(public_ads, country, currency) + new_formula = f"coingeckoxmrusd*usd{currency.lower()}*{new_margin}" + rtrn = self.agora.ad_equation(ad_id, new_formula) + 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 {country}/{currency}: {margin}", country=country, currency=currency, margin=new_margin) + + def autoprice(self, ads, country, currency): + """ + Helper function to automatically adjust the price up/down in certain markets + in order to gain the most profits and sales. + """ + ads, place = self.sort_ads_annotate_place(ads) + if place == 0: + if len(ads) > 1: + next_max = float(ads[1][3]) + our_max_adjusted = next_max - 0.01 + if our_max_adjusted > float(settings.Agora.MaxMargin): + our_max_adjusted = float(settings.Agora.MaxMargin) + # Lowball next competitor + self.log.info( + "Lowballing next competitor for {country}/{currency}: {margin}", + country=country, + currency=currency, + margin=our_max_adjusted, + ) + return our_max_adjusted + else: + our_max_adjusted = float(settings.Agora.MaxMargin) + # Set the max rates as people have no choice + return our_max_adjusted + iter_count = 0 + while place > 1 and not iter_count > 100: + iter_count += 1 + recommended_margin = float(ads[place][2]) + 0.01 + if recommended_margin <= float(settings.Agora.MinMargin): + break + ads[place][3] = recommended_margin + + ads, place = self.sort_ads_annotate_place(ads) + # Return our adjusted (or left) margin + return ads[place][3] + def nuke_ads(self): """ Delete all of our adverts. diff --git a/handler/agoradesk_py.py b/handler/agoradesk_py.py index 8c27290..c6b659f 100644 --- a/handler/agoradesk_py.py +++ b/handler/agoradesk_py.py @@ -17,9 +17,7 @@ __copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py" __version__ = "0.1.0" # set logging -logging.basicConfig( - level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" -) +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") logging.getLogger("requests.packages.urllib3").setLevel(logging.INFO) logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO) logger = logging.getLogger(__name__) @@ -60,8 +58,7 @@ class AgoraDesk: headers = { "Content-Type": "application/json", - "User-Agent": f"agoradesk_py/{__version__} " - f"https://codeberg.org/MarvinsCryptoTools/agoradesk_py", + "User-Agent": f"agoradesk_py/{__version__} " f"https://codeberg.org/MarvinsCryptoTools/agoradesk_py", "Authorization": self.api_key, } @@ -94,9 +91,7 @@ class AgoraDesk: ) else: - response = httpx.get( - url=api_call_url, headers=headers, params=query_values - ) + response = httpx.get(url=api_call_url, headers=headers, params=query_values) logger.debug(response) result["response"] = json.loads(response.text) @@ -220,9 +215,7 @@ class AgoraDesk: # =========================== # post/feedback/{username} • Give feedback to a user - def feedback( - self, username: str, feedback: str, msg: Optional[str] - ) -> Dict[str, Any]: + def feedback(self, username: str, feedback: str, msg: Optional[str]) -> Dict[str, Any]: """See Agoradesk API. https://agoradesk.com/api-docs/v1#operation/setUserFeedback @@ -248,9 +241,7 @@ class AgoraDesk: https://agoradesk.com/api-docs/v1#operation/markPaid """ - return self._api_call( - api_method=f"contact_mark_as_paid/{trade_id}", http_method="POST" - ) + return self._api_call(api_method=f"contact_mark_as_paid/{trade_id}", http_method="POST") # post/contact_cancel/{trade_id} • Cancel the trade def contact_cancel( @@ -270,9 +261,7 @@ class AgoraDesk: # post/contact_escrow/{trade_id} • Enable escrow # get/contact_messages/{trade_id} • Get trade messages - def contact_messages( - self, trade_id: str, after: Optional[arrow.Arrow] = None - ) -> Dict[str, Any]: + def contact_messages(self, trade_id: str, after: Optional[arrow.Arrow] = None) -> Dict[str, Any]: """See Agoradesk API. https://agoradesk.com/api-docs/v1#operation/getTradeMessages @@ -327,9 +316,7 @@ class AgoraDesk: # Todo: Add image upload functionality # post/contact_message_post/{trade_id} • Send a chat message/attachment - def contact_message_post( - self, trade_id: str, msg: Optional[str] = None - ) -> Dict[str, Any]: + def contact_message_post(self, trade_id: str, msg: Optional[str] = None) -> Dict[str, Any]: """See Agoradesk API. https://agoradesk.com/api-docs/v1#operation/sendChatMessage @@ -479,9 +466,7 @@ class AgoraDesk: if track_max_amount: params["track_max_amount"] = 1 if track_max_amount else 0 if require_trusted_by_advertiser: - params["require_trusted_by_advertiser"] = ( - 1 if require_trusted_by_advertiser else 0 - ) + params["require_trusted_by_advertiser"] = 1 if require_trusted_by_advertiser else 0 if verified_email_required: params["verified_email_required"] = 1 if verified_email_required else 0 if online_provider: @@ -652,8 +637,7 @@ class AgoraDesk: params = self._generic_search_parameters(amount, page) return self._api_call( - api_method=f"{direction}-{main_currency}-online/" - f"{exchange_currency}{add_to_api_method}", + api_method=f"{direction}-{main_currency}-online/" f"{exchange_currency}{add_to_api_method}", query_values=params, ) @@ -796,8 +780,7 @@ class AgoraDesk: params = self._generic_search_parameters(amount, page) return self._api_call( - api_method=f"{direction}-{main_currency}-with-cash/" - f"{exchange_currency}/{country_code}/{lat}/{lon}", + api_method=f"{direction}-{main_currency}-with-cash/" f"{exchange_currency}/{country_code}/{lat}/{lon}", query_values=params, ) @@ -912,9 +895,7 @@ class AgoraDesk: # Statistics related API Methods # ============================== - def moneroaverage( - self, currency: Optional[str] = "ticker-all-currencies" - ) -> Dict[str, Any]: + def moneroaverage(self, currency: Optional[str] = "ticker-all-currencies") -> Dict[str, Any]: """See Agoradesk API. https://agoradesk.com/api-docs/v1#operation/getXmrTicker and @@ -1004,9 +985,7 @@ class AgoraDesk: if otp: params["otp"] = otp - return self._api_call( - api_method="wallet-send", http_method="POST", query_values=params - ) + return self._api_call(api_method="wallet-send", http_method="POST", query_values=params) def wallet_send_xmr( self, @@ -1031,6 +1010,4 @@ class AgoraDesk: if otp: params["otp"] = otp - return self._api_call( - api_method="wallet-send/XMR", http_method="POST", query_values=params - ) + return self._api_call(api_method="wallet-send/XMR", http_method="POST", query_values=params) diff --git a/handler/commands.py b/handler/commands.py index 611db80..2f93bcb 100644 --- a/handler/commands.py +++ b/handler/commands.py @@ -293,3 +293,38 @@ class IRCCommands(object): return balance = rtrn["response"]["data"]["total"]["balance"] msg(f"Wallet balance: {balance}XMR") + + class pubads(object): + name = "pubads" + authed = True + helptext = "View public adverts. Usage: pubads " + + @staticmethod + def run(cmd, spl, length, authed, msg, agora, revolut, tx): + if length == 3: + country = spl[1] + currency = spl[2] + rtrn = agora.wrap_public_ads(country, currency) + for ad in rtrn: + msg(f"({ad[0]}) {ad[1]} {ad[2]} {ad[3]}") + + class cheat(object): + name = "cheat" + authed = True + helptext = "Cheat the markets by manipulating our prices to exploit people." + + @staticmethod + def run(cmd, spl, length, authed, msg, agora, revolut, tx): + rtrn = agora.update_prices() + msg(dumps(rtrn)) + + class ads(object): + name = "ads" + authed = True + helptext = "Get all our ad regions" + + @staticmethod + def run(cmd, spl, length, authed, msg, agora, revolut, tx): + ads = agora.enum_ads() + for ad in ads: + msg(f"({ad[0]}) {ad[1]} {ad[2]}")