Bid to Avg Position - no changes

212 views
Skip to first unread message

Nori Tauhidi

unread,
May 7, 2018, 11:16:49 PM5/7/18
to AdWords Scripts Forum
Hi,

I tried implementing a script that would allow for me to bid for keywords to stay at a specific position (2.5). Designated keywords that I wish the script to work on have all been labeled "Position 2.5"

I have put in the script and I can't seem to find any issues. However, when I click preview - it says no changes were made. When I run it, it has not seemed to be affecting any of my keywords. 

Am I implementing it wrong in some way? Is something wrong with the code?

Thanks in advance for your assistance!

The Script is below:

/**
*
* Average Position Bidding Tool
*
* This script changes keyword bids so that they target specified positions,
* based on recent performance.
*
* Version: 1.5
* Updated 2015-09-28 to correct for report column name changes
* Updated 2016-02-05 to correct label reading, add extra checks and
* be able to adjust maximum bid increases and decreases separately
* Updated 2016-08-30 to correct label reading from reports
* Updated 2016-09-14 to update keywords in batches
* Updated 2016-10-26 to avoid DriveApp bug
* Google AdWords Script maintained on brainlabsdigital.com
*
**/
   
// Options
   
var maxBid = 6.00;
// Bids will not be increased past this maximum.
   
var minBid = 0.20;
// Bids will not be decreased below this minimum.
   
var firstPageMaxBid = 0.90;
// The script avoids reducing a keyword's bid below its first page bid estimate. If you think
// Google's first page bid estimates are too high then use this to overrule them.
   
var dataFile = "AveragePositionData.txt";
// This name is used to create a file in your Google Drive to store today's performance so far,
// for reference the next time the script is run.
   
var useFirstPageBidsOnKeywordsWithNoImpressions = true;
// If this is true, then if a keyword has had no impressions since the last time the script was run
// its bid will be increased to the first page bid estimate (or the firsPageMaxBid if that is smaller).
// If this is false, keywords with no recent impressions will be left alone.
   
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
   
// Advanced Options
var bidIncreaseProportion = 0.4;
var bidDecreaseProportion = 0.4;
var targetPositionTolerance = 0.3;
   
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
   
