Greasy Fork is available in English.

jetlend

Удобства пользования сайтом

// ==UserScript==
// @name         jetlend
// @description  Удобства пользования сайтом
// @author       Я
// @match        *://jetlend.ru/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=jetlend.ru
// @grant        none
// @license MIT
// @version 0.0.1.20240207165557
// @namespace https://greasyfork.org/users/1256967
// ==/UserScript==

/*
document.querySelectorAll('[data-colindex="3"]').forEach((item, i)=>{
    if (i > 1) {
        if (Number(item.textContent) < 16) {
            item.closest('tr').style.background = "pink";
            console.log(item.textContent)
        }
    }
})
*/

(function () {
  "use strict";
})();

// Инфо обо мне
const urlDetails =
  "https://jetlend.ru/invest/api/account/details?exclude_npd=true";

// Инфо о Первичном рынке
const urlReqWaiting = "https://jetlend.ru/invest/api/requests/waiting";

// Инфо о Вторичном рынке
const urlLoans = "https://jetlend.ru/invest/api/exchange/loans";

// Инфо о всех нотификациях
const urlNotifications =
  "https://jetlend.ru/invest/api/account/notifications/v3";

// Инфо о конкретном займе
const urlLoanOverview =
  "https://jetlend.ru/invest/api/exchange/loans/11988/overview";

// инфо о стакане займа
const url6 = "https://jetlend.ru/invest/api/exchange/loans/11988/dom/records";

// Аналитика по id займа https://jetlend.ru/invest/api/requests/18163/analytics
// На сколько использован лимит на платформе https://jetlend.ru/invest/api/requests/18163/limits
// Инфо о займе по id, в том числе статус Игнора https://jetlend.ru/invest/api/requests/18163/info

window.levelBigInterest = 0.4;
window.loanOptions = {
  interest_rate: 0.19,
  financial_discipline: 0.8,
  term: 720,
  term_left: 1000,
  ytm: 0.3,
  progress: 0.3,
  min_price: 0.99,
  status: "active",
  status2: "restructured",
  invested_company_debt: 200,
};
window.loans = [];
window.bigDataLoans = [];
window.filteredLoans = [];
window.filteredGoodLoans = [];
window.overviewCounter = 0;
window.loansSlice = 100;
window.totalLoans = 0;
window.offsetLoans = 0;

window.makeBigOverview = () => makeBigOverview();
window.makeGlobalOverview = () => makeGlobalOverview();
window.updateBigOverview = () => updateBigOverview();
window.updateGoodLoansOverview = () => updateGoodLoansOverview();
window.makeOverviewById = (id) => makeOverviewById(id);
window.loanPreview = (odt) => loanPreview(odt);
window.clearPriceByODT = (odt) => clearPriceByODT(odt);
window.buyLoanByODT = (odt) => buyLoanByODT(odt);
window.sellLoanByODT = (odt) => sellLoanByODT(odt);
window.findIndexById = (id) => findIndexById(id);

async function reqMyDetails() {
  const data = await fetchAsync(urlDetails);
  /* console.log("myDetails:", data?.data); */
  window.myDetails = data?.data;
  return data?.data;
}

// получить первичный рынок и записать с большим процентом.

async function reqWaiting() {
  const data = await fetchAsync(urlReqWaiting);
  /* console.log("Req Waiting:", data?.requests); */
  window.loanReqWaiting = data?.requests;
  window.levelBigInterest = window.levelBigInterest || 0.3;
  window.bigInterestReqWaiting = window.loanReqWaiting.filter((element) => {
    return (
      element.interest_rate > window.levelBigInterest &&
      element.investing_amount === null &&
      element.company_investing_amount === null
    );
  });

  if (window.bigInterestReqWaiting.length === 0) {
    console.log("Новых займов выше " + window.levelBigInterest + " нет.");
    window.levelBigInterest = (window.levelBigInterest - 0.01).toFixed(2);
  } else {
    console.log(window.bigInterestReqWaiting);
  }

  /* window.bigInterestReqWaiting.forEach((loan) => {
    if (loan.interest_rate > 0.35) {
      sendMessage(loan);
    }
  }); */

  return data?.requests;
}

