// ==UserScript==
// @name Robinhood QOL Trading
// @namespace Trading
// @match https://robinhood.com/legend/layout/*
// @grant none
// @version 1.3
// @license MIT
// @author AdSam
// @description A Robinhood quality of life script
// ==/UserScript==
const TIMEOUT = 5;
const BUY_KEY = "b";
const SELL_KEY = "s";
const TRAILING_STOP_KEY = "t";
const SET_SHARES_KEY = "z";
const SET_AMOUNT_KEY = "x";
function WaitForElement(selector, index, timeout = 0) {
let iter = 0;
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (timeout != 0 && iter >= timeout * 1000) {
reject(new Error("Timeout"));
}
const elements = document.querySelectorAll(selector);
if (elements[index]) {
clearInterval(interval);
resolve(elements[index]);
}
iter++;
}, 1);
});
}
async function buy(shares) {
let menuBtn = await WaitForElement(
'[data-testid="legend_buy_button"]',
0,
TIMEOUT
);
menuBtn.click();
let dropdown = await WaitForElement('[role="combobox"]', 0, TIMEOUT);
dropdown.click();
let market = await WaitForElement('[role="option"]', 1, TIMEOUT);
market.click();
let quantity = await WaitForElement('[name="quantity"]', 0, TIMEOUT);
quantity.focus();
document.execCommand("selectAll");
document.execCommand("insertText", false, shares.toString());
while (quantity.value != shares.toString()) {}
let buyBtn = await WaitForElement('[type="submit"]', 0, TIMEOUT);
buyBtn.click();
}
async function sell(shares) {
let menuBtn = await WaitForElement(
'[data-testid="legend_sell_button"]',
0,
TIMEOUT
);
menuBtn.click();
let dropdown = await WaitForElement('[role="combobox"]', 0, TIMEOUT);
dropdown.click();
let market = await WaitForElement('[role="option"]', 1, TIMEOUT);
market.click();
let quantity = await WaitForElement('[name="quantity"]', 0, TIMEOUT);
quantity.focus();
document.execCommand("selectAll");
document.execCommand("insertText", false, shares.toString());
while (quantity.value != shares.toString()) {}
let sellBtn = await WaitForElement('[type="submit"]', 0, TIMEOUT);
sellBtn.click();
}
async function trailing_stop(shares, amount) {
let menuBtn = await WaitForElement(
'[data-testid="legend_sell_button"]',
0,
TIMEOUT
);
menuBtn.click();
let dropdown = await WaitForElement('[role="combobox"]', 0, TIMEOUT);
dropdown.click();
let ts = await WaitForElement('[role="option"]', 4, TIMEOUT);
ts.click();
let quantity = await WaitForElement('[name="quantity"]', 0, TIMEOUT);
quantity.focus();
document.execCommand("selectAll");
document.execCommand("insertText", false, shares.toString());
let trail = await WaitForElement('[role="combobox"]', 1, TIMEOUT);
trail.click();
let a = await WaitForElement('[role="option"]', 8, TIMEOUT);
a.click();
let ta = await WaitForElement('[name="trailAmount"]', 1, TIMEOUT);
ta.focus();
document.execCommand("selectAll");
document.execCommand("insertText", false, amount.toString());
let tif = await WaitForElement('[role="combobox"]', 2, TIMEOUT);
tif.click();
let gtc = await WaitForElement('[role="option"]', 6, TIMEOUT);
gtc.click();
while (quantity.value != shares.toString() || !ta.value.includes(amount.toString())) {}
await new Promise(r => setTimeout(r, 100));
let sellBtn = await WaitForElement('[type="submit"]', 0, TIMEOUT);
sellBtn.click();
}
let shares = 0;
let amount = 0;
let debounce = false;
document.addEventListener("keydown", async (event) => {
if (!debounce) {
debounce = true;
try {
if (event.ctrlKey && event.key == BUY_KEY) {
event.preventDefault();
await buy(shares);
} else if (event.ctrlKey && event.key == SELL_KEY) {
event.preventDefault();
await sell(shares);
} else if (event.ctrlKey && event.key == TRAILING_STOP_KEY) {
event.preventDefault();
await trailing_stop(shares, amount)
} else if (event.ctrlKey && event.key == SET_SHARES_KEY) {
event.preventDefault();
const res = prompt("Enter number of shares:");
if (res != null) {
shares = parseFloat(res);
}
} else if (event.ctrlKey && event.key == SET_AMOUNT_KEY) {
event.preventDefault();
const res = prompt("Enter trailing amount:");
if (res != null) {
amount = parseFloat(res);
}
}
} catch (e) {}
debounce = false;
}
});