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 số tiền 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 đến chúng; ví dụ: "Tôi muốn chi tiêu 5.000 đô la dẫn đến đợt giảm giá mùa thu của chúng tôi". Chiến lược đặt giá thầu cho phép bạn kiểm soát cách chi tiêu ngân sách hằng ngày, nhưng không có quyền kiểm soát cách chi tiêu ngân sách trong suốt thời gian chạy chiến dịch.

Ví dụ: nếu chúng ta chỉ muốn chi tiêu 5000 đô la để quảng cáo cho chương trình giảm giá mùa thu và muốn quảng cáo trong 10 ngày, 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 tôi sẽ chi tiêu toàn bộ số tiền mỗi ngày VÀ chúng tôi muốn chi tiêu đồ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ẽ tự động điều chỉnh ngân sách chiến dịch hằng ngày bằng lược đồ phân phối 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 bao gồm một số mã kiểm thử để mô phỏng tác độ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 mức phân bổ ngân sách đồng đều là 500 USD chi tiêu 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 nhận xét, cho biết rằng hàm này sẽ chỉ chạy mã kiểm thử. Sau đây là kết quả trong 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 ngân sách mới để đảm bảo mức chi tiêu ngân sách được phân bổ đồng đều. Khi đạt đến giới hạn ngân sách được phân bổ, ngân sách sẽ được đặt thành 0 và tạm dừng việc chi tiêu.

Bạn có thể thay đổi chiến lược ngân sách đang sử dụng bằng cách thay đổi hàm được sử dụng hoặc sửa đổi hàm đó. Tập lệnh đi kèm với 2 chiến lược được tạo sẵn: calculateBudgetEvenlycalculateBudgetWeighted. Để đặt chiến lược ngân sách thử nghiệm có 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 kết quả đầ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 sẽ phân bổ ít ngân sách hơn sớm trong khoảng thời gian đó và phân bổ nhiều ngân sách hơn trong vài ngày qua.

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 hàm tính toán ngân sách và thử phương pháp phân phối 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
Đã phân bổ mức chi tiêu 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ừ ngày START_DATE đến ngày END_DATE.

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

Cụ thể, chiến lược ngân sách này sẽ tính ra lượng ngân sách còn lại (totalBudget - costSoFar) và chia cho số ngày còn lại. Chiến lược này sẽ cân nhắc việc phân bổ ngân sách đến khi kết thúc chiến dịch. Khi sử dụng chi phí kể từ START_DATE, hệ thống cũng tính đến "số ngày chậm" khi 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 của mình, bạn cần thực hiện một vài thay đổi trước khi lên lịch để tập lệnh này chạ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 ngày 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 ngày 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 thử nghiệm này 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 nhiều nhất có thể. Tuy nhiên, hãy lưu ý rằng dữ liệu báo cáo được truy xuất như chi phí có thể bị trễ khoảng 3 giờ. Vì vậy, tham số costSoFar có thể đang tham chiếu 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

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

    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 có 500 đô la Mỹ trong 10 ngày. Đầu ra của trình ghi nhật ký phản ánh ngày được mô phỏng, ngân sách được phân bổ cho ngày đó và tổng số tiền đã chi tiêu đế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}`);
}