Orçamentos flexíveis: conta de administrador

This is a Manager Account script. For operating on a single account, use the Single Account version of the script.

Os orçamentos flexíveis da MCC estendem a execução dos orçamentos flexíveis a várias contas em uma única MCC. Os orçamentos flexíveis podem ajustar dinamicamente o orçamento diário da sua campanha com um esquema personalizado de distribuição do orçamento.

O script lê uma planilha para cada conta/campanha especificada e o orçamento correspondente (associado à data de início e de término). Depois, localiza a campanha, calcula o orçamento para a data atual, define-o como o orçamento diário da campanha e registra o resultado na planilha. Ele não altera as campanhas que não foram especificadas na planilha.

Como funciona

O script funciona da mesma forma que o script de orçamento flexível de conta única. A única funcionalidade adicional é que ele suporta várias contas por meio da planilha especificada.

As duas primeiras colunas especificam a campanha pra a qual calcular o orçamento. As três colunas seguintes especificam as informações do orçamento e os últimos registros resultantes da execução.

O ID da conta deve ser uma conta de anunciante, não uma conta da MCC. É possível ter vários orçamentos para a mesma conta/campanha, mas apenas um orçamento pode estar ativo por vez. Caso contrário, um cálculo de orçamento mais novo pode substituir um mais velho. Se uma conta/campanha não for especificada na planilha, o script não definirá um orçamento flexível para ela.

Iterações em todas as contas gerenciadas

Inicialmente, o script é executado no contexto da conta da MCC. Primeiro ele chama

var mccAccount = AdWordsApp.currentAccount();
para armazenar a conta da MCC. Em seguida, ele chama
var accountIterator = MccApp.accounts().get();
para gerar um iterador de todas as contas gerenciadas na MCC.

Em cada iteração, o script chama

MccApp.select(account);
para mudar para o contexto da conta gerenciada. Assim, ele pode fazer operações nessa conta. Depois de processar todas as contas gerenciadas, o script chama
MccApp.select(mccAcount);
para restaurar o contexto da conta da MCC.

Teste de estratégias de orçamento

O script inclui um código de teste para simular os efeitos da execução em vários dias. Isso dá uma ideia melhor de o que acontecerá quando o script for agendado para ser executado diariamente durante um período.

Por padrão, este script simulará a distribuição uniforme de um orçamento de US$ 500 ao longo de 10 dias.

function main() {
  testBudgetStrategy(calculateBudgetEvenly, 10, 500);
//  setNewBudget(calculateBudgetEvenly, campaignName, totalBudget, startDate, endDate);
}

A chamada da função setNewBudget está comentada, o que significa que só executaremos o código de teste. Aqui está a saída do exemplo:

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

Todos os dias calculamos um novo orçamento para termos a certeza de que estamos gastando o orçamento uniformemente a cada dia. Depois de exceder a alocação do orçamento inicial, definimos o orçamento com zero para interromper os gastos.

Você pode alterar a estratégia de orçamento utilizada mudando a função usada ou modificando a função propriamente dita. O script acompanha duas estratégias previamente criadas: calculateBudgetEvenly e calculateBudgetWeighted. Acabamos de testar a primeira. Atualize a linha testBudgetStrategy para usar a segunda:

testBudgetStrategy(calculateBudgetWeighted, 10, 500);

Clique em Visualizar e verifique a saída do registro. Essa estratégia de orçamento aloca menos orçamento no início do período e mais durante os últimos dias.

Você pode usar esse método de teste para simular alterações feitas nas funções de cálculo do orçamento e testar sua própria abordagem na distribuição de um orçamento.

Alocação de um orçamento

Vejamos mais detalhadamente a estratégia de orçamento calculateBudgetWeighted:

function calculateBudgetWeighted(costSoFar, totalBudget, daysSoFar, totalDays) {
  var daysRemaining = totalDays - daysSoFar;
  var budgetRemaining = totalBudget - costSoFar;
  if (daysRemaining <= 0) {
    return budgetRemaining;
  } else {
    return budgetRemaining / (2 * daysRemaining - 1) ;
  }
}

Essa função usa os seguintes argumentos:

  • costSoFar: quanto a campanha acumulou em custos desde a startDate até hoje.
  • totalBudget: quanto desejamos gastar de startDate até endDate.
  • daysSoFar: quantos dias decorridos desde a startDate até hoje.
  • totalDays: número total de dias entre a startDate e a endDate.

