def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Internal state handling ''' self.execQueue = [] ''' Information store ''' self.products = {} self.offers = {} ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop()
def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Setup ML model ''' self.models_per_product = self.load_models_from_filesystem() self.last_learning = datetime.datetime.now() trigger_learning(self.merchant_token, self.merchant_id, settings['kafka_reverse_proxy_url']) ''' Start Logic Loop ''' self.run_logic_loop()
def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop()
class MerchantSampleLogic(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Internal state handling ''' self.execQueue = [] ''' Information store ''' self.products = {} self.offers = {} ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop() def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] def update_settings(self, new_settings): MerchantBaseLogic.update_settings(self, new_settings) self.update_api_endpoints() return self.settings ''' Implement Abstract methods / Interface ''' def sold_offer(self, offer): self.execQueue.append((self.sold_product, [offer])) ''' Merchant Logic ''' def setup(self): try: for i in range(settings['initialProducts']): self.buy_product_and_update_offer() except Exception as e: print('error on setup:', e) def execute_logic(self): # execute queued methods tmp_queue = [e for e in self.execQueue] self.execQueue = [] for method, args in tmp_queue: method(*args) try: offers = self.marketplace_api.get_offers() except Exception as e: print('error on fetching current offers:', e) missing_offers = self.settings["initialProducts"] - len(self.offers) for missing_offer in range(missing_offers): self.buy_product_and_update_offer() for product in self.products.values(): competitor_offers = [] for offer in offers: if offer.merchant_id != self.merchant_id and offer.id == product.id: competitor_offers.append(offer.price) if len(competitor_offers) > 0: offer = self.offers[product.uid] self.adjust_prices( offer=offer, product=product, lowest_competitor_price=min(competitor_offers)) return settings['max_req_per_sec'] / 60 def adjust_prices(self, offer=None, product=None, lowest_competitor_price=0): if not offer or not product: return min_price = product.price + settings['minPriceMargin'] max_price = product.price + settings['maxPriceMargin'] price = lowest_competitor_price - settings['priceDecrease'] price = min(price, max_price) if price < min_price: price = max_price offer.price = price try: self.marketplace_api.update_offer(offer) except Exception as e: print('error on updating an offer:', e) def sold_product(self, sold_offer): if sold_offer.uid in self.offers: offer = self.offers[sold_offer.uid] offer.amount -= sold_offer.amount_sold product = self.products[sold_offer.uid] product.amount -= sold_offer.amount_sold self.buy_product_and_update_offer() def add_new_product_to_offers(self, new_product): new_offer = Offer.from_product(new_product) new_offer.price += settings['maxPriceMargin'] new_offer.shipping_time = { 'standard': settings['shipping'], 'prime': settings['primeShipping'] } new_offer.prime = True self.products[new_product.uid] = new_product try: new_offer.offer_id = self.marketplace_api.add_offer( new_offer).offer_id self.offers[new_product.uid] = new_offer except Exception as e: print('error on adding a new offer:', e) def restock_existing_product(self, new_product): product = self.products[new_product.uid] product.amount += new_product.amount product.signature = new_product.signature offer = self.offers[product.uid] offer.amount = product.amount offer.signature = product.signature try: self.marketplace_api.restock(offer.offer_id, new_product.amount, offer.signature) except Exception as e: print('error on restocking an offer:', e) def buy_product_and_update_offer(self): new_product = self.producer_api.buy_product() if new_product.uid in self.products: self.restock_existing_product(new_product) else: self.add_new_product_to_offers(new_product)
class BaysianMerchant(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Setup ML model ''' self.models_per_product = self.load_models_from_filesystem() self.last_learning = datetime.datetime.now() trigger_learning(self.merchant_token, self.merchant_id, settings['kafka_reverse_proxy_url']) ''' Start Logic Loop ''' self.run_logic_loop() @staticmethod def load_models_from_filesystem(folder='models'): result = {} for root, dirs, files in os.walk(make_relative_path(folder)): pkl_files = [f for f in files if f.endswith('.pkl')] for pkl_file in pkl_files: complete_path = os.path.join(root, pkl_file) try: product_id = int(pkl_file.split('.')[0]) result[product_id] = joblib.load(complete_path) # print(result[product_id].coef_) except ValueError: # do not load model files, that don't have the naming scheme pass break return result def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] ''' Implement Abstract methods / Interface ''' def update_settings(self, new_settings): MerchantBaseLogic.update_settings(self, new_settings) self.update_api_endpoints() return self.settings def sold_offer(self, offer): pass ''' Merchant Logic ''' def price_product(self, product_or_offer, product_prices_by_uid, current_offers=None): """ Computes a price for a product based on trained models or (exponential) random fallback :param product_or_offer: product object that is to be priced :param current_offers: list of offers :return: """ price = product_prices_by_uid[product_or_offer.uid] try: model = self.models_per_product[product_or_offer.product_id] offer_df = pd.DataFrame([o.to_dict() for o in current_offers]) offer_df = offer_df[offer_df['product_id'] == product_or_offer.product_id] own_offers_mask = offer_df['merchant_id'] == self.merchant_id features = [] for potential_price in range(1, 100, 1): potential_price_candidate = potential_price / 10.0 potential_price = price + potential_price_candidate #product_or_offer.price + potential_price_candidate offer_df.loc[own_offers_mask, 'price'] = potential_price features.append( extract_features_from_offer_snapshot( offer_df, self.merchant_id, product_id=product_or_offer.product_id)) data = pd.DataFrame(features).dropna() try: filtered = data[[ 'amount_of_all_competitors', 'average_price_on_market', 'distance_to_cheapest_competitor', 'price_rank', 'quality_rank' ]] data['sell_prob'] = model.predict( filtered) #model.predict_proba(filtered)[:,1] data['expected_profit'] = data['sell_prob'] * ( data['own_price'] - price) print("set price as ", data['own_price'][data['expected_profit'].argmax()]) except Exception as e: print("Setting price failed: ", e) return data['own_price'][data['expected_profit'].argmax()] except (KeyError, ValueError) as e: return price * (np.random.exponential() + 0.99) except Exception as e: pass def execute_logic(self): next_training_session = self.last_learning \ + datetime.timedelta(minutes=self.settings['minutes_between_learnings']) if next_training_session <= datetime.datetime.now(): self.last_learning = datetime.datetime.now() trigger_learning(self.merchant_token, self.merchant_id, settings['kafka_reverse_proxy_url']) request_count = 0 self.models_per_product = self.load_models_from_filesystem() try: offers = self.marketplace_api.get_offers(include_empty_offers=True) except Exception as e: print('error on getting offers:', e) return max(1.0, request_count) / settings['max_req_per_sec'] own_offers = [ offer for offer in offers if offer.merchant_id == self.merchant_id ] own_offers_by_uid = {offer.uid: offer for offer in own_offers} missing_offers = settings['max_amount_of_offers'] - sum( offer.amount for offer in own_offers) new_products = [] for _ in range(missing_offers): try: prod = self.producer_api.buy_product() new_products.append(prod) except: pass products = self.producer_api.get_products() product_prices_by_uid = { product.uid: product.price for product in products } for own_offer in own_offers: if own_offer.amount > 0: own_offer.price = self.price_product(own_offer, product_prices_by_uid, current_offers=offers) try: self.marketplace_api.update_offer(own_offer) request_count += 1 except Exception as e: print('error on updating offer:', e) for product in new_products: try: if product.uid in own_offers_by_uid: offer = own_offers_by_uid[product.uid] offer.amount += product.amount offer.signature = product.signature try: self.marketplace_api.restock( offer.offer_id, amount=product.amount, signature=product.signature) except Exception as e: print('error on restocking an offer:', e) offer.price = self.price_product(product, product_prices_by_uid, current_offers=offers) try: self.marketplace_api.update_offer(offer) request_count += 1 except Exception as e: print('error on updating an offer:', e) else: offer = Offer.from_product(product) offer.prime = True offer.shipping_time['standard'] = self.settings['shipping'] offer.shipping_time['prime'] = self.settings[ 'primeShipping'] offer.merchant_id = self.merchant_id offer.price = self.price_product(product, product_prices_by_uid, current_offers=offers + [offer]) try: self.marketplace_api.add_offer(offer) except Exception as e: print('error on adding an offer to the marketplace:', e) except Exception as e: print('could not handle product:', product, e) return max(1.0, request_count) / settings['max_req_per_sec']
class MerchantD(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Internal state handling ''' self.execQueue = [] self.marketplace_requests = [] ''' Information store ''' self.products = {} self.offers = {} ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url'], debug=False) self.producer_api = ProducerApi(host=self.settings['producer_url'], debug=False) ''' Start Logic Loop ''' self.run_logic_loop() def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] def get_settings(self): return self.settings def update_settings(self, new_settings): def cast_to_expected_type(key, value, def_settings=self.settings): if key in def_settings: return type(def_settings[key])(value) else: return value new_settings_casted = dict([ (key, cast_to_expected_type(key, new_settings[key])) for key in new_settings ]) self.settings.update(new_settings_casted) self.update_api_endpoints() return self.settings def sold_offer(self, offer): print("sold offer") self.execQueue.append((self.sold_product, [offer])) def buy_missing_products(self): for i in range(self.settings["initialProducts"] - sum(offer.amount for offer in self.offers.values())): self.buy_product_and_update_offer() def setup(self): try: # get all products for later comparison over all qualities self.product = {} for product in self.producer_api.get_products(): self.products[product.uid] = product # get all existing offers from marketplace self.offer = {} for offer in self.marketplace_api.get_offers(): if offer.merchant_id == self.merchant_id: self.offers[offer.uid] = offer # buy new products if none or not enough exist self.buy_missing_products() except Exception as e: print('error on setup:', e) def execute_logic(self): # execute queued methods tmp_queue = [e for e in self.execQueue] self.execQueue = [] for method, args in tmp_queue: method(*args) # if initialProducts setting increased after start, get new products self.buy_missing_products() self.update_market_situation() return self.calculate_intervall() def base_price_diff(self, offer): product = self.products[offer.uid] return offer.price - product.price def adjust_prices(self, offer=None, lowest_price_diff=0): product = self.products[offer.uid] if not offer or not product: return price_diff = min(lowest_price_diff - settings['priceDecrease'], settings['maxPriceMargin']) if price_diff < settings['minPriceMargin']: price_diff = settings['maxPriceMargin'] new_product_price = product.price + price_diff if new_product_price != offer.price: offer.price = new_product_price print("update to new price ", new_product_price) self.marketplace_api.update_offer(offer) self.request_done() def update_market_situation(self): marketplace_offers = self.marketplace_api.get_offers() for own_offer in self.offers.values(): if self.quora_exhausted(): break if own_offer.amount > 0: competitor_offers_price_diff = [] for marketplace_offer in marketplace_offers: if marketplace_offer.merchant_id != self.merchant_id and marketplace_offer.product_id == own_offer.product_id: competitor_offers_price_diff.append( self.base_price_diff(marketplace_offer)) if len(competitor_offers_price_diff) > 0: self.adjust_prices( offer=own_offer, lowest_price_diff=min(competitor_offers_price_diff)) else: self.adjust_prices(offer=own_offer) def sold_product(self, sold_offer): print('soldProduct, offer:', sold_offer) if sold_offer.uid in self.offers: # print('found in offers') offer = self.offers[sold_offer.uid] offer.amount -= sold_offer.amount_sold product = self.products[sold_offer.uid] product.amount -= sold_offer.amount_sold if product.amount <= 0: print('product {:d} is out of stock!'.format(product.uid)) self.buy_product_and_update_offer() def add_new_product_to_offers(self, new_product): new_offer = Offer.from_product(new_product) new_offer.price += settings['maxPriceMargin'] new_offer.shipping_time = { 'standard': settings['shipping'], 'prime': settings['primeShipping'] } new_offer.prime = True # self.products[new_product.uid] = new_product new_offer.offer_id = self.marketplace_api.add_offer(new_offer).offer_id self.offers[new_product.uid] = new_offer def restock_existing_product(self, new_product): print('restock product', new_product) product = self.products[new_product.uid] product.amount += new_product.amount product.signature = new_product.signature offer = self.offers[product.uid] print('in this offer:', offer) offer.amount = product.amount offer.signature = product.signature self.marketplace_api.restock(offer.offer_id, new_product.amount, offer.signature) def buy_product_and_update_offer(self): print('buy Product and update') new_product = self.producer_api.buy_product() if new_product.uid in self.offers: self.restock_existing_product(new_product) else: self.add_new_product_to_offers(new_product) def request_done(self): self.marketplace_requests.insert(0, time.time()) def quora_exhausted(self): while len(self.marketplace_requests) > 0: last = self.marketplace_requests.pop() now = time.time() if now - last < 1: self.marketplace_requests.append(last) break return len(self.marketplace_requests) >= settings['max_req_per_sec'] def active_offers_count(self): offer_count = 0 for offer in self.offers.values(): if offer.amount > 0: offer_count += 1 return offer_count def calculate_intervall(self): if len(self.marketplace_requests) == 0: return 0 else: offer_count = self.active_offers_count() remaining_reqs = settings['max_req_per_sec'] - len( self.marketplace_requests) time_to_next_release = time.time() - self.marketplace_requests[ len(self.marketplace_requests) - 1] return (offer_count / (remaining_reqs + 1)) * time_to_next_release
class MerchantSampleLogic(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi(host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop() def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] ''' Implement Abstract methods / Interface ''' def update_settings(self, new_settings): MerchantBaseLogic.update_settings(self, new_settings) self.update_api_endpoints() return self.settings def sold_offer(self, offer): print('sold offer:', offer) ''' Merchant Logic ''' def price_product(self, product): return (1.0 + self.settings['fixed_margin_perc'] / 100.0) * product.price def execute_logic(self): try: offers = self.marketplace_api.get_offers(include_empty_offers=True) except Exception as e: print('error on getting offers from the marketplace:', e) return 1.0 / settings['max_req_per_sec'] own_offers = [offer for offer in offers if offer.merchant_id == self.merchant_id] own_offers_by_uid = {offer.uid: offer for offer in own_offers} missing_offers = settings['max_amount_of_offers'] - sum(offer.amount for offer in own_offers) new_products = [] for _ in range(missing_offers): try: prod = self.producer_api.buy_product() new_products.append(prod) except: pass for product in new_products: try: if product.uid in own_offers_by_uid: offer = own_offers_by_uid[product.uid] offer.amount += product.amount offer.signature = product.signature self.marketplace_api.restock(offer.offer_id, amount=product.amount, signature=product.signature) offer.price = self.price_product(product) self.marketplace_api.update_offer(offer) else: offer = Offer.from_product(product) offer.price = self.price_product(product) offer.prime = True offer.shipping_time['standard'] = self.settings['shipping'] offer.shipping_time['prime'] = self.settings['primeShipping'] self.marketplace_api.add_offer(offer) except Exception as e: print('could not handle product:', product, e) return 1.0 / settings['max_req_per_sec']
def __init__(self, merchant_token, marketplace_url, producer_url): PricewarsRequester.add_api_token(merchant_token) self.marketplace_api = MarketplaceApi(host=marketplace_url) self.producer_api = ProducerApi(host=producer_url) self.request_counter = 0
class Api(ApiAbstraction): def __init__(self, merchant_token, marketplace_url, producer_url): PricewarsRequester.add_api_token(merchant_token) self.marketplace_api = MarketplaceApi(host=marketplace_url) self.producer_api = ProducerApi(host=producer_url) self.request_counter = 0 def add_offer(self, offer: Offer) -> Offer: try: return self.marketplace_api.add_offer(offer) except Exception as e: print('error on adding an offer to the marketplace:', e) def unregister_merchant(self, merchant_token=''): return self.marketplace_api.unregister_merchant(merchant_token) def register_merchant(self, api_endpoint_url='', merchant_name='', algorithm_name='') -> MerchantRegisterResponse: return self.marketplace_api.register_merchant(api_endpoint_url, merchant_name, algorithm_name) def update_offer(self, offer: Offer): try: self.increase_request_counter() return self.marketplace_api.update_offer(offer) except Exception as e: logging.warning( 'Could not update offer on marketplace: {}'.format(e)) def get_offers(self, include_empty_offers=False) -> List[Offer]: try: return self.marketplace_api.get_offers(include_empty_offers) except Exception as e: logging.warning( 'Could not receive offers from marketplace: {}'.format(e)) raise e def restock(self, offer_id=-1, amount=0, signature=''): try: return self.marketplace_api.restock(offer_id, amount, signature) except Exception as e: print('error on restocking an offer:', e) def add_product(self, product: Product): return self.producer_api.add_product(product) def get_product(self, product_uid) -> Product: return self.producer_api.get_product(product_uid) def add_products(self, products: List[Product]): return self.producer_api.add_products(products) def update_product(self, product: Product): return self.producer_api.update_product(product) def update_products(self, products: List[Product]): return self.producer_api.update_products(products) def delete_product(self, product_uid): return self.producer_api.delete_product(product_uid) def get_products(self) -> List[Product]: try: return self.producer_api.get_products() except Exception as e: logging.warning( 'Could not receive products from producer api: {}'.format(e)) return list() def buy_product(self) -> Product: try: return self.producer_api.buy_product() except Exception as e: logging.warning( 'Could not buy new product from producer api: {}'.format(e)) raise e def update_marketplace_url(self, marketplace_url: str): self.marketplace_api.host = marketplace_url def update_producer_url(self, producer_url: str): self.producer_api.host = producer_url def increase_request_counter(self): self.request_counter += 1 def reset_request_counter(self): self.request_counter = 0
class MerchantSampleLogic(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Internal state handling ''' self.execQueue = [] ''' Information store ''' self.products = {} self.offers = {} ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop() def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] ''' Implement Abstract methods / Interface ''' def get_settings(self): return self.settings def update_settings(self, new_settings): def cast_to_expected_type(key, value, def_settings=self.settings): if key in def_settings: return type(def_settings[key])(value) else: return value new_settings_casted = dict([ (key, cast_to_expected_type(key, new_settings[key])) for key in new_settings ]) self.settings.update(new_settings_casted) self.update_api_endpoints() return self.settings def sold_offer(self, offer): self.execQueue.append((self.sold_product, [offer])) ''' Merchant Logic ''' def setup(self): try: for i in range(settings['initialProducts']): self.buy_product_and_update_offer() except Exception as e: print('error on setup:', e) def execute_logic(self): # execute queued methods tmp_queue = [e for e in self.execQueue] self.execQueue = [] for method, args in tmp_queue: method(*args) try: offers = self.marketplace_api.get_offers() for product in self.products.values(): if product.amount > 0: competitor_offers = [] for offer in offers: if offer.merchant_id != self.merchant_id and offer.product_id == product.product_id: competitor_offers.append(offer.price) offer = self.offers[product.uid] if len(competitor_offers) > 0: competitor_offers.sort() if len(competitor_offers) > 2: self.adjust_prices( offer=offer, product=product, lowest_competitor_price=competitor_offers[0], second_competitor_price=competitor_offers[1], third_competitor_price=competitor_offers[2]) elif len(competitor_offers) > 1: self.adjust_prices( offer=offer, product=product, lowest_competitor_price=competitor_offers[0], second_competitor_price=competitor_offers[1], third_competitor_price=0) else: self.adjust_prices( offer=offer, product=product, lowest_competitor_price=competitor_offers[0], second_competitor_price=0, third_competitor_price=0) except Exception as e: print('error on executing logic:', e) # returns sleep value; return 60.0 / settings['max_req_per_sec'] def adjust_prices(self, offer=None, product=None, lowest_competitor_price=0, second_competitor_price=0, third_competitor_price=0): if not offer or not product: return min_price = product.price max_price = product.price * 2 target_position = randint(1, 3) if (target_position == 3 and third_competitor_price > 0): price = third_competitor_price - settings['price_decrement'] elif (target_position == 2 and second_competitor_price > 0): price = second_competitor_price - settings['price_decrement'] else: price = lowest_competitor_price - settings['price_decrement'] price = min(price, max_price) if price < min_price: price = max_price if price != offer.price: offer.price = price try: self.marketplace_api.update_offer(offer) except Exception as e: print('error on updating offer:', e) def sold_product(self, sold_offer): print('soldProduct, offer:', sold_offer) if sold_offer.uid in self.offers: print('found in offers') offer = self.offers[sold_offer.uid] offer.amount -= sold_offer.amount_sold product = self.products[sold_offer.uid] product.amount -= sold_offer.amount_sold if product.amount <= 0: print('product {:d} is out of stock!'.format(product.uid)) self.buy_product_and_update_offer() def add_new_product_to_offers(self, new_product): new_offer = Offer.from_product(new_product) new_offer.price = new_offer.price * 2 new_offer.shipping_time = { 'standard': settings['shipping'], 'prime': settings['primeShipping'] } new_offer.prime = True self.products[new_product.uid] = new_product new_offer.offer_id = self.marketplace_api.add_offer(new_offer).offer_id self.offers[new_product.uid] = new_offer def restock_existing_product(self, new_product): print('restock product', new_product) product = self.products[new_product.uid] product.amount += new_product.amount product.signature = new_product.signature offer = self.offers[product.uid] print('in this offer:', offer) offer.amount = product.amount offer.signature = product.signature try: self.marketplace_api.restock(offer.offer_id, new_product.amount, offer.signature) except Exception as e: print('error on restocking offer:', e) def buy_product_and_update_offer(self): print('buy Product and update') try: new_product = self.producer_api.buy_product() if new_product.uid in self.products: self.restock_existing_product(new_product) else: self.add_new_product_to_offers(new_product) except Exception as e: print('error on buying a new product:', e)
class SecondCheapestMerchantApp(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop() ''' save purchase prices for offer updates ''' self.purchase_prices = {} def update_api_endpoints(self): self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] def update_settings(self, new_settings): MerchantBaseLogic.update_settings(self, new_settings) self.update_api_endpoints() return self.settings def initialize_purchase_price_map(self): try: available_products = self.producer_api.get_products() except Exception as e: print('error on getting products from the producer:', e) for product in available_products: self.purchase_prices[product.uid] = product.price def buy_product_and_post_to_marketplace(self, all_offers): print('buy Product and update') new_product = self.buy_product() existing_offers = self.get_existing_offers_for_product_id_from_marketplace( all_offers, new_product.product_id) target_price = self.get_second_cheapest_price(existing_offers, new_product.price) existing_offer = self.get_own_offer_for_product_uid( existing_offers, new_product.uid) return self.post_offer(new_product, target_price, existing_offer) def buy_product(self): try: new_product = self.producer_api.buy_product() if new_product.uid not in self.purchase_prices: self.purchase_prices[new_product.uid] = new_product.price return new_product except Exception as e: print('error on buying a new product:', e) def get_existing_offers_for_product_id_from_marketplace( self, all_offers, product_id): product_id_offers = [ offer for offer in all_offers if offer.product_id == product_id ] return product_id_offers def get_second_cheapest_price(self, offers, purchase_price): maximum_price = 2 * purchase_price minimum_price = purchase_price * ( 1 + (self.settings['minimumMarginPercentile'] / 100.0)) second_cheapest_offer = cheapest_offer = maximum_price for offer in offers: if offer.merchant_id == self.merchant_id: continue if offer.price < cheapest_offer: second_cheapest_offer = cheapest_offer cheapest_offer = offer.price elif cheapest_offer < offer.price < second_cheapest_offer: second_cheapest_offer = offer.price target_price = second_cheapest_offer - self.settings['price_decrement'] if second_cheapest_offer <= maximum_price and target_price >= cheapest_offer: second_cheapest_offer = target_price if second_cheapest_offer < minimum_price: second_cheapest_offer = minimum_price return second_cheapest_offer def get_own_offer_for_product_uid(self, offers, product_uid): return next( (offer for offer in offers if offer.merchant_id == self.merchant_id and offer.uid == product_uid), None) def post_offer(self, product, price, existing_offer): new_offer = Offer.from_product(product) new_offer.price = price new_offer.shipping_time = { 'standard': settings['shipping'], 'prime': settings['primeShipping'] } new_offer.prime = True try: if existing_offer is None: return self.marketplace_api.add_offer(new_offer) else: self.marketplace_api.restock(existing_offer.offer_id, product.amount, product.signature) return None except Exception as e: print('error on posting an offer:', e) def update_offer(self, own_offer, target_price): if own_offer.price != target_price: own_offer.price = target_price try: self.marketplace_api.update_offer(own_offer) except Exception as e: print('error on updating an offer:', e) def get_own_offers(self, all_offers): return [ offer for offer in all_offers if offer.merchant_id == self.merchant_id ] def get_amount_of_own_offers(self, all_offers): return sum(offer.amount for offer in self.get_own_offers(all_offers)) def adjust_prices(self, all_offers): print('Update offer') try: my_offered_product_ids = [ offer.product_id for offer in all_offers if offer.merchant_id == self.merchant_id ] all_offers_i_offer_as_well = [ offer for offer in all_offers if offer.product_id in my_offered_product_ids ] # Create a map with the product_id as key and the amount of offers as value (includes my own offers) offers_per_traded_product = {} for offer in all_offers_i_offer_as_well: offers_per_traded_product[ offer.product_id] = offers_per_traded_product.get( offer.product_id, 0) + 1 # Iterate over the traded product IDs in descending order of the amount of competitor offers for product_id in [ offer_product_id[0] for offer_product_id in sorted( offers_per_traded_product.items(), key=operator.itemgetter(1), reverse=True) ]: existing_offers_for_product_id = self.get_existing_offers_for_product_id_from_marketplace( all_offers_i_offer_as_well, product_id) # Iterate over my offers based on the quality, starting with the best quality (lowest quality number) for product_uid in [ offer.uid for offer in sorted( self.get_own_offers( existing_offers_for_product_id), key=lambda offer_entry: offer_entry.quality) ]: purchase_price = self.purchase_prices[product_uid] target_price = self.get_second_cheapest_price( existing_offers_for_product_id, purchase_price) existing_offer = self.get_own_offer_for_product_uid( existing_offers_for_product_id, product_uid) self.update_offer(existing_offer, target_price) except Exception as e: print('error on adjusting prices:', e) def refill_offers(self, all_offers=None): if not all_offers: try: all_offers = self.marketplace_api.get_offers( include_empty_offers=True) except Exception as e: print('error on fetching offers from the marketplace:', e) existing_offers = self.get_amount_of_own_offers(all_offers) try: for i in range(settings['listedOffers'] - existing_offers): new_product = self.buy_product_and_post_to_marketplace( all_offers) if new_product: all_offers.append(new_product) # else: product already offered and restocked except Exception as e: print('error on refilling offers:', e) def setup(self): try: self.initialize_purchase_price_map() all_offers = self.marketplace_api.get_offers( include_empty_offers=True) self.refill_offers(all_offers) except Exception as e: print('error on setting up offers:', e) def execute_logic(self): try: all_offers = self.marketplace_api.get_offers( include_empty_offers=True) self.adjust_prices(all_offers) self.refill_offers(all_offers) except Exception as e: print('error on executing logic:', e) # ToDo: Return true value (calculate!) return self.interval def sold_offer(self, offer_json): if self.state != 'running': return try: all_offers = self.marketplace_api.get_offers( include_empty_offers=True) self.refill_offers(all_offers) except Exception as e: print('error on handling sold offers:', e)
class MerchantSampleLogic(MerchantBaseLogic): def __init__(self): MerchantBaseLogic.__init__(self) global settings self.settings = settings ''' Information store ''' self.products = {} self.offers = {} ''' Predefined API token ''' self.merchant_id = settings['merchant_id'] self.merchant_token = merchant_token ''' Setup API ''' PricewarsRequester.add_api_token(self.merchant_token) self.marketplace_api = MarketplaceApi( host=self.settings['marketplace_url']) self.producer_api = ProducerApi(host=self.settings['producer_url']) ''' Start Logic Loop ''' self.run_logic_loop() def update_api_endpoints(self): """ Updated settings may contain new endpoints, so they need to be set in the api client as well. However, changing the endpoint (after simulation start) may lead to an inconsistent state :return: None """ self.marketplace_api.host = self.settings['marketplace_url'] self.producer_api.host = self.settings['producer_url'] ''' Implement Abstract methods / Interface ''' def get_settings(self): return self.settings def update_settings(self, new_settings): def cast_to_expected_type(key, value, def_settings=self.settings): if key in def_settings: return type(def_settings[key])(value) else: return value new_settings_casted = dict([ (key, cast_to_expected_type(key, new_settings[key])) for key in new_settings ]) self.settings.update(new_settings_casted) self.update_api_endpoints() return self.settings def sold_offer(self, offer): #TODO: we store the amount in self.offers but do not decrease it here if self.state != 'running': return try: offers = self.marketplace_api.get_offers() self.buy_product_and_update_offer(offers) except Exception as e: print('error on handling a sold offer:', e) ''' Merchant Logic for being the cheapest ''' def setup(self): try: marketplace_offers = self.marketplace_api.get_offers() for i in range(settings['initialProducts']): self.buy_product_and_update_offer(marketplace_offers) except Exception as e: print('error on setup:', e) def execute_logic(self): try: offers = self.marketplace_api.get_offers() items_offered = sum( o.amount for o in offers if o.merchant_id == self.settings['merchant_id']) while items_offered < (settings['initialProducts'] - 1): self.buy_product_and_update_offer(offers) items_offered = sum( o.amount for o in self.marketplace_api.get_offers() if o.merchant_id == self.settings['merchant_id']) for product in self.products.values(): if product.uid in self.offers: offer = self.offers[product.uid] offer.price = self.calculate_prices( offers, product.uid, product.price, product.product_id) try: self.marketplace_api.update_offer(offer) except Exception as e: print('error on updating an offer:', e) else: print('ERROR: product UID is not in offers; skipping.') except Exception as e: print('error on executing the logic:', e) return settings['maxReqPerSec'] / 10 def calculate_prices(self, marketplace_offers, product_uid, purchase_price, product_id): competitive_offers = [] [ competitive_offers.append(offer) for offer in marketplace_offers if offer.merchant_id != self.merchant_id and offer.product_id == product_id ] cheapest_offer = 999 if len(competitive_offers) == 0: return 2 * purchase_price for offer in competitive_offers: if offer.price < cheapest_offer: cheapest_offer = offer.price new_price = cheapest_offer - settings['price_decrement'] if new_price < purchase_price: new_price = purchase_price return new_price def add_new_product_to_offers(self, new_product, marketplace_offers): new_offer = Offer.from_product(new_product) new_offer.price = self.calculate_prices(marketplace_offers, new_product.uid, new_product.price, new_product.product_id) new_offer.shipping_time = { 'standard': settings['shipping'], 'prime': settings['primeShipping'] } new_offer.prime = True try: new_offer.offer_id = self.marketplace_api.add_offer( new_offer).offer_id self.products[new_product.uid] = new_product self.offers[new_product.uid] = new_offer except Exception as e: print('error on adding a new offer:', e) def restock_existing_product(self, new_product, marketplace_offers): # print('restock product', new_product) product = self.products[new_product.uid] product.amount += new_product.amount product.signature = new_product.signature offer = self.offers[product.uid] # print('in this offer:', offer) offer.price = self.calculate_prices(marketplace_offers, product.uid, product.price, product.product_id) offer.amount = product.amount offer.signature = product.signature try: self.marketplace_api.restock(offer.offer_id, new_product.amount, offer.signature) except Exception as e: print('error on restocking an offer:', e) def buy_product_and_update_offer(self, marketplace_offers): try: new_product = self.producer_api.buy_product() if new_product.uid in self.products: self.restock_existing_product(new_product, marketplace_offers) else: self.add_new_product_to_offers(new_product, marketplace_offers) except Exception as e: print('error on buying a new product:', e)