返回首页 - Notes - 2019

借助 Nightmare 自动购买基金


理论依据

基金定投,上涨少买,下跌多买,等大盘回暖,零花钱总是有的

这里定投十只指数基金,对 A股 各板块有着相对完整的覆盖

  1. 上证50
  2. 沪深300
  3. 中证500
  4. 创业板
  5. 银行
  6. 医药
  7. 电子
  8. 计算机
  9. 证券保险
  10. 食品饮料

具体的基金产品选择的是 天弘 旗下的 C类 基金,无它,种类全,费率相对较低

涨跌数据来源:东方财富,购买基金的平台:雪球蛋卷基金


技术说明

获取每日涨跌数据使用的是 PHP

自动购买基金使用的是 NodeNightmare


抓取涨跌数据

文件名:fund,使用 chmod a+x fund 使其成为可执行文件

具体代码:

#!/usr/bin/env php
<?php

/*
 * 基金短线辅助交易工具(使用东方财富数据)
 */

// 基准金额
$base_money = isset($argv[1]) ? intval(trim($argv[1])) : 100;

//----------------------------------------------------------------------
// 蛋卷基金自动购买专用

$is_danjuan = false;

if (isset($argv[2]) && $argv[2] == 'danjuan') {
    $is_danjuan = true;
    $rate_list  = get_rate_list();
    $code_list  = [
        '上证50'   => '001549',
        '沪深300'  => '005918',
        '中证500'  => '005919',
        '创业板'   => '001593',
        '银行'     => '001595',
        '医药'     => '001551',
        '电子'     => '001618',
        '计算机'   => '001630',
        '证券保险' => '001553',
        '食品饮料' => '001632',
    ];

    $ret_list = [];

    foreach ($rate_list as $name => $rate) {
        $name  = trim($name);
        $code = $code_list[$name];
        $money = calc_purchase_money($base_money, $rate);

        $ret_list[$code] = $money;
    }

    exit(json_encode($ret_list));
}

//----------------------------------------------------------------------

echo '-----------------------------' . PHP_EOL;
$rate_list = get_rate_list();
echo '-----------------------------' . PHP_EOL;

echo '        基准金额 ' . $base_money . PHP_EOL . PHP_EOL;

$sum_money = 0;
foreach ($rate_list as $name => $rate) {
    $money     = calc_purchase_money($base_money, $rate);
    $sum_money += $money;
    echo "{$name}  => 购买金额  {$money}元" . PHP_EOL;
}

echo PHP_EOL;
echo '        购买总额 ' . $sum_money . PHP_EOL . PHP_EOL;

echo '-----------------------------' . PHP_EOL;
echo date('Y-m-d H:i:s') . '    ^-^' . PHP_EOL;

//----------------------------------------------------------------------

function get_rate_list()
{
    global $is_danjuan;

    $token = '7bc05d0d4c3c22ef9fca8c2a912d779c';

    $base_url1 = 'http://nuff.eastmoney.com/EM_Finance2015TradeInterface/JS.ashx?id=';
    $base_url2 = "http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&sty=FDPBPFB&token={$token}&cmd=";

    $fund_list = [
        '上证50  ' => ['/zs000016.html', '0000161', 1],
        '沪深300 ' => ['/zs000300.html', '0003001', 1],
        '中证500 ' => ['/zs000905.html', '0009051', 1],
        '创业板  ' => ['/zs399006.html', '3990062', 1],
        '银行    ' => ['/web/BK04751.html', 'BK04751', 2],
        '医药    ' => ['/web/BK04651.html', 'BK04651', 2],
        '电子    ' => ['/web/BK04591.html', 'BK04591', 2],
        '计算机  ' => ['/web/BK04471.html', 'BK04471', 2],
        '证券保险' => ['/web/BK04731.html', 'BK04731', 2],
        '食品饮料' => ['/web/BK04381.html', 'BK04381', 2],
    ];

    $rate_list = [];

    foreach ($fund_list as $name => [$path, $code, $type]) {
        $rate = 0;

        if ($type == 1) {
            $url  = $base_url1 . $code;
            $data = file_get_contents($url);

            $data = ltrim($data, 'callback(');
            $data = rtrim($data, ')');

            $data = json_decode($data, true);
            $rate = $data['Value'][29];
        } elseif ($type == 2) {
            $url  = $base_url2 . $code;
            $data = file_get_contents($url);

            $data = ltrim($data, '(');
            $data = rtrim($data, ')');

            $data = json_decode($data, true);
            $data = $data[0];

            $data = explode(',', $data);
            $rate = $data[5];
        }

        $rate             = round($rate, 2);
        $rate_list[$name] = $rate;

        if (!$is_danjuan) {
            if ($rate > 0) {
                printf("%s  => 今日上涨  %.2f%%\n", $name, $rate);
            } elseif ($rate < 0) {
                $rate *= -1;
                printf("%s  => 今日下跌  %.2f%% !!!\n", $name, $rate);
            } else {
                echo "{$name}  => 今日持平" . PHP_EOL;
            }
        }

        usleep(100000);
    }

    return $rate_list;
}

