AdWords
2.5K members online now
2.5K members online now
Dive into advanced features like Remarketing, Flexible Bid Strategies, AdWords Editor, and AdWords Scripts
Guide Me
star_border
Reply

Bidding script for PLA campaigns

Visitor ✭ ✭ ✭
# 1
Visitor ✭ ✭ ✭

Hi

 

I'd like to write script that would modify max cpc in pla campaigns:

max_product_cpc=((product price/1.23)*all-time conversion rate )*certain digit

 

Unfortunately i don't know what to do, because I can't code. 

Do you know how to write such a script?

2 Expert replyverified_user

Re: Bidding script for PLA campaigns

[ Edited ]
Top Contributor
# 2
Top Contributor

Hi @Rafał K,

 

If you lack coding skills, you may want to consider the following options.

 

A super granular PLA structure - where each product has its own ad group - allows you to bid on the ad group level which in turn will enable you to create automated rules - as well as scripts. In other respects regarding automated rules, you are advised to read this fundamental article authored by @Spike-Patching:

 

***   Save Your Time & Money with AdWords Automated Rules   ***

https://www.en.advertisercommunity.com/t5/Articles/Save-Your-Time-amp-Money-with-AdWords-Automated-R...

 

Best,

Lakatos

Re: Bidding script for PLA campaigns

Top Contributor
# 3
Top Contributor

Hi @Rafał K this is a script that I use (see bottom of post).  It doesn't use the formula you posted, but instead optimises for ROAS.  The settings at the top allow modifications, the most important being whether you want to simply increase/decrease bids by a percentage, or change the bid entirely to match a desired ROAS target.  Feel free to play with it and modify as necessary.  Please make sure you PREVIEW the script before running, to check what it's doing matches your requirements, then run once a day.

 

// Shopping bids update (c) Jon Gritton 2016