function updateBigIntReqWaiting() {
  window.bigInterestReqWaiting = window.loanReqWaiting.filter((element) => {
    return (
      element.interest_rate > window.levelBigInterest &&
      element.investing_amount === null
    );
  });

  if (window.bigInterestReqWaiting.length === 0) {
    console.log("Новых займов выше " + window.levelBigInterest + " нет.");
  } else {
    console.log("Найден первичный займ с большой ставкой.");
    console.log(window.bigInterestReqWaiting);
  }
}

// ------------------------------------

// получить Вторичный рынок и фильтрануть по опциям.
async function fetchLoans() {
  let data = await fetchAsync(
    `https://jetlend.ru/invest/api/exchange/loans?limit=100&offset=${window.offsetLoans}`
  );
  /* console.log("Loans:", data?.data); */

  window.totalLoans = data?.total;

  data?.data.forEach((loan) => {
    const oldLoan = window?.loans.find(
      (item, i) => item.loan_id === loan.loan_id
    );

    if (oldLoan) {
      oldLoan.recommended_price = loan.recommended_price;
      oldLoan.best_price = loan.best_price;
      oldLoan.best_price_count = loan.best_price_count;
      oldLoan.best_price_amount = loan.best_price_amount;
    } else {
      window?.loans.push(loan);
    }
  });

  // сдвигаем счётчик уже загруженных займов, или начинаем заново.
  if (window.offsetLoans > window.totalLoans) {
    window.offsetLoans = 0;
  } else {
    window.offsetLoans = window.offsetLoans + 100;
  }

  window.filteredLoans = getLoansByOptions(window.loanOptions);

  const bigDataFilteredByClearPrice = window.bigDataLoans.filter(
    (item) => item.clear_price
  );

  bigDataFilteredByClearPrice.forEach((item) => {
    window.filteredLoans.forEach((innerItem) => {
      if (innerItem.loan_id === item.loan_id) {
        innerItem.clear_price = item.clear_price;
        innerItem.commission = item.commission;
        innerItem.ndfl = item.ndfl;
        innerItem.available_count = item.available_count;
        innerItem.summary_interest_revenue = item.summary_interest_revenue;
      }
    });
  });

  return data?.data;
}
// ------------------------------------

// получить данные по займу.
async function fetchLoanOverview(loanId) {
  const data = await fetchAsync(
    `https://jetlend.ru/invest/api/exchange/loans/${loanId}/overview`
  );

  const loanIndex = window.bigDataLoans.findIndex(
    (el) => el.loan_id === loanId
  );

  window.bigDataLoans[loanIndex].recommended_price =
    data?.data.sell.recommended_price;
  window.bigDataLoans[loanIndex].best_price = data?.data.sell.best_price;
  window.bigDataLoans[loanIndex].has_any_overdue = data?.data.has_any_overdue;
  window.bigDataLoans[loanIndex].history_overdue_days =
    data?.data.loan.history_overdue_days;
  window.bigDataLoans[loanIndex].best_price_count =
    data?.data.sell.best_price_count;
  window.bigDataLoans[loanIndex].best_price_amount =
    data?.data.sell.best_price_amount;

  window.currentLoanOverview = data?.data;

  return data?.data;
}
// ------------------------------------

// получить данные по займам по которым нет данных
function updateBigOverview() {
  console.log("updateBigOverview()");
  window.bigDataLoans.forEach((loan) => {
    if (!loan.best_price) {
      fetchLoanOverview(loan.loan_id);
    }
  });
}

async function fetchAsync(url) {
  const response = await fetch(url);
  const data = await response.json();

  return data;
}

function createMyInfoBlock() {
  const htmlBlock = document.createElement("DIV");
  htmlBlock.id = "infoblock";
  // htmlBlock.style.overflow = "auto";
  htmlBlock.style.position = "relative";
  htmlBlock.style.zIndex = "999";

  const blockContent1El = document.querySelectorAll(
    'div [class^="block_content__"]'
  )[0];
  blockContent1El.appendChild(htmlBlock);

  window.myInfoBlockEl = document.getElementById("infoblock");

  /* console.log("InfoBlockEl created"); */
}

function updateMyInfoBlock() {
  window.myInfoBlockEl.innerHTML = createMyInfoBlockEl();
}