function calc_purchase_money($base_money, $rate)
{
    $money = $base_money;
    $rate  = $rate / 100;

    if ($rate > 0) {
        $money = $base_money * (1 - $rate * 30);
    } elseif ($rate < 0) {
        $money = $base_money * (1 - $rate * 50);
    }

    $money = ceil($money);

    if ($base_money >= 100 && $money < 10) {
        $money = 10;
    } elseif ($base_money >= 10 && $money < 1) {
        $money = 1;
    } elseif ($money < 0) {
        $money = 0;
    }

    return $money;
}

//----------------------------------------------------------------------

输出演示:

-----------------------------
上证50    => 今日下跌  1.20% !!!
沪深300   => 今日下跌  0.98% !!!
中证500   => 今日下跌  0.67% !!!
创业板    => 今日下跌  1.09% !!!
银行      => 今日下跌  1.18% !!!
医药      => 今日下跌  0.02% !!!
电子      => 今日下跌  0.44% !!!
计算机    => 今日下跌  1.09% !!!
证券保险  => 今日下跌  1.56% !!!
食品饮料  => 今日下跌  0.71% !!!
-----------------------------
        基准金额 100

上证50    => 购买金额  160元
沪深300   => 购买金额  149元
中证500   => 购买金额  134元
创业板    => 购买金额  155元
银行      => 购买金额  159元
医药      => 购买金额  101元
电子      => 购买金额  122元
计算机    => 购买金额  155元
证券保险  => 购买金额  178元
食品饮料  => 购买金额  136元

        购买总额 1449

-----------------------------
2019-08-13 14:29:14    ^-^

自动购买基金

安装依赖:yarn add nightmare,主干文件名:app.js

具体代码:

const { execFile } = require('child_process');
const Nightmare = require('nightmare');
const nightmare = Nightmare({
    show: true,
    openDevTools: {
        mode: 'detach',
    },
});

//----------------------------------------------------------------------

let username  = '蛋卷账号,应该是手机号码';
let password1 = '登录密码';
let password2 = '支付密码';
let sum_money = 0;

//----------------------------------------------------------------------

const login = (nm) => {
    return nm
        .goto('https://danjuanapp.com/account/login')
        .click('.pass_switch')
        .type('#telno', username)
        .type('#pass', password1)
        .click('#next')
};

const buy = (nm, code, money) => {

    sum_money += money;

    return nm
        .wait(3000)
        .goto(`https://danjuanapp.com/fund/${code}/purchase`)
        .wait(1000)
        .type('input[name="amount"]', money)
        .wait(1000)
        .click('.dj-button')
        .wait(1000)
        .type('input[type="password"]', password2)
};

const end = (nm) =>  {
    return nm
        .goto('https://danjuanapp.com/my-money')
        .evaluate(() => {
            console.log('-----------------');
            console.log('全部购买完毕');
            console.log('-----------------');
        })
        .catch(error => {
            console.error('Error: ', error);
        });
};

//----------------------------------------------------------------------

execFile('/your/path/to/fund', [100, 'danjuan'], (err, stdout, stderr) => {
    if (err) {
        console.log(err);
        return;
    }

    let money_list = JSON.parse(stdout);

    console.log('-------------------------');
    console.log(money_list);
    console.log('-------------------------');

    let nm = login(nightmare);

    nm = buy(nm, '001549', money_list['001549']);  // 上证50
    nm = buy(nm, '005918', money_list['005918']);  // 沪深300
    nm = buy(nm, '005919', money_list['005919']);  // 中证500
    nm = buy(nm, '001593', money_list['001593']);  // 创业板
    nm = buy(nm, '001595', money_list['001595']);  // 银行
    nm = buy(nm, '001551', money_list['001551']);  // 医药
    nm = buy(nm, '001618', money_list['001618']);  // 电子
    nm = buy(nm, '001630', money_list['001630']);  // 计算机
    nm = buy(nm, '001553', money_list['001553']);  // 证券保险
    nm = buy(nm, '001632', money_list['001632']);  // 食品饮料

    end(nm);

    console.log(`总购买金额 ${sum_money} 元`);
});

//----------------------------------------------------------------------

跑起来

每个交易日下午三点前,跑一次脚本即可:node app.js

可自定义每天定投的基准金额:修改 app.jsexecFile 的第一个参数,由 100 改成其他值

除此之外,只需保证自己蛋卷账户余额充足就行了,就是这么卡哇伊


date:2019-08-13、2019-08-19