June 2, 2025

Правильная таблица криптана — отслеживем портфель на Maximum.

В этой статье речь пойдёт о создании "автоматической" таблицы в Google и Excel за бесплатно, которая будет:
• Отслеживать текущею цену ваших активов с обновлением каждый час;
• Отслеживать изменение цены активов за 24 часа и 7 дней;
• Отслеживать капитализации и FDV;
• Отслеживать среднюю цену покупки и ваш P&L.

Step 1:

Для начала переходим на CoinMarketCap API, входим своим аккаунтом, выбираем базовый план, в своем кабинете копируем личный API ключ. Сохраняем его.

Step 2:

Переходим в Google таблицы, создаём собственную таблицу для отслеживания портфеля (если её у вас нет), можете использовать макет который я оставил в комментариях под этим постом. Важно чтобы ваша таблица соблюдала указаные мною фишки ранее, у нее должны разделы как для цены актива так и для изменений за сутки/неделю и.т.д.

Step 3:

Теперь вам нужно решить, какие данные вы хотите отслеживать, если речь идёт только о цене активов в вашем портфеле тогда:

Находясь в таблице, в строке поиске ищем расширение «Script Apps», открываем и сразу создаём триггер, который будет обновлять цену активов каждый час автоматически, для этого нажимаем на третий значок слева, это часы.

Создаём новый триггер, повторяем все точно так как на картинке снизу:

Поздравляю, теперь цена будет обновляться сама, каждый час, далее переходим в раздел редактора (второй значок <>) и вставляем этот код:

