Robinhood QOL Trading

A Robinhood quality of life script

当前为 2025-01-23 提交的版本,查看 最新版本

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址