Você pode escrever sua própria função, desde que ela use esses argumentos. Usando esses valores, você pode comparar quanto gastou em dinheiro até agora com quanto deve gastar no geral e determinar em que ponto está no momento dentro do cronograma de todo o orçamento.

Em especial, essa estratégia de orçamento procura saber quanto orçamento resta (totalBudget - costSoFar) e divide esse valor pelo dobro do número de dias restantes. Ela pondera a distribuição do orçamento mais para o final da campanha. Usando o custo desde a startDate, ela também leva em conta "dias fracos", quando você não gasta todo o orçamento definido.

Criação de um orçamento de verdade

Se estiver satisfeito com sua estratégia de orçamento, você precisará fazer algumas alterações antes de agendar este script para ser executado diariamente.

Primeiro, atualize a planilha para especificar conta, campanha, orçamento, data de início, data de término (uma linha para cada orçamento da campanha).

  • Account ID: o ID da conta (no formato xxx-xxx-xxxx) da campanha à qual a estratégia de orçamento deve ser aplicada.
  • Campaign Name: o nome da campanha à qual a estratégia de orçamento deve ser aplicada.
  • Start Date: a data de início da sua estratégia de orçamento. Ela deve ser a data atual ou um dia no passado.
  • End Date: o último dia no qual você deseja anunciar com esse orçamento.
  • Total Budget: o valor total que você está tentando gastar. Esse valor está na moeda da conta e pode ser excedido dependendo do agendamento de execução do script.

Em seguida, desative o teste e ative a lógica para alterar efetivamente o orçamento:

function main() {
//  testBudgetStrategy(calculateBudgetEvenly, 10, 500);
  setNewBudget(calculateBudgetWeighted, campaignName, totalBudget, startDate, endDate);
}

O resultado de cada campanha será registrado na coluna Execution Result.

Agendamento

Agende este script para ser executado diariamente, à meia-noite ou pouco depois no fuso horário local, para que ele direcione o máximo possível o orçamento do dia seguinte. Porém, dados de relatórios recuperados, como custos, podem sofrer atrasos de aproximadamente três horas. Assim, o parâmetro "costSoFar" pode estar referenciando o total de ontem em um script agendado para ser executado depois da meia-noite.

Configuração

  • Configure um script com base em planilha usando o código-fonte abaixo. Use a planilha modelo dos orçamentos flexíveis da MCC.
  • Atualize SPREADSHEET_URL no código para refletir o URL da sua planilha.
  • Agende o script para ser executado Diariamente.

Código-fonte

// 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 MCC Flexible Budgets
 *
 * @overview The MCC Flexible Budgets script dynamically adjusts campaign budget
 *     daily for accounts under an MCC account with a custom budget distribution
 *     scheme. See https://developers.google.com/adwords/scripts/docs/solutions/mccapp-flexible-budgets
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.4
 *
 * @changelog
 * - version 1.0.4
 *   - Add support for video and shopping campaigns.
 * - version 1.0.3
 *   - Added validation for external spreadsheet setup.
 * - version 1.0.2
 *   - Fix a minor bug in variable naming.
 *   - Use setAmount on the budget instead of campaign.setBudget.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */

var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';

// Please fix the following variables if you need to reformat the spreadsheet
// column numbers of each config column. Column A in your spreadsheet has
// column number of 1, B has number of 2, etc.
var COLUMN = {
  accountId: 2,
  campaignName: 3,
  startDate: 4,
  endDate: 5,
  totalBudget: 6,
  results: 7
};

// Actual config (without header and margin) starts from this row
var CONFIG_START_ROW = 5;

function main() {
  // Uncomment the following function to test your budget strategy function
  // testBudgetStrategy(calculateBudgetEvenly, 10, 500);
  setNewBudget(calculateBudgetWeighted);
}

