Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🩹 improvement on PPU (#506) #520

Merged
merged 34 commits into from
Apr 4, 2021
Merged

🩹 improvement on PPU (#506) #520

merged 34 commits into from
Apr 4, 2021

Conversation

idinium96
Copy link
Member

@idinium96 idinium96 commented Mar 28, 2021

Closes #506

The old logics: #337

The new logics:

New pricelist entry key (or parameter):

New options added:

  • sendAlert.partialPrice.onResetAfterThreshold (default is true).
  • sendAlert.partialPrice.onBulkUpdatePartialPriced (default is true).

General Information

  • The new logic will not update the current buying price if stock exists (and maximum only set to 1).
  • Since the bot does not get the last bought price, setting the current buying price to static will ensure that we never lose any profit.
  • The bot will update only the selling price if the new selling price is more than the current static buying price OR the new selling price is more than the current selling price, but if these two conditions are not met, the new selling price will be the current static buying price + 0.11 ref (minimum profit).

Precautions ⚠️

  • Since the buying price is kept static, you might receive an offer for review with an OVERSTOCKED reason with their side value is MORE than our side value.
    • Yes, even if you've set offerReceived.overstocked.autoAcceptOverpay to true, the bot will not automatically accept it if isPartialPriced parameter is true (meaning partial price is still active on that particular item).
    • This is to prevent your bot from buying items with an outdated buying price.
    • Please check the latest price of that item.

image

Codes

A. On price change/update old prices

if (match !== null && match.autoprice) {
const oldPrice = {
buy: new Currencies(match.buy),
sell: new Currencies(match.sell)
};
let pricesChanged = false;
const currentStock = this.bot.inventoryManager.getInventory.getAmount(match.sku, true);
const ppu = opt.pricelist.partialPriceUpdate;
const isInStock = currentStock > 0;
const isNotExceedThreshold = data.time - match.time < ppu.thresholdInSeconds;
const isNotExcluded = !['5021;6'].concat(ppu.excludeSKU).includes(match.sku);
const maxIsOne = match.max === 1;
if (ppu.enable) {
log.debug('ppu status - onHandlePriceChange', {
sku: match.sku,
inStock: isInStock,
notExceed: isNotExceedThreshold,
notExclude: isNotExcluded
});
}
// https://github.com/TF2Autobot/tf2autobot/issues/506
// https://github.com/TF2Autobot/tf2autobot/pull/520
if (ppu.enable && isInStock && isNotExceedThreshold && isNotExcluded && maxIsOne) {
const keyPrice = this.getKeyPrice.metal;
const newBuyValue = newPrices.buy.toValue(keyPrice);
const newSellValue = newPrices.sell.toValue(keyPrice);
// TODO: Use last bought prices instead of current buying prices
const currBuyingValue = match.buy.toValue(keyPrice);
const currSellingValue = match.sell.toValue(keyPrice);
const isNegativeDiff = newSellValue - currBuyingValue <= 0;
const isBuyingChanged = currBuyingValue !== newBuyValue;
log.debug('ppu', {
newBuyValue: newBuyValue,
newSellValue: newSellValue,
currBuyingValue: currBuyingValue,
currSellingValue: currSellingValue,
isNegativeDiff: isNegativeDiff,
isBuyingChanged: isBuyingChanged,
isAlreadyPartialPriced: match.isPartialPriced
});
if (match.isPartialPriced || isNegativeDiff || isBuyingChanged) {
if (newSellValue > currBuyingValue || newSellValue > currSellingValue) {
log.debug('ppu - update selling price with the latest price');
match.sell = newPrices.sell;
} else {
log.debug('ppu - update selling price with minimum profit of 1 scrap');
match.sell = Currencies.toCurrencies(currBuyingValue + 1, keyPrice);
}
match.isPartialPriced = true;
pricesChanged = true;
const msg = this.generatePartialPriceUpdateMsg(oldPrice, match, newPrices);
if (opt.sendAlert.partialPrice.onUpdate) {
if (this.isDwAlertEnabled) {
sendAlert('isPartialPriced', this.bot, msg);
} else {
this.bot.messageAdmins('Partial price update\n\n' + msg, []);
}
}
} else {
if (!match.isPartialPriced) {
match.buy = newPrices.buy;
match.sell = newPrices.sell;
match.time = data.time;
pricesChanged = true;
}
}
} else {
if (!match.isPartialPriced || (match.isPartialPriced && !(isNotExceedThreshold || isInStock))) {
match.buy = newPrices.buy;
match.sell = newPrices.sell;
match.time = data.time;
if (match.isPartialPriced) {
match.isPartialPriced = false; // reset to default
sendAlert('autoResetPartialPrice', this.bot);
}
pricesChanged = true;
}
}
if (pricesChanged) {
this.priceChanged(match.sku, match);
}

B. On item sold out

const isUpdatePartialPricedItem =
inPrice !== null &&
inPrice.autoprice &&
inPrice.isPartialPriced &&
bot.inventoryManager.getInventory.getAmount(sku, true) < 1 && // current stock
isNotPureOrWeapons;

} else if (isUpdatePartialPricedItem) {
// If item exist in pricelist with "isPartialPriced" set to true and we no longer have that in stock,
// then update entry with the latest prices.
const oldPrice = {
buy: new Currencies(inPrice.buy),
sell: new Currencies(inPrice.sell)
};
const oldTime = inPrice.time;
const entry = {
sku: sku,
enabled: inPrice.enabled,
autoprice: true,
min: inPrice.min,
max: inPrice.max,
intent: inPrice.intent,
group: inPrice.group,
isPartialPriced: false
} as EntryData;
bot.pricelist
.updatePrice(entry, true)
.then(data => {
const msg =
`${dwEnabled ? `[${name}](https://www.prices.tf/items/${sku})` : name} (${sku})\n▸ ` +
[
`old: ${oldPrice.buy.toString()}/${oldPrice.sell.toString()}`,
`new: ${data.buy.toString()}/${data.sell.toString()}`
].join('\n▸ ') +
`\n - Partial priced since ${dayjs.unix(oldTime).fromNow()}` +
`\n - Current prices last update: ${dayjs.unix(data.time).fromNow()}`;
log.debug(msg);
if (opt.sendAlert.enable && opt.sendAlert.partialPrice.onSuccessUpdatePartialPriced) {
if (dwEnabled) {
sendAlert('autoUpdatePartialPriceSuccess', bot, msg);
} else {
bot.messageAdmins('✅ Automatically update partially priced item - ' + msg, []);
}
}
})
.catch(err => {
const msg = `❌ Failed to update prices for ${name} (${sku}): ${(err as Error).message}`;
log.warn(msg);
if (opt.sendAlert.enable && opt.sendAlert.partialPrice.onFailedUpdatePartialPriced) {
if (dwEnabled) {
sendAlert('autoUpdatePartialPriceFailed', bot, msg);
} else {
bot.messageAdmins(msg, []);
}
}
});

Codes crackdown:

  • A. On price change/update old prices
    • (1) The bot will first determine if this option is enabled, and that particular item is (i) currently in stock, (ii) does not exceed the time threshold, (iii) is not excluded item(s), AND (iv) the max is only set to 1:

      const ppu = opt.pricelist.partialPriceUpdate;
      const isInStock = currentStock > 0;
      const isNotExceedThreshold = data.time - match.time < ppu.thresholdInSeconds;
      const isNotExcluded = !['5021;6'].concat(ppu.excludeSKU).includes(match.sku);
      const maxIsOne = match.max === 1;

      if (ppu.enable && isInStock && isNotExceedThreshold && isNotExcluded && maxIsOne) {

      • (1.1) if (1) is true, the bot will then determine if (i) the item already has isPartialPriced set to true, (ii) the difference between the new selling price and the current buying price is negative or zero, OR (iii) the buying price changed:

        const isNegativeDiff = newSellValue - currBuyingValue <= 0;
        const isBuyingChanged = currBuyingValue !== newBuyValue;

        if (match.isPartialPriced || isNegativeDiff || isBuyingChanged) {

        • (1.1.1) if (1.1) is true, then the bot will check if (i) the new selling price is more than the current buying price, OR (ii) the new selling price is more than the current selling price. If true, then we update ONLY the selling price.
          if (newSellValue > currBuyingValue || newSellValue > currSellingValue) {
          log.debug('ppu - update selling price with the latest price');
          match.sell = newPrices.sell;
        • (1.1.2) else if (1.1.1) is false, the bot will update the selling price with an addition of 1 scrap from the buying price (the minimum profit).
          } else {
          log.debug('ppu - update selling price with minimum profit of 1 scrap');
          match.sell = Currencies.toCurrencies(currBuyingValue + 1, keyPrice);
          }
      • (1.2) else if (1.1) is false, if the item isPartialPrice is false, the bot will update the prices as usual.

        } else {
        if (!match.isPartialPriced) {
        match.buy = newPrices.buy;
        match.sell = newPrices.sell;
        match.time = data.time;
        pricesChanged = true;
        }

    • (2) else if (1) is false, the bot will first check if that particular item's isPartialPriced is false, OR if it's true, then check if it's already exceeded the time threshold OR no longer in stock. If everything true, the bot will update the prices as usual and reset isPartialPriced to false.

      } else {
      if (!match.isPartialPriced || (match.isPartialPriced && !(isNotExceedThreshold || isInStock))) {
      match.buy = newPrices.buy;
      match.sell = newPrices.sell;
      match.time = data.time;
      if (match.isPartialPriced) {
      match.isPartialPriced = false; // reset to default
      sendAlert('autoResetPartialPrice', this.bot);
      }
      pricesChanged = true;
      }
      }

B. On item sold out
- When that particular item is sold out, the bot will update the buying and selling prices to the latest prices.tf or custom pricer, and isPartialPriced parameter will be reset to false.

Some screenshots:

1

  • The bot bought for 18.66 ref:
    image

  • The PPU in action:
    image

  • Loss prevented by selling it at 18.77 ref instead of the current selling price of 16.22 ref:
    image

2 (updated)

image

image

image

@idinium96
Copy link
Member Author

image

@idinium96 idinium96 linked an issue Mar 28, 2021 that may be closed by this pull request
@idinium96 idinium96 merged commit 125d837 into development Apr 4, 2021
@idinium96 idinium96 mentioned this pull request Apr 4, 2021
idinium96 added a commit that referenced this pull request Apr 6, 2021
#548

## Improvements/Major Changes
- 🩹 improvement on PPU - closes issue #506 (read #520)
    - once updated to v3.8.0, your pricelist.json is no longer usable on the lower versions.
    - only effective on newly bought items.

## Added
- ✨ an option to show detailed time taken (#549)
    - new options: `tradeSummary.showDetailedTimeTaken` (default is `true`).

## Changed
- 🔨 include the old group name in high-value disabled item notification (#544)

## Fixes
- 🔨 fixed bot crash if enableSocket set to false (#543)
- 🐛 possible fix negative profit when accepting invalid items (#545, a344d0d)
- 🔨 fixed catch the wrong error while updating `oldPrices` (mostly if use custom pricer) (#547)
@idinium96 idinium96 deleted the issue-506 branch April 7, 2021 01:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve partialPriceUpdate while keeping work to a minimum
1 participant