// ==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 + " ₽ </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);
}
});
}