Ngân sách linh hoạt – Một tài khoản

Biểu tượng công cụ

Google Ads cho phép bạn đặt ngân sách hằng ngày cho mỗi chiến dịch. Tuy nhiên, một số sáng kiến tiếp thị sẽ có chi phí cố định liên quan; ví dụ: "Tôi muốn chi tiêu 5.000 đô la Mỹ trước khi bắt đầu chương trình giảm giá mùa thu". Chiến lược đặt giá thầu cho phép bạn kiểm soát một phần cách chi tiêu ngân sách hằng ngày, nhưng không kiểm soát được cách sử dụng ngân sách trong chiến dịch.

Ví dụ: nếu chỉ muốn chi tiêu 5.000 đô la để quảng cáo chương trình giảm giá mùa thu và muốn quảng cáo trong 10 ngày, thì chúng ta có thể đặt ngân sách hằng ngày là 500 đô la để sử dụng hết toàn bộ ngân sách. Tuy nhiên, điều này giả định rằng chúng ta sẽ chi tiêu toàn bộ số tiền mỗi ngày VÀ chúng ta muốn chi tiêu số tiền đó một cách đồng đều. Bạn không thể cho Google Ads biết rằng bạn muốn chi tiêu phần lớn ngân sách trong vài ngày qua.

Tập lệnh này sẽ linh hoạt điều chỉnh ngân sách của chiến dịch mỗi ngày bằng một kế hoạch phân bổ ngân sách tuỳ chỉnh.

Cách hoạt động

Thử nghiệm chiến lược ngân sách

Tập lệnh này bao gồm một số mã kiểm thử để mô phỏng hiệu ứng của việc chạy trong nhiều ngày. Điều này giúp bạn hiểu rõ hơn về những gì có thể xảy ra khi tập lệnh được lên lịch chạy hằng ngày trong một khoảng thời gian.

Theo mặc định, tập lệnh này mô phỏng việc phân bổ ngân sách đều đặn với mức chi tiêu 500 đô la trong 10 ngày.

function main() {
  testBudgetStrategy(calculateBudgetEvenly, 10, 500);
  // setNewBudget(calculateBudgetEvenly, CAMPAIGN_NAME, TOTAL_BUDGET, START_DATE, END_DATE);
}

Lệnh gọi hàm setNewBudget được chú thích, cho biết rằng lệnh gọi này sẽ chỉ chạy mã kiểm thử. Dưới đây là kết quả của ví dụ:

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

Mỗi ngày, tập lệnh sẽ tính toán một ngân sách mới để đảm bảo rằng mức chi tiêu ngân sách được phân bổ đồng đều. Khi đạt đến hạn mức ngân sách được phân bổ, ngân sách sẽ được đặt thành 0, tạm dừng chi tiêu.

Bạn có thể thay đổi chiến lược ngân sách được sử dụng bằng cách thay đổi hàm được sử dụng hoặc chỉnh sửa chính hàm đó. Tập lệnh này đi kèm với hai chiến lược tạo sẵn: calculateBudgetEvenlycalculateBudgetWeighted. Để đặt chiến lược ngân sách kiểm thử theo trọng số, hãy thay đổi testBudgetStrategy như sau:

testBudgetStrategy(calculateBudgetWeighted, 10, 500);

Nhấp vào Xem trước và kiểm tra đầu ra của trình ghi nhật ký. Xin lưu ý rằng chiến lược ngân sách này phân bổ ít ngân sách hơn vào đầu khoảng thời gian và nhiều ngân sách hơn trong vài ngày cuối cùng.

Bạn có thể sử dụng phương thức kiểm thử này để mô phỏng các thay đổi đối với các hàm tính toán ngân sách và thử phương pháp phân bổ ngân sách của riêng bạn.

Phân bổ ngân sách

Chiến lược ngân sách calculateBudgetWeighted được triển khai thông qua hàm sau:

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) ;
  }
}

Hàm này nhận các đối số sau:

costSoFar
Chi phí tích luỹ của chiến dịch từ START_DATE đến hôm nay.
totalBudget
Mức chi tiêu được phân bổ từ START_DATE đến END_DATE.
daysSoFar
Số ngày đã trôi qua từ START_DATE đến hôm nay.
totalDays
Tổng số ngày từ START_DATE đến END_DATE.

Bạn có thể viết hàm của riêng mình miễn là hàm đó nhận các đối số này. Khi sử dụng các giá trị này, bạn có thể so sánh số tiền đã chi tiêu cho đến thời điểm hiện tại với tổng số tiền cần chi tiêu và xác định vị trí hiện tại của bạn trong tiến trình chi tiêu theo toàn bộ ngân sách.

Cụ thể, chiến lược ngân sách này tính toán số ngân sách còn lại (totalBudget - costSoFar) và chia số đó cho gấp đôi số ngày còn lại. Điều này sẽ cân nhắc việc phân bổ ngân sách vào cuối chiến dịch. Bằng cách sử dụng chi phí kể từ START_DATE, hàm này cũng tính đến "những ngày chậm" mà ngân sách đã đặt không được chi tiêu hết.

Lập ngân sách thực tế

Khi đã hài lòng với chiến lược ngân sách, bạn cần thực hiện một vài thay đổi trước khi lên lịch chạy tập lệnh này hằng ngày.

Trước tiên, hãy cập nhật các hằng số ở đầu tệp:

  • START_DATE: Đặt giá trị này thành thời điểm bắt đầu chiến lược ngân sách. Đây phải là ngày hiện tại hoặc một ngày trong quá khứ.
  • END_DATE: Đặt giá trị này thành ngày cuối cùng bạn muốn quảng cáo bằng ngân sách này.
  • TOTAL_BUDGET: Tổng số tiền bạn đang muốn chi tiêu. Giá trị này được tính bằng đơn vị tiền tệ của tài khoản và có thể vượt quá tuỳ thuộc vào thời điểm chạy tập lệnh theo lịch.
  • CAMPAIGN_NAME: Tên của chiến dịch để áp dụng chiến lược ngân sách.

Tiếp theo, hãy tắt quy trình kiểm thử và bật logic để thực sự thay đổi ngân sách:

function main() {
  // testBudgetStrategy(calculateBudgetEvenly, 10, 500);
  setNewBudget(calculateBudgetWeighted, CAMPAIGN_NAME, TOTAL_BUDGET, START_DATE, END_DATE);
}

Lập lịch

Hãy lên lịch để tập lệnh này chạy hằng ngày, vào lúc hoặc ngay sau nửa đêm theo múi giờ địa phương để phân bổ ngân sách của ngày tiếp theo ở mức nhiều nhất có thể. Tuy nhiên, lưu ý rằng dữ liệu báo cáo được truy xuất (chẳng hạn như chi phí) có thể bị trễ khoảng 3 giờ, vì vậy, tham số costSoFar có thể tham chiếu đến tổng số của ngày hôm qua cho một tập lệnh được lên lịch chạy sau nửa đêm.

Thiết lập

  • Nhấp vào nút bên dưới để tạo tập lệnh trong tài khoản Google Ads.

    Cài đặt mẫu tập lệnh

  • Lưu tập lệnh rồi nhấp vào nút Xem trước. Tập lệnh này (theo mặc định) mô phỏng một chiến lược ngân sách với 500 đô la trong 10 ngày. Đầu ra của trình ghi lại phản ánh ngày đang được mô phỏng, ngân sách được phân bổ cho ngày đó và tổng số tiền đã chi tiêu cho đến nay.

Mã nguồn

// 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}`);
}