def get_listings_with_other_rarity_tags(
        look_for_profile_backgrounds,
        retrieve_listings_with_another_rarity_tag_from_scratch=False):
    if retrieve_listings_with_another_rarity_tag_from_scratch:
        other_rarity_fields = set(get_rarity_fields()).difference({'common'})
        for rarity_tag in other_rarity_fields:
            update_all_listings_for_items_other_than_cards(rarity=rarity_tag)

    if look_for_profile_backgrounds:
        all_listings_for_uncommon = load_all_listings(
            listing_output_file_name=
            get_listing_output_file_name_for_profile_backgrounds(
                rarity='uncommon'))
        all_listings_for_rare = load_all_listings(
            listing_output_file_name=
            get_listing_output_file_name_for_profile_backgrounds(
                rarity='rare'))

    else:
        all_listings_for_uncommon = load_all_listings(
            listing_output_file_name=get_listing_output_file_name_for_emoticons(
                rarity='uncommon'))
        all_listings_for_rare = load_all_listings(
            listing_output_file_name=get_listing_output_file_name_for_emoticons(
                rarity='rare'))

    return all_listings_for_uncommon, all_listings_for_rare
Exemplo n.º 2
0
def filter_listings(
    all_listings=None,
    min_sell_price=30,  # in cents
    min_num_listings=20,  # to remove listings with very few sellers, who chose unrealistic sell prices
    verbose=True):
    if all_listings is None:
        all_listings = load_all_listings()

    # Sort listing hashes with respect to the ask

    sorted_listing_hashes = sorted(all_listings,
                                   reverse=True,
                                   key=lambda x: all_listings[x]['sell_price'])

    # *Heuristic* filtering of listing hashes

    filtered_listing_hashes = list(
        filter(
            lambda x: all_listings[x]['sell_price'] >= min_sell_price and
            all_listings[x]['sell_listings'] >= min_num_listings,
            sorted_listing_hashes))

    if verbose:
        print('{} hashes found.\n'.format(len(filtered_listing_hashes)))

    return filtered_listing_hashes
Exemplo n.º 3
0
def update_all_listing_details(listing_hashes=None,
                               listing_details_output_file_name=None):
    # Caveat: this is mostly useful if download_all_listing_details() failed in the middle of the process, and you want
    # to restart the process without risking to lose anything, in case the process fails again.

    if listing_details_output_file_name is None:
        listing_details_output_file_name = get_listing_details_output_file_name(
        )

    try:
        with open(listing_details_output_file_name, 'r',
                  encoding='utf-8') as f:
            all_listing_details = json.load(f)
            print('Loading {} listing details from disk.'.format(
                len(all_listing_details)))
    except FileNotFoundError:
        print('Downloading listing details from scratch.')
        all_listing_details = None

    if listing_hashes is None:
        all_listings = load_all_listings()
        listing_hashes = list(all_listings.keys())

    all_listing_details = get_listing_details_batch(
        listing_hashes,
        all_listing_details,
        save_to_disk=True,
        listing_details_output_file_name=listing_details_output_file_name)

    return all_listing_details
def get_sell_prices_without_fee(app_ids, price_offset_in_euros=0.0):
    # Load sell prices (without fee).
    #
    # NB: an arbitrary price offset (greater than or equal to zero) can be input to constrain the problem even more.
    # This is a security: if the price offset is positive (>0), then we know that we can under-cut the lowest sell order
    # and still be able to make a profit if someone agrees to buy from us.

    data = load_all_listings()

    sell_prices = dict()

    for listing_hash in data:
        app_id_as_int = convert_listing_hash_to_app_id(listing_hash)
        app_id = str(app_id_as_int)

        if app_id in app_ids:
            current_data = data[listing_hash]

            sell_price_in_cents = current_data['sell_price']
            sell_price_in_euros = int(sell_price_in_cents) / 100
            sell_price_after_arbitrary_offset = sell_price_in_euros - abs(
                price_offset_in_euros)
            sell_price_in_euros_without_fee = compute_sell_price_without_fee(
                sell_price_after_arbitrary_offset)

            sell_prices[app_id] = sell_price_in_euros_without_fee

    return sell_prices
