在 Google Ads 中,您可以为每个广告系列设置每日预算金额。不过,有些营销活动会产生固定费用;例如,“我希望在秋季促销活动前支出 5,000 美元”。借助出价策略,您可以对每日预算的支出方式进行一定程度的控制,但无法控制广告系列期间的预算消耗方式。
例如,如果我们只想花 5,000 美元宣传秋季促销活动,并且希望投放 10 天,则可以将每日预算设置为 500 美元,以便用完整个预算。不过,这假定我们每天都会花完这笔费用,并且希望均匀地花费。您无法告知 Google Ads 您希望在最后几天内支出大部分预算。
此脚本却可以按照自定义的预算分配方案每天动态调整广告系列预算。
工作原理
测试预算策略
该脚本包含一些测试代码,用于模拟运行数天的影响。这样,您就可以更好地了解在安排脚本在一段时间内每天运行时可能会发生的情况。
默认情况下,此脚本会模拟均匀的预算分配,即在 10 天内支出 500 美元。
function main() {
testBudgetStrategy(calculateBudgetEvenly, 10, 500);
// setNewBudget(calculateBudgetEvenly, CAMPAIGN_NAME, TOTAL_BUDGET, START_DATE, END_DATE);
}
setNewBudget
函数调用已注释掉,表示它只会运行测试代码。以下是上述示例的输出结果:
Day 1.0 of 10.0, new budget 50.0, cost so far 0.0
Day 2.0 of 10.0, new budget 50.0, cost so far 50.0
Day 3.0 of 10.0, new budget 50.0, cost so far 100.0
Day 4.0 of 10.0, new budget 50.0, cost so far 150.0
Day 5.0 of 10.0, new budget 50.0, cost so far 200.0
Day 6.0 of 10.0, new budget 50.0, cost so far 250.0
Day 7.0 of 10.0, new budget 50.0, cost so far 300.0
Day 8.0 of 10.0, new budget 50.0, cost so far 350.0
Day 9.0 of 10.0, new budget 50.0, cost so far 400.0
Day 10.0 of 10.0, new budget 50.0, cost so far 450.0
Day 11.0 of 10.0, new budget 0.0, cost so far 500.0
该脚本每天都会计算新的预算,以确保预算支出均匀分配。当达到分配的预算上限时,系统会将预算设为零,停止支出。
您可以通过更改使用的函数或修改函数本身来更改所用的预算策略。该脚本附带两个预构建策略:calculateBudgetEvenly
和 calculateBudgetWeighted
。如需设置加权测试预算策略,请按如下方式更改 testBudgetStrategy
:
testBudgetStrategy(calculateBudgetWeighted, 10, 500);
点击预览并检查记录器输出。请注意,此预算策略会在期初分配较少的预算,在最后几天分配较多的预算。
您可以使用这种测试方法来模拟预算计算函数的变更,并尝试用自己的方法分配预算。
分配预算
calculateBudgetWeighted
预算策略通过以下函数实现:
function calculateBudgetWeighted(costSoFar, totalBudget, daysSoFar, totalDays) {
const daysRemaining = totalDays - daysSoFar;
const budgetRemaining = totalBudget - costSoFar;
if (daysRemaining <= 0) {
return budgetRemaining;
} else {
return budgetRemaining / (2 * daysRemaining - 1) ;
}
}
此函数接受以下参数:
costSoFar
- 广告系列从
START_DATE
到今天的累计费用。 totalBudget
- 从
START_DATE
到END_DATE
的分配支出。 daysSoFar
START_DATE
到今天的天数。totalDays
START_DATE
和END_DATE
之间的总天数。
您可以编写自己的函数,但必须包含这些参数。使用这些值,您可以比较到目前为止已经支出的金额与总预算金额,并确定您当前在整个预算时间表内的位置。
具体而言,此预算策略会计算剩余预算 (totalBudget - costSoFar
),然后将其除以剩余天数的两倍。这会在广告系列结束时对预算分配进行权衡。通过使用 START_DATE
以来的费用,还会考虑未完全用完设定预算的“慢日子”。
根据真实情况设置预算
确定预算策略后,您需要进行一些更改,然后才能安排此脚本每天运行。
首先,更新文件顶部的常量:
START_DATE
:将此值设为预算策略的开始时间。应该是当前或过去的日期。END_DATE
:将此值设置为您要使用此预算投放广告的最后一天。TOTAL_BUDGET
:您预计支出的总金额。此值以账号币种为单位,实际支出可能会超出该值,具体取决于脚本计划运行的时间。CAMPAIGN_NAME
:要应用此预算策略的广告系列的名称。
接下来,停用测试并启用逻辑以实际更改预算:
function main() {
// testBudgetStrategy(calculateBudgetEvenly, 10, 500);
setNewBudget(calculateBudgetWeighted, CAMPAIGN_NAME, TOTAL_BUDGET, START_DATE, END_DATE);
}
时间安排
将此脚本设置为每天在本地时区的午夜或过后不久运行,从而尽可能地充分利用新的一天的预算。不过,请注意,检索到的报告数据(例如费用)可能会延迟约 3 小时,因此 costSoFar
参数可能引用的是安排在午夜之后运行的脚本的昨天总和。
设置
点击下方的按钮,即可在 Google Ads 账号中创建脚本。
保存脚本并点击预览按钮。此脚本(默认)会模拟 10 天内预算为 500 美元的预算策略。日志记录器输出反映了正在模拟的日期、当天分配的预算以及截至目前的支出总金额。
源代码
// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @name Flexible Budgets
*
* @overview The Flexible budgets script dynamically adjusts campaign budget for
* an advertiser account with a custom budget distribution scheme on a daily
* basis. See
* https://developers.google.com/google-ads/scripts/docs/solutions/flexible-budgets
* for more details.
*
* @author Google Ads Scripts Team [adwords-scripts@googlegroups.com]
*
* @version 2.1
*
* @changelog
* - version 2.1
* - Split into info, config, and code.
* - version 2.0
* - Updated to use new Google Ads scripts features.
* - version 1.0.3
* - Add support for video and shopping campaigns.
* - version 1.0.2
* - Use setAmount on the budget instead of campaign.setBudget.
* - version 1.0.1
* - Improvements to time zone handling.
* - version 1.0
* - Released initial version.
*/
/**
* Configuration to be used for the Flexible Budgets script.
*/
CONFIG = {
'total_budget': 500,
'campaign_name': 'Special Promotion',
'start_date': 'November 1, 2021 0:00:00 -0500',
'end_date': 'December 1, 2021 0:00:00 -0500'
};
const TOTAL_BUDGET = CONFIG.total_budget;
const CAMPAIGN_NAME = CONFIG.campaign_name;
const START_DATE = new Date(CONFIG.start_date);
const END_DATE = new Date(CONFIG.end_date);
function main() {
testBudgetStrategy(calculateBudgetEvenly, 10, 500);
// setNewBudget(calculateBudgetEvenly, CAMPAIGN_NAME, TOTAL_BUDGET,
// START_DATE, END_DATE);
}
function setNewBudget(budgetFunction, campaignName, totalBudget, start, end) {
const today = new Date();
if (today < start) {
console.log('Not ready to set budget yet');
return;
}
const campaign = getCampaign(campaignName);
const costSoFar = campaign.getStatsFor(
getDateStringInTimeZone('yyyyMMdd', start),
getDateStringInTimeZone('yyyyMMdd', end)).getCost();
const daysSoFar = datediff(start, today);
const totalDays = datediff(start, end);
const newBudget = budgetFunction(costSoFar, totalBudget, daysSoFar,
totalDays);
campaign.getBudget().setAmount(newBudget);
}
function calculateBudgetEvenly(costSoFar, totalBudget, daysSoFar, totalDays) {
const daysRemaining = totalDays - daysSoFar;
const budgetRemaining = totalBudget - costSoFar;
if (daysRemaining <= 0) {
return budgetRemaining;
} else {
return budgetRemaining / daysRemaining;
}
}
function calculateBudgetWeighted(costSoFar, totalBudget, daysSoFar,
totalDays) {
const daysRemaining = totalDays - daysSoFar;
const budgetRemaining = totalBudget - costSoFar;
if (daysRemaining <= 0) {
return budgetRemaining;
} else {
return budgetRemaining / (2 * daysRemaining - 1);
}
}
function testBudgetStrategy(budgetFunc, totalDays, totalBudget) {
let daysSoFar = 0;
let costSoFar = 0;
while (daysSoFar <= totalDays + 2) {
const newBudget = budgetFunc(costSoFar, totalBudget, daysSoFar, totalDays);
console.log(`Day ${daysSoFar + 1} of ${totalDays}, new budget ` +
`${newBudget}, cost so far ${costSoFar}`);
costSoFar += newBudget;
daysSoFar += 1;
}
}
/**
* Returns number of days between two dates, rounded up to nearest whole day.
*/
function datediff(from, to) {
const millisPerDay = 1000 * 60 * 60 * 24;
return Math.ceil((to - from) / millisPerDay);
}
function getDateStringInTimeZone(format, date, timeZone) {
date = date || new Date();
timeZone = timeZone || AdsApp.currentAccount().getTimeZone();
return Utilities.formatDate(date, timeZone, format);
}
/**
* Finds a campaign by name, whether it is a regular, video, or shopping
* campaign, by trying all in sequence until it finds one.
*
* @param {string} campaignName The campaign name to find.
* @return {Object} The campaign found, or null if none was found.
*/
function getCampaign(campaignName) {
const selectors = [AdsApp.campaigns(), AdsApp.videoCampaigns(),
AdsApp.shoppingCampaigns()];
for (const selector of selectors) {
const campaignIter = selector
.withCondition(`CampaignName = "${campaignName}"`)
.get();
if (campaignIter.hasNext()) {
return campaignIter.next();
}
}
throw new Error(`Could not find specified campaign: ${campaignName}`);
}