var MAXCPC = 1.70; // bids will never exceed this figure var MINCPC = 0.20; // bids will never go below this figure var PERINC = 1.03; // increases bids by this percentage (1.03 = 3%) var MONDEC = 0.02; // decreases bids by this amount var ROASTAR = 10.0; // target ROAS var DURING = 30; // lookback time for counting conversions/ROAS var ROASABS = true; // if true, bids are calculated to attempt to hit the ROAS, if false, bids are simply raised and lowered gradually over time var ITEMSPROC = 0; // a simple counter to show how many items have been inspected, shown in the log output function main() { var dateTimes = getDates(DURING); var campIter = getCampaigns(); while (campIter.hasNext()) { var thisCamp = campIter.next(); Logger.log("*** " + thisCamp.getName()); var groupIter = thisCamp.adGroups() .withCondition("AdGroupStatus = 'ENABLED'") .get(); while (groupIter.hasNext() ) { var thisGroup = groupIter.next(); Logger.log("** " + thisGroup.getName()); var root = thisGroup.rootProductGroup(); walkHierarchy(root, 0, dateTimes.now, dateTimes.then,thisCamp.getName(),thisGroup.getName()); } } } function getCampaigns() { var campIter = AdWordsApp.shoppingCampaigns() .withCondition("Status = ENABLED") .orderBy("Cost DESC") .forDateRange("LAST_7_DAYS") .get(); return campIter; } function walkHierarchy(productGroup, level, now, then, campName, grpName) { var description = ''; if (productGroup.isOtherCase()) { description = 'Other'; } else if (productGroup.getDimension() == 'CATEGORY') { // Shows how to process a product group differently based on its type. description = productGroup.asCategory().getName(); } else { description = productGroup.getValue(); } var thisDimension = productGroup.getDimension(); if(thisDimension = "ITEM_ID"){ var currentCPC = productGroup.getMaxCpc(); var newCPC = currentCPC; var d = new Date(); var thisConversions = productGroup.getStatsFor(then, now).getConversions(); if(thisConversions > 0 && level ==2 && description != "Other") { var perfReport = getSIStats(now,then,campName,grpName,description); var thisCost = perfReport['Cost']; var thisValue = perfReport['AllConversionValue']; var thisAvCPC = perfReport['AverageCpc']; var thisSIS = parseFloat(perfReport['SIS']); var ROAS = (thisValue.replace(",","") / thisCost.replace(",","")).toFixed(2); if(!ROASABS) { // alter CPCs gradually if(ROAS <= ROASTAR) { if (ROAS >= (ROASTAR*0.8)) { newCPC = ((currentCPC - MONDEC) > (MINCPC*1.5))? currentCPC - MONDEC : MINCPC*1.5; } else { newCPC = currentCPC - MONDEC; } } if(ROAS > (ROASTAR*1.3)) { //without any other conditions, increase bid newCPC = currentCPC * PERINC; if((newCPC - currentCPC) < 0.02) { newCPC = currentCPC + 0.02; } } } else { // calculate CPC to hit ROAS exactly newCPC = (ROAS / ROASTAR) * thisAvCPC; // factor for impression share if(thisSIS < 90 && ((ROAS / ROASTAR) > 2)) { // only boost for SIS if (well) over ROASTAR newCPC *= 90 / thisSIS; } } } } else if(level > 0 && thisConversions == 0 && description !="Other") { newCPC = currentCPC - MONDEC; } // check for absolute limits and log if(newCPC > MAXCPC) newCPC = MAXCPC; if(newCPC < MINCPC) newCPC = MINCPC; newCPC = newCPC.toFixed(2); if(thisConversions > 0 && level==2 && currentCPC != newCPC) { Logger.log('Desc: %s, ItemsProc: %s, Conversions: %s, Cost: %s, Value: %s, ROAS: %s, SIS: %s, Curr.CPC: %s, Av.CPC: %s, New CPC: %s', description, ITEMSPROC, thisConversions, thisCost, thisValue, ROAS, thisSIS, currentCPC, thisAvCPC, newCPC ); } try { productGroup.setMaxCpc(newCPC); } catch(err) { Logger.log("Error: " + err); } // Note: Child product groups may not have a max cpc if it is excluded. var childProductGroups = productGroup.children().get(); while (childProductGroups.hasNext()) { ITEMSPROC++; var childProductGroup = childProductGroups.next(); walkHierarchy(childProductGroup, level + 1, now, then, campName, grpName); } } function getSIStats(now,then,campaignName,adGName,id) { var SIS = "NotFound"; var report = AdWordsApp.report( "SELECT ProductGroup, Cost, AllConversions, AllConversionValue, AverageCpc, SearchImpressionShare " + "FROM PRODUCT_PARTITION_REPORT " + "WHERE CampaignName = '" + campaignName + "' AND AdGroupName = '" + adGName + "' AND ProductGroup CONTAINS '" + id + "' " + "DURING " + then + "," + now); var rows = report.rows(); while(rows.hasNext()) { var thisRow = rows.next(); if(thisRow['SearchImpressionShare'] == '--') { var start = 13; var end = thisRow['ProductGroup'].indexOf("\"", start); var brand = thisRow['ProductGroup'].substring(start,end); var reportG = AdWordsApp.report( "SELECT SearchImpressionShare " + "FROM PRODUCT_PARTITION_REPORT " + "WHERE CampaignName = '" + campaignName + "' AND AdGroupName = '" + adGName + "' AND ProductGroup CONTAINS '" + brand + "' " + "AND ProductGroup DOES_NOT_CONTAIN 'item id' " + "DURING " + then + "," + now); var rowsG = reportG.rows(); while(rowsG.hasNext()) { var thisRowG = rowsG.next(); SIS = thisRowG['SearchImpressionShare']; } } else { SIS = thisRow['SearchImpressionShare']; } } return {Cost:thisRow['Cost'], AllConversions:thisRow['AllConversions'], AllConversionValue:thisRow['AllConversionValue'], AverageCpc:thisRow['AverageCpc'], SIS:SIS}; } function getDates(duration) { var d = new Date(Utilities.formatDate(new Date(), AdWordsApp.currentAccount().getTimeZone(), "MMM dd,yyyy HH:mm:ss")); var year = d.getFullYear(); var month = d.getMonth() + 1; var totalDays = new Date(year, month, 0).getDate(); // daysSoFar is -1 because "today" hasn't happened yet var todayDate = d.getDate(); var daysSoFar = todayDate - 1; var todayDay = d.getDay(); var now = Utilities.formatDate(new Date(),AdWordsApp.currentAccount().getTimeZone(), 'yyyyMMdd'); d.setDate(d.getDate()-duration); var then = Utilities.formatDate(d,AdWordsApp.currentAccount().getTimeZone(), 'yyyyMMdd'); return {daysLeft:(totalDays - daysSoFar).toFixed(0),totalDays:totalDays.toFixed(0),year:year.toFixed(0),month:month.toFixed(0),todayDate:todayDate.toFixed(0),todayDay:todayDay.toFixed(0),now:now,then:then};

 

 

 

AdWords Top Contributor Google+ Profile | Partner Profile | AdWords Audits