Exemplo n.º 5
0
def load_aggregated_badge_data(retrieve_listings_from_scratch=False,
                               enforced_sack_of_gems_price=None,
                               minimum_allowed_sack_of_gems_price=None,
                               from_javascript=False):
    badge_creation_details = parse_badge_creation_details(
        from_javascript=from_javascript)

    if retrieve_listings_from_scratch:
        update_all_listings()

    all_listings = load_all_listings()

    all_listings = filter_out_dubious_listing_hashes(all_listings)

    badge_matches = match_badges_with_listing_hashes(badge_creation_details,
                                                     all_listings)

    retrieve_gem_price_from_scratch = bool(enforced_sack_of_gems_price is None)

    aggregated_badge_data = aggregate_badge_data(
        badge_creation_details,
        badge_matches,
        all_listings=all_listings,
        enforced_sack_of_gems_price=enforced_sack_of_gems_price,
        minimum_allowed_sack_of_gems_price=minimum_allowed_sack_of_gems_price,
        retrieve_gem_price_from_scratch=retrieve_gem_price_from_scratch)

    return aggregated_badge_data
def get_listings(listing_output_file_name,
                 retrieve_listings_from_scratch=False):
    if retrieve_listings_from_scratch:
        # Caveat: this update is only for items of Common rarity!
        update_all_listings_for_items_other_than_cards(rarity='common')

    all_listings = load_all_listings(listing_output_file_name)

    return all_listings
Exemplo n.º 7
0
def aggregate_badge_data(badge_creation_details,
                         badge_matches,
                         all_listings=None,
                         enforced_sack_of_gems_price=None,
                         minimum_allowed_sack_of_gems_price=None,
                         retrieve_gem_price_from_scratch=False):
    # Aggregate data:
    #       owned appID --> (gem PRICE, sell price)
    # where:
    # - the gem price is the price required to buy gems on the market to then craft a booster pack for this game,
    # - the sell price is the price which sellers are asking for this booster pack.
    #
    # NB: ensure the same currency is used.

    if all_listings is None:
        all_listings = load_all_listings()

    gem_price = get_gem_price(
        enforced_sack_of_gems_price=enforced_sack_of_gems_price,
        minimum_allowed_sack_of_gems_price=minimum_allowed_sack_of_gems_price,
        retrieve_gem_price_from_scratch=retrieve_gem_price_from_scratch)

    badge_app_ids = list(badge_creation_details.keys())

    aggregated_badge_data = dict()

    for app_id in badge_app_ids:
        app_name = badge_creation_details[app_id]['name']
        gem_amount_required_to_craft_booster_pack = badge_creation_details[
            app_id]['gem_value']
        try:
            next_creation_time = badge_creation_details[app_id][
                'next_creation_time']
        except KeyError:
            next_creation_time = None
        listing_hash = badge_matches[app_id]

        if listing_hash is None:
            # For some reason for Conran - The dinky Raccoon (appID = 612150), there is no listing of any "Booster Pack"
            # Reference: https://steamcommunity.com/market/search?appid=753&category_753_Game%5B0%5D=tag_app_612150
            continue
        else:
            sell_price_in_cents = all_listings[listing_hash]['sell_price']
            sell_price_in_euros = sell_price_in_cents / 100

        aggregated_badge_data[app_id] = dict()
        aggregated_badge_data[app_id]['name'] = app_name
        aggregated_badge_data[app_id]['listing_hash'] = listing_hash
        aggregated_badge_data[app_id][
            'gem_amount'] = gem_amount_required_to_craft_booster_pack
        aggregated_badge_data[app_id][
            'gem_price'] = gem_amount_required_to_craft_booster_pack * gem_price
        aggregated_badge_data[app_id]['sell_price'] = sell_price_in_euros
        aggregated_badge_data[app_id][
            'next_creation_time'] = next_creation_time

    return aggregated_badge_data
