自动挂单买卖股票

根据设定的价格条件自动买入或卖出指定股票

当前为 2025-07-18 提交的版本,查看 最新版本

// ==UserScript==
// @name         自动挂单买卖股票
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  根据设定的价格条件自动买入或卖出指定股票
// @author       AGSV骄阳
// @match        https://stock.agsvpt.cn/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(async function () {
    'use strict';

    const API_BASE_URL = 'https://stock.agsvpt.cn/api';
    const CONFIG = {
        STOCK_INFO_URL: `${API_BASE_URL}/stocks/info`,
        TOKEN_KEY: 'auth_token',
    };

    const token = localStorage.getItem(CONFIG.TOKEN_KEY);
    if (!token) {
        console.warn('未找到认证Token,脚本无法运行。');
        return;
    }

    function fetchApiData(url, options) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: options.method || 'GET',
                url,
                responseType: 'json',
                timeout: 8000,
                headers: options.headers || {},
                data: options.body || null,
                onload: response => {
                    if (response.status >= 200 && response.status < 300) {
                        resolve(response.response);
                    } else {
                        reject(new Error(`HTTP Error: ${response.status} ${response.statusText}`));
                    }
                },
                onerror: response => reject(new Error('请求失败: ' + response.statusText)),
                ontimeout: () => reject(new Error('请求超时')),
            });
        });
    }

    const getCurrentPrice = async function (stockCode) {
        const url = `${CONFIG.STOCK_INFO_URL}?code=${stockCode}`;
        const responseData = await fetchApiData(url, {
            headers: {
                'Authorization': 'Bearer ' + token
            }
        });

        console.log('API响应数据:', responseData);
        const stockInfo = responseData.find(stock => stock.code === stockCode);
        return stockInfo ? stockInfo.price : undefined;
    };

    async function buyStock(stockCode, quantity) {
        const url = `${API_BASE_URL}/stocks/${stockCode}/buy`;
        const data = { quantity: quantity };

        try {
            const response = await fetchApiData(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + token
                },
                body: JSON.stringify(data)
            });

            console.log('买入响应:', response);
        } catch (error) {
            console.error('买入请求失败:', error);
        }
    }

    async function sellStock(stockCode, quantity) {
        const url = `${API_BASE_URL}/stocks/${stockCode}/sell`;
        const data = { quantity: quantity };

        try {
            const response = await fetchApiData(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + token
                },
                body: JSON.stringify(data)
            });

            console.log('卖出响应:', response);
        } catch (error) {
            console.error('卖出请求失败:', error);

            // 如果是因为T+1限制导致的错误,可以针对性输出提示
            if (error.message.includes("400")) {
                console.error('卖出请求失败,可能是由于T+1限制,请检查是否符合卖出条件。');
            }
        }
    }

    function createInputDialog() {
        return new Promise((resolve) => {
            const dialogHtml = `
                <div id="inputDialog" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); background:white; padding:20px; border:1px solid #ccc; z-index:1000;">
                    <h2>设置股票交易参数</h2>
                    <label for="stockCode">股票代码:</label>
                    <select id="stockCode">
                        <option value="TSLA">TSLA</option>
                        <option value="AAPL">AAPL</option>
                        <option value="GOOGL">GOOGL</option>
                    </select><br><br>
                    <label for="buyPrice">买入价格:</label>
                    <input type="number" id="buyPrice" value="1600" step="0.01"><br><br>
                    <label for="sellPrice">卖出价格:</label>
                    <input type="number" id="sellPrice" value="1900" step="0.01"><br><br>
                    <label for="buyQuantity">买入数量:</label>
                    <input type="number" id="buyQuantity" value="10" min="1"><br><br>
                    <label for="sellQuantity">卖出数量:</label>
                    <input type="number" id="sellQuantity" value="10" min="1"><br><br>
                    <button id="submitBtn">确认</button>
                    <button id="cancelBtn">取消</button>
                </div>
                <div id="overlay" style="position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:999;"></div>
            `;

            document.body.insertAdjacentHTML('beforeend', dialogHtml);

            document.getElementById('submitBtn').onclick = function () {
                const stockCode = document.getElementById('stockCode').value;
                const buyPrice = parseFloat(document.getElementById('buyPrice').value);
                const sellPrice = parseFloat(document.getElementById('sellPrice').value);
                const buyQuantity = parseInt(document.getElementById('buyQuantity').value, 10);
                const sellQuantity = parseInt(document.getElementById('sellQuantity').value, 10);

                // 输入验证
                if (isNaN(buyPrice) || isNaN(sellPrice) || isNaN(buyQuantity) || isNaN(sellQuantity) || buyQuantity <= 0 || sellQuantity <= 0) {
                    alert('请输入有效的价格和数量');
                    return;
                }

                document.getElementById('inputDialog').remove();
                document.getElementById('overlay').remove();
                resolve({ stockCode, buyPrice, sellPrice, buyQuantity, sellQuantity });
            };

            document.getElementById('cancelBtn').onclick = function () {
                document.getElementById('inputDialog').remove();
                document.getElementById('overlay').remove();
                resolve(null);
            };
        });
    }

    const userInput = await createInputDialog();
    if (!userInput) return; // 用户取消

    let { stockCode, buyPrice, sellPrice, buyQuantity, sellQuantity } = userInput;

    async function main() {
        let lastAction = null;

        while (true) {
            try {
                const currentPrice = await getCurrentPrice(stockCode);
                console.log(`当前价格: ${currentPrice}`);

                if (currentPrice !== undefined) {
                    // 买入逻辑
                    if (currentPrice <= buyPrice && lastAction !== 'buy') {
                        lastAction = 'buy';
                        await buyStock(stockCode, buyQuantity);
                    }
                    // 卖出逻辑
                    else if (currentPrice >= sellPrice && lastAction !== 'sell') {
                        lastAction = 'sell';
                        await sellStock(stockCode, sellQuantity);
                    }
                    // 重置操作状态
                    else if (currentPrice < buyPrice || currentPrice > sellPrice) {
                        lastAction = null;
                    }
                } else {
                    console.warn(`无法获取 ${stockCode} 的当前价格`);
                }

                await new Promise(resolve => setTimeout(resolve, 10000)); // 每10秒检查一次价格
            } catch (error) {
                console.error('发生错误:', error);
                await new Promise(resolve => setTimeout(resolve, 10000)); // 遇到错误时也延迟检查
            }
        }
    }

    main();
})();

QingJ © 2025

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