基金净值估算程序 C#实现

By jerrxjr1220 at 2023-07-24 • 0人收藏 • 973人看过

原来天天基金或者支付宝上都能直接看到当天的基金估值,从今天开始基金的当天估值全部下架了(真是把股民/基民当韭菜了,而且是套着麻袋割

为了能实时看到基金的净值估值,只能自己通过基金公布的重仓股票份额自行估算涨跌。

于是就有了下面的代码,C#实现 Linqpad版,基金的持股份额保存在SQLite数据库中,其实也可以通过查询获取。

var jj = new JiJin("002910");
var stocks = Stocks.Select(x=>x);
foreach (var item in stocks) {    
    var change = GetStockChange(item.Stockname);
    jj.Stock[item.Stockname] = ((double)item.Percentage, change*0.01);
}
jj.Calculate();
jj.Dump();
$"{DateTime.Now.ToShortDateString()} 基金002910的净值估计变动{jj.TotalChange*100}%".Dump();

double GetStockChange(string code)
{
    double change = 0;
    var wc = new WebClient();
    wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183");
    var str = wc.DownloadString("http://qt.gtimg.cn/q=" + code);
    var data = str.Split("~");
    change = Convert.ToDouble(data[32].Trim());
    return change;
}

class JiJin
{
    public string Name { get; private set; }
    public Dictionary<string, (double, double)> Stock {get; set;} = new();
    public double TotalChange {get; set;}
    public JiJin(string name) {
        Name = name;
    }
    public double Calculate() {
        double total=0;
        double totalpercent=0;
        double totalchange=0;
        double count=0;
        foreach (var item in Stock) {
            total += item.Value.Item1 * item.Value.Item2;
            totalpercent += item.Value.Item1;
            totalchange += item.Value.Item2;
            count++;
        }
        totalchange /= count;
        total += (1-totalpercent)*totalchange;
        TotalChange = total;
        return TotalChange;
    }
}

jj.png

13 个回复 | 最后更新于 2023-10-20
2023-07-24   #1

估计的净值,难免是有误差,但是总好过被套着麻袋割韭菜

2023-07-24   #2

改进了一下,直接输入基金代码可以自动查询持仓并计算净值。

通过解析天天基金的网页进行计算。

var jj = new JiJin("012414");
jj.GetChiCang();
jj.Calculate();
$"{DateTime.Now.ToShortDateString()} 基金{jj.Name}的净值估计{jj.TotalChange*100}%".Dump();

class JiJin
{
    public string Name { get; private set; }
    public Dictionary<string, (double, double)> Stock { get; set; } = new();
    public double TotalChange { get; set; }
    public JiJin(string name)
    {
        Name = name;
    }
    public double Calculate()
    {
        double total = 0;
        double totalpercent = 0;
        double totalchange = 0;
        double count = 0;
        foreach (var item in Stock)
        {
            total += item.Value.Item1 * item.Value.Item2;
            totalpercent += item.Value.Item1;
            totalchange += item.Value.Item2;
            count++;
        }
        totalchange /= count;
        total += (1 - totalpercent) * totalchange;
        TotalChange = total;
        return TotalChange;
    }

    public void GetChiCang()
    {
        var wc = new WebClient();
        wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183");
        var str = wc.DownloadString($"http://fund.eastmoney.com/{Name}.html");
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(str);
        var node = doc.DocumentNode.SelectSingleNode("//*[@id='position_shares']/div[1]/table");
        var content = node.InnerText;
        var c = content.Split("  ").Where(x => x.Length > 0);
        var list = c.ToArray()[4..];
        for (var i=0; i<list.Length;i++) {
            if (list[i].EndsWith("%") && list[i+1].EndsWith("%")) {
                Stock[list[i-1]] = (Convert.ToDouble(list[i][..^1])*0.01, Convert.ToDouble(list[i+1][..^1])*0.01);
            }
        }
    }
}

2023/7/24 星期一 基金012414的净值估计-1.1309255000000003%

jj.pngjj.png

对比实际当天净值,基本上还可以。

2023-07-25   #3

牛, 生产力工具好评

2023-07-26   #4

都缺money

大佬可不可以用aardio改写这个估值程序?

2023-07-26   #6

回复#5 @鸿湖重工业株式会社 :

完整工程文件下载:

MyJiJin.zip

aardio不是可以直接调用C#的类的吗?

import console;
console.setTitle("MyJiJin");

import dotNet;
var dll = dotNet.load("\res\JiJin.dll");
var JiJin = dll.import("JiJin");

var jj = JiJin.JiJin("002910");
jj.GetChiCang();
jj.Calculate();

console.log(jj.TotalChange);
 
console.pause();

jj.png

2023-07-27   #7

换了个异步写法,调用天天基金的api接口并解析json列表

var myjj = new JiJin("002910");
myjj.Adjustment = 0.9;
myjj.GetStocks();
myjj.Calculate();
myjj.Dump();


public class JiJin {
    public string _Code { get; private set; }
    public string _Name { get; private set; }
    public double TotalChange {get; set;}
    public double Adjustment {get; set;} = 1;
    public List<Dictionary<string, (double, double)>> Stocks = new List<Dictionary<string, (double, double)>>();
    public JiJin (string code) {
        _Code = code;
    }
    JToken GetChiCang() {
        var wc = new HttpClient();
        wc.DefaultRequestHeaders.Add("User-Agent", "EMProjJijin/6.2.8 (iPhone; iOS 13.6; Scale/2.00)");
        wc.DefaultRequestHeaders.Add("GTOKEN", "98B423068C1F4DEF9842F82ADF08C5db");
        wc.DefaultRequestHeaders.Add("clientInfo", "ttjj-iPhone10,1-iOS-iOS13.6");
        wc.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded");
        wc.DefaultRequestHeaders.Add("Host", "fundmobapi.eastmoney.com");
        wc.DefaultRequestHeaders.Add("Referer", "https://mpservice.com/516939c37bdb4ba2b1138c50cf69a2e1/release/pages/FundHistoryNetWorth");
        var resp = wc.GetStringAsync($"https://fundmobapi.eastmoney.com/FundMNewApi/FundMNInverstPosition?FCODE={_Code}&OSVersion=14.3&appVersion=6.3.8&deviceid=3EA024C2-7F22-408B-95E4-383D38160FB3&plat=Iphone&product=EFund&serverVersion=6.3.6&version=6.3.8");
        return JConstructor.Parse(resp.Result)["Datas"]["fundStocks"];
    }

    async Task<double> GetStockChange(string code)
    {
        double change = 0;
        var wc = new HttpClient();
        wc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183");
        var bytes = await wc.GetByteArrayAsync("http://qt.gtimg.cn/q=" + code);
        var str = Encoding.UTF8.GetString(bytes);
        var data = str.Split("~");
        change = Convert.ToDouble(data[32].Trim());
        return change;
    }

    public void GetStocks() {
        var chicang = GetChiCang();
        foreach (var each in chicang) {
            var stock = ((string)each["GPDM"]).StartsWith("6") ? "sh"+(string)each["GPDM"] : "sz"+(string)each["GPDM"] ;
            var ccbl = (double)each["JZBL"];
            var change = GetStockChange(stock).Result;
            Dictionary<string, (double, double)> s = new Dictionary<string, (double, double)>();
            s[stock] = (ccbl, change);
            Stocks.Add(s);
        }
    }

    public double Calculate()
    {
        double total = 0;
        double totalpercent = 0;
        double totalchange = 0;
        int count = 0;
        foreach (var stock in Stocks)
        {
            KeyValuePair<string, (double, double)> pair = stock.FirstOrDefault();
            total += pair.Value.Item1 * pair.Value.Item2 * 0.0001;
            totalpercent += pair.Value.Item1 * 0.01;
            totalchange += pair.Value.Item2 * 0.01;
            count++;
        }
        totalchange /= count;
        total += (1 - totalpercent) * totalchange;
        TotalChange = total * Adjustment;
        return TotalChange;
    }
}


回复#6 @jerryxjr1220 :

是的,大佬,aardio可以调用C#的dll,只是我还没琢磨透,有的dll调用没问题,有的dll调用就出问题,一直搞不清楚是什么原因。。。

2023-07-28   #9

回复#8 @鸿湖重工业株式会社 :

这个还要看你调用的dll,有些比如申明了允许外部调用,有些可能是私有的,还有些可能是受保护的,或者是只读或者只写的属性。

例如,上面的这个类

public class JiJin {
    public string _Code { get; private set; }
    public string _Name { get; private set; }
    public double TotalChange {get; set;}
    public double Adjustment {get; set;} = 1;
    public List<Dictionary<string, (double, double)>> Stocks = new List<Dictionary<string, (double, double)>>();
    public JiJin (string code) {
        _Code = code;
    }
    ...

这个类的“_Code”的写属性就是私有的,只能在创建时修改“_Code”的值,外部调用时只能是只读的,你要写就会报错。

2023-10-19   #10
<hc:GlowWindow x:Class="WPFFundation.Views.MainWindow"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:vm="clr-namespace:WPFFundation.ViewModels"
               xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
               xmlns:hc="https://handyorg.github.io/handycontrol"
               d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
               mc:Ignorable="d"
               Title="MainWindow" Height="750" Width="600"
               FontFamily="JetBrains Mono"
               FontSize="{StaticResource TextFontSize}" Background="White">
    <Window.Resources>
    </Window.Resources>
    <hc:SimplePanel>
        <ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="5*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="4*" />
                </Grid.ColumnDefinitions>
                <WrapPanel Grid.ColumnSpan="3" VerticalAlignment="Center">
                    <TextBlock Text="Fundation" Style="{StaticResource TextBlockDefaultDanger}"
                               FontSize="20" Margin="20" />
                    <TextBox hc:InfoElement.Placeholder="Input Fundation Code ..." FontSize="20" Foreground="Brown"
                             Style="{StaticResource TextBoxExtend}" Height="45" Width="300"
                             Text="{Binding FundationCode, Mode=TwoWay}" />
                    <Button Style="{StaticResource ButtonDashedDanger}" Margin="20"
                            Content="Search" FontSize="20" Height="40"
                            Command="{Binding SearchCommand}" />
                </WrapPanel>

                <StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center">
                    <TextBox hc:InfoElement.Title="Adjustment" hc:InfoElement.TitlePlacement="Left"
                             hc:InfoElement.Placeholder="1" hc:TitleElement.Foreground="{StaticResource PrimaryBrush}"
                             Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,10"
                             Text="{Binding FundationAdjustment, Mode=TwoWay}" />
                    <TextBox hc:InfoElement.Title="Fundation Name" hc:InfoElement.TitlePlacement="Left"
                             hc:TitleElement.Foreground="{StaticResource PrimaryBrush}"
                             Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,5"
                             Text="{Binding FundationName}" />
                    <TextBox x:Name="estimate" hc:InfoElement.Title="Fundation Estimation"
                             hc:InfoElement.TitlePlacement="Left"
                             hc:TitleElement.Foreground="{StaticResource PrimaryBrush}"
                             Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,5"
                             Text="{Binding FundationEstimation}" />
                    <i:Interaction.Triggers>
                        <i:DataTrigger Binding="{Binding FundationColor}" Value="green">
                            <i:ChangePropertyAction TargetName="estimate" PropertyName="Foreground" Value="Green" />
                        </i:DataTrigger>
                        <i:DataTrigger Binding="{Binding FundationColor}" Value="red">
                            <i:ChangePropertyAction TargetName="estimate" PropertyName="Foreground" Value="Red" />
                        </i:DataTrigger>
                    </i:Interaction.Triggers>
                </StackPanel>
                <ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3">
                    <ListBox ItemsSource="{Binding Fundation.Stocks}">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <WrapPanel>
                                    <TextBlock x:Name="name" Text="{Binding StockName}" Margin="70,10,10,10"
                                               FontSize="20" Width="150" />
                                    <TextBlock x:Name="price" Text="{Binding StockPrice}" Margin="10"
                                               FontSize="16" Width="80" />
                                    <TextBlock x:Name="percent"
                                               Text="{Binding StockChangePercent, StringFormat={}{0} %}" Margin="10"
                                               FontSize="16" Width="80" />
                                    <TextBlock x:Name="volume"
                                               Text="{Binding StockVolumePercentage, StringFormat={}{0} %}" Margin="10"
                                               FontSize="14" />
                                    <i:Interaction.Triggers>
                                        <i:DataTrigger Binding="{Binding StockColor}" Value="green">
                                            <i:ChangePropertyAction TargetName="percent" PropertyName="Foreground"
                                                                    Value="Green" />
                                        </i:DataTrigger>
                                        <i:DataTrigger Binding="{Binding StockColor}" Value="red">
                                            <i:ChangePropertyAction TargetName="percent" PropertyName="Foreground"
                                                                    Value="Red" />
                                        </i:DataTrigger>
                                    </i:Interaction.Triggers>
                                </WrapPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ScrollViewer>
            </Grid>
        </ScrollViewer>
    </hc:SimplePanel>
</hc:GlowWindow>
using System;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;

namespace WPFFundation.Models;

public partial class StockModel : ObservableObject
{
    [ObservableProperty] private double _stockChangePercent;
    [ObservableProperty] private string _stockColor = "Grey";
    [ObservableProperty] private string? _stockName;
    [ObservableProperty] private double _stockPrice;
    [ObservableProperty] private double _stockVolumePercentage;

    public StockModel()
    {
        StockName = string.Empty;
    }
}

public partial class FundationModel : ObservableObject
{
    [ObservableProperty] private double _adjustment;
    [ObservableProperty] private double? _fundationChange;
    [ObservableProperty] private string _fundationName;
    [ObservableProperty] private double? _fundationValue;
    [ObservableProperty] private BindingList<StockModel> _stocks;
    [ObservableProperty] private DateTime _updateDateTime;

    public FundationModel()
    {
        Stocks = new BindingList<StockModel>();
    }
}
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Newtonsoft.Json.Linq;
using WPFFundation.Models;

namespace WPFFundation.ViewModels;

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty] private FundationModel _fundation;
    [ObservableProperty] private double _fundationAdjustment = 1;
    [ObservableProperty] private string? _fundationCode;
    [ObservableProperty] private string _fundationColor = "black";
    [ObservableProperty] private string? _fundationEstimation;
    [ObservableProperty] private string? _fundationName;

    public MainViewModel()
    {
        Fundation = new FundationModel();
        //发送消息
        //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel()));
    }

    [RelayCommand]
    public async Task SearchAsync()
    {
        if(FundationCode.Length>=6)
            await GetStocksAsync(FundationCode);
    }

    public async Task<JToken> GetChiCangAsync(string fundationCode)
    {
        var wc = new HttpClient();
        wc.DefaultRequestHeaders.Add("User-Agent", "EMProjJijin/6.2.8 (iPhone; iOS 13.6; Scale/2.00)");
        wc.DefaultRequestHeaders.Add("GTOKEN", "98B423068C1F4DEF9842F82ADF08C5db");
        wc.DefaultRequestHeaders.Add("clientInfo", "ttjj-iPhone10,1-iOS-iOS13.6");
        wc.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded");
        wc.DefaultRequestHeaders.Add("Host", "fundmobapi.eastmoney.com");
        wc.DefaultRequestHeaders.Add("Referer",
            "https://mpservice.com/516939c37bdb4ba2b1138c50cf69a2e1/release/pages/FundHistoryNetWorth");
        var resp = await wc.GetStringAsync(
            $"https://fundmobapi.eastmoney.com/FundMNewApi/FundMNInverstPosition?FCODE={fundationCode}&OSVersion=14.3&appVersion=6.3.8&deviceid=3EA024C2-7F22-408B-95E4-383D38160FB3&plat=Iphone&product=EFund&serverVersion=6.3.6&version=6.3.8");
        return JObject.Parse(resp)["Datas"]!["fundStocks"]!;
    }

    public async Task<(double, double)> GetStockChangeAsync(string code)
    {
        double change;
        double price;
        var wc = new HttpClient();
        wc.DefaultRequestHeaders.Add("User-Agent",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183");
        var bytes = await wc.GetByteArrayAsync("http://qt.gtimg.cn/q=" + code);
        var str = Encoding.UTF8.GetString(bytes);
        var data = str.Split("~");
        change = Convert.ToDouble(data[32].Trim());
        price = Convert.ToDouble(data[3]);
        return (price, change);
    }

    public async Task GetStocksAsync(string fundationCode)
    {
        var chicang = await GetChiCangAsync(fundationCode);
        Fundation.Stocks.Clear();
        foreach (var each in (JArray)chicang)
        {
            double stockPrice;
            double stockChangePercent;
            var stockCode = ((string)each["GPDM"]!).StartsWith("6")
                ? string.Format("sh{0:D6}", int.Parse((string)each["GPDM"]!))
                : string.Format("sz{0:D6}", int.Parse((string)each["GPDM"]!));
            var ccbl = (double)each["JZBL"]!;
            var stockName = each["GPJC"]!.ToString();
            (stockPrice, stockChangePercent) = await GetStockChangeAsync(stockCode);
            var color = stockChangePercent > 0 ? "red" : "green";
            Fundation.Stocks.Add(new StockModel
            {
                StockName = stockName, StockPrice = stockPrice, StockChangePercent = stockChangePercent,
                StockVolumePercentage = ccbl, StockColor = color
            });
            Calculate();
        }
    }

    public void Calculate()
    {
        double sum = 0;
        double restPercentage = 100;
        double averageChangePercent = 0;
        foreach (var stock in Fundation.Stocks)
        {
            sum += stock.StockChangePercent * stock.StockVolumePercentage * 0.01;
            restPercentage -= stock.StockVolumePercentage;
            averageChangePercent += stock.StockChangePercent;
        }

        averageChangePercent /= Fundation.Stocks.Count;
        sum += restPercentage * averageChangePercent * 0.01;
        FundationEstimation = $"{sum * FundationAdjustment:F2} %";
        FundationColor = sum > 0 ? "red" : "green";
    }
}

//定义消息
// public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..)
// {
//     public CustomizedMessage(MessageModel value) : base(value)
//     {
//     }
// }
using HandyControl.Controls;
using WPFFundation.ViewModels;

namespace WPFFundation.Views;

/// <summary>
///     Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : GlowWindow
{
    private readonly MainViewModel mainViewModel;

    public MainWindow()
    {
        InitializeComponent();

        #region 注册并接受消息

        //WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); });

        #endregion


        mainViewModel = new MainViewModel();
        DataContext = mainViewModel;
    }
}


2023-10-20   #11

能赚钱吗

我问的比较春。。。。

2023-10-20   #12

回复#11 @hi_aardio :

量化基金赚钱, 只是慢而已

2023-10-20   #13

回复#11 @hi_aardio :

我只分享编程技术,不是推荐投资基金啊

亏惨了别怨我

登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...