function createMyInfoBlockEl() {
  if (window.myDetails) {
    return `<div>
        <table>
          <tr>
            <td>
        <div>Свободно сейчас = <b style="font-size: 1.25rem;">${window.myDetails?.balance.free.toFixed(
          2
        )} руб</b></div>
        <div>ПРОФИТ = <b style="color: green; font-size: 1.25rem;">${window.myDetails?.summary.profit.toFixed(
          2
        )} руб</b></div>
         <div>В сейфе = <b>${window.myDetails?.balance.safe.toFixed(
           2
         )} руб</b></div>
         <div>Потери / Дефолты = <b style="color: red;">${window.myDetails?.summary.loss.toFixed(
           2
         )} руб</b></div>
      <div>Моя доходность = <b>${(
        window.myDetails?.summary.yield_rate * 100
      ).toFixed(2)}%</b></div>
      <div>Мой XIRR = <b>${(
        window.myDetails?.summary.yield_rate_xirr * 100
      ).toFixed(2)}%</b></div> 
      <div>В резерве = <b>${window.myDetails?.balance.reserved.toFixed(
        2
      )} руб</b></div>
      <div>Interest / APY = <b>${window.myDetails?.summary_year.details.interest.toFixed(
        2
      )}%</b></div>
      <div>Fine = <b>${(
        window.myDetails?.summary_year.details.fine * 100
      ).toFixed(2)}%</b></div>
      <div>Годовой процент = <b>${window.myDetails?.summary_year.yield_rate.toFixed(
        2
      )}%</b></div>
      <div>Годовой процент xirr = <b>${window.myDetails?.summary_year.yield_rate_xirr.toFixed(
        2
      )}%</b></div>
          </td>

          <td>
            <div>${
              window?.loanReqWaiting?.length
            } - Всего займов на первичке</div>
            <div>${window?.totalLoans} - Всего займов на вторичке</div>
            <div>${window?.loans.length} - Всего займов загружено</div>
            <div>${window?.filteredLoans?.length} - Отфильтровано займов</div>
            <form>
              <div>
                <input value='${
                  window.loanOptions.interest_rate
                }' onchange='window.loanOptions.interest_rate=this.value' />
                <span>Ставка</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.ytm
                }' onchange='window.loanOptions.ytm=this.value' />
                <span>YTM</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.min_price
                }' onchange='window.loanOptions.min_price=this.value' />
                <span>Мин.цена</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.progress
                }' onchange='window.loanOptions.progress=this.value' />
                <span>Прогресс</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.term_left
                }' onchange='window.loanOptions.term_left=this.value' />
                <span>Дн.ост-сь</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.financial_discipline
                }' onchange='window.loanOptions.financial_discipline=this.value' />
                <span>ФД</span>
              </div>
              <div>
                <input value='${
                  window.loanOptions.invested_company_debt
                }' onchange='window.loanOptions.invested_company_debt=this.value' />
                <span>Отсчечка по долгу</span>
              </div>
              <div>
                <input value='${
                  window.loansSlice
                }' onchange='window.loansSlice=this.value' />
                <span>Сколько выводить?</span>
              </div>
               
            </form> 
          </td>

          <td>
            <button style='cursor: pointer;' type='button' onclick='window.makeGlobalOverview()'>Overview глобал</button>
            <hr>
            <button style='cursor: pointer;' type='button' onclick='window.updateBigOverview()'>Overview обновить</button>
            <hr>
            <button style='cursor: pointer;' type='button' onclick='window.updateGoodLoansOverview()'>Overview обн-ть хор.</button>
          </td>
        </tr>
      </table>
      </div>
      <hr>
      <div>Новые займы с % выше <b>${window.levelBigInterest}</b></div>
       
      <table>${makeAnkerTable(window.bigInterestReqWaiting).innerHTML}</table>
       
      <hr>

      <div>Хороший процент</div>
      <table id='filtered-good-loans'>${
        makeAnkerTable(window.filteredGoodLoans).innerHTML
      }</table>

      <hr>

      <div>Отфильтрованные займы</div>
       
      <table id='filtered-loans'>${
        makeAnkerTable(window.filteredLoans.slice(0, window.loansSlice))
          .innerHTML
      }</table>
       
       `;
  } else {
    console.log("window.myDetails is ", window.myDetails);
  }
}