Exemplo n.º 8
0
def match_badges_with_listing_hashes(badge_creation_details=None,
                                     all_listings=None,
                                     verbose=True):
    # Badges for games which I own

    if badge_creation_details is None:
        badge_creation_details = parse_badge_creation_details()

    badge_app_ids = list(badge_creation_details.keys())

    # Listings for ALL the existing Booster Packs

    if all_listings is None:
        all_listings = load_all_listings()

    all_listing_hashes = list(all_listings.keys())

    # Dictionaries to match appIDs or app names with listing hashes

    listing_matches_with_app_ids = dict()
    listing_matches_with_app_names = dict()
    for listing_hash in all_listing_hashes:
        app_id = convert_listing_hash_to_app_id(listing_hash)
        app_name = convert_listing_hash_to_app_name(listing_hash)

        listing_matches_with_app_ids[app_id] = listing_hash
        listing_matches_with_app_names[app_name] = listing_hash

    # Match badges with listing hashes

    badge_matches = dict()
    for app_id in badge_app_ids:
        app_name = badge_creation_details[app_id]['name']

        try:
            badge_matches[app_id] = listing_matches_with_app_ids[app_id]
        except KeyError:

            try:
                badge_matches[app_id] = listing_matches_with_app_names[
                    app_name]
                if verbose:
                    print('Match for {} (appID = {}) with name instead of id.'.
                          format(app_name, app_id))
            except KeyError:
                badge_matches[app_id] = None
                if verbose:
                    print('No match found for {} (appID = {})'.format(
                        app_name, app_id))

    if verbose:
        print('#badges = {} ; #matching hashes found = {}'.format(
            len(badge_app_ids), len(badge_matches)))

    return badge_matches
def load_apps_with_trading_cards(verbose=True):
    all_listings = load_all_listings()

    apps_with_trading_cards = [
        convert_listing_hash_to_app_id(listing_hash)
        for listing_hash in all_listings
    ]

    if verbose:
        print('Apps with trading cards: {}'.format(
            len(apps_with_trading_cards)))

    return apps_with_trading_cards
Exemplo n.º 10
0
def main(populate_all_item_name_ids=False):
    if populate_all_item_name_ids:
        # Pre-retrieval of ALL of the MISSING item name ids.
        # Caveat: this may require a long time, due to API rate limits.

        all_listings = load_all_listings()
        item_nameids = get_item_nameid_batch(listing_hashes=all_listings)

    else:
        aggregated_badge_data = load_aggregated_badge_data()
        populate_random_samples_of_badge_data(aggregated_badge_data,
                                              num_samples=50)

    return True
def get_listings_for_foil_cards(retrieve_listings_from_scratch,
                                listing_output_file_name=None,
                                verbose=True):
    if retrieve_listings_from_scratch:
        update_all_listings_for_foil_cards()

    if listing_output_file_name is None:
        listing_output_file_name = get_listing_output_file_name_for_foil_cards(
        )

    all_listings = load_all_listings(listing_output_file_name)

    if verbose:
        print('#listings = {}'.format(len(all_listings)))

    return all_listings
Exemplo n.º 12
0
def main(retrieve_listings_from_scratch=False,
         retrieve_market_orders_online=False,
         force_update_from_steam_card_exchange=False,
         enforced_sack_of_gems_price=None,
         minimum_allowed_sack_of_gems_price=None,
         use_a_constant_price_threshold=False,
         min_sell_price=30,
         min_num_listings=3,
         num_packs_to_display=10):
    # Load list of all listing hashes

    if retrieve_listings_from_scratch:
        update_all_listings()

    all_listings = load_all_listings()

    all_listings = filter_out_dubious_listing_hashes(all_listings)

    # Import information from SteamCardExchange

    aggregated_badge_data = fill_in_badge_data_with_data_from_steam_card_exchange(
        all_listings,
        force_update_from_steam_card_exchange=
        force_update_from_steam_card_exchange,
        enforced_sack_of_gems_price=enforced_sack_of_gems_price,
        minimum_allowed_sack_of_gems_price=minimum_allowed_sack_of_gems_price)

    # *Heuristic* filtering of listing hashes

    if use_a_constant_price_threshold:
        filtered_listing_hashes = filter_listings(
            all_listings,
            min_sell_price=min_sell_price,
            min_num_listings=min_num_listings)

        filtered_badge_data = fill_in_badge_data_with_data_from_steam_card_exchange(
            filtered_listing_hashes,
            force_update_from_steam_card_exchange=
            force_update_from_steam_card_exchange,
            enforced_sack_of_gems_price=enforced_sack_of_gems_price,
            minimum_allowed_sack_of_gems_price=
            minimum_allowed_sack_of_gems_price)

    else:
        filtered_badge_data = filter_out_badges_with_low_sell_price(
            aggregated_badge_data)

        filtered_listing_hashes = [
            filtered_badge_data[app_id]['listing_hash']
            for app_id in filtered_badge_data
        ]

    # Pre-retrieval of item name ids

    item_nameids = get_item_nameid_batch(filtered_listing_hashes)

    # Download market orders

    market_order_dict = load_market_order_data(
        filtered_badge_data,
        trim_output=True,
        retrieve_market_orders_online=retrieve_market_orders_online)

    # Only keep marketable booster packs

    marketable_market_order_dict, unknown_market_order_dict = filter_out_unmarketable_packs(
        market_order_dict)

    # Sort by bid value
    hashes_for_best_bid = sort_according_to_buzz(market_order_dict,
                                                 marketable_market_order_dict)

    # Display the highest ranked booster packs

    print_packs_with_high_buzz(hashes_for_best_bid,
                               market_order_dict,
                               num_packs_to_display=num_packs_to_display)

    # Detect potential arbitrages

    badge_arbitrages = find_badge_arbitrages(filtered_badge_data,
                                             market_order_dict)

    print('\n# Results for detected *potential* arbitrages\n')
    print_arbitrages(badge_arbitrages,
                     use_numbered_bullet_points=True,
                     use_hyperlink=True)

    return
