feat: 增加财务信息采集
This commit is contained in:
@@ -23,10 +23,9 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TuShareService {
|
||||
public static final DateTimeFormatter TRADE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final String API_URL = "https://api.tushare.pro";
|
||||
private static final String API_TOKEN = "64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e";
|
||||
public static final DateTimeFormatter TRADE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public TuShareService(Jackson2ObjectMapperBuilder builder) {
|
||||
@@ -108,6 +107,124 @@ public class TuShareService {
|
||||
return tuShareResponse;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TuShareResponse incomeList(int year) {
|
||||
var response = HttpUtil.post(API_URL, buildRequest(
|
||||
"income_vip",
|
||||
Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)),
|
||||
List.of(
|
||||
"ts_code",
|
||||
"basic_eps",
|
||||
"diluted_eps",
|
||||
"total_revenue",
|
||||
"revenue",
|
||||
"total_cogs",
|
||||
"oper_cost",
|
||||
"sell_exp",
|
||||
"admin_exp",
|
||||
"fin_exp",
|
||||
"oper_exp",
|
||||
"operate_profit",
|
||||
"non_oper_income",
|
||||
"non_oper_exp",
|
||||
"total_profit",
|
||||
"income_tax",
|
||||
"n_income",
|
||||
"n_income_attr_p",
|
||||
"compr_inc_attr_p",
|
||||
"compr_inc_attr_m_s",
|
||||
"ebit",
|
||||
"ebida",
|
||||
"undist_profit",
|
||||
"distable_profit",
|
||||
"rd_exp",
|
||||
"fin_exp_int_exp",
|
||||
"continued_net_profit",
|
||||
"end_net_profit"
|
||||
)
|
||||
));
|
||||
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
||||
if (tuShareResponse.code != 0) {
|
||||
throw new RuntimeException(tuShareResponse.message);
|
||||
}
|
||||
return tuShareResponse;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TuShareResponse balanceList(int year) {
|
||||
var response = HttpUtil.post(API_URL, buildRequest(
|
||||
"balancesheet_vip",
|
||||
Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)),
|
||||
List.of(
|
||||
"ts_code",
|
||||
"total_share",
|
||||
"cap_rese",
|
||||
"undist_profit",
|
||||
"money_cap",
|
||||
"accounts_receiv",
|
||||
"inventories",
|
||||
"total_cur_assets",
|
||||
"lt_eqt_invest",
|
||||
"lt_rec",
|
||||
"fix_assets",
|
||||
"r_and_d",
|
||||
"goodwill",
|
||||
"total_nca",
|
||||
"total_assets",
|
||||
"lt_borr",
|
||||
"st_borr",
|
||||
"acct_payable",
|
||||
"adv_receipts",
|
||||
"total_cur_lab",
|
||||
"total_ncl",
|
||||
"total_lab",
|
||||
"total_hldr_eqy_exc_min_int",
|
||||
"total_hldr_eqy_inc_min_int",
|
||||
"total_lab_hldr_eqy",
|
||||
"acc_receivable",
|
||||
"payables",
|
||||
"accounts_receiv_bill",
|
||||
"accounts_pay_bill",
|
||||
"oth_rcv_total",
|
||||
"fix_assets_total"
|
||||
)
|
||||
));
|
||||
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
||||
if (tuShareResponse.code != 0) {
|
||||
throw new RuntimeException(tuShareResponse.message);
|
||||
}
|
||||
return tuShareResponse;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TuShareResponse cashFlowList(int year) {
|
||||
var response = HttpUtil.post(API_URL, buildRequest(
|
||||
"cashflow_vip",
|
||||
Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)),
|
||||
List.of(
|
||||
"ts_code",
|
||||
"net_profit",
|
||||
"fin_exp",
|
||||
"c_f_sale_sg",
|
||||
"c_f_oth_oper_a",
|
||||
"c_paid_to_for_empl",
|
||||
"c_paid_for_taxes",
|
||||
"st_cashflow_act",
|
||||
"stoc_inflows_inv_act",
|
||||
"c_paid_subs_oth_biz",
|
||||
"stoc_cashout_inv_act",
|
||||
"stoc_cashout_fin_act",
|
||||
"c_cash_equ_beg_period",
|
||||
"c_cash_equ_end_period"
|
||||
)
|
||||
));
|
||||
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
||||
if (tuShareResponse.code != 0) {
|
||||
throw new RuntimeException(tuShareResponse.message);
|
||||
}
|
||||
return tuShareResponse;
|
||||
}
|
||||
|
||||
public record TuShareResponse(
|
||||
Integer code,
|
||||
@JsonProperty("msg")
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.service.task;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet;
|
||||
import com.lanyuanxiaoyao.leopard.core.entity.CashFlow;
|
||||
import com.lanyuanxiaoyao.leopard.core.entity.Income;
|
||||
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
||||
import com.lanyuanxiaoyao.leopard.core.repository.BalanceSheetRepository;
|
||||
import com.lanyuanxiaoyao.leopard.core.repository.CashFlowRepository;
|
||||
import com.lanyuanxiaoyao.leopard.core.repository.IncomeRepository;
|
||||
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.TaskService;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.TuShareService;
|
||||
import com.yomahub.liteflow.annotation.LiteflowComponent;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 更新财务数据
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250911
|
||||
*/
|
||||
@Slf4j
|
||||
@LiteflowComponent("update_finance")
|
||||
public class UpdateFinanceNode extends TaskNodeComponent {
|
||||
private final TuShareService tuShareService;
|
||||
|
||||
private final StockRepository stockRepository;
|
||||
private final IncomeRepository incomeRepository;
|
||||
private final BalanceSheetRepository balanceSheetRepository;
|
||||
private final CashFlowRepository cashFlowRepository;
|
||||
|
||||
public UpdateFinanceNode(TaskService taskService, TuShareService tuShareService, StockRepository stockRepository, IncomeRepository incomeRepository, BalanceSheetRepository balanceSheetRepository, CashFlowRepository cashFlowRepository) {
|
||||
super(taskService);
|
||||
this.tuShareService = tuShareService;
|
||||
this.stockRepository = stockRepository;
|
||||
this.incomeRepository = incomeRepository;
|
||||
this.balanceSheetRepository = balanceSheetRepository;
|
||||
this.cashFlowRepository = cashFlowRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
var stocks = stockRepository.findAll();
|
||||
var stocksMap = stocks.stream().collect(Collectors.toMap(Stock::getCode, stock -> stock));
|
||||
var currentYear = LocalDate.now().getYear();
|
||||
for (int year = 1990; year < currentYear; year++) {
|
||||
|
||||
var response = tuShareService.incomeList(year);
|
||||
for (List<String> item : response.data().items()) {
|
||||
var code = item.get(0);
|
||||
if (!stocksMap.containsKey(code)) {
|
||||
continue;
|
||||
}
|
||||
var stock = stocksMap.get(code);
|
||||
var income = new Income();
|
||||
income.setStock(stock);
|
||||
income.setYear(year);
|
||||
income.setBasicEarningsPerShare(Double.parseDouble(item.get(1)));
|
||||
income.setDilutedEarningsPerShare(Double.parseDouble(item.get(2)));
|
||||
income.setTotalOperatingRevenue(Double.parseDouble(item.get(3)));
|
||||
income.setOperatingRevenue(Double.parseDouble(item.get(4)));
|
||||
income.setTotalOperatingCost(Double.parseDouble(item.get(5)));
|
||||
income.setOperatingCost(Double.parseDouble(item.get(6)));
|
||||
income.setSellingExpense(Double.parseDouble(item.get(7)));
|
||||
income.setAdministrativeExpense(Double.parseDouble(item.get(8)));
|
||||
income.setFinancialExpense(Double.parseDouble(item.get(9)));
|
||||
income.setOperatingExpense(Double.parseDouble(item.get(10)));
|
||||
income.setOperatingProfit(Double.parseDouble(item.get(11)));
|
||||
income.setAddNonOperatingIncome(Double.parseDouble(item.get(12)));
|
||||
income.setLessNonOperatingExpense(Double.parseDouble(item.get(13)));
|
||||
income.setTotalProfit(Double.parseDouble(item.get(14)));
|
||||
income.setIncomeTaxExpense(Double.parseDouble(item.get(15)));
|
||||
income.setNetProfitIncludingMinorityInterest(Double.parseDouble(item.get(16)));
|
||||
income.setNetProfitExcludingMinorityInterest(Double.parseDouble(item.get(17)));
|
||||
income.setComprehensiveIncomeAttributableToParent(Double.parseDouble(item.get(18)));
|
||||
income.setComprehensiveIncomeAttributableToMinorityShareholders(Double.parseDouble(item.get(19)));
|
||||
income.setEarningsBeforeInterestAndTax(Double.parseDouble(item.get(20)));
|
||||
income.setEarningsBeforeInterestTaxDepreciationAndAmortization(Double.parseDouble(item.get(21)));
|
||||
income.setBeginningUndistributedProfit(Double.parseDouble(item.get(22)));
|
||||
income.setDistributableProfit(Double.parseDouble(item.get(23)));
|
||||
income.setResearchAndDevelopmentExpense(Double.parseDouble(item.get(24)));
|
||||
income.setFinancialExpenseInterestExpense(Double.parseDouble(item.get(25)));
|
||||
income.setNetProfitFromContinuingOperations(Double.parseDouble(item.get(26)));
|
||||
income.setNetProfitFromDiscontinuedOperations(Double.parseDouble(item.get(27)));
|
||||
incomeRepository.save(income);
|
||||
}
|
||||
|
||||
response = tuShareService.balanceList(year);
|
||||
for (List<String> item : response.data().items()) {
|
||||
var code = item.get(0);
|
||||
if (!stocksMap.containsKey(code)) {
|
||||
continue;
|
||||
}
|
||||
var stock = stocksMap.get(code);
|
||||
var balanceSheet = new BalanceSheet();
|
||||
balanceSheet.setStock(stock);
|
||||
balanceSheet.setYear(year);
|
||||
balanceSheet.setEndingTotalShares(Double.parseDouble(item.get(1)));
|
||||
balanceSheet.setCapitalSurplus(Double.parseDouble(item.get(2)));
|
||||
balanceSheet.setUndistributedProfit(Double.parseDouble(item.get(3)));
|
||||
balanceSheet.setMonetaryFunds(Double.parseDouble(item.get(4)));
|
||||
balanceSheet.setAccountsReceivable(Double.parseDouble(item.get(5)));
|
||||
balanceSheet.setInventories(Double.parseDouble(item.get(6)));
|
||||
balanceSheet.setTotalCurrentAssets(Double.parseDouble(item.get(7)));
|
||||
balanceSheet.setLongTermEquityInvestments(Double.parseDouble(item.get(8)));
|
||||
balanceSheet.setLongTermReceivables(Double.parseDouble(item.get(9)));
|
||||
balanceSheet.setFixedAssets(Double.parseDouble(item.get(10)));
|
||||
balanceSheet.setResearchAndDevelopmentExpenditures(Double.parseDouble(item.get(11)));
|
||||
balanceSheet.setGoodwill(Double.parseDouble(item.get(12)));
|
||||
balanceSheet.setTotalNonCurrentAssets(Double.parseDouble(item.get(13)));
|
||||
balanceSheet.setTotalAssets(Double.parseDouble(item.get(14)));
|
||||
balanceSheet.setLongTermBorrowings(Double.parseDouble(item.get(15)));
|
||||
balanceSheet.setShortTermBorrowings(Double.parseDouble(item.get(16)));
|
||||
balanceSheet.setAccountsPayable(Double.parseDouble(item.get(17)));
|
||||
balanceSheet.setAdvancesReceived(Double.parseDouble(item.get(18)));
|
||||
balanceSheet.setTotalCurrentLiabilities(Double.parseDouble(item.get(19)));
|
||||
balanceSheet.setTotalNonCurrentLiabilities(Double.parseDouble(item.get(20)));
|
||||
balanceSheet.setTotalLiabilities(Double.parseDouble(item.get(21)));
|
||||
balanceSheet.setTotalShareholdersEquityExcludingMinorityInterest(Double.parseDouble(item.get(22)));
|
||||
balanceSheet.setTotalShareholdersEquityIncludingMinorityInterest(Double.parseDouble(item.get(23)));
|
||||
balanceSheet.setTotalLiabilitiesAndShareholdersEquity(Double.parseDouble(item.get(24)));
|
||||
balanceSheet.setAccountsReceivable(Double.parseDouble(item.get(25)));
|
||||
balanceSheet.setPayables(Double.parseDouble(item.get(26)));
|
||||
balanceSheet.setNotesAndAccountsReceivable(Double.parseDouble(item.get(27)));
|
||||
balanceSheet.setNotesAndAccountsPayable(Double.parseDouble(item.get(28)));
|
||||
balanceSheet.setOtherReceivablesTotal(Double.parseDouble(item.get(29)));
|
||||
balanceSheet.setFixedAssetsTotal(Double.parseDouble(item.get(30)));
|
||||
balanceSheetRepository.save(balanceSheet);
|
||||
}
|
||||
|
||||
response = tuShareService.cashFlowList(year);
|
||||
for (List<String> item : response.data().items()) {
|
||||
var code = item.get(0);
|
||||
if (!stocksMap.containsKey(code)) {
|
||||
continue;
|
||||
}
|
||||
var stock = stocksMap.get(code);
|
||||
var cashFlow = new CashFlow();
|
||||
cashFlow.setStock(stock);
|
||||
cashFlow.setYear(year);
|
||||
cashFlow.setNetProfit(Double.parseDouble(item.get(1)));
|
||||
cashFlow.setFinancialExpense(Double.parseDouble(item.get(2)));
|
||||
cashFlow.setCashReceivedFromSalesAndServices(Double.parseDouble(item.get(3)));
|
||||
cashFlow.setSubtotalOfCashInflowsFromOperatingActivities(Double.parseDouble(item.get(4)));
|
||||
cashFlow.setCashPaidToAndForEmployees(Double.parseDouble(item.get(5)));
|
||||
cashFlow.setCashPaidForVariousTaxes(Double.parseDouble(item.get(6)));
|
||||
cashFlow.setNetCashFlowFromOperatingActivities(Double.parseDouble(item.get(7)));
|
||||
cashFlow.setSubtotalOfCashInflowsFromInvestingActivities(Double.parseDouble(item.get(8)));
|
||||
cashFlow.setCashPaidForAcquisitionOfFixedIntangibleAndOtherLongTermAssets(Double.parseDouble(item.get(9)));
|
||||
cashFlow.setSubtotalOfCashOutflowsFromInvestingActivities(Double.parseDouble(item.get(10)));
|
||||
cashFlow.setSubtotalOfCashOutflowsFromFinancingActivities(Double.parseDouble(item.get(11)));
|
||||
cashFlow.setBeginningBalanceOfCashAndCashEquivalents(Double.parseDouble(item.get(12)));
|
||||
cashFlowRepository.save(cashFlow);
|
||||
}
|
||||
|
||||
setStep((year - 1990) * 100 / (currentYear - 1990));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user