// сделать читаемую ссылку
function makeAnker(array) {
  const newBlocks = array.map((el) => {
    return `<div>${el.company} | <a href="/invest/v3/company/${
      el.id || el.loan_id
    }">${el.id || el.loan_id}</a> | ${(el.interest_rate * 100).toFixed(2)}% | ${
      el.ytm ? "ytm " + (el.ytm * 100).toFixed(2) + "% |" : ""
    } ${
      el.progress ? "progress " + (el.progress * 100).toFixed() + "% |" : ""
    } ${
      el.min_price ? "min_price " + (el.min_price * 100).toFixed(2) + "% |" : ""
    }  ${el.term} дн. | ${el.financial_discipline}</div>`;
  });

  //console.log(newBlocks);

  return newBlocks;
}

function makeAnkerTable(array) {
  const table = document.createElement("TABLE");
  const newTrs = array.map((el, i) => {
    const tr = `<tr style='${
      el.invested_company_debt === null
        ? "background-color: rgba(96, 245, 39, 0.15);"
        : ""
    }'> 
      <td>${i}</td>  
      <td style="font-style: 0.6rem;">${el.rating} | ${el.company}</td> 
      <!--<td>${el.rating}</td>-->
      <td>
        <a href="/invest/v3/company/${el.id || el.loan_id}">${
      el.id || el.loan_id
    }</a>
      </td> 
      <td>Ставка <br> ${(el.interest_rate * 100).toFixed(2)}%</td> 
      
      ${
        el.ytm && el.ytm >= 0.35
          ? "<td style='color: green; font-size: 1.25rem;'>YTM <br>" +
            (el.ytm * 100).toFixed(2) +
            "% </td>"
          : el.ytm
          ? "<td>YTM <br>" + (el.ytm * 100).toFixed(2) + "% </td>"
          : ""
      } 
      ${
        el.min_price
          ? "<td>Мин.цена <br> " + (el.min_price * 100).toFixed(2) + "% </td>"
          : ""
      }  
      ${
        el.progress
          ? "<td>Прогресс <br> " + (el.progress * 100).toFixed() + "% </td>"
          : ""
      } 
      <!--<td>Дней всего <br>${el.term}</td>--> 
      ${el.term_left ? "<td>Дней остсь <br> " + el.term_left + "</td>" : ""} 
      <td>ФД <br>${el.financial_discipline}</td> 
      ${
        el.invested_company_debt
          ? "<td>Долг <br> " + el.invested_company_debt + "&nbsp;₽ </td>"
          : "<td></td>"
      }   

      ${
        el.recommended_price
          ? "<td>Рек.цена <br> " +
            (el.recommended_price * 100).toFixed(2) +
            "% </td>"
          : ""
      }

      ${
        el.recommended_price && el.recommended_price - el.min_price >= 0.01
          ? "<td style='color: green;font-weight: bold;'>Разница <br> " +
            ((el.recommended_price - el.min_price) * 100).toFixed(2) +
            "% </td>"
          : ""
      }   
      ${
        el.recommended_price && el.recommended_price - el.min_price < 0.01
          ? "<td>Разница <br> " +
            ((el.recommended_price - el.min_price) * 100).toFixed(2) +
            "% </td>"
          : ""
      }  

      <!--
      ${
        el.has_any_overdue
          ? "<td>Просрочки? <br> " + el.has_any_overdue + " </td>"
          : "<td></td>"
      } 
      -->

      ${
        el.history_overdue_days
          ? "<td>Всего дн. просрочки <br> " + el.history_overdue_days + " </td>"
          : "<td></td>"
      } 

      ${
        el.best_price_count
          ? "<td>best price count <br> " + el.best_price_count + " </td>"
          : "<td></td>"
      } 
      ${
        el.best_price_amount
          ? "<td>best price amount <br> " + el.best_price_amount + " </td>"
          : "<td></td>"
      } 
      ${
        el.best_price_amount && el.best_price_count
          ? "<td>Лучшая цена <br> " +
            (
              (el.best_price_amount * el.min_price) /
              el.best_price_count
            ).toFixed(2) +
            " </td>"
          : "<td></td>"
      } 
      ${
        el.min_price
          ? "<td><button type='button' onclick='window.makeOverviewById(" +
            el.loan_id +
            ")'>update overview</button></td>"
          : "<td></td>"
      } 
      ${
        el.clear_price
          ? "<td> Точная цена <br>" + el.clear_price.toFixed(2) + "</td>"
          : "<td></td>"
      } 
      ${
        el.best_price
          ? "<td><button type='button' onclick='window.clearPriceByODT(" +
            JSON.stringify({
              loanId: el.loan_id,
              count: 1,
              maxPrice: el.best_price,
            }) +
            ")'>Уточнить цену</button></td>"
          : "<td></td>"
      } 
      ${
        el.best_price
          ? "<td><button style='border-color: green; cursor: pointer;' type='button' onclick='window.buyLoanByODT(" +
            JSON.stringify({
              loanId: el.loan_id,
              count: 1,
              maxPrice: el.min_price,
            }) +
            ")'>Купить</button></td>"
          : "<td></td>"
      } 
      ${
        el.best_price
          ? "<td><button style='border-color: red; cursor: pointer;' type='button' onclick='window.sellLoanByODT(" +
            JSON.stringify({
              loanId: el.loan_id,
              count: 1,
              recommededPrice: el.recommended_price,
            }) +
            ")'>Продать</button></td>"
          : "<td></td>"
      } 
    </tr>`;

    return tr;
  });

  table.innerHTML = newTrs.join(" ");

  return table;
}