// Core logic for calculating and setting campaign daily budget
function setNewBudget(budgetFunc) {
  Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
  var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
  var sheet = spreadsheet.getSheets()[0];

  var endRow = sheet.getLastRow();

  var mccAccount = AdWordsApp.currentAccount();
  sheet.getRange(2, 6, 1, 2).setValue(mccAccount.getCustomerId());

  for (var i = CONFIG_START_ROW; i <= endRow; i++) {
    Logger.log('Processing row %s', i);

    var accountId = sheet.getRange(i, COLUMN.accountId).getValue();
    var campaignName = sheet.getRange(i, COLUMN.campaignName).getValue();
    var startDate = new Date(sheet.getRange(i, COLUMN.startDate).getValue());
    var endDate = new Date(sheet.getRange(i, COLUMN.endDate).getValue());
    var totalBudget = sheet.getRange(i, COLUMN.totalBudget).getValue();
    var resultCell = sheet.getRange(i, COLUMN.results);

    var accountIter = MccApp.accounts().withIds([accountId]).get();
    if (!accountIter.hasNext()) {
      resultCell.setValue('Unknown account');
      continue;
    }
    var account = accountIter.next();
    MccApp.select(account);

    var campaign = getCampaign(campaignName);
    if (!campaign) {
      resultCell.setValue('Unknown campaign');
      continue;
    }

    var today = new Date();
    if (today < startDate) {
      resultCell.setValue('Budget not started yet');
      continue;
    }
    if (today > endDate) {
      resultCell.setValue('Budget already finished');
      continue;
    }

  var costSoFar = campaign.getStatsFor(
      getDateStringInTimeZone('yyyyMMdd', startDate),
      getDateStringInTimeZone('yyyyMMdd', endDate)).getCost();
    var daysSoFar = datediff(startDate, today);
    var totalDays = datediff(startDate, endDate);
    var newBudget = budgetFunc(costSoFar, totalBudget, daysSoFar, totalDays);
    campaign.getBudget().setAmount(newBudget);
    Logger.log('AccountId=%s, CampaignName=%s, StartDate=%s, EndDate=%s, ' +
               'CostSoFar=%s, DaysSoFar=%s, TotalDays=%s, NewBudget=%s',
               accountId, campaignName, startDate, endDate,
               costSoFar, daysSoFar, totalDays, newBudget);
    resultCell.setValue('Set today\'s budget to ' + newBudget);
  }

  // update "Last execution" timestamp
  sheet.getRange(1, 3).setValue(today);
  MccApp.select(mccAccount);
}

// One calculation logic that distributes remaining budget evenly
function calculateBudgetEvenly(costSoFar, totalBudget, daysSoFar, totalDays) {
  var daysRemaining = totalDays - daysSoFar;
  var budgetRemaining = totalBudget - costSoFar;
  if (daysRemaining <= 0) {
    return budgetRemaining;
  } else {
    return budgetRemaining / daysRemaining;
  }
}

// One calculation logic that distributes remaining budget in a weighted manner
function calculateBudgetWeighted(costSoFar, totalBudget, daysSoFar, totalDays) {
  var daysRemaining = totalDays - daysSoFar;
  var budgetRemaining = totalBudget - costSoFar;
  if (daysRemaining <= 0) {
    return budgetRemaining;
  } else {
    return budgetRemaining / (2 * daysRemaining - 1);
  }
}

// Test function to verify budget calculation logic
function testBudgetStrategy(budgetFunc, totalDays, totalBudget) {
  var daysSoFar = 0;
  var costSoFar = 0;
  while (daysSoFar <= totalDays + 2) {
    var newBudget = budgetFunc(costSoFar, totalBudget, daysSoFar, totalDays);
    Logger.log('Day %s of %s, new budget %s, cost so far %s',
               daysSoFar + 1, totalDays, newBudget, costSoFar);
    costSoFar += newBudget;
    daysSoFar += 1;
  }
}

// Return number of days between two dates, rounded up to nearest whole day.
function datediff(from, to) {
  var millisPerDay = 1000 * 60 * 60 * 24;
  return Math.ceil((to - from) / millisPerDay);
}

// Produces a formatted string representing a given date in a given time zone.
function getDateStringInTimeZone(format, date, timeZone) {
  date = date || new Date();
  timeZone = timeZone || AdWordsApp.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) {
  var selectors = [AdWordsApp.campaigns(), AdWordsApp.videoCampaigns(),
      AdWordsApp.shoppingCampaigns()];
  for(var i = 0; i < selectors.length; i++) {
    var campaignIter = selectors[i].
        withCondition('CampaignName = "' + campaignName + '"').
        get();
    if (campaignIter.hasNext()) {
      return campaignIter.next();
    }
  }
  return null;
}

/**
 * Validates the provided spreadsheet URL to make sure that it's set up
 * properly. Throws a descriptive error message if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.