def determine_whether_an_arbitrage_might_exist_for_foil_cards(
        eligible_listing_hashes,
        all_goo_details,
        app_ids_with_unreliable_goo_details=None,
        app_ids_with_unknown_goo_value=None,
        all_listings=None,
        listing_output_file_name=None,
        sack_of_gems_price_in_euros=None,
        retrieve_gem_price_from_scratch=True,
        verbose=True):
    if sack_of_gems_price_in_euros is None:
        # Load the price of a sack of 1000 gems
        sack_of_gems_price_in_euros = load_sack_of_gems_price(
            retrieve_gem_price_from_scratch=retrieve_gem_price_from_scratch,
            verbose=verbose)

    if listing_output_file_name is None:
        listing_output_file_name = get_listing_output_file_name_for_foil_cards(
        )

    if all_listings is None:
        all_listings = load_all_listings(
            listing_output_file_name=listing_output_file_name)

    if app_ids_with_unreliable_goo_details is None:
        app_ids_with_unreliable_goo_details = []

    if app_ids_with_unknown_goo_value is None:
        app_ids_with_unknown_goo_value = []

    num_gems_per_sack_of_gems = get_num_gems_per_sack_of_gems()

    sack_of_gems_price_in_cents = 100 * sack_of_gems_price_in_euros

    arbitrages = dict()

    for listing_hash in eligible_listing_hashes:
        app_id = convert_listing_hash_to_app_id(listing_hash)

        if app_id in app_ids_with_unreliable_goo_details:
            # NB: This is for goo details which were retrieved with the default item type n° (=2), which can be wrong.
            if verbose:
                print(
                    '[X]\tUnreliable goo details for {}'.format(listing_hash))
            continue

        goo_value_in_gems = safe_read_from_dict(input_dict=all_goo_details,
                                                input_key=app_id)

        if app_id in app_ids_with_unknown_goo_value or goo_value_in_gems is None:
            # NB: This is when the goo value is unknown, despite a correct item type n° used to download goo details.
            if verbose:
                print('[?]\tUnknown goo value for {}'.format(listing_hash))
            continue

        goo_value_in_cents = goo_value_in_gems / num_gems_per_sack_of_gems * sack_of_gems_price_in_cents

        current_listing = all_listings[listing_hash]
        ask_in_cents = current_listing['sell_price']

        if ask_in_cents == 0:
            # NB: The ask cannot be equal to zero. So, we skip the listing because of there must be a bug.
            if verbose:
                print('[!]\tImpossible ask price ({:.2f}€) for {}'.format(
                    ask_in_cents / 100,
                    listing_hash,
                ))
            continue

        profit_in_cents = goo_value_in_cents - ask_in_cents
        is_arbitrage = bool(profit_in_cents > 0)

        if is_arbitrage:
            arbitrage = dict()
            arbitrage['profit'] = profit_in_cents / 100
            arbitrage['ask'] = ask_in_cents / 100
            arbitrage['goo_amount'] = goo_value_in_gems
            arbitrage['goo_value'] = goo_value_in_cents / 100

            arbitrages[listing_hash] = arbitrage

    return arbitrages