// Получить займы по опциям.
function getLoansByOptions(options) {
  const loansArray = window.loans.filter((loan) => {
    return (
      loan?.financial_discipline > options?.financial_discipline &&
      loan?.progress > options?.progress &&
      loan?.interest_rate > options?.interest_rate &&
      loan?.min_price < options?.min_price &&
      loan?.ytm > options?.ytm &&
      loan?.loan_id > 9999 &&
      loan?.status === "active" &&
      (loan?.invested_company_debt === null ||
        loan?.invested_company_debt < options.invested_company_debt) &&
      loan?.term_left < options?.term_left
    );
  });

  // сортировка по YTM
  loansArray.sort((a, b) => {
    if (a.ytm <= b.ytm) {
      return 1;
    }
    if (a.ytm > b.ytm) {
      return -1;
    }
    return 0;
  });

  loansArray.forEach((loan) => {
    // Попытка найти займ в большом списке
    const oldLoan = window?.bigDataLoans.find(
      (item, i) => item.loan_id === loan.loan_id
    );

    // Если займа есть в списке, то взять данные из него, если его нет в списке, то добавить его
    if (oldLoan) {
      loan.recommended_price = oldLoan.recommended_price;
      loan.best_price = oldLoan.best_price;
      loan.has_any_overdue = oldLoan.has_any_overdue;
      loan.history_overdue_days = oldLoan.history_overdue_days;
      loan.best_price_count = oldLoan.best_price_count;
      loan.best_price_amount = oldLoan.best_price_amount;
    } else {
      window?.bigDataLoans.push(loan);
    }

    window.filteredGoodLoans = loansArray.filter((loan) => {
      return loan?.recommended_price - loan?.min_price >= 0.01;
    });

    // сортировка по разнец в процентах минимальной цены от рекомендованной
    window.filteredGoodLoans.sort((a, b) => {
      if (
        a.recommended_price - a.min_price <=
        b.recommended_price - b.min_price
      ) {
        return 1;
      }
      if (
        a.recommended_price - a.min_price >
        b.recommended_price - b.min_price
      ) {
        return -1;
      }
      return 0;
    });

    if (loan.ytm > 0.35) {
      sendMessage(loan);
    }

    if (loan.recommended_price - loan.min_price >= 0.01) {
      sendMessageDiffPrice(loan);
    }
  });

  /* console.log("Отфильтрованные займы");
  console.log(loansArray); */

  if (loansArray?.length === 0) {
    console.log("Нет займов соответствующих фильтру поиска.");
  }

  return loansArray;
}

// подтянуть данные по займам по одному
function makeBigOverview() {
  console.log("makeBigOverview()");
  if (window.bigDataLoans.length > 0) {
    const loan = window.bigDataLoans[window.overviewCounter];

    fetchLoanOverview(loan.loan_id);

    // если дошли до последнего элемента в массиве, то обнулим счётчик, иначе +1
    if (window.bigDataLoans.length - 1 <= window.overviewCounter) {
      window.overviewCounter = 0;
    } else {
      window.overviewCounter++;
    }
  }
}

function makeGlobalOverview() {
  console.log("makeGlobalOverview()");
  if (window.bigDataLoans.length > 0) {
    window.bigDataLoans.forEach((loan) => {
      fetchLoanOverview(loan.loan_id);
    });
  }
}

