Skip to content

Instantly share code, notes, and snippets.

@ubarbaxor
Last active March 30, 2023 20:27
Show Gist options
  • Save ubarbaxor/e4e68cc2061bdab43ba62838ecebdc15 to your computer and use it in GitHub Desktop.
Save ubarbaxor/e4e68cc2061bdab43ba62838ecebdc15 to your computer and use it in GitHub Desktop.
Quick margin / PNL ticker + CSV extraction for degens. Runs as-is or in ViolentMonkey.
// ==UserScript==
// @name YOLOREKT UI EDITS
// @namespace Violentmonkey Scripts
// @match https://www.yolorekt.finance/game?gametype=G_LVG
// @grant none
// @version 1.1.5d
// @author ubarbaxor 'Fishotron' will fork for wood
// @description If you like this, feel free to buy me a coffee. ETH/USDC(Polygon) : 0x7cd974D9Ce1014a3Feb5f359c5D9b83174209782
// @downloadURL https://gist.github.com/ubarbaxor/e4e68cc2061bdab43ba62838ecebdc15/raw/fisherTick.js
// @updateURL https://gist.github.com/ubarbaxor/e4e68cc2061bdab43ba62838ecebdc15/raw/fisherTick.js
// @homepageURL https://gist.github.com/ubarbaxor/e4e68cc2061bdab43ba62838ecebdc15
// ==/UserScript==
// DATA SCRAPING / EXTRACTION
dataClasses = [
'status',
'asset',
'entry',
'bidamount',
'bust',
'multiplier',
'exit',
'pandl',
]
format = data => {
const r = data
.replace(/[^\x00-\x7F]/g, '') // remove non-ascii
.replace(',', '') // clean commas from financials
return r.includes('$')// clean dollar sign in numbers
? Number(r.replace('$', ''))
: r
}
extractData = bidElem => dataClasses.reduce((data, index) => {
const dedupLast = bidElem.querySelectorAll(`.${index}`)
const dataText = dedupLast[dedupLast.length - 1].outerText
return {
...data,
[index]: format(dataText)
}
}, {})
sum = field => arr => arr.reduce((total, item) => total + item[field], 0)
getBids = _ => [...document.getElementsByClassName('yourbids_table_row')]
// POLLUTE SCOPE FOR CONVENIENCE
Object.defineProperties(window, {
bids: {
get: getBids
},
bidsData: {
get: function () { return window.bids
.map(extractData) }
},
openBids: {
get: function () { return window.bidsData
.filter(d => d.status === 'Open')
},
},
lockedMargin: {
get: function () {
return sum('bidamount')(window.openBids)
}
},
unrealised: {
get: function () {
return sum('pandl')(window.openBids)
}
}
})
// CSV EXPORT
const genCSV = _ => {
const bid2arr = dataBid => dataClasses.map(c => dataBid[c])
const genRow = arr => arr.join(',')
const header = genRow(dataClasses)
const blob = [
header,
...bidsData.map(bid => genRow(bid2arr(bid)))
].join('\n')
return blob
}
const exportCSV = (filename = 'yolorekt.csv') => {
const blob = new Blob([genCSV()], {type: 'text/csv'})
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename)
}
else {
const el = document.createElement('a')
el.href = window.URL.createObjectURL(blob)
el.download = filename
document.body.appendChild(el)
el.click()
document.body.removeChild(el)
}
}
// UI EDITS
const colors = {
win: 'rgb(0, 194, 19)',
grey: 'rgb(73, 76, 80)',
loss: 'rgb(221, 14, 83)',
white: 'rgb(255, 255, 255)',
}
// ANNOYING SELECTABLE BUTTON FIX
roiCalc = document.querySelector('.roiCalculator')
for (tabsBtn of roiCalc.parentElement.children) {
tabsBtn.style.userSelect = 'none'
}
// CSV EXPORTER
exportBtn = roiCalc.cloneNode(true)
exportBtn.classList.remove('roiCalculator')
exportBtn.style.userSelect = 'none'
exportBtn.innerHTML = 'EXPORT BIDS'
exportBtn.onclick = _ => exportCSV()
roiCalc.after(exportBtn)
// LOCKED MARGIN / PNL TICKER
const balance = document.querySelector('[href="/game/wallet"] span')
let lockedEl = document.getElementById('marginLocked')
if (!lockedEl) {
lockedEl = balance.cloneNode(true)
lockedEl.id = 'marginLocked'
lockedEl.dataset['hint'] = 'Locked margin'
lockedEl.classList.add(
'hint--medium',
'hint--bottom',
'hint--rounded'
)
lockedEl.style.color = colors.grey
lockedEl.style.margin = '1em'
lockedEl.style.textAlign = 'center'
lockedEl.innerText = ''
balance.before(lockedEl)
}
let PandEl = document.getElementById('profitsAnLosses')
if (!PandEl) {
PandEl = balance.cloneNode(true)
PandEl.id = 'profitsAnLosses'
PandEl.dataset['hint'] = 'Open PNL'
PandEl.classList.add(
'hint--small',
'hint--bottom',
'hint--rounded'
)
PandEl.style.textAlign = 'center'
PandEl.innerText = ''
lockedEl.before(PandEl)
}
const updateLocked = _ => {
lockedEl.innerText = lockedMargin.toFixed(2)
}
const updatePnL = _ => {
PandEl.innerText = unrealised.toFixed(2)
PandEl.style.color = unrealised > 0
? colors.win : unrealised < 0
? colors.loss
: colors.grey
}
// Auto-close toasts
let cleanupToasts = true
const updateToasts = _ => {
const latestToast = document.querySelector('[role="alert"]')
if (!latestToast)
return
// Set latest toast to disappear
if (cleanupToasts && !latestToast.matches('.timed')) {
latestToast.classList.add('timed')
setTimeout(_ => { // can't just remove() toast because reasons
const deleteButton = latestToast.querySelector('button')
if (deleteButton)
deleteButton.click()
}, 3000)
}
}
// CASHOUT ALL
window.closeAll = _ => {
const openBEls = bids.filter(b => b
.querySelector('.status')
.matches('.open'))
const closeBtns = openBEls.map(bidEl => bidEl.querySelector('.cashout'))
closeBtns.forEach(btn => btn.click())
}
closeAllBtn = exportBtn.cloneNode(true)
closeAllBtn.innerHTML = 'CLOSE ALL'
closeAllBtn.onclick = _ => { window.closeAll() }
exportBtn.after(closeAllBtn)
const updateCloseAll = _ => {
if (!openBids.length) {
closeAllBtn.disabled = true
closeAllBtn.style.color = colors.grey
} else {
closeAllBtn.removeAttribute('disabled')
closeAllBtn.style.color = unrealised > 0
? colors.win : unrealised < 0
? colors.loss
: colors.white
}
}
// BATCH BIDS
amountInput = document.querySelector('input[type=dial]')
batchInput = amountInput.cloneNode('true')
bidBtn = document.querySelector('.bidInput')
bidBox = bidBtn.parentElement
batchBox = bidBox.cloneNode(true)
batchBtn = batchBox.querySelector('.bidInput')
batchInput.placeholder = '0'
batchBtn.innerHTML = 'BATCH'
bidBox.after(batchBox)
batchBtn.before(batchInput)
batchBid = async _ => {
const getCount = _ => Number(batchInput.value)
const setCount = value => { batchInput.value = value }
const bidCount = getCount()
const bidamount = Number(amountInput.value)
const sendBid = amount => {
const previousValue = amountInput.value
amountInput.value = `${amount}`
amountInput._valueTracker.setValue(previousValue)
amountInput.dispatchEvent(new Event('change', { bubbles: true }))
bidBtn.click()
// UI cooldown
return new Promise((resolve) => { setImmediate(resolve, 10) })
}
while ( getCount() ) {
setCount(getCount() - 1)
await sendBid(bidamount)
}
}
let BBTO
const resetBBtn = _ => {
batchBtn.innerHTML = 'BATCH'
clearTimeout(BBTO)
}
batchBtn.onclick = _ => {
batchBtn.innerHTML = 'DOUBLE-CLICK 😉'
clearTimeout(BBTO)
BBTO = setTimeout(resetBBtn, 1000)
}
batchBtn.ondblclick = _ => {
batchBid()
resetBBtn()
}
batchBtn.disabled = false
// Updates
const refreshTick = _ => {
updateLocked()
updatePnL()
updateToasts()
updateCloseAll()
}
setInterval(refreshTick, 500)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment