灵活预算 - 单一账号

工具图标

在 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

脚本每天会计算一个新预算,以确保预算支出均匀分配。在达到分配的预算上限时,预算会设置为零,从而停止支出。

您可以通过更改使用的函数或修改函数本身来更改预算策略。该脚本有两个预构建的策略:calculateBudgetEvenlycalculateBudgetWeighted。如需设置加权测试预算策略,请按如下方式更改 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_DATEEND_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}`);
}