function updatePricesCMC() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("КриптоПортфель");
if (!sheet) {
SpreadsheetApp.getUi().alert("Лист 'КриптоПортфель' не найден!");
return;
}
const apiKey = "ВСТАВЬ_СЮДА_СВОЙ_API_КЛЮЧ"; //
const symbols = [
{ symbol: "BTC", row: 2 },
{ symbol: "ETH", row: 3 },
{ symbol: "SOL", row: 4 }
];
const symbolList = symbols.map(s => s.symbol).join(",");
const url = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${symbolList}&convert=USD`;
const options = {
method: "get",
headers: {
"X-CMC_PRO_API_KEY": apiKey
},
muteHttpExceptions: true
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
symbols.forEach(token => {
const price = data.data[token.symbol]?.quote?.USD?.price ?? "Ошибка";
sheet.getRange(token.row, 10).setValue(price); // Колонка J (10)
});
} catch (e) {
SpreadsheetApp.getUi().alert("Ошибка при получении данных: " + e.message);
}
}

Теперь нужно сохранить файл (Ctrl+S), в панели выбираеим функцию updatePricesCMC, нажимаем на▶️ "Выполнить" → Google попросит доступ → нажём "Разрешить".

Для того чтобы получить ценовые данные просто вводим функцию «...» в нужной ячейке.

Повышаем уровень до максимума:

Переходим к самому вкусному, теперь мы будем отслеживать цену, рыночной капитализацию и изменения цен. Долго игрался с кодом но зато теперь все идеально и с ним можно манипулировать в дальнейшем легко.

Как обычно, открываем Script Apps, раздел редактора, вставляем код:

function updatePricesCMC() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Название вашего листа");
if (!sheet) {
SpreadsheetApp.getUi().alert("Лист 'Название вашего листа' не найден!");
return;
}
const apiKey = "Ваш API ключ";
const symbols = [
{ symbol: "BTC", row: 2 },
{ symbol: "ETH", row: 3 },
{ symbol: "SOL", row: 4 },
{ symbol: "SUI", row: 5 }
];
const symbolList = symbols.map(s => s.symbol).join(",");
const url = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${symbolList}&convert=USD`;
const options = {
method: "get",
headers: {
"X-CMC_PRO_API_KEY": apiKey
},
muteHttpExceptions: true
};
const formatPrice = price => price >= 10
? `${Math.round(price)} Правильная таблица криптана — отслеживем портфель на Maximum. — Teletype
: `${price.toFixed(2)} Правильная таблица криптана — отслеживем портфель на Maximum. — Teletype ;
const formatPercent = val => (val !== null && val !== undefined)
? `${val.toFixed(2)}%`
: "";
const formatCompact = val => {
if (val >= 1e12) return `${(val / 1e12).toFixed(1)}T`;
if (val >= 1e9) return `${(val / 1e9).toFixed(1)}B`;
if (val >= 1e6) return `${(val / 1e6).toFixed(1)}M`;
return val.toFixed(0);
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
symbols.forEach(token => {
const info = data.data[token.symbol];
const quote = info?.quote?.USD;
if (!quote) return;
const price = formatPrice(quote.price);
const change24h = formatPercent(quote.percent_change_24h);
const change7d = formatPercent(quote.percent_change_7d);
const marketCap = formatCompact(quote.market_cap ?? 0);
const fdv = formatCompact(quote.fully_diluted_market_cap ?? 0);
const supplyPercent = (info.total_supply && info.circulating_supply)
? `${((info.circulating_supply / info.total_supply) * 100).toFixed(2)}%`
: "";
sheet.getRange(token.row, 10).setValue(price); // J
sheet.getRange(token.row, 12).setValue(change24h); // L
sheet.getRange(token.row, 13).setValue(change7d); // M
sheet.getRange(token.row, 16).setValue(marketCap); // P
sheet.getRange(token.row, 17).setValue(fdv); // Q
sheet.getRange(token.row, 18).setValue(supplyPercent); // R
});
} catch (e) {
SpreadsheetApp.getUi().alert("Ошибка при получении данных: " + e.message);
}
}
// 🔁 ФУНКЦИЯ РАСЧЁТА P&L
function calculatePL() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("КриптоПортфель");
const lastRow = sheet.getLastRow();
for (let row = 2; row <= lastRow; row++) {
const priceCells = sheet.getRange(row, 5).getValue().toString().split('\n'); // E
const amountCells = sheet.getRange(row, 6).getValue().toString().split('\n'); // F
const currentPriceCell = sheet.getRange(row, 10).getValue().toString().replace("quot;, "").replace(",", ""); // J
let totalAmount = 0;
let totalCost = 0;
for (let i = 0; i < priceCells.length; i++) {
const priceText = priceCells[i].trim();
const amountText = amountCells[i]?.trim();
const priceMatch = priceText.match(/^\d+\.\s*([\d.,]+)/);
const amountMatch = amountText?.match(/^\d+\.\s*([\d.,]+)/);
if (priceMatch && amountMatch) {
const price = parseFloat(priceMatch[1].replace(",", ""));
const amount = parseFloat(amountMatch[1].replace(",", ""));
if (!isNaN(price) && !isNaN(amount)) {
totalAmount += amount;
totalCost += price;
}
}
}
const avgBuyPrice = totalAmount ? totalCost / priceCells.length : null;
const currentPrice = parseFloat(currentPriceCell);
if (avgBuyPrice && currentPrice && !isNaN(currentPrice)) {
const plDollar = (currentPrice - avgBuyPrice) * totalAmount;
const plPercent = ((currentPrice / avgBuyPrice) - 1) * 100;
sheet.getRange(row, 19).setValue(`${plPercent.toFixed(2)}%`); // S – P&L %
sheet.getRange(row, 20).setValue(`${plDollar.toFixed(2)} Правильная таблица криптана — отслеживем портфель на Maximum. — Teletype ); // T – P&L $
} else {
sheet.getRange(row, 19).setValue("—");
sheet.getRange(row, 20).setValue("—");
}
}
}

Обратите внимание на указаные жирныи шрифтом части кода, там вы должны будь использовать свой данные.

В этом случае, чтобы добавить или удалить актив придётся заходить в скрипт: находим раздел показанный на скриншоте ниже, копируем эту строку: { symbol: "SUI", row: 5 }{ symbol: "тут пишем название актива", row: здесь номер ячейки, например 6 }.
После закрытия строки } у предпоследней строки должна быть запятая а у последней нет.

Касательно P&L у нас есть функция calculatePL(). Она:

• Находит все строки с покупками по шаблону 1., 2., 3. и т.д. в колонках E (цена покупки) и F (кол-во).
• Считает среднюю цену.
• Сравнивает с текущей ценой из колонки J.

Записывает:
— P&L % в колонку S
— P&L $ в колонку T

В дополнение добавлю что разные покупки одного и того же актива я буду разделять по типу 1. 2. 3., можно для этого использовать буквы но или настроить под себя более удобным способом, мне и так нормально. Чтобы функция calculatePL() работа после измений просто отправьте код выше и свой док любой ИИ объяснив ситуацию.