function updateGoodLoansOverview() {
  if (window?.filteredGoodLoans?.length > 0) {
    console.log("updateGoodLoansOverview()");
    window.filteredGoodLoans.forEach((loan) => {
      fetchLoanOverview(loan.loan_id);
    });
  }
}

function makeOverviewById(id) {
  if (window.bigDataLoans.length > 0) {
    fetchLoanOverview(id);
  } else {
    console.log("no loans in bigDataLoans");
  }
}

function updateFilteredLoans() {
  window.filteredLoans = getLoansByOptions(window.loanOptions);

  window.filteredLoans.forEach((loan) => {
    if (loan.ytm > 0.35) {
      sendMessage(loan);
    }
  });
}
// ------------------------------------

function sendMessage(data) {
  //console.log("-----------------");
  //console.log("Появились займы с большой эффективной ставкой");
  //console.log(data);
  const odt = {
    title: "Появились займы с большой эффективной ставкой",
    text: (data.loan_id || data.id) + " YTM:" + data.ytm,
  };
  notifyMe(odt);
  //console.log("-----------------");
}
function sendMessageDiffPrice(data) {
  //console.log("-----------------");
  //console.log("Появились займы с разницей в ставке");
  //console.log(data.loan_id || data.id);
  //console.log(data.recommended_price - data.min_price);
  //console.log(data);
  const odt = {
    title: "Появились займы с разницей в ставке",
    text: (data.loan_id || data.id) + " YTM:" + data.ytm,
  };
  notifyMe(odt);
  //console.log("-----------------");
}

function tikTak() {
  console.log("TitTak");

  reqMyDetails();
  reqWaiting();
  fetchLoans();
  updateMyInfoBlock();
  updateGoodLoansOverview();

  setTimeout(() => {
    tikTak();
  }, 5000);
}

function tikTak2() {
  console.log("TitTak2");

  window.levelBigInterest = 0.4;

  window.updateBigOverview();

  setTimeout(() => {
    tikTak2();
  }, 30000);
}

if (window.location.href === "https://jetlend.ru/invest/v3") {
  // window.location.href.includes("jetlend.ru/invest")
  createMyInfoBlock();
  tikTak();
  tikTak2();

  console.log("jetlend.ru/invest");
}

const notifyMe = function (odt) {
  //playSound("bing");
  beep(100, 110, 1);
  const notification = new Notification(odt?.title || "title", {
    tag: odt?.tag || "tag",
    body: odt?.text || "text",
    silent: true,
    icon:
      odt?.icon ||
      "https://w7.pngwing.com/pngs/225/161/png-transparent-money-banknote-ruble-coin-money-horse-saving-payment.png",
  });
};

function playSound(url) {
  const audio = new Audio(url);
  audio.play();
}

// Превью конкретного займа
async function loanPreview(odt) {
  const csrftoken = document.cookie
    .split(";")
    .find((el) => {
      return el.includes("csrftoken");
    })
    .slice(-32);
  const response = await fetch(
    `https://jetlend.ru/invest/api/exchange/loans/${odt.loanId}/buy/preview`,
    {
      headers: {
        accept: "application/json, text/plain, */*",
        "accept-language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
        "content-type": "application/json;charset=UTF-8",
        "x-csrftoken": csrftoken,
      },

      body: `{"count":${odt.count},"max_price":${odt.maxPrice}}`,
      method: "POST",
    }
  );

  const data = await response.json();

  if (data.status === "error") {
    console.log(data.error);
  }

  //console.log("this loan preview", data?.data);

  return data?.data;

  /* 
  {
    loanId: number,
    count: number,
    maxPrice: number, 
  }
  */

  /*
  {
    "status": "OK",
    "data": {
      "amount": 36.98,
      "overall_amount": 36.98,
      "summary_interest_revenue": 0.43,
      "ytm": 0.3047752462782121,
      "available_count": 1,
      "left_count": 0,
      "commission": 0,
      "ndfl": 0.15
    }
  }
  */
}

/* 
  {
    loanId: number,
    count: number,
    maxPrice: number, 
  }
  */
