分类: Technology|技术

  • 使用机器学习预测世界杯赛果

    使用机器学习预测世界杯赛果

    目标

    本文的目标是在学习了TensorFlow的官方Graph Execution示例后,通过举一反三运用其他例子来进一步理解官方示例。因此,本文中的所有结果仅作学术研究用途。

    前提

    IDE准备

    Visual Studio 2017

    打开Visual Studio Installer修改当前使用的VS,确保工作负载Web和云分类下的Python开发被选中。然后切换到单个组件,确保开发活动中的Python语言支持编译器、生成工具和运行时中的Python 3 64-bit被勾选。

    点击右下角的修改,等待组件被下载和安装。

    项目准备

    新建一个Python应用程序项目。在解决方案资源管理器中找到Python环境,并右键添加虚拟环境。虚拟环境的基础解析器为Python 3中的某个版本。

    右键点击新建的虚拟环境,选择安装Python包

    分别安装pip、pandas和tensorflow。这些包在安装的同时,会把所有需要的包进行链式安装。以防万一,以下列出需要用到的所有包及版本。
    [code lang=”text” collapse=”true” title=”requirements.txt”]
    absl-py==0.2.2
    astor==0.6.2
    bleach==1.5.0
    certifi==2018.4.16
    chardet==3.0.4
    cycler==0.10.0
    gast==0.2.0
    get==1.0.3
    grpcio==1.13.0
    html5lib==0.9999999
    idna==2.7
    kiwisolver==1.0.1
    Markdown==2.6.11
    matplotlib==2.2.2
    numpy==1.14.5
    pandas==0.23.1
    pip==10.0.1
    post==1.0.2
    protobuf==3.6.0
    public==1.0.3
    pyparsing==2.2.0
    python-dateutil==2.7.3
    pytz==2018.5
    query-string==1.0.2
    request==1.0.2
    requests==2.19.1
    setuptools==39.2.0
    six==1.11.0
    tensorboard==1.8.0
    tensorflow==1.8.0
    termcolor==1.1.0
    urllib3==1.23
    Werkzeug==0.14.1
    wheel==0.31.1
    [/code]

    数据准备

    既然要进行机器学习,就需要准备好训练数据集测试数据集。训练数据集用于构建出一个能根据特征值推断出标签值模型,而测试数据集用于评估这个模型的准确度,它们都来自一个数据超集。为了有效地对模型进行评判,测试数据集与训练数据集不能有交集。

    我构建的数据超集每一行代表一场比赛,因为我希望以一场比赛为单位预测它的赛果,并有如下的列,并描述为什么我要纳入这些列:

    • 序号:描述这是第几场比赛。随着赛程时间的进行,球员逐渐适应气候环境的情况以及产生的疲倦都可能会影响赛果。
    • 主队:以数字的方式表示这场比赛的主队。我按出场顺序为每支球队定义了一个编号,实际上编号的大小无所谓,只要在整个数据集中用同一个编号表示同一支球队即可。编号对应的球队在后面会给出。
    • 客队:以数字的方式表示这场比赛的客队。
    • 主队胜的平均初始赔率:赔率代表着某家公司对该场比赛该队表现的看法,它本身即是各种可能影响比赛因素的一个综合提炼。而取多家公司的平均赔率更具客观性。
    • 平局的平均初始赔率
    • 主队负的平局初始赔率
    • 主队胜的平局即时赔率:通过与同一行中的初始赔率作比较,获得赔率变化。这个变化可能是因为某些临赛前影响比赛的因素变化所导致的,也可能是公司一开始在掩盖自己的真实意图。即时赔率在数据超集中为截止投注前的最后一次公布的赔率,而在预测条件中为当前得知的最后一次公布的赔率。
    • 平局的平均即时赔率
    • 主队负的平均即时赔率
    • 主队近10场比赛的胜场数:近期比赛会在一定程度上反映出该队伍的状态。
    • 主队近10场比赛的平场数
    • 主队近10场比赛的负场数
    • 客队近10场比赛的胜场数
    • 客队近10场比赛的平场数
    • 客队近10场比赛的负场数
    • 主队积分:这场比赛开始前,按小组赛规则的积分。因为数据集中也纳入了淘汰赛的数据,所以也按胜方积3分,平局各积1分,负方不积分的方式统计。积分在一定程度上反映了之前的比赛结果对该队伍造成的影响。
    • 客队积分
    • 赛果:以数字方式表述这场比赛的结果。主胜为2,平局为1,主负为0.

    前17列为特征列,每列中的每个值即为特征值。最后一列为标签列,每列中的每个值即为标签值。机器学习的目的就是找出已知的特征值与特征值组合和已知标签值之间的关联,然后根据已知的特征值去预测未知的标签。

    这些数据的来源通常可以在投注网站找得到。

    这时前提准备已经完成,可以往项目中添加.py文件及代码。

    代码展示

    先上代码,然后对代码进行讲解。
    [code lang="python" collapse="true" title="WorldCupPredict.py"]
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function

    import argparse
    import tensorflow as tf

    import game_data

    parser = argparse.ArgumentParser()
    parser.add_argument('–batch_size', default=4, type=int, help='捆绑大小')
    parser.add_argument('–train_steps', default=2000, type=int,
    help='训练步数')

    def main(argv):
    args = parser.parse_args(argv[1:])

    # 拉取数据
    (train_x, train_y), (test_x, test_y) = game_data.load_data()

    # 描述输入特征列
    my_feature_columns = []
    for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

    # 构建DNN分类器
    classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # 3个隐藏层
    hidden_units=[20, 15, 5],
    # 设置3个类别
    n_classes=3)

    # 训练模型
    classifier.train(
    input_fn=lambda:game_data.train_input_fn(train_x, train_y,
    args.batch_size),
    steps=args.train_steps)

    # 评估模型
    eval_result = classifier.evaluate(
    input_fn=lambda:game_data.eval_input_fn(test_x, test_y,
    args.batch_size))

    print('\n测试集准确度: {accuracy:0.3f}\n'.format(**eval_result))

    # 建立需要使用模型做出预测的数据
    predict_x = {
    'Game': [57],
    'Home': [4],
    'Away': [9],
    'WinInitialOdds': [4.3],
    'DrawInitialOdds': [3.17],
    'LossInitialOdds': [2.00],
    'WinCurrentOdds': [4.44],
    'DrawCurrentOdds': [3.11],
    'LossCurrentOdds': [2.03],
    'HomeWinCount': [8],
    'HomeDrawCount': [1],
    'HomeLossCount': [1],
    'AwayWinCount': [6],
    'AwayDrawCount': [3],
    'AwayLossCount': [1],
    'HomePoint': [12],
    'AwayPoint': [10]
    }

    predictions = classifier.predict(
    input_fn=lambda:game_data.eval_input_fn(predict_x,
    labels=None,
    batch_size=args.batch_size))

    template = ('\预测结果是"{}" ({:.1f}%)"')

    for pred_dict in predictions:
    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]

    print(template.format(game_data.RESULT[class_id],
    100 * probability))

    if __name__ == '__main__':
    tf.logging.set_verbosity(tf.logging.INFO)
    tf.app.run(main)
    [/code]

    [code lang="python" collapse="true" title="game_data.py"]
    import pandas as pd
    import tensorflow as tf

    TRAIN_URL = "http://120.77.11.178/wp-content/uploads/2018/07/game_training.csv"
    TEST_URL = "http://120.77.11.178/wp-content/uploads/2018/07/game_test.csv"

    CSV_COLUMN_NAMES = ['Game','Home','Away',
    'WinInitialOdds','DrawInitialOdds','LossInitialOdds',
    'WinCurrentOdds','DrawCurrentOdds','LossCurrentOdds',
    'HomeWinCount','HomeDrawCount','HomeLossCount',
    'AwayWinCount','AwayDrawCount','AwayLossCount',
    'HomePoint','AwayPoint','Result']

    RESULT = ['Loss','Draw','Win']

    def maybe_download():
    train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
    test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)

    return train_path, test_path

    def load_data(y_name='Result'):
    """以(train_x, train_y), (test_x, test_y)的形式返回比赛数据集"""
    train_path, test_path = maybe_download()

    train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
    train_x, train_y = train, train.pop(y_name)

    test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)
    test_x, test_y = test, test.pop(y_name)

    return (train_x, train_y), (test_x, test_y)

    def train_input_fn(features, labels, batch_size):
    """训练输入函数"""
    # 将输入转换为数据集
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # 打乱、重复以及捆绑样本
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # 返回数据集
    return dataset

    def eval_input_fn(features, labels, batch_size):
    """验证和预测输入函数"""
    features=dict(features)
    if labels is None:
    # 没有标签,只有特征
    inputs = features
    else:
    inputs = (features, labels)

    # 将输入转换为数据集
    dataset = tf.data.Dataset.from_tensor_slices(inputs)

    # 捆绑样本
    assert batch_size is not None, "batch_size must not be None"
    dataset = dataset.batch(batch_size)

    # 返回数据集
    return dataset

    CSV_TYPES = [[0.0], [0.0], [0.0],
    [0.0], [0.0], [0,0],
    [0.0], [0.0], [0.0],
    [0.0], [0.0], [0.0],
    [0.0], [0.0], [0.0],
    [0.0], [0.0], [0]]

    def _parse_line(line):
    # 解码行
    fields = tf.decode_csv(line, record_defaults=CSV_TYPES)

    # 将结果打包为字典
    features = dict(zip(CSV_COLUMN_NAMES, fields))

    # 将标签从特征中分离
    label = features.pop('Species')

    return features, label

    def csv_input_fn(csv_path, batch_size):
    # 创建一个包含多行文本的数据集
    dataset = tf.data.TextLineDataset(csv_path).skip(1)

    # 转换每一行
    dataset = dataset.map(_parse_line)

    # 打乱、重复和捆绑样本
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # 返回数据集
    return dataset
    [/code]
    首先说WorldCupPredict.py的代码。引用了一些固定的模块,然后确定了步数(step)和捆绑(batch)两个参数。步长并不是越大越好,模型的准确度会从第一步开始逐渐波动上升,到达某个最大值值后可能会下降一点然后再上升一点。当训练数据多的时候,捆绑值可以设大一点。具体意义参考TensorFlow官网的术语表。

    然后会调用game_data模块获得训练数据和测试数据。再对特征值进行标记。之后构建了一个分类器,因为实质上这是个按特征把比赛归类为主胜、平局和主负三种不同赛果的问题。

    分类器中设置了三个隐藏层,每一个特征值都会流进第一层的每个神经元里,第一层神经元的计算结果又会流进第二层,如此类推。直至输出到三个不同的赛果类别中。最佳的隐藏层数的设置和每层神经元数的设置需要通过重复实验和一定的经验决定。往往多的层和多的神经元需要更多的训练数据去达到有效的训练。

    训练完成后会使用测试数据对模型进行评估,并给出准确度。之后可以手动构造一个或多个比赛数据给模型预测。这里使用的是第57场乌拉圭对战法国的数据,这里并没有给出赛果作为标签值。

    模型会根据特征值预测出归属于不同标签的可能性,这里文本输出了最大可能性的标签及其可能性。

    接下来说一下game_data.py的代码。首先定义了两个数据源,采用的是本网站整理好的训练集和测试集。数据源的形式是csv文件,是一种以换行表示数据行,逗号表示一行中的数据列的表格型数据。

    首先需要从网络把数据源下载回到本地,然后将csv数据源整理成可以被计算的张量(tensor)。解析csv文件需要用到一个数据模板向程序说明每列是一个什么数据类型的数据。这里把前面17列的特征值作为浮点数,最后一列作为整数。因为张量转换过程中所有数据的类型要相同。这个文件的代码主要是做了数据整理的工作。

    运行结果

    过程中会给出在训练模型时的信息,包括了步数、进行到当前步数的损失和每秒进行了多少步。

    模型训练完成后,会放入测试集,用于评估模型的准确度。最后会给出一个无标签的特征值的标签预测结果。如上图所示,即评估准确度达到50%,而有39.5%的概率本场(乌拉圭VS法国)的比赛中,主队(乌拉圭)会输。

    读者可以尝试进行以下几种工作:

    1. 调整步长和捆绑对训练和评估的影响。
    2. 调整隐藏层数和神经元个数对训练和评估的影响。
    3. 加入新的特征列和删除已有特征列对训练和评估的影响。
    4. 把该项目作为模板处理其他分类问题。
    5. 壕砸2块钱法国赢。

    数据集文件

    点此下载

  • 基于C#的股票数据获取并进行基础分析的程序

    基于C#的股票数据获取并进行基础分析的程序

    本程序基于C#控制台应用开发,二进制文件(文末附下载地址)需求支持库.NET Framework 4.6.1。主要实现了从HTTPS的JSON数据源获取结构化的股票数据,然后对数据进行分析处理,给出分析结论的功能。

    先放上代码,后面再对代码进行解释。
    [code lang=”csharp” collapse=”true” title=”program.cs”]
    using System;
    using System.Collections.Generic;

    namespace FundsInvestment
    {
    class Program
    {

    static void Main(string[] args)
    {
    Console.WriteLine("!!!免责声明!!!");
    Console.WriteLine("本程序仅用于演示如何从JSON数据源获取数据,并进行基本的分析");
    Console.WriteLine("演示数据源:雪球 www.xueqiu.com");
    Console.WriteLine("演示分析算法:蚂蚁财富 慧定投");
    Console.WriteLine("自动放弃本程序版权,不收取费用或打赏,也不会对你的投资盈亏负责。");
    Console.WriteLine("PROGRAMMING BY:暂置元帅");
    Console.WriteLine("——————–");
    List<Index> list = new List<Index>(5)
    {
    new Index("SH000016"),
    new Index("SH000905"),
    new Index("SZ399006"),
    new Index("NASDAQ"),
    new Index("HKHSCEI")
    };
    foreach (var item in list)
    {
    if (item.FetchData() == true)
    {
    Console.WriteLine(item.Symbol+"拉取数据成功");
    Console.WriteLine("最近一日收盘价" + item.Close);
    Console.WriteLine(item.GetAntFortuneAlgrithmAdvice());
    }
    else
    {
    Console.WriteLine("拉取数据失败");
    }
    Console.WriteLine("——————–");
    }
    while (true)
    {
    try
    {
    Console.Write("输入股票或指数代码(SH/SZ+六位数字):");
    string symbol = Console.ReadLine();
    Console.Write("输入最后一个查询日(年/月/日):");
    DateTime endTime = DateTime.Parse(Console.ReadLine());
    Console.Write("输入往前查询天数(整数):");
    int dayCount = Int32.Parse(Console.ReadLine());
    Console.Write("基础投资额:");
    double baseLine = double.Parse(Console.ReadLine());
    Console.WriteLine("——–数据拉取中——–");

    Index myIndex = new Index(symbol, endTime, dayCount);
    myIndex.BaseLine = baseLine;
    if (myIndex.FetchData() == true)
    {
    Console.WriteLine(myIndex.Symbol + "拉取数据成功");
    Console.WriteLine("最近一日收盘价" + myIndex.Close);
    Console.WriteLine(myIndex.GetAntFortuneAlgrithmAdvice());
    }
    else
    {
    Console.WriteLine("拉取数据失败");
    }
    Console.WriteLine("——————–");
    }
    catch (Exception)
    {
    Console.WriteLine("输入格式有误");
    }

    }
    }
    }
    }
    [/code]
    [code lang=”csharp” collapse=”true” title=”Index.cs”]
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;

    namespace FundsInvestment
    {
    /// <summary>
    /// 指数类
    /// </summary>
    class Index
    {
    /// <summary>
    /// 指数符号
    /// </summary>
    public string Symbol { get; set; }

    /// <summary>
    /// 数据储存对象
    /// </summary>
    private Rootobject DataArray { get; set; }

    /// <summary>
    /// 数据结束日期时间戳
    /// </summary>
    private string EndTimeStamp { get; set; }

    /// <summary>
    /// 数据起始日期时间戳
    /// </summary>
    private string BeginTimeStamp { get; set; }

    /// <summary>
    /// 交易日数量
    /// </summary>
    private int ExchangeDayCount { get; set; }

    /// <summary>
    /// 均值
    /// </summary>
    public double MA { get { return GetMA(); } }

    /// <summary>
    /// 投资基线(默认值20元)
    /// </summary>
    private double baseLine = 20;

    /// <summary>
    /// 投资基线(默认值20元)
    /// </summary>
    public double BaseLine { get => baseLine; set => baseLine = value; }

    /// <summary>
    /// 最近一日收盘价
    /// </summary>
    public double Close
    {
    get
    {
    if(DataArray != null && DataArray.chartlist.Length != 0)
    {
    return DataArray.chartlist.Last().close;
    }
    return 0;
    }
    }

    /// <summary>
    /// 构造指数查询地址(默认从180个交易日前到今天)
    /// </summary>
    /// <param name="Symbol">指数符号</param>
    public Index(string Symbol)
    {
    this.Symbol = Symbol;
    DateTime endDay = DateTime.Today;
    //超量请求数据,填充非交易日
    DateTime beginDay = new DateTime(endDay.Ticks – new TimeSpan(180 * 2, 0, 0, 0).Ticks);
    BeginTimeStamp = GetTimeStamp(beginDay);
    EndTimeStamp = GetTimeStamp(endDay);
    ExchangeDayCount = 180;
    }

    /// <summary>
    /// 构造指数查询地址
    /// </summary>
    /// <param name="Symbol">指数符号</param>
    /// <param name="EndDay">结束日期</param>
    /// <param name="DayCount">交易日个数</param>
    public Index(string Symbol,DateTime EndDay,int DayCount)
    {
    this.Symbol = Symbol;
    DateTime endDay = EndDay;
    //超量请求数据,填充非交易日
    DateTime beginDay = new DateTime(endDay.Ticks – new TimeSpan(DayCount * 2, 0, 0, 0).Ticks);
    BeginTimeStamp = GetTimeStamp(beginDay);
    EndTimeStamp = GetTimeStamp(endDay);
    ExchangeDayCount = DayCount;
    }

    /// <summary>
    /// 拉取数据
    /// </summary>
    /// <returns>是否拉取成功</returns>
    public bool FetchData()
    {
    string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36";
    CookieContainer container = new CookieContainer();
    try
    {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.xueqiu.com");
    request.UserAgent = UserAgent;
    request.Method = "GET";
    request.CookieContainer = container;
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    var jsonRequest = WebRequest.CreateHttp(BuildUri());
    jsonRequest.UserAgent = UserAgent;
    jsonRequest.CookieContainer = container;
    jsonRequest.Method = "GET";
    HttpWebResponse jsonResponse = (HttpWebResponse)jsonRequest.GetResponse();
    var stream = jsonResponse.GetResponseStream();
    var streamReader = new StreamReader(stream);
    var content = streamReader.ReadToEnd();

    DataArray = JsonConvert.DeserializeObject<Rootobject>(content);

    if(DataArray.success == "true")
    {
    return true;
    }
    else
    {
    return false;
    }
    }
    catch (Exception e)
    {
    return false;
    }
    }

    /// <summary>
    /// 获得蚂蚁财富慧定投算法推荐定投额
    /// </summary>
    public string GetAntFortuneAlgrithmAdvice()
    {
    int length = DataArray.chartlist.Length;
    if (length == 0)
    {
    return "无可供计算的数据";
    }
    string advice = string.Empty;
    double ma = GetMA();
    double indexValue = DataArray.chartlist.Last().close;
    if(indexValue > ma)
    {
    double rate = (indexValue – ma) / ma;
    advice += "当前指数值高于均线" + Math.Round((rate * 100),2) + "%,建议本轮投资";
    if (rate < 0.15)
    {
    advice += (BaseLine * 0.9) + "元";
    }
    else if(rate >= 0.15 && rate < 0.5)
    {
    advice += (BaseLine * 0.8) + "元";
    }
    else if (rate >= 0.5 && rate < 1)
    {
    advice += (BaseLine * 0.7) + "元";
    }
    else if (rate >= 1)
    {
    advice += (BaseLine * 0.6) + "元";
    }
    }
    else if(indexValue == ma)
    {
    advice += ("当前指数值等于均线,建议本轮投资" + BaseLine + "元");
    }
    else if(indexValue < ma)
    {
    double rate = (ma – indexValue) / ma;
    advice += "当前指数值低于均线" + Math.Round((rate * 100),2) + "%,";
    List<double> tenDay = new List<double>(10);
    int start = 0;
    //防止数组总长小于10
    if(length >= 10)
    {
    start = length – 10;
    }
    else
    {
    start = 0;
    }
    for (; start < length; start++)
    {
    tenDay.Add(DataArray.chartlist[start].close);
    }
    if ((tenDay.Max() / tenDay.Min()) – 1 > 0.05)
    {
    advice += "过去10日振幅大于5%,建议本轮投资";
    if(rate < 0.05)
    {
    advice += (BaseLine * 0.6) + "元";
    }
    else if(rate >= 0.05 && rate < 0.1)
    {
    advice += (BaseLine * 0.7) + "元";
    }
    else if (rate >= 0.1 && rate < 0.2)
    {
    advice += (BaseLine * 0.8) + "元";
    }
    else if (rate >= 0.2 && rate < 0.3)
    {
    advice += (BaseLine * 0.9) + "元";
    }
    else if (rate >= 0.3 && rate < 0.4)
    {
    advice += BaseLine + "元";
    }
    else if (rate >= 0.4)
    {
    advice += (BaseLine * 1.1) + "元";
    }
    }
    else if ((tenDay.Max() / tenDay.Min()) – 1 <= 0.05)
    {
    advice += "过去10日振幅小于或等于5%,建议本轮投资";
    if (rate < 0.05)
    {
    advice += (BaseLine * 1.6) + "元";
    }
    else if (rate >= 0.05 && rate < 0.1)
    {
    advice += (BaseLine * 1.7) + "元";
    }
    else if (rate >= 0.1 && rate < 0.2)
    {
    advice += (BaseLine * 1.8) + "元";
    }
    else if (rate >= 0.2 && rate < 0.3)
    {
    advice += (BaseLine * 1.9) + "元";
    }
    else if (rate >= 0.3 && rate < 0.4)
    {
    advice += (BaseLine * 2) + "元";
    }
    else if (rate >= 0.4)
    {
    advice += (BaseLine * 2.1) + "元";
    }
    }
    }
    return advice;
    }

    /// <summary>
    /// 获取均值
    /// </summary>
    /// <returns>均值</returns>
    private double GetMA()
    {
    double summary = 0;
    int length = DataArray.chartlist.Length;
    for (int i = length – ExchangeDayCount; i < length; i++)
    {
    summary += DataArray.chartlist[i].close;
    }
    return summary / ExchangeDayCount;
    }

    /// <summary>
    /// 构造指数JSON数据源请求地址
    /// </summary>
    /// <returns>请求地址</returns>
    private string BuildUri()
    {
    string requestUri = string.Empty;
    string headStr = "https://xueqiu.com/stock/forchartk/stocklist.json?";
    requestUri += headStr;
    string symbolStr = "symbol=" + Symbol;
    requestUri += symbolStr;
    requestUri += "&";
    string periodStr = "period=1day";
    requestUri += periodStr;
    requestUri += "&";
    string typeStr = "type=before";
    requestUri += typeStr;
    requestUri += "&";
    string beginStr = "begin=" + BeginTimeStamp;
    requestUri += beginStr;
    requestUri += "&";
    string endStr = "end=" + EndTimeStamp;
    requestUri += endStr;
    return requestUri;
    }

    /// <summary>
    /// 获得时间戳
    /// </summary>
    /// <param name="time">时间</param>
    /// <returns>时间戳</returns>
    private string GetTimeStamp(DateTime time)
    {
    TimeSpan timeSpan = time – new DateTime(1970, 1, 1, 0, 0, 0);
    return Convert.ToInt64(timeSpan.Ticks / 10000).ToString();
    }
    }
    }
    [/code]
    [code lang=”csharp” collapse=”true” title=”Rootobject.cs”]
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace FundsInvestment
    {
    public class Rootobject
    {
    /// <summary>
    /// 股票对象
    /// </summary>
    public Stock stock { get; set; }
    /// <summary>
    /// 查询是否成功
    /// </summary>
    public string success { get; set; }
    public Chartlist[] chartlist { get; set; }
    public class Stock
    {
    /// <summary>
    /// 股票符号
    /// </summary>
    public string symbol { get; set; }
    }
    public class Chartlist
    {
    public long volume { get; set; }
    /// <summary>
    /// 开盘价
    /// </summary>
    public double open { get; set; }
    /// <summary>
    /// 最高价
    /// </summary>
    public double high { get; set; }
    /// <summary>
    /// 收盘价
    /// </summary>
    public double close { get; set; }
    /// <summary>
    /// 最低价
    /// </summary>
    public double low { get; set; }
    /// <summary>
    /// 涨跌额
    /// </summary>
    public double chg { get; set; }
    /// <summary>
    /// 涨跌幅
    /// </summary>
    public double percent { get; set; }
    /// <summary>
    /// 换手率
    /// </summary>
    public double turnrate { get; set; }
    /// <summary>
    /// 5日均值
    /// </summary>
    public double ma5 { get; set; }
    /// <summary>
    /// 10日均值
    /// </summary>
    public double ma10 { get; set; }
    /// <summary>
    /// 20日均值
    /// </summary>
    public double ma20 { get; set; }
    /// <summary>
    /// 30日均值
    /// </summary>
    public double ma30 { get; set; }
    /// <summary>
    /// DIFF值
    /// </summary>
    public double dif { get; set; }
    /// <summary>
    /// DEA值
    /// </summary>
    public double dea { get; set; }
    /// <summary>
    /// MACD值
    /// </summary>
    public double macd { get; set; }
    public long lot_volume { get; set; }
    public long timestamp { get; set; }
    public string time { get; set; }
    }
    }
    }
    [/code]
    Program.cs类是项目自动生成的类。作为程序的入口,并对程序的执行流程进行控制。流程控制主要分为几个部分:1.声明免责信息。2.构造Index类的对象集合,处理例子数据。3.通过无限循环读取用户输入的值构造Index对象,处理特定数据。

    其中使用了Index类的两种构造方法。第一种只需输入股票符号,默认按从180个交易日前到今天进行数据获取。第二种需要输入股票符号、结束日期和交易日个数。

    Rootobject.cs类由JSON字符串通过Visual Studio 2017的“将JSON粘贴为类”功能自动生成,作为反序列化后的模板和DataModel使用。需要注意的是,某些字段的基本变量类型要定义为长整形才由足够的空间存放数据。

    Index.cs类为本程序的业务逻辑类。由构造函数负责获取拼接出股票数据源地址所需的所有参数。

    FetchData方法中使用了HttpWebRequest去获取雪球数据源地址的JSON数据,并把JSON字符串反序列化为Rootobject对象,最后判断是否正确获得数据。这里两次进行GET Request是因为数据源需要先访问主页获取到Cookies权限才能正常访问到数据。

    GetAntFortuneAlgrithmAdvice方法对指定交易日内的数据进行了遍历统计和分析。分析方法参照了蚂蚁财富的慧定投算法。

    编写这个程序的目的是为了按照慧定投的模式对我购买5只互相关度最低指数型基金进行投资,关于股市指数的相关度自动计算程序可能以后会开发。

    慧定投官方给出的跟踪标的并没有我需要的指数,而每个投资周期都去手动计算投资额又过于繁琐。所以本着“重复性的劳动交给程序处理,……”这一人生哲学信条,开发了本程序以解放劳动,同时得以回馈社区。

    最后再次声明:本程序不代表任何投资建议,入市自负盈亏。

    二进制文件

  • dot Net体系命名指引——第二节:选词规则

    dot Net体系命名指引——第二节:选词规则

    .net 体系命名指引

    第二节 选词规则

    原则一:选择易读性强的命名方式。

    解释:当名称由多个单词组成时,以可读性最强的方式排列它们。

    例子:当需要命名一个表格形式的视图容器成员变量时,使用GridView可以比ViewGrid更清晰的定义这个变量。因为“View”既可以作为名词,也可以作为动词。当作为后者时,容易与函数命名的“动词+名词”模式搞混。

    原则二:易读性优于缩略性。

    解释:诚然使用较短的名称能够缩短每行代码的长度,但它不应该以牺牲易读性为前提。

    例子:理科公式中很多量使用了单个西文字母表示,当需要将这些公式编程为函数时,这些量应该用其本来代表的实际意义来表示,至少也要用其读音来表示希腊字母。例如“ω”在某些公式中代表角度,这个量应该命名为Angle,至少也要命名为Omiga,而不是一个“w”。

    原则三:尽量只用“英文字母和数字”的字符。

    解释:即使某些语言允许使用除了英文字母和数字之外的字符来命名,也要少用。一定要用也要有统一的规则。

    例子:为界面上的控件——“确定”按钮命名时,使用BtnConfirm已经能够清晰地分割“按钮”和“确定”两个语义。但是,有时Btn_Confirm这样的命名方式更快地传达出这是一个UI控件的成员,通常由模板生成的,不能自由删改。

    原则四:彻底抛弃匈牙利命名法。

    解释:使用帕斯卡命名法和驼峰命名法已经足够对付dot Net体系的程序元素。首先目前宇宙最强大的IDE——Visual Studio 2017已经能够非常娴熟地对付类型不匹配等早期错误。然后匈牙利命名法和帕斯卡命名法是完全冲突的。

    例子:虽然不应该严格遵守匈牙利命名法,但是其中某些要素是可以吸收改造的。可以使用Is、Can等含有是否、能否语义的单词来代替b组合一个布尔型变量,如使用IsOpen而不是bOpen

    原则五:不使用被各种编程语言广泛作为关键字的单词。

    解释:很明显这会和编程语言本身产生语义级别的冲突。需要注意的是,C#的关键字就有一百多个。

    例子:readonly是关键字之一,所以表示一个只读的变量时,使用IsReadonly

    关于缩略语

    不要自定缩略语。尤其是自定的缩略语会和已有的单词产生歧义。

    尽量少使用缩略语,即使要使用缩略语,也要用被广泛接受的缩略语。

    关于语言特有的命名

    关注语义去命名而不是语言特性关键字。

    例如GetLengthGetSizeGetCount是不同类里面的方法,返回的是一个整型的变量。这时使用前三者而不是GetInt更好。

    使用一些常见的名字,如valueitem,而不是将成员和类型的名字起得一样。除非这个成员没有明显的语义,或者作为函数参数来说它的类型不重要。

  • dot Net体系命名指引——第一节:命名法与分词

    dot Net体系命名指引——第一节:命名法与分词

    .net 体系命名指引

    第一节 命名法与分词

    帕斯卡命名法

    所有单词的首字母大写

    PascalCasing

    只由一个单词构成

    Seed

    单词只由一个字母构成

    CSharp

    由两个单词的首字母缩写的词,都作大写

    IPAddress

    由大于两个单词的首字母缩写的词,只大写第一个字母

    NbaStars

    不是由首字母缩写的词,也只大写第一个字母

    IdCard

    驼峰命名法

    除第一个单词首字母小写外,其余单词首字母大写

    camelCasing

    只由一个单词构成

    value

    单词只由一个字母构成

    ePay

    由两个单词的首字母缩写的词,都作小写

    uiFramework

    由大于两个单词的首字母缩写的词,都作小写

    cssBuilder

    不是由首字母缩写的词,都作小写

    lvHolding

    合成词的处理

    计算机科学专业英语中,有很多常见的合成词。即能从一个单词中明显拆分出两个或以上的独立单词。

    该合成词已被权威英语词典收录时,按一个单词处理

    Password/password

    否则按多个单词处理

    UserName/userName

    至于按哪本权威词典算,由组织内部决定,牛津、柯林斯等都可以。

    美式英语偏好

    由于编程语言由美国公司或组织发明、维护、贡献较多,且美式英语单词往往比英式英语更短,所以偏好使用美式英语词汇。

    color colour

    大小写敏感性

    不同的语言对大小写敏感的支持不同。因此,不能以大小写的不同来区分不同的内容。

  • 全栈工程师硬度指数

    1 Extremely Soft EXS 极软级
    二三线工作,管理,采购,财务,扫地,打水等

    2 Ultra Soft US超软级
    与编码无关的工作,算法设计,美工等

    3 Very Soft VS 甚软级
    网页前端设计等

    4 Soft S 软级
    桌面程序设计,移动设备应用设计,数据库设计,通讯协议设计,信息管理与处理等

    5 Server Network SN 服务器与网络级
    与后台服务器及网络有关的工作,服务器软硬件环境架设,组网等

    6 Embedded E 嵌入级
    与硬件编码有关的工作,嵌入式编程,单片机编程,PLC,FPGA等

    7 Hard H 硬级
    与实施设备安装与维护有关的工作,仪器的分拆,组装,维修等

    8 Very Hard VH 甚硬级
    与实施设备电路有关的工作,装置电路板设计等

    9 Ultra Hard UH 超硬级
    与实施设备机械有关的工作,装置结构设计与制造等

    10 Extremely Hard EXH 极硬级
    与实施环境有关的工作,土木,水路,强电布置与设计等