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; ví dụ: "Tôi muốn chi 5.000 đô la Mỹ trước đợt 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 5.000 USD để 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, chúng ta có thể đặt ngân sách hằng ngày là 500 USD để 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 đều đặn. 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 cuối cùng.

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 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 các hiệu ứng khi 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 là 5.000.000 VND được 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 lệnh gọi này sẽ chỉ chạy mã kiểm thử. Sau đâ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 ngân sách được chi tiêu đồ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, khiến mức chi tiêu dừng lại.

Bạn có thể thay đổi chiến lược ngân sách bằng cách thay đổi hàm được sử dụng hoặc sửa đổi chính hàm đó. Tập lệnh này đ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 đầ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 giai đoạn và nhiều hơn trong vài ngày cuối cùng.

Bạn có thể sử dụng phương pháp 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ừ ngày 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 kể từ START_DATE đến hôm nay.
totalDays
Tổng số ngày trong khoảng 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. Bằng cách sử dụng những giá trị này, bạn có thể so sánh số tiền bạn đã chi tiêu cho đến nay 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 dòng thời gian cho toàn bộ ngân sách.

Cụ thể, chiến lược ngân sách này sẽ tính toán số tiền còn lại trong ngân sách (totalBudget - costSoFar) rồi chia số tiền đó cho số ngày còn lại nhân đôi. Điều này sẽ phân bổ ngân sách nhiều hơn vào cuối chiến dịch. Bằng cách sử dụng chi phí kể từ START_DATE, chỉ số này cũng tính đến "những ngày có tốc độ 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ế

Sau 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 số 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 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 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, hãy 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ể đang tham chiếu tổng chi phí của ngày hôm qua đối với 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ủ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 với 5.000.000 VND 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 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}`);
}