async function clearPriceByODT(odt) {
  console.log("clearPriceByODT()");
  //console.log(odt);

  const data = await loanPreview(odt);

  const loanIndex = window.bigDataLoans.findIndex(
    (el) => el.loan_id === odt.loanId
  );

  window.bigDataLoans[loanIndex].commission = data?.commission;
  window.bigDataLoans[loanIndex].ndfl = data?.ndfl;
  window.bigDataLoans[loanIndex].available_count = data?.available_count;
  window.bigDataLoans[loanIndex].summary_interest_revenue =
    data?.summary_interest_revenue;
  window.bigDataLoans[loanIndex].clear_price =
    data?.amount +
    data?.commission +
    data?.ndfl +
    data?.summary_interest_revenue;

  //console.log("price is:", window.bigDataLoans[loanIndex].clear_price);

  return (
    data?.amount +
    data?.commission +
    data?.ndfl +
    data?.summary_interest_revenue
  );
}

function findIndexById(id) {
  const index = window.bigDataLoans.findIndex((el) => el.loan_id == id);

  console.log(index);
  return index;
}

async function buyLoanByODT(odt) {
  const csrftoken = document.cookie
    .split(";")
    .find((el) => {
      return el.includes("csrftoken");
    })
    .slice(-32);

  const response = await fetch(
    `https://jetlend.ru/invest/api/exchange/loans/${odt.loanId}/buy`,
    {
      headers: {
        accept: "application/json, text/plain, */*",
        "accept-language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
        "content-type": "application/json;charset=UTF-8",
        "x-csrftoken": csrftoken,
      },
      body: `{"count":${odt.count}, "max_price":${odt.maxPrice}}`,
      method: "POST",
    }
  );

  const data = await response.json();
  console.log("loan buyed", data?.data);

  return data?.data;

  /* {
  "amount": 77.17,
  "overall_amount": 77.17,
  "summary_interest_revenue": 0.94,
  "ytm": 0.3050130983567512,
  "available_count": 1,
  "left_count": 0,
  "commission": 0,
  "ndfl": 0.14
} */
}

async function sellLoanByODT(odt) {
  const csrftoken = document.cookie
    .split(";")
    .find((el) => {
      return el.includes("csrftoken");
    })
    .slice(-32);

  const response = await fetch(
    `https://jetlend.ru/invest/api/exchange/loans/${odt.loanId}/sell`,
    {
      headers: {
        accept: "application/json, text/plain, */*",
        "accept-language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
        "content-type": "application/json;charset=UTF-8",
        "x-csrftoken": csrftoken,
      },
      body: `{"count":${odt.count}, "min_price":${
        odt.recommededPrice - 0.0001
      }}`,
      method: "POST",
    }
  );

  const data = await response.json();
  console.log("loan selled", data?.data);

  return data?.data;

  /* 
  {
  "amount": 0,
  "overall_amount": 68.31,
  "summary_interest_revenue": 0,
  "ytm": 0.30476719534430446,
  "available_count": 0,
  "left_count": 1,
  "commission": 0.12,
  "ndfl": 0
}
  */
}

const myAudioContext = new AudioContext();

/**
 * Helper function to emit a beep sound in the browser using the Web Audio API.
 *
 * @param {number} duration - The duration of the beep sound in milliseconds.
 * @param {number} frequency - The frequency of the beep sound.
 * @param {number} volume - The volume of the beep sound.
 *
 * @returns {Promise} - A promise that resolves when the beep sound is finished.
 */
function beep(duration, frequency, volume) {
  return new Promise((resolve, reject) => {
    // Set default duration if not provided
    duration = duration || 200;
    frequency = frequency || 440;
    volume = volume || 100;

    try {
      let oscillatorNode = myAudioContext.createOscillator();
      let gainNode = myAudioContext.createGain();
      oscillatorNode.connect(gainNode);

      // Set the oscillator frequency in hertz
      oscillatorNode.frequency.value = frequency;

      // Set the type of oscillator
      oscillatorNode.type = "square";
      gainNode.connect(myAudioContext.destination);

      // Set the gain to the volume
      gainNode.gain.value = volume * 0.01;

      // Start audio with the desired duration
      oscillatorNode.start(myAudioContext.currentTime);
      oscillatorNode.stop(myAudioContext.currentTime + duration * 0.001);

      // Resolve the promise when the sound is finished
      oscillatorNode.onended = () => {
        resolve();
      };
    } catch (error) {
      reject(error);
    }
  });
}