function main() {
     
  var fieldJoin = ",";
  var lineJoin = "$";
  var idJoin = "#";
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  var files = DriveApp.getFilesByName(dataFile);
  if (!files.hasNext()) {
    var file = DriveApp.createFile(dataFile,"\n");
    Logger.log("File '" + dataFile + "' has been created.");
  } else {
    var file = files.next();
    if (files.hasNext()) {
      Logger.log("Error - more than one file named '" + dataFile + "'");
      return;
    }
    Logger.log("File '" + dataFile + "' has been read.");
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  var labelIds = [];
     
  var labelIterator = AdWordsApp.labels()
  .withCondition("KeywordsCount > 0")
  .withCondition("LabelName CONTAINS_IGNORE_CASE 'Position '")
  .get();
     
  while (labelIterator.hasNext()) {
    var label = labelIterator.next();
    if (label.getName().substr(0,"position ".length).toLowerCase() == "position ") {
      labelIds.push(label.getId());
    }
  }
     
  if (labelIds.length == 0) {
    Logger.log("No position labels found.");
    return;
  }
  Logger.log(labelIds.length + " position labels have been found.");
    
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  var keywordData = {
    //UniqueId1: {LastHour: {Impressions: , AveragePosition: }, ThisHour: {Impressions: , AveragePosition: },
    //CpcBid: , FirstPageCpc: , MaxBid, MinBid, FirstPageMaxBid, PositionTarget: , CurrentAveragePosition:,
    //Criteria: }
  }
     
  var ids = [];
  var uniqueIds = [];
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  var report = AdWordsApp.report(
    'SELECT Id, Criteria, AdGroupId, AdGroupName, CampaignName, Impressions, AveragePosition, CpcBid, FirstPageCpc, Labels, BiddingStrategyType ' +
    'FROM KEYWORDS_PERFORMANCE_REPORT ' +
    'WHERE Status = ENABLED AND AdGroupStatus = ENABLED AND CampaignStatus = ENABLED ' +
    'AND LabelIds CONTAINS_ANY [' + labelIds.join(",") + '] ' +
    'AND AdNetworkType2 = SEARCH ' +
      'AND Device NOT_IN ["HIGH_END_MOBILE"] ' +
        'DURING TODAY'
      );
     
  var rows = report.rows();
     
  while(rows.hasNext()){
    var row = rows.next();
       
    if (row["BiddingStrategyType"] != "cpc") {
      if (row["BiddingStrategyType"] == "Enhanced CPC"
          || row["BiddingStrategyType"] == "Target search page location"
          || row["BiddingStrategyType"] == "Target Outranking Share"
          || row["BiddingStrategyType"] == "None"
          || row["BiddingStrategyType"] == "unknown") {
        Logger.log("Warning: keyword " + row["Criteria"] + "' in campaign '" + row["CampaignName"] +
                   "' uses '" + row["BiddingStrategyType"] + "' rather than manual CPC. This may overrule keyword bids and interfere with the script working.");
      } else {
        Logger.log("Warning: keyword " + row["Criteria"] + "' in campaign '" + row["CampaignName"] +
                   "' uses the bidding strategy '" + row["BiddingStrategyType"] + "' rather than manual CPC. This keyword will be skipped.");
        continue;
      }
    }
       
    var positionTarget = "";
       
    if (row["Labels"].trim() == "--") {
      continue;
    }
    var labels = JSON.parse(row["Labels"].toLowerCase()); // Labels are returned as a JSON formatted string
       
    for (var i=0; i<labels.length; i++) {
      if (labels[i].substr(0,"position ".length) == "position ") {
        var positionTarget = parseFloat(labels[i].substr("position ".length-1).replace(/,/g,"."),10);
        break;
      }
    }
    if (positionTarget == "") {
      continue;
    }
    if (integrityCheck(positionTarget) == -1) {
      Logger.log("Invalid position target '" + positionTarget +  "' for keyword '" + row["Criteria"] + "' in campaign '" + row["CampaignName"] + "'");
      continue;
    }
       
    ids.push(parseFloat(row['Id'],10));
    var uniqueId = row['AdGroupId'] + idJoin + row['Id'];
    uniqueIds.push(uniqueId);
       
    keywordData[uniqueId] = {};
    keywordData[uniqueId]['Criteria'] = row['Criteria'];
    keywordData[uniqueId]['ThisHour'] = {};
       
    keywordData[uniqueId]['ThisHour']['Impressions'] = parseFloat(row['Impressions'].replace(/,/g,""),10);
    keywordData[uniqueId]['ThisHour']['AveragePosition'] = parseFloat(row['AveragePosition'].replace(/,/g,""),10);
       
    keywordData[uniqueId]['CpcBid'] = parseFloat(row['CpcBid'].replace(/,/g,""),10);
    keywordData[uniqueId]['FirstPageCpc'] = parseFloat(row['FirstPageCpc'].replace(/,/g,""),10);
       
    setPositionTargets(uniqueId, positionTarget);
  }
    
  Logger.log(uniqueIds.length + " labelled keywords found");
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  setBidChange();
  setMinMaxBids();
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  var currentHour = parseInt(Utilities.formatDate(new Date(), AdWordsApp.currentAccount().getTimeZone(), "HH"), 10);
     
  if (currentHour != 0) {
    var data = file.getBlob().getDataAsString();
    var data = data.split(lineJoin);
    for(var i = 0; i < data.length; i++){
      data[i] = data[i].split(fieldJoin);
      var uniqueId = data[i][0];
      if(keywordData.hasOwnProperty(uniqueId)){
        keywordData[uniqueId]['LastHour'] = {};
        keywordData[uniqueId]['LastHour']['Impressions'] = parseFloat(data[i][1],10);
        keywordData[uniqueId]['LastHour']['AveragePosition'] = parseFloat(data[i][2],10);
      }
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  findCurrentAveragePosition();
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  //Batch the keyword IDs, as the iterator can't take them all at once
  var idBatches = [];
  var batchSize = 5000;
  for (var i=0; i<uniqueIds.length; i += batchSize) {
    idBatches.push(uniqueIds.slice(i,i+batchSize));
  }
    
  Logger.log("Updating keywords");
     
  // Update each batch
  for (var i=0; i<idBatches.length; i++) {
    try {
      updateKeywords(idBatches[i]);
    } catch (e) {
      Logger.log("Error updating keywords: " + e);
      Logger.log("Retrying after one minute.");
      Utilities.sleep(60000);
      updateKeywords(idBatches[i]);
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  Logger.log("Writing file.");
  var content = resultsString();
  file.setContent(content);
    
  Logger.log("Finished.");
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  // Functions
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function integrityCheck(target){
    var n = parseFloat(target, 10);
    if(!isNaN(n) && n >= 1){
      return n;
    }
    else{
      return -1;
    }
       
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function setPositionTargets(uniqueId, target){
    if(target !== -1){
      keywordData[uniqueId]['HigherPositionTarget'] = Math.max(target-targetPositionTolerance, 1);
      keywordData[uniqueId]['LowerPositionTarget'] = target+targetPositionTolerance;
    }
    else{
      keywordData[uniqueId]['HigherPositionTarget'] = -1;
      keywordData[uniqueId]['LowerPositionTarget'] = -1;
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function bidChange(uniqueId){
       
    var newBid = -1;
    if(keywordData[uniqueId]['HigherPositionTarget'] === -1){
      return newBid;
    }
       
    var cpcBid = keywordData[uniqueId]['CpcBid'];
    var minBid = keywordData[uniqueId]['MinBid'];
    var maxBid = keywordData[uniqueId]['MaxBid'];
       
    if (isNaN(keywordData[uniqueId]['FirstPageCpc'])) {
      Logger.log("Warning: first page CPC estimate is not a number for keyword '" + keywordData[uniqueId]['Criteria'] + "'. This keyword will be skipped");
      return -1;
    }
       
    var firstPageBid = Math.min(keywordData[uniqueId]['FirstPageCpc'], keywordData[uniqueId]['FirstPageMaxBid'], maxBid);
       
    var currentPosition = keywordData[uniqueId]['CurrentAveragePosition'];
    var higherPositionTarget = keywordData[uniqueId]['HigherPositionTarget'];
    var lowerPositionTarget = keywordData[uniqueId]['LowerPositionTarget'];
       
    var bidIncrease = keywordData[uniqueId]['BidIncrease'];
    var bidDecrease = keywordData[uniqueId]['BidDecrease'];
       
    if((currentPosition > lowerPositionTarget) && (currentPosition !== 0)){
      var linearBidModel = Math.min(2*bidIncrease,(2*bidIncrease/lowerPositionTarget)*(currentPosition-lowerPositionTarget));
      var newBid = Math.min((cpcBid + linearBidModel), maxBid);
    }
    if((currentPosition < higherPositionTarget) && (currentPosition !== 0)) {
      var linearBidModel = Math.min(2*bidDecrease,((-4)*bidDecrease/higherPositionTarget)*(currentPosition-higherPositionTarget));
      var newBid = Math.max((cpcBid-linearBidModel),minBid);
      if (cpcBid > firstPageBid) {
        var newBid = Math.max(firstPageBid,newBid);
      }
    }
    if((currentPosition === 0) && useFirstPageBidsOnKeywordsWithNoImpressions && (cpcBid < firstPageBid)){
      var newBid = firstPageBid;
    }
       
    if (isNaN(newBid)) {
      Logger.log("Warning: new bid is not a number for keyword '" + keywordData[uniqueId]['Criteria'] + "'. This keyword will be skipped");
      return -1;
    }
       
    return newBid;
       
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function findCurrentAveragePosition(){
    for(var x in keywordData){
      if(keywordData[x].hasOwnProperty('LastHour')){
        keywordData[x]['CurrentAveragePosition'] = calculateAveragePosition(keywordData[x]);
      } else {
        keywordData[x]['CurrentAveragePosition'] = keywordData[x]['ThisHour']['AveragePosition'];
      }
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function calculateAveragePosition(keywordDataElement){
    var lastHourImpressions = keywordDataElement['LastHour']['Impressions'];
    var lastHourAveragePosition = keywordDataElement['LastHour']['AveragePosition'];
       
    var thisHourImpressions = keywordDataElement['ThisHour']['Impressions'];
    var thisHourAveragePosition = keywordDataElement['ThisHour']['AveragePosition'];
       
    if(thisHourImpressions == lastHourImpressions){
      return 0;
    }
    else{
      var currentPosition = (thisHourImpressions*thisHourAveragePosition-lastHourImpressions*lastHourAveragePosition)/(thisHourImpressions-lastHourImpressions);
      if (currentPosition < 1) {
        return 0;
      } else {
        return currentPosition;
      }
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function keywordUniqueId(keyword){
    var id = keyword.getId();
    var idsIndex = ids.indexOf(id);
    if(idsIndex === ids.lastIndexOf(id)){
      return uniqueIds[idsIndex];
    }
    else{
      var adGroupId = keyword.getAdGroup().getId();
      return adGroupId + idJoin + id;
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function setMinMaxBids(){
    for(var x in keywordData){
      keywordData[x]['MinBid'] = minBid;
      keywordData[x]['MaxBid'] = maxBid;
      keywordData[x]['FirstPageMaxBid'] = firstPageMaxBid;
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function setBidChange(){
    for(var x in keywordData){
      keywordData[x]['BidIncrease'] = keywordData[x]['CpcBid'] * bidIncreaseProportion/2;
      keywordData[x]['BidDecrease'] = keywordData[x]['CpcBid'] * bidDecreaseProportion/2;
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function updateKeywords(idBatch) {
    var keywordIterator = AdWordsApp.keywords()
    .withIds(idBatch.map(function(str){return str.split(idJoin);}))
    .get();
    while(keywordIterator.hasNext()){
      var keyword = keywordIterator.next();
         
      var uniqueId = keywordUniqueId(keyword);
         
      var newBid = bidChange(uniqueId);
         
      if(newBid !== -1){
        keyword.setMaxCpc(newBid);
      }
         
    }
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
  function resultsString(){
       
    var results = [];
    for(var uniqueId in keywordData){
      var resultsRow = [uniqueId, keywordData[uniqueId]['ThisHour']['Impressions'], keywordData[uniqueId]['ThisHour']['AveragePosition']];
      results.push(resultsRow.join(fieldJoin));
    }
       
    return results.join(lineJoin);
  }
     
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
     
}

Adrian Catambay (AdWords Scripts Team)

unread,
May 8, 2018, 2:18:47 AM5/8/18
to AdWords Scripts Forum
Hello Nori,

Please note that no changes will be applied when a script is ran using the Preview button. Also, according to the 3rd-party script's page, if the script runs and then stops without performing any changes, then you should try running the script using the Preview button and click on Logs to see if there are any messages that explain what happened.

Thanks,
Adrian
AdWords Scripts Team

Nori Tauhidi

unread,
May 8, 2018, 4:44:26 PM5/8/18
to AdWords Scripts Forum
Hi Adrian,

Thanks for the speedy response. 

In regards to your response " Also, according to the 3rd-party script's page, if the script runs and then stops without performing any changes, then you should try running the script using the Preview button and click on Logs to see if there are any messages that explain what happened."

When I run the Preview it says "no changes" - when I click over to Logs it displays this:

File 'AveragePositionData.txt' has been read.
1 position labels have been found.
42 labelled keywords found
Updating keywords
Writing file.
Finished.

It gives no clear information as to why no change were made. It recognizes that the keywords are labeled, it says updating keywords. Why then are no changes made in the preview to display potential bidding changes for these keywords?

Thanks so much!
Nori Tauhidi

Adrian Catambay (AdWords Scripts Team)

unread,
May 9, 2018, 12:29:12 AM5/9/18
to AdWords Scripts Forum
Hello Nori,

Could you please Reply privately to author with your CID and script name so I can further investigate the issue?

Fabio

unread,
Jul 9, 2018, 6:43:31 AM7/9/18
to AdWords Scripts Forum
Hi, I'm using teh same script and I have the same problem. Sometimes in preview mode it show some changes (7-8) but if I run it It doesn't change anything or change just 1 bid.

Fabio

Sravani Yelamarthi (AdWords Scripts Team)

unread,
Jul 9, 2018, 3:36:30 PM7/9/18
to AdWords Scripts Forum
Hi Fabio,

Can you please reply privately to author with the script name and CID? I will have a look in your account, but I would also suggest reaching out to the author of this third party script, since they will be better equipped to answer any questions.

Thanks,
Sravani Yelamarthi
AdWords Scripts Team

Austin Pena

unread,
Jul 10, 2018, 6:57:34 AM7/10/18
to AdWords Scripts Forum
Is there a solution found? I have the same issue.

Anthony Madrigal

unread,
Jul 13, 2018, 3:10:04 PM7/13/18
to AdWords Scripts Forum
Hi Austin,

I can have a look at your script. Can you please reply privately to author your CID and script name so I can have further investigate?

Thanks,
Anthony
AdWords Scripts Team

Vachik Ghadimian

unread,
Feb 11, 2019, 6:03:47 AM2/11/19
to Google Ads Scripts Forum
Hello,
I am having the same issue with the script. I have set the variables as my needs. I am running CPC bidding, I have set the proper labels and the script runs to completion without throwing any exceptions/errors. When I look in the changes tab, there are 0 changes.

googleadsscrip...@google.com

unread,
Feb 11, 2019, 9:35:27 AM2/11/19
to Vachik Ghadimian via Google Ads Scripts Forum, Google Ads Scripts Forum
Hi Vachik,

Since this is an old thread, can you please create a new one detailing your issues?

Regards,
Anthony
Google Ads Scripts Team


=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
Also find us on our blog and discussion group:
    http://googleadsdeveloper.blogspot.com/search/label/adwords_scripts
    https://developers.google.com/google-ads/scripts/community/
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
Reply all
Reply to author
Forward
0 new messages