Skip to content

Commit

Permalink
Merge pull request #13 from jack-richards/development
Browse files Browse the repository at this point in the history
Add Option to Fallback Onto Prices.tf
  • Loading branch information
jack-richards authored Apr 27, 2024
2 parents 52e6c76 + f14f80a commit 9c52c54
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 90 deletions.
220 changes: 130 additions & 90 deletions bptf-autopricer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const blockedAttributes = config.blockedAttributes;

const alwaysQuerySnapshotAPI = config.alwaysQuerySnapshotAPI;

const fallbackOntoPricesTf = config.fallbackOntoPricesTf;

// Create database instance for pg-promise.
const pgp = require('pg-promise')({
schema: config.database.schema
Expand Down Expand Up @@ -367,6 +369,10 @@ const insertListing = async (response_item, sku, currencies, intent, steamid) =>
};

const insertListings = async (unformattedListings, sku, name) => {
// If there are no listings from the snapshot just return. No update can be done.
if (!unformattedListings) {
throw new Error(`No listings found for ${name} in the snapshot. The name: '${name}' likely doesn't match the version on the items bptf listings page.`);
}
try {
let formattedListings = [];
const uniqueSet = new Set(); // Create a set to store unique combinations
Expand Down Expand Up @@ -486,13 +492,50 @@ const determinePrice = async (name, sku) => {
var buyListings = await getListings(name, 'buy');
var sellListings = await getListings(name, 'sell');

// Check for undefined.
if (!buyListings || !sellListings) {
throw new Error(`| UPDATING PRICES |: ${name} not enough listings...`);

// Get the price of the item from the in-memory external pricelist.
var data;
try {
data = Methods.getItemPriceFromExternalPricelist(sku, external_pricelist);
} catch (e) {
throw new Error(`| UPDATING PRICES |: Couldn't price ${name}. Issue with Price.tf.`);
}

if (buyListings.rowCount === 0 || sellListings.rowCount === 0) {
throw new Error(`| UPDATING PRICES |: ${name} not enough listings...`);
var pricetfItem = data.pricetfItem;

if (
(pricetfItem.buy.keys === 0 && pricetfItem.buy.metal === 0) ||
(pricetfItem.sell.keys === 0 && pricetfItem.sell.metal === 0)
) {
throw new Error(`| UPDATING PRICES |: Couldn't price ${name}. Item is not priced on price.tf, therefore we can't
compare our average price to it's average price.`);
}


try {
// Check for undefined. No listings.
if (!buyListings || !sellListings) {
throw new Error(`| UPDATING PRICES |: ${name} not enough listings...`);
}

if (buyListings.rowCount === 0 || sellListings.rowCount === 0) {
throw new Error(`| UPDATING PRICES |: ${name} not enough listings...`);
}
} catch (e) {
if(fallbackOntoPricesTf) {
const final_buyObj = {
keys: pricetfItem.buy.keys,
metal: pricetfItem.buy.metal
};
const final_sellObj = {
keys: pricetfItem.sell.keys,
metal: pricetfItem.sell.metal
};
// Return price.tf price.
return [final_buyObj, final_sellObj];
}
// If we don't fallback onto prices.tf, re-throw the error.
throw e;
}

// Sort buyListings into descending order of price.
Expand Down Expand Up @@ -546,7 +589,7 @@ const determinePrice = async (name, sku) => {
});

try {
let arr = getAverages(name, buyFiltered, sellFiltered, sku);
let arr = getAverages(name, buyFiltered, sellFiltered, sku, pricetfItem);
return arr;
} catch (e) {
throw new Error(e);
Expand Down Expand Up @@ -590,7 +633,7 @@ const filterOutliers = listingsArray => {
return filteredMean;
};

const getAverages = (name, buyFiltered, sellFiltered, sku) => {
const getAverages = (name, buyFiltered, sellFiltered, sku, pricetfItem) => {
// Initialse two objects to contain the items final buy and sell prices.
var final_buyObj = {
keys: 0,
Expand All @@ -601,95 +644,92 @@ const getAverages = (name, buyFiltered, sellFiltered, sku) => {
metal: 0
};

// Get the price of the item from the in-memory external pricelist.
var data;
try {
data = Methods.getItemPriceFromExternalPricelist(sku, external_pricelist);
} catch (e) {
throw new Error(`| UPDATING PRICES |: Couldn't price ${name}. Issue with Price.tf.`);
}

var pricetfItem = data.pricetfItem;

if (
(pricetfItem.buy.keys === 0 && pricetfItem.buy.metal === 0) ||
(pricetfItem.sell.keys === 0 && pricetfItem.sell.metal === 0)
) {
throw new Error(`| UPDATING PRICES |: Couldn't price ${name}. Item is not priced on price.tf, therefore we can't
compare our average price to it's average price.`);
}

if (buyFiltered.length < 3) {
throw new Error(`| UPDATING PRICES |: ${name} not enough buy listings...`);
} else if (buyFiltered.length > 3 && buyFiltered.length < 10) {
var totalValue = {
keys: 0,
metal: 0
};
for (var i = 0; i <= 2; i++) {
totalValue.keys += Object.is(buyFiltered[i].currencies.keys, undefined) ?
if (buyFiltered.length < 3) {
throw new Error(`| UPDATING PRICES |: ${name} not enough buy listings...`);
} else if (buyFiltered.length > 3 && buyFiltered.length < 10) {
var totalValue = {
keys: 0,
metal: 0
};
for (var i = 0; i <= 2; i++) {
totalValue.keys += Object.is(buyFiltered[i].currencies.keys, undefined) ?
0 :
buyFiltered[i].currencies.keys;
totalValue.metal += Object.is(buyFiltered[i].currencies.metal, undefined) ?
0 :
buyFiltered[i].currencies.metal;
}
final_buyObj = {
keys: Math.trunc(totalValue.keys / i),
metal: totalValue.metal / i
};
} else {
// Filter out outliers from set, and calculate a mean average price in terms of metal value.
let filteredMean = filterOutliers(buyFiltered);
// Caclulate the maximum amount of keys that can be made with the metal value returned.
let keys = Math.trunc(filteredMean / keyobj.metal);
// Calculate the remaining metal value after the value of the keys has been removed.
let metal = Methods.getRight(filteredMean - keys * keyobj.metal);
// Create the final buy object.
final_buyObj = {
keys: keys,
metal: metal
};
}
// Decided to pick the very first sell listing as it's ordered by the lowest sell price. I.e., the most competitive.
// However, I decided to prioritise 'trusted' listings by certain steamids. This may result in a very high sell price, instead
// of a competitive one.
if (sellFiltered.length > 0) {
final_sellObj.keys = Object.is(sellFiltered[0].currencies.keys, undefined) ?
0 :
buyFiltered[i].currencies.keys;
totalValue.metal += Object.is(buyFiltered[i].currencies.metal, undefined) ?
sellFiltered[0].currencies.keys;
final_sellObj.metal = Object.is(sellFiltered[0].currencies.metal, undefined) ?
0 :
buyFiltered[i].currencies.metal;
sellFiltered[0].currencies.metal;
} else {
throw new Error(`| UPDATING PRICES |: ${name} not enough sell listings...`); // Not enough
}
final_buyObj = {
keys: Math.trunc(totalValue.keys / i),
metal: totalValue.metal / i
};
} else {
// Filter out outliers from set, and calculate a mean average price in terms of metal value.
let filteredMean = filterOutliers(buyFiltered);
// Caclulate the maximum amount of keys that can be made with the metal value returned.
let keys = Math.trunc(filteredMean / keyobj.metal);
// Calculate the remaining metal value after the value of the keys has been removed.
let metal = Methods.getRight(filteredMean - keys * keyobj.metal);
// Create the final buy object.
final_buyObj = {
keys: keys,
metal: metal
};
}
// Decided to pick the very first sell listing as it's ordered by the lowest sell price. I.e., the most competitive.
// However, I decided to prioritise 'trusted' listings by certain steamids. This may result in a very high sell price, instead
// of a competitive one.
if (sellFiltered.length > 0) {
final_sellObj.keys = Object.is(sellFiltered[0].currencies.keys, undefined) ?
0 :
sellFiltered[0].currencies.keys;
final_sellObj.metal = Object.is(sellFiltered[0].currencies.metal, undefined) ?
0 :
sellFiltered[0].currencies.metal;
} else {
throw new Error(`| UPDATING PRICES |: ${name} not enough sell listings...`); // Not enough
}

var usePrices = false;
try {
// Will return true or false. True if we are ok with the autopricers price, false if we are not.
// We use prices.tf as a baseline.
usePrices = Methods.calculatePricingAPIDifferences(pricetfItem, final_buyObj, final_sellObj, keyobj);
} catch (e) {
// Will create a log of this difference.
console.log(`Our autopricer determined that name ${name} should sell for : ${final_sellObj.keys} keys and
${final_sellObj.metal} ref, and buy for ${final_buyObj.keys} keys and ${final_buyObj.metal} ref. Prices.tf
determined I should sell for ${pricetfItem.sell.keys} keys and ${pricetfItem.sell.metal} ref, and buy for
${pricetfItem.buy.keys} keys and ${pricetfItem.buy.metal} ref. Message returned by the method: ${e.message}`);

throw new Error(`| UPDATING PRICES |: ${name} pricing average generated by autopricer is too dramatically
different to one returned by prices.tf`);
}
var usePrices = false;
try {
// Will return true or false. True if we are ok with the autopricers price, false if we are not.
// We use prices.tf as a baseline.
usePrices = Methods.calculatePricingAPIDifferences(pricetfItem, final_buyObj, final_sellObj, keyobj);
} catch (e) {
// Create an error object with a message detailing this difference.
throw new Error(`| UPDATING PRICES |: Our autopricer determined that name ${name} should sell for : ${final_sellObj.keys} keys and
${final_sellObj.metal} ref, and buy for ${final_buyObj.keys} keys and ${final_buyObj.metal} ref. Prices.tf
determined I should sell for ${pricetfItem.sell.keys} keys and ${pricetfItem.sell.metal} ref, and buy for
${pricetfItem.buy.keys} keys and ${pricetfItem.buy.metal} ref. Message returned by the method: ${e.message}`);
}

// if-else statement probably isn't needed, but I'm just being cautious.
if (usePrices) {
// The final averages are returned here. But work is still needed to be done. We can't assume that the buy average is
// going to be lower than the sell average price. So we need to check for this later.
return [final_buyObj, final_sellObj];
} else {
throw new Error(`| UPDATING PRICES |: ${name} pricing average generated by autopricer is too dramatically
different to one returned by prices.tf`);
}
// if-else statement probably isn't needed, but I'm just being cautious.
if (usePrices) {
// The final averages are returned here. But work is still needed to be done. We can't assume that the buy average is
// going to be lower than the sell average price. So we need to check for this later.
return [final_buyObj, final_sellObj];
} else {
throw new Error(`| UPDATING PRICES |: ${name} pricing average generated by autopricer is too dramatically
different to one returned by prices.tf`);
}
} catch (error) {
// If configured, we fallback onto prices.tf for the price.
if(fallbackOntoPricesTf) {
const final_buyObj = {
keys: pricetfItem.buy.keys,
metal: pricetfItem.buy.metal
};
const final_sellObj = {
keys: pricetfItem.sell.keys,
metal: pricetfItem.sell.metal
};
return [final_buyObj, final_sellObj];
} else {
// We rethrow the error.
throw error;
}
};
};

const finalisePrice = (arr, name, sku) => {
Expand Down
1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"sell": -8
},
"alwaysQuerySnapshotAPI": true,
"fallbackOntoPricesTf": false,
"excludedSteamIDs": [
"76561199384015307",
"76561199495073910",
Expand Down

0 comments on commit 9c52c54

Please sign in to comment.