diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..52c909e Binary files /dev/null and b/.DS_Store differ diff --git a/plugin-finance-function/.DS_Store b/plugin-finance-function/.DS_Store new file mode 100644 index 0000000..037447d Binary files /dev/null and b/plugin-finance-function/.DS_Store differ diff --git a/plugin-finance-function/build.gradle b/plugin-finance-function/build.gradle new file mode 100644 index 0000000..89632b2 --- /dev/null +++ b/plugin-finance-function/build.gradle @@ -0,0 +1,122 @@ + +apply plugin: 'java' + + +ext { + /** + * 项目中依赖的jar的路径 + * 1.如果依赖的jar需要打包到zip中,放置在lib根目录下 + * 2.如果依赖的jar仅仅是编译时需要,防止在lib下子目录下即可 + */ + libPath = "$projectDir/../webroot/WEB-INF/lib" + + /** + * 是否对插件的class进行加密保护,防止反编译 + */ + guard = true + + def pluginInfo = getPluginInfo() + pluginPre = "fine-plugin" + pluginName = pluginInfo.id + pluginVersion = pluginInfo.version + + outputPath = "$projectDir/../webroot/WEB-INF/plugins/plugin-" + pluginName + "-1.0/classes" +} + +group = 'com.fr.plugin' +version = '10.0' +sourceCompatibility = '8' + +sourceSets { + main { + java.outputDir = file(outputPath) + output.resourcesDir = file(outputPath) + } +} + +ant.importBuild("encrypt.xml") +//定义ant变量 +ant.projectDir = projectDir +ant.references["compile.classpath"] = ant.path { + fileset(dir: libPath, includes: '**/*.jar') + fileset(dir: ".",includes:"**/*.jar" ) +} + +classes.dependsOn('clean') + +task copyFiles(type: Copy,dependsOn: 'classes'){ + from outputPath + into "$projectDir/classes" +} + +task preJar(type:Copy,dependsOn: guard ? 'compile_encrypt_javas' : 'compile_plain_javas'){ + from "$projectDir/classes" + into "$projectDir/transform-classes" + include "**/*.*" +} +jar.dependsOn("preJar") + +task makeJar(type: Jar,dependsOn: preJar){ + from fileTree(dir: "$projectDir/transform-classes") + baseName pluginPre + appendix pluginName + version pluginVersion + destinationDir = file("$buildDir/libs") + + doLast(){ + delete file("$projectDir/classes") + delete file("$projectDir/transform-classes") + } +} + +task copyFile(type: Copy,dependsOn: ["makeJar"]){ + from "$buildDir/libs" + from("$projectDir/lib") { + include "*.jar" + } + from "$projectDir/plugin.xml" + into file("$buildDir/temp/plugin") +} + +task zip(type:Zip,dependsOn:["copyFile"]){ + from "$buildDir/temp/plugin" + destinationDir file("$buildDir/install") + baseName pluginPre + appendix pluginName + version pluginVersion +} + +//控制build时包含哪些文件,排除哪些文件 +processResources { +// exclude everything +// 用*.css没效果 +// exclude '**/*.css' +// except this file +// include 'xx.xml' +} + +/*读取plugin.xml中的version*/ +def getPluginInfo(){ + def xmlFile = file("plugin.xml") + if (!xmlFile.exists()) { + return ["id":"none", "version":"1.0.0"] + } + def plugin = new XmlParser().parse(xmlFile) + def version = plugin.version[0].text() + def id = plugin.id[0].text() + return ["id":id,"version":version] +} + +repositories { + mavenLocal() + maven { + url = uri('http://mvn.finedevelop.com/repository/maven-public/') + } +} + +dependencies { + //使用本地jar + implementation fileTree(dir: 'lib', include: ['**/*.jar']) + implementation fileTree(dir: libPath, include: ['**/*.jar']) +} + diff --git a/plugin-finance-function/build/.DS_Store b/plugin-finance-function/build/.DS_Store new file mode 100644 index 0000000..82221db Binary files /dev/null and b/plugin-finance-function/build/.DS_Store differ diff --git a/plugin-finance-function/build/install/fine-plugin-com.fr.plugin.function.finance-3.0.zip b/plugin-finance-function/build/install/fine-plugin-com.fr.plugin.function.finance-3.0.zip new file mode 100644 index 0000000..5f0364b Binary files /dev/null and b/plugin-finance-function/build/install/fine-plugin-com.fr.plugin.function.finance-3.0.zip differ diff --git a/plugin-finance-function/build/libs/fine-plugin-com.fr.plugin.function.finance-3.0.jar b/plugin-finance-function/build/libs/fine-plugin-com.fr.plugin.function.finance-3.0.jar new file mode 100644 index 0000000..4de59d7 Binary files /dev/null and b/plugin-finance-function/build/libs/fine-plugin-com.fr.plugin.function.finance-3.0.jar differ diff --git a/plugin-finance-function/build/temp/plugin/fine-plugin-com.fr.plugin.function.finance-3.0.jar b/plugin-finance-function/build/temp/plugin/fine-plugin-com.fr.plugin.function.finance-3.0.jar new file mode 100644 index 0000000..4de59d7 Binary files /dev/null and b/plugin-finance-function/build/temp/plugin/fine-plugin-com.fr.plugin.function.finance-3.0.jar differ diff --git a/plugin-finance-function/build/temp/plugin/plugin.xml b/plugin-finance-function/build/temp/plugin/plugin.xml new file mode 100644 index 0000000..5d7d19e --- /dev/null +++ b/plugin-finance-function/build/temp/plugin/plugin.xml @@ -0,0 +1,85 @@ + + com.fr.plugin.function.finance + + yes + 3.0 + 10.0~11.0 + 2020-04-01 + yanghui + + + ]]> + + ]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin-finance-function/build/tmp/makeJar/MANIFEST.MF b/plugin-finance-function/build/tmp/makeJar/MANIFEST.MF new file mode 100644 index 0000000..59499bc --- /dev/null +++ b/plugin-finance-function/build/tmp/makeJar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/plugin-finance-function/encrypt.xml b/plugin-finance-function/encrypt.xml new file mode 100644 index 0000000..1401cd1 --- /dev/null +++ b/plugin-finance-function/encrypt.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin-finance-function/plugin.xml b/plugin-finance-function/plugin.xml new file mode 100644 index 0000000..5d7d19e --- /dev/null +++ b/plugin-finance-function/plugin.xml @@ -0,0 +1,85 @@ + + com.fr.plugin.function.finance + + yes + 3.0 + 10.0~11.0 + 2020-04-01 + yanghui + + + ]]> + + ]]> + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin-finance-function/readme.md b/plugin-finance-function/readme.md new file mode 100644 index 0000000..143418f --- /dev/null +++ b/plugin-finance-function/readme.md @@ -0,0 +1,48 @@ +# 函数插件 +该插件实现Excel中的PMT,RATE,IRR,XIRR的财务函数; + +XIRR函数的功能:计算一组定期或者不定期现金流的内部收益率; +XIRR函数的语法结构:XIRR(values, dates, [guess]), +values为现金流; +dates为现金流相对应的日期; +guess为选填参数,表示预期利率,不填系统默认为10%; +XIRR使用示例:XIRR(A1:A25,B1:B25)或XIRR(A1:A25,B1:B25,0.11),所选区域支持去空,比如只有24期数据,选择A1:A34也是可以的,但为保证计算结果的准确性,必须保证中间数据是连续的; + + +IRR函数的功能:计算一组定期现金流的内部收益率; +IRR函数的语法结构:XIRR(values, [guess])。 +values为必填参数,表示现金流; +guess为选填参数,表示预期利率,不填系统默认为10%; +IRR使用示例:XIRR(A1:A25)或XIRR(A1:A250.11),所选区域支持去空,比如只有24期数据,选择A1:A34也是可以的,但为保证计算结果的准确性,必须保证中间数据是连续的; + +RATE函数的功能:计算每期年金的利率; +RATE函数的语法结构:RATE(nper, pmt, pv, [fv], [type], [guess]), +nper必填参数,为年金的付款总期数; +pmt必填参数,为每期的付款金额; +pv必填参数,为现值即一系列未来付款当前值的总和; +fv可选参数。 表示未来值,或在最后一次付款后希望得到的现金余额。 如果省略 fv,则假定其值为 0; +type可选参数,用以指定各期的付款时间是在期初还是期末,传值为数字 0-期末 或 1-期初,默认为期末; +guess可选参数,表示预期利率。如果省略 guess,则假定其值为 10%。 +RATE函数使用示例:rate(24,14708.05,-315450),在相同条件下贷款的年利率为rate(24,14708.05,-315450)*12 + + +PMT函数的功能:用于根据固定付款额和固定利率计算贷款的付款额; +PMT函数的语法结构:PMT(rate, nper, pv, [fv], [type]); +rate参数必填,表示贷款利率; +nper参数必填,表示该项贷款的付款总数,即期数; +pv参数必填,表示现值,或一系列未来付款额现在所值的总额,也叫本金; +fv可选参数。 表示未来值,或在最后一次付款后希望得到的现金余额。 如果省略 fv,则假定其值为 0; +type可选参数,用以指定各期的付款时间是在期初还是期末,传值为数字 0-期末 或 1-期初,默认为期末; +PMT使用的示例:pmt(0.078/12,24,20000,,1)或pmt(0.078/12,24,20000) + + + + + + + + + + + + diff --git a/plugin-finance-function/src/.DS_Store b/plugin-finance-function/src/.DS_Store new file mode 100644 index 0000000..672f0f5 Binary files /dev/null and b/plugin-finance-function/src/.DS_Store differ diff --git a/plugin-finance-function/src/main/.DS_Store b/plugin-finance-function/src/main/.DS_Store new file mode 100644 index 0000000..35f7ddd Binary files /dev/null and b/plugin-finance-function/src/main/.DS_Store differ diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/DateUtil.java b/plugin-finance-function/src/main/java/com/fr/plugin/DateUtil.java new file mode 100644 index 0000000..e03bab8 --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/DateUtil.java @@ -0,0 +1,565 @@ +package com.fr.plugin; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * + * @{# DateUtil.java Create on 2013-4-9 + * + * class desc: + * + *

+ * Copyright: Copyright(c) 2013 + *

+ *

+ * Company: morelap + *

+ * @Version 1.0 + * @Author Morelap + * + * + */ +public class DateUtil { + public static final long Minute_MilliSecond = 60 * 1000; + public static final long Hour_MilliSecond = Minute_MilliSecond * 60; + /** + * milliseconds of a day + */ + public static final long Day_MilliSecond = Hour_MilliSecond * 24; + /** + * milliseconds of a week + */ + public static final long Week_MilliSecond = Day_MilliSecond * 7; + /** + * milliseconds of a month + */ + public static final long Month_MilliSecond = Week_MilliSecond * 30; + /** + * yyyyMMdd + */ + public static final String Date_Default_Formate = "yyyyMMdd"; + /** + * yyyy-MM-dd HH:mm:ss 2010-05-11 17:22:26 + */ + public static final String Date_Formate_All = "yyyy-MM-dd HH:mm:ss"; + /** + * dd/MM/yyyy, hh:mm + */ + public static final String DATE_FORMATE_TRANSACTION = "dd/MM/yyyy, hh:mm"; + /** + * MM/dd HH:mm + */ + public static final String DATE_FORMATE_DAY_HOUR_MINUTE = "MM/dd HH:mm"; + /** + * HH:mm + */ + public static final String DATE_FORMATE_HOUR_MINUTE = "HH:mm"; + public static final String DATE_FORMATE_HOUR_MINUTE_SECOND = "HH:mm:ss"; + + public static SimpleDateFormat dateFormate = new SimpleDateFormat(); + + /** + * 获取当前的字符串日期 + * + * @param splite + * 格式的分割线如 - 则获取的日期时间格式如下: + * @return 返回2013-07-19 + */ + public static String getNowStringDate(String splite) { + StringBuffer format = new StringBuffer(); + if (splite == null) { + format.append(Date_Default_Formate); + } else { + format.append("yyyy").append(splite).append("MM").append(splite) + .append("dd"); + } + return (new SimpleDateFormat(format.toString())).format(new Date()); + } + + /** + * 获取当前的字符串日期时间 + * + * @param splite + * 格式的分割线如 - 则获取的日期时间格式如下: + * @return 返回2013-07-19 09:08:22 + */ + public static String getNowStringDateTime(String splite) { + StringBuffer format = new StringBuffer(); + if (splite == null) { + format.append(Date_Default_Formate); + } else { + format.append("yyyy").append(splite).append("MM").append(splite) + .append("dd").append(" HH:mm:ss"); + } + return (new SimpleDateFormat(format.toString())).format(new Date()); + } + + /** + * 获取当前的字符串期时间 + * + * @param splite + * 格式的分割线如 - 则获取的日期时间格式如下: + * @return 返回09:08:22 + */ + public static String getNowStringTime() { + StringBuffer format = new StringBuffer(DATE_FORMATE_HOUR_MINUTE_SECOND); + return (new SimpleDateFormat(format.toString())).format(new Date()); + + } + + /** + * 获取昨天的日期 + * + * @return Date + */ + public static Date getYesterdayDate() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DATE, -1); + return calendar.getTime(); + } + + /** + * 获取指定日期的后面的一天 即日期+1 + * + * @param stringDate + * 格式为:2012-02-02 + * @return Date + */ + public static Date getForwardDate(String stringDate) { + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + + c.add(Calendar.DATE, 1);// 在此日期的基础上+1 + return c.getTime(); + } + + /** + * 获取指定日期的后面的一天 即日期+1 + * + * @param stringDate + * 格式为:2012-02-02 + * @return Date + */ + public static String getForwardStringDate(String stringDate, String splite) { + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.DATE, 1);// 在此日期的基础上+1 + return getStringDateByDate(c.getTime(), splite); + } + + /** + * 根据传进来的日期date 和格式分割线返回一个字符串的日期 + * + * @param date + * 日期 + * @param splite + * 日期格式的分割线 如:- + * @return 2013-07-19 + */ + public static String getStringDateByDate(Date date, String splite) { + StringBuffer format = new StringBuffer(); + if (splite == null) { + format.append(Date_Default_Formate); + } else { + format.append("yyyy").append(splite).append("MM").append(splite) + .append("dd"); + } + if (date == null) { + date = new Date(); + } + return (new SimpleDateFormat(format.toString())).format(date); + } + + /** + * 根据传进来的日期stringdate 和格式分割线返回一个字符串的日期 + * + * @param stringDate + * 日期 + * @param splite + * 日期格式的分割线 如:- + * @return 2013-07-19 + */ + public static String getStringDateByStringDate(String stringDate, + String splite) { + Date date = getDateByStringTime(stringDate); + return getStringDateByDate(date, splite); + } + + /** + * 根据传进来的整数获取一个日期Date + * + * @param year + * 年 + * @param month + * 月 + * @param day + * 日 + * @return Date + */ + public static Date getDateByIntegerDate(int year, int month, int day) { + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + return c.getTime(); + } + + /** + * 根据传进来的字符串日期格式 获得一个date + * + * @param stringTime + * 格式如:2013-02-13 中间的-可以为其他字符 + * @return date + */ + public static Date getDateByStringTime(String stringTime) { + int year = Integer.parseInt(stringTime.substring(0, 4)); + int month = Integer.parseInt(stringTime.substring(5, 7)); + int day = Integer.parseInt(stringTime.substring(8, 10)); + return getDateByIntegerDate(year, month, day); + } + + /** + * 获得指定日期 在1970年到现在的天数 + * + * @param date + * @return 天数 + */ + public static long getDaysFrom1970(Date date) { + String stringTime = DateUtil.getStringDateByDate(date, "-"); + int year = Integer.parseInt(stringTime.substring(0, 4)); + int month = Integer.parseInt(stringTime.substring(5, 7)); + int day = Integer.parseInt(stringTime.substring(8, 10)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + return (int) (c.getTimeInMillis() / 86400000); + } + + /** + * 获得指定日期 在1970年到现在的天数 + * + * @param 年 + * 月 日 + * @return 天数 + */ + public static long getDaysFrom1970(int year, int month, int day) { + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + return (int) (c.getTimeInMillis() / 86400000); + } + + /** + * 获得指定日期 在1970年到现在的天数 + * + * @param date 格式2013-03-05 中间的-可以是其他符号 或者没有符号 + * @param hasSplite 代表中间是否有符号,默认是有的,如果为false则日期的格式为:20130305 + * @return 天数 + */ + public static long getDaysFrom1970(String stringTime,boolean hasSplite) { + int year,month,day; + if(hasSplite){ + year = Integer.parseInt(stringTime.substring(0, 4)); + month = Integer.parseInt(stringTime.substring(5, 7)); + day = Integer.parseInt(stringTime.substring(8, 10)); + }else{ + year = Integer.parseInt(stringTime.substring(0, 4)); + month = Integer.parseInt(stringTime.substring(4, 6)); + day = Integer.parseInt(stringTime.substring(6, 8)); + } + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + return (int) (c.getTimeInMillis() / 86400000); + } + /** + * 获得今年年初的第一天日期 如2013-01-01 + * + * @param splite + * 分隔符号 + * @return 2013-01-01(字符串形式) + */ + public static String getYearStartString(String splite) { + String sDate = getNowStringDate(splite); + int year = Integer.parseInt(sDate.substring(0, 4)); + String yearStart = new StringBuffer().append(year).append(splite) + .append("01").append(splite).append("01").toString(); + return yearStart; + } + + /** + * 获得今年年初的第一天日期 如2013-01-01 + * + * @return 2013-01-01日期格式 + */ + public static Date getYearStartDate() { + String sDate = getNowStringDate("-"); + int year = Integer.parseInt(sDate.substring(0, 4)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, 0); + c.set(Calendar.DAY_OF_MONTH, 1); + return c.getTime(); + } + + /** + * 获得上个周末的 字符串日期, 指星期日 + * @param splite + * 分隔符号 - + * @return String 格式:2013-06-22 + */ + public static String getLastWeekStringDate(String splite) { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + int sundayPlus=getSundayPlus(); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.DATE, -sundayPlus);// 在此日期的基础上减去sundayPlus就是上个周末的日期 + return getStringDateByDate(c.getTime(), splite); + } + + /** + * 获得上个周末的日期,date格式 + * + * @return Date + */ + public static Date getLastWeekDate() { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + int sundayPlus=getSundayPlus(); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.DATE, -sundayPlus);// 在此日期的基础上减去sundayPlus就是上个周末的日期 + return c.getTime(); + + } + + /** + * 获得上个月末的 字符串日期 + * @return String + */ + public static String getLastMonthStringDate(String splite) { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + int monthPlus=getMonthPlus(); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.DATE, -monthPlus);// 在此日期的基础上减去sundayPlus就是上个周末的日期 + return getStringDateByDate(c.getTime(), splite); + } + + /** + * 获得上个月末的日期,date格式 + * + * @return Date + */ + public static Date getLastMonthDate() { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + int day = Integer.parseInt(stringDate.substring(8, 10)); + int monthPlus=getMonthPlus(); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.DATE, -monthPlus);// 在此日期的基础上减去sundayPlus就是上个周末的日期 + return c.getTime(); + + } + + + /** + * 获得上个季度末的 字符串日期 + * @return String 2012-03-30 + */ + public static String getLastQuarterStringDate(String splite) { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + switch (month) { + case 1: + case 2: + case 3: + month=12; + year--; + break; + case 4: + case 5: + case 6: + month=3; + break; + case 7: + case 8: + case 9: + month=6; + break; + case 10: + case 11: + case 12: + month=9; + break; + default: + break; + } + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month);//这里月份本来是要-1的,所以这里就相当于月份month+1, + c.set(Calendar.DAY_OF_MONTH, 1);//此时的日期刚好为month+1月份的一号 + c.add(Calendar.DATE, -1);// 在1号的基础上减去一天,就是month月末的日期 + return getStringDateByDate(c.getTime(), splite); + } + + /** + * 获得上个季度末的日期,date格式 + * @return Date + */ + public static Date getLastQuarterDate() { + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + int month = Integer.parseInt(stringDate.substring(5, 7)); + switch (month) { + case 1: + case 2: + case 3: + month=12; + year--; + break; + case 4: + case 5: + case 6: + month=3; + break; + case 7: + case 8: + case 9: + month=6; + break; + case 10: + case 11: + case 12: + month=9; + break; + default: + break; + } + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month);//这里月份本来是要-1的,所以这里就相当于月份month+1, + c.set(Calendar.DAY_OF_MONTH, 1);//此时的日期刚好为month+1月份的一号 + c.add(Calendar.DATE, -1);// 在1号的基础上减去一天,就是month月末的日期 + return c.getTime(); + } + /** + * 获得指定月份的,月末日期 + * @param month 月 + * @return String + */ + public static String getMonthEndStringDateByMonth(int month,String splite){ + String stringDate=getNowStringDate("-"); + int year = Integer.parseInt(stringDate.substring(0, 4)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month);//这里月份本来是要-1的,所以这里就相当于月份month+1, + c.set(Calendar.DAY_OF_MONTH, 1);//此时的日期刚好为month+1月份的一号 + c.add(Calendar.DATE, -1);// 在1号的基础上减去一天,就是month月末的日期 + return getStringDateByDate(c.getTime(), splite); + } + + /** + * 获得当前日期与上周日相差的天数 按照外国的理解:星期日是第一天,星期一是第二天. 中国:星期一是第一天. + * + * @return int今天和上周日的相差日期 + */ + public static int getSundayPlus() { + Calendar c = Calendar.getInstance(); + // 获得今天是一周的第几天,星期日是第一天,星期二是第二天...... + int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1; // 因为按中国礼拜一,作为第一天所以这里减1 + if(dayOfWeek==0){ + dayOfWeek=7; + } + return dayOfWeek; + } + + /** + * 获得当前日期与上个月末日期的相差天数 + * + * @return int 今天和上月末的相差日期 + */ + public static int getMonthPlus() { + Calendar c = Calendar.getInstance(); + // 获得今天是一周的第几天,星期日是第一天,星期二是第二天...... + int dayOfMonth = c.get(Calendar.DAY_OF_MONTH); + return dayOfMonth; + } + /** + * 获得两个时间相差的时间间隔,以分钟来计算 + * @param time01 格式为:2012-02-02 08:08:20 + * @param time02 格式为:2012-02-02 08:08:20 + * @return 相差的分钟数 + */ + public static long getTwoTimeInterval(String time01,String time02){ + long t01=stringTimeToMilliseconds(time01); + long t02=stringTimeToMilliseconds(time02); + return Math.abs((t01-t02)/60000); + } + /** + * 把一个字符串日期 转换成毫秒数 + * @param time 格式为:2012-02-02 08:08:20 + * @return 毫秒数 + */ + public static long stringTimeToMilliseconds(String time){ + int year = Integer.parseInt(time.substring(0, 4)); + int month = Integer.parseInt(time.substring(5, 7)); + int day = Integer.parseInt(time.substring(8, 10)); + int hour= Integer.parseInt(time.substring(11, 13)); + int minus= Integer.parseInt(time.substring(14, 16)); + int second= Integer.parseInt(time.substring(17, 19)); + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month - 1); + c.set(Calendar.DAY_OF_MONTH, day); + c.add(Calendar.HOUR_OF_DAY, hour); + c.add(Calendar.MINUTE, minus); + c.add(Calendar.SECOND, second); + return c.getTimeInMillis(); + } + // java.util.Date 是 java.sql.Date 的父类 + // java.sql.Date转为java.util.Date + // java.sql.Date date=new java.sql.Date(); + // java.util.Date d=new java.util.Date (date.getTime()); + // + + // + // java.util.Date转为java.sql.Date + // java.util.Date utilDate=new Date(); + // java.sql.Date sqlDate=new java.sql.Date(utilDate.getTime()); + +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/FV.java b/plugin-finance-function/src/main/java/com/fr/plugin/FV.java new file mode 100644 index 0000000..c4f0b8d --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/FV.java @@ -0,0 +1,60 @@ +package com.fr.plugin; + +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.exception.FormulaException; +import com.fr.stable.fun.Authorize; +import com.fr.third.v2.org.apache.poi.ss.formula.functions.FinanceLib; + +import java.math.BigDecimal; +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class FV extends AbstractFunction { + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] objects) throws FormulaException { + if (PluginContexts.currentContext().isAvailable()) { + return cal(objects); + } else { + return "插件未激活,请购买使用"; + } + } + + private Object cal(Object[] objects) { + //年利率 + double rate = trans(objects[0]).doubleValue(); + //贷款期数 + double nper = trans(objects[1]).doubleValue(); + //每期的付款金额 + double pmt= trans(objects[2]).doubleValue(); + //pv 可选 + double pv = 0D; + if(objects.length == 4) { + pv = trans(objects[3]).doubleValue(); + } + //type 可选 + boolean type=false; + if(objects.length == 5) { + pv = trans(objects[3]).doubleValue(); + type = trans(objects[4]).doubleValue()==1; + } + return FinanceLib.fv(rate,nper,pmt,pv,type); + } + + private static BigDecimal trans(Object ele){ + if (ele == null|| StringUtils.isBlank(ele.toString())) { + return new BigDecimal("0"); + } + try{ + String val = GeneralUtils.objectToString(ele); + return new BigDecimal(val.trim()); + }catch(Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFinder.java b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFinder.java new file mode 100644 index 0000000..fd23f22 --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFinder.java @@ -0,0 +1,10 @@ +package com.fr.plugin; + +import com.fr.stable.fun.impl.AbstractLocaleFinder; + +public class FinanceFinder extends AbstractLocaleFinder { + @Override + public String find() { + return "com/fr/plugin/demo"; + } +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFunctionConstants.java b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFunctionConstants.java new file mode 100644 index 0000000..d94ee9b --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceFunctionConstants.java @@ -0,0 +1,6 @@ +package com.fr.plugin; + +public class FinanceFunctionConstants { + + public static final String PLUGIN_ID = "com.fr.plugin.function.finance"; +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/FinanceGroup.java b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceGroup.java new file mode 100644 index 0000000..93eba0e --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/FinanceGroup.java @@ -0,0 +1,29 @@ +package com.fr.plugin; + +import com.fr.stable.fun.impl.AbstractFunctionDefContainer; +import com.fr.stable.script.FunctionDef; + +public class FinanceGroup extends AbstractFunctionDefContainer { + @Override + public FunctionDef[] getFunctionDefs() { + return new FunctionDef[]{ + new FunctionDef("IPMT","基于固定利率及等额分期付款方式,返回给定期数内对投资的利息偿还额函数IPMT:参数1为每期期利率,参数2为用于计算其利息数额的期数,参数3为总期数,参数4现值pv,参数5未来值fv,参数6付款时间是在期初还是期末,前四个参数必填,后面两个选填。",IPMT.class.getName()), + new FunctionDef("PPMT","PPMT函数返回根据定期固定付款和固定利率而定的投资在已知期间内的本金偿付额:参数1为每期利率,参数2为期数,参数3为总期数,参数4现值pv,参数5为未来值fv,参数6付款时间是在期初还是期末,前四个参数必填,后面两个选填。",PPMT.class.getName()), + new FunctionDef("PMT","PMT函数根据固定付款额和固定利率计算贷款的付款额:参数1为每期利率,参数2为贷款总期数,参数3现值pv即本金,参数4为未来值fv,参数5为付款时间是在期初还是期末,前三个参数必填,后面两个选填。",PMT.class.getName()), + new FunctionDef("PV","投资的现值PV:参数1为每期利率,参数2为总期数,参数3为每期付款金额,参数4未来值fv,参数5付款时间是在期初还是期末,前三个参数必填,后面两个选填。",PV.class.getName()), + new FunctionDef("FV","计算投资价值的函数FV:参数1为利率,参数2为总期数,参数3为每期付款金额,参数4现值pv,参数5每期付款时间是在期初还是期末,前三个参数必填,后面两个选填。",FV.class.getName()), + new FunctionDef("NPV","使用贴现率和一系列未来支出(负值)和收益(正值)来计算一项投资的净现值NPV:参数1为利率,可变参数-现金流,按正确的顺序输入支出值和收益值。",NPV.class.getName()), + new FunctionDef("NPER","基于固定利率及等额分期付款方式,返回某项投资的总期数NPER:参数1为各期利率,参数2为每期付款金额,参数3为现值pv,参数4未来值fv,参数5付款时间是在期初还是期末,前三个参数必填,后面两个选填。",NPER.class.getName()), + new FunctionDef("IRR","IRR函数返回由值中的数字表示的一系列现金流的内部收益率:参数1为现金流数组,参数2为估计值,参数2为选填,如果省略guess,则假定它为0.1。",IRR.class.getName()), + new FunctionDef("XIRR","XIRR函数返回一组不一定定期发生的现金流的内部收益率:参数1为现金流数组,参数2为对应还款日期列表,参数3估计值为选填。",XIRR.class.getName()), + new FunctionDef("Rate","RATE函数返回年金每期的利率:参数1为贷款总期数,参数2为每期付款金额,参数3为付款金额的总和,参数4为未来值fv,参数5为type付款时间是在期初还是期末,参数6为预期利率,省略的话默认为0.1,前三个参数必填,后三个选填。",Rate.class.getName()), + new FunctionDef("MIRR","MIRR函数返回一系列定期现金流的修改后内部收益率,同时考虑投资的成本和现金再投资的收益率:参数1为现金流数组,参数2为资金支付利率,参数3为再投资收益率。",MIRR.class.getName()) + + }; + } + + @Override + public String getGroupName() { + return "财务函数"; + } +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/IPMT.java b/plugin-finance-function/src/main/java/com/fr/plugin/IPMT.java new file mode 100644 index 0000000..5dbb404 --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/IPMT.java @@ -0,0 +1,66 @@ +package com.fr.plugin; + +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.exception.FormulaException; +import com.fr.stable.fun.Authorize; +import com.fr.third.v2.org.apache.poi.ss.formula.functions.Finance; + +import java.math.BigDecimal; +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class IPMT extends AbstractFunction { + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] objects) throws FormulaException { + if (PluginContexts.currentContext().isAvailable()) { + return cal(objects); + } else { + return "插件未激活,请购买使用"; + } + + } + + private Object cal(Object[] objects) { + // 各期利率 必需 + double rate = trans(objects[0]).doubleValue(); + // 指定期数 per 必需 + int per = trans(objects[1]).intValue(); + //年金的付款总期数 必需 + int nper = trans(objects[2]).intValue(); + //pv 必需 现值即一系列未来付款当前值的总和 + double pv = trans(objects[3]).doubleValue(); + + if (objects.length == 4) { + return Finance.ipmt(rate,per,nper,pv); + }else if (objects.length == 5) { + //未来值 + double fv = trans(objects[4]).doubleValue(); + return Finance.ipmt(rate,per,nper,pv,fv); + }else if (objects.length == 6) { + double fv = trans(objects[4]).doubleValue(); + int type= trans(objects[5]).intValue(); + return Finance.ipmt(rate,per,nper,pv,fv,type); + }else { + throw new RuntimeException("输入参数有误"); + } + } + + private static BigDecimal trans(Object ele){ + if (ele == null|| StringUtils.isBlank(ele.toString())) { + return new BigDecimal("0"); + } + try{ + String val = GeneralUtils.objectToString(ele); + return new BigDecimal(val.trim()); + }catch(Exception e){ + throw new RuntimeException(e); + } + } + +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/IRR.java b/plugin-finance-function/src/main/java/com/fr/plugin/IRR.java new file mode 100644 index 0000000..769609c --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/IRR.java @@ -0,0 +1,196 @@ +package com.fr.plugin; /** + * @file: PACKAGE_NAME.IRR + * @desc: + * @date:2020/7/7 16:57 + * @author:yanghui + **/ + +import com.fr.general.FArray; +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.fun.Authorize; + +import java.math.BigDecimal; + +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class IRR extends AbstractFunction { + + private static final long serialVersionUID = 7634415917398642321L; + private static final String ERROR_VALUE = "#NUM!"; + + + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] args) { + + if (PluginContexts.currentContext().isAvailable()) { + return cal(args); + } else { + return "插件未激活,请购买使用"; + } + + } + + private Object cal(Object[] args) { + try{ + if(1 == args.length){ + return run( transArr( (FArray) args[0] ) ); + }else if(2 == args.length){ + return run( transArr( (FArray) args[0] ), trans( args[1] ) ); + }else if(3==args.length){ + return "参数个数不合格!"; + } + else{ + FArray cashflow = new FArray(); + BigDecimal guess = new BigDecimal(0.1d); + for (int i = 0; i < args.length; i++) { + if(args[i] == null|| StringUtils.isBlank(args[i].toString())){ + continue; + } + BigDecimal var = new BigDecimal(GeneralUtils.objectToString(args[i]).trim()); + + if ((var.compareTo(BigDecimal.ONE) <1) && (var.compareTo(BigDecimal.ZERO)>-1)) { + guess = var; + }else { + cashflow.add(var); + } + } + return run(cashflow,guess); + } + }catch(Exception e){ + System.out.println(e); + } + return ERROR_VALUE; + } + + /** + * 将其他类型的数字转换为大数(保证精度) + * @param ele + * @return + */ + private static BigDecimal trans(Object ele){ + try{ + String val = GeneralUtils.objectToString(ele); + return new BigDecimal(val.trim()); + }catch(Exception e){ + throw new RuntimeException(e); + } + } + + /** + * 将数组转换为大数数组 + * @param in + * @return + */ + private static FArray transArr(FArray in){ + FArray rt = new FArray(); + for(int i=0;i cashflow){ + return run( cashflow, new BigDecimal(0.1d) ); + } + + private static BigDecimal run(FArray cashflow, BigDecimal guess){ + BigDecimal maxrate = initRateMax(cashflow,guess); + BigDecimal minrate = initRateMin(cashflow,guess); + for( int i=0; i cashflow, BigDecimal guess){ + for( int i=0; i cashflow, BigDecimal guess){ + for( int i=0; i cashflow, BigDecimal rate){ + BigDecimal npv = ZERO; + BigDecimal rpowj = ONE;//(1+r)^0 + BigDecimal radd1 = rate.add(ONE);//1+r + for( int j=0; j 0.0D) { + fv += anIn * Math.pow(1.0D + financeRate, (double)(numOfYears - indexN++)); + } + } + + if (fv != 0.0D && pv != 0.0D) { + value = Math.pow(-fv / pv, 1.0D / (double)numOfYears) - 1.0D; + } + + return value; + } + +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/NPER.java b/plugin-finance-function/src/main/java/com/fr/plugin/NPER.java new file mode 100644 index 0000000..81364ac --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/NPER.java @@ -0,0 +1,62 @@ +package com.fr.plugin; + +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.exception.FormulaException; +import com.fr.stable.fun.Authorize; +import com.fr.third.v2.org.apache.poi.ss.formula.functions.FinanceLib; + +import java.math.BigDecimal; + +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class NPER extends AbstractFunction { + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] objects) throws FormulaException { + if (PluginContexts.currentContext().isAvailable()) { + return cal(objects); + } else { + return "插件未激活,请购买使用"; + } + } + + private Object cal(Object[] objects) { + //某一期间的贴现率 + double rate = trans(objects[0]).doubleValue(); + + //每期的付款金额 + double pmt= trans(objects[1]).doubleValue(); + //pv 可选 + double pv = trans(objects[2]).doubleValue(); + //fv 可选 + double fv = 0D; + if(objects.length == 4) { + fv = trans(objects[3]).doubleValue(); + } + //type 可选 + boolean type=false; + if(objects.length == 5) { + fv = trans(objects[3]).doubleValue(); + type = trans(objects[4]).doubleValue()==1; + } + return FinanceLib.nper(rate,pmt,pv,fv,type); + } + + private static BigDecimal trans(Object ele){ + if (ele == null|| StringUtils.isBlank(ele.toString())) { + return new BigDecimal("0"); + } + try{ + String val = GeneralUtils.objectToString(ele); + return new BigDecimal(val.trim()); + }catch(Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/NPV.java b/plugin-finance-function/src/main/java/com/fr/plugin/NPV.java new file mode 100644 index 0000000..cbfc866 --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/NPV.java @@ -0,0 +1,52 @@ +package com.fr.plugin; + +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.exception.FormulaException; +import com.fr.stable.fun.Authorize; +import com.fr.third.v2.org.apache.poi.ss.formula.functions.FinanceLib; + +import java.math.BigDecimal; +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class NPV extends AbstractFunction { + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] objects) throws FormulaException { + if (PluginContexts.currentContext().isAvailable()) { + return cal(objects); + } else { + return "插件未激活,请购买使用"; + } + + } + + private Object cal(Object[] objects){ + //某一期间的贴现率 + double rate = trans(objects[0]).doubleValue(); + + double[] doubles = new double[objects.length]; + for (int i = 1; i FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) { + rate = (y1 * x0 - y0 * x1) / (y1 - y0); + x0 = x1; + x1 = rate; + + if (Math.abs(rate) < FINANCIAL_PRECISION) { + y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv; + } else { + f = Math.exp(nper * Math.log(1 + rate)); + y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv; + } + + y0 = y1; + y1 = y; + ++i; + } + return rate; + + } + + public double simpleCalculateRate(int nper, double pmt, double pv) { + + double fv = 0; + + //0或省略-期末支付 + double type = 0; + + //如果省略预期利率,则假设该值为 10%。 + double guess = 0.1; + + return calculateRate(nper, pmt, pv, fv, type, guess); + } + + + +} \ No newline at end of file diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/UpbaaDate.java b/plugin-finance-function/src/main/java/com/fr/plugin/UpbaaDate.java new file mode 100644 index 0000000..b57bd02 --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/UpbaaDate.java @@ -0,0 +1,111 @@ +package com.fr.plugin; + +/** + * + * @author 小木桩(staker) 这个类是辅助计算xirr的类, 主要是以时间对应现金的形式组成一条现金流 而xirr则是由多条现金流计算出来的 + */ +public class UpbaaDate { + public int year; + public int month; + public int day; + public double payment;// 对应的现金 + + public UpbaaDate(int year, int month, int day, double payment) { + this.year = year; + this.month = month; + this.day = day; + this.payment = payment; + } + + /** + * + * @param stringTime + * 格式为:2013/02/05 + * @param payment 对应的现金 + */ + public UpbaaDate(String stringTime, double payment) { + try { + int start = stringTime.indexOf("-")>stringTime.indexOf("/")?stringTime.indexOf("-"):stringTime.indexOf("/"); + + int end = stringTime.lastIndexOf("-")>stringTime.lastIndexOf("/")?stringTime.lastIndexOf("-"):stringTime.lastIndexOf("/"); + String month = stringTime.substring(start + 1, end); + if (month.length() == 1) { + month = "0" + month; + } + + + String day = stringTime.substring(end + 1); + if (day.length() == 1) { + day = "0" + day; + } + this.year=Integer.parseInt(stringTime.substring(0, 4)); + this.month = Integer.parseInt(month); + this.day=Integer.parseInt(day); + } catch (Exception e) { + // TODO: handle exception + } + this.payment=payment; + } + + /** + * 使用默认构造方法的话,则对应的是当前的时间,即今天的时间 + */ + public UpbaaDate() { + String stringTime=DateUtil.getNowStringDate("/"); + setStringDate(stringTime); + } + /** + * 设置年月日的字符串格式的时间 + * @param stringTime 格式为:2013/02/05 + */ + public void setStringDate(String stringTime){ + try { + this.year=Integer.parseInt(stringTime.substring(0, 4)); + this.month=Integer.parseInt(stringTime.substring(5, 7)); + this.day=Integer.parseInt(stringTime.substring(8, 10)); + } catch (Exception e) { + // TODO: handle exception + } + } + + /** + * 获得1970年到现在的天数 + * @return long + */ + public long getDaysFrom1970(){ + return DateUtil.getDaysFrom1970(year, month, day); + } + /** + * 获得字符串的日期 + * @param split 分隔符 如 - + * @return 如:2013-02-22 + */ + public String getStringDate(String split){ + if(split==null){ + split="-"; + } + StringBuffer stringDate=new StringBuffer(); + stringDate.append(year).append(split); + if(month<10){ + stringDate.append(0).append(month); + }else{ + stringDate.append(month); + } + stringDate.append(split); + if(day<10){ + stringDate.append(0).append(day); + }else{ + stringDate.append(day); + } + return stringDate.toString(); + } + + public static void main(String[] args) { + String a = "2020/7/3"; + int year=Integer.parseInt(a.substring(0, 4)); + + int month=Integer.parseInt(a.substring(5, 7)); + int day=Integer.parseInt(a.substring(8, 10)); + + } +} diff --git a/plugin-finance-function/src/main/java/com/fr/plugin/XIRR.java b/plugin-finance-function/src/main/java/com/fr/plugin/XIRR.java new file mode 100644 index 0000000..cb3129b --- /dev/null +++ b/plugin-finance-function/src/main/java/com/fr/plugin/XIRR.java @@ -0,0 +1,136 @@ +package com.fr.plugin; + +import com.fr.general.FArray; +import com.fr.general.GeneralUtils; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.script.AbstractFunction; +import com.fr.stable.StringUtils; +import com.fr.stable.exception.FormulaException; +import com.fr.stable.fun.Authorize; + +import java.math.BigDecimal; +import java.util.ArrayList; + +/** + * @file: PACKAGE_NAME.XIRR + * @desc: + * @date:2020/7/7 17:41 + * @author:yanghui + **/ +@EnableMetrics +@Authorize(callSignKey = FinanceFunctionConstants.PLUGIN_ID) +public class XIRR extends AbstractFunction { + + private static final String ERROR_VALUE = "#NUM!"; + + @Override + @Focus(id = "com.fr.plugin.function.finance", text = "Plugin-Test_Function_Finance", source = Original.PLUGIN) + public Object run(Object[] objects) throws FormulaException { + if (PluginContexts.currentContext().isAvailable()) { + return cal(objects); + } else { + return "插件未激活,请购买使用"; + } + } + + private Object cal(Object[] objects) { + try{ + double guess = 0.1; + if (objects.length == 3) { + guess = trans(objects[2]).doubleValue(); + } + if (objects.length > 3) { + FArray cash = new FArray(); + FArray date = new FArray<>(); + for (int i = 0; i < objects.length; i++) { + if(objects[i] == null|| StringUtils.isBlank(objects[i].toString())){ + continue; + } + String trim = GeneralUtils.objectToString(objects[i]).trim(); + int start = trim.indexOf("-")>trim.indexOf("/")?trim.indexOf("-"):trim.indexOf("/"); + if (start > 0) { + date.add(trim); + }else{ + BigDecimal var = new BigDecimal(trim); + if ((var.compareTo(BigDecimal.ONE) <1) && (var.compareTo(BigDecimal.ZERO)>-1)) { + guess = var.doubleValue(); + }else { + cash.add(var); + } + } + } + return run(cash, date, guess); + } + return run( transArr( (FArray) objects[0] ), transToString((FArray)objects[1]),guess); + }catch(Exception e){ + System.out.println(e); + } + return ERROR_VALUE; + } + + /** + * 将数组转换为大数数组 + * @param in + * @return + */ + private static FArray transArr(FArray in){ + FArray rt = new FArray(); + for(int i=0;i transToString(FArray in){ + FArray rt = new FArray<>(); + for(int i=0;i cashflow, FArray dueDate,double guess){ + + ArrayList list=new ArrayList(); + for(int i=0;i listUpbaa; + public XirrData(ArrayList listUpbaa){ + this.listUpbaa=listUpbaa; + if (listUpbaa != null) { + try { + startDays = listUpbaa.get(0).getDaysFrom1970(); + } catch (Exception e) { + } + } + } + /** + * 计算收益值 + * @return double + */ + public double getPal(){ + if(listUpbaa==null){ + return 0.0; + } + double pal=0; + int count=listUpbaa.size(); + for (int i = 0; i < count; i++) { + pal=pal+listUpbaa.get(i).payment; + } + return pal; + } + /** + * 通过传进来的多条现金流获得xirr值 + * @return 返回收益率 + */ + public double getXirr(double guess) { + if(listUpbaa == null){ + return Error_Null_List; + } + int count=listUpbaa.size(); + if (count <= 1) { + return Error_Less_Cash;// 如果只有一条现金流则返回Error_Less_Cash + } + + if (listUpbaa.get(0).payment > 0) { + return Error_First_Payment; + } + + for (int i = 0; i < count; i++) { + if (listUpbaa.get(1).getDaysFrom1970() < startDays) { + return Error_Date;// 如果不止一条现金流则判断第一条现金流是否为时间最早的,如果不是的话则返回ERROR_DATE + } + } + + boolean isEarn = getXNPVByRate(0) > 0;// 记录是赚钱了还是亏本了 + + if (isEarn) { + return calXirr(0, Max_Rate,guess); + } else { + return calXirr(Min_Rate, 0,guess); + } + } + + private double calXirr(double min,double Max,double guess){ + int calculateCount = 50; + double XIRR = guess; + while (calculateCount > 0) { + XIRR = (min + Max) / 2f; + double xnvp = getXNPVByRate(XIRR); + if (xnvp > 0) { + min = XIRR; + } else { + Max = XIRR; + } + if (Math.abs(XIRR) < Critical) { + break; + } + calculateCount--; + } + return XIRR; + } + + private double getXNPVByRate(double rate) { + double result = 0; + int size = listUpbaa.size(); + for (int i = 0; i < size; i++) { + UpbaaDate date = listUpbaa.get(i); + result = result + + getOneValue(date.payment, rate, (int) date.getDaysFrom1970() + - (int) startDays); + } + return result; + } + + private double getOneValue(double payment, double rate, int dateDistance) { + return payment / ((Math.pow((1 + rate), dateDistance / 365f))); + } +} diff --git a/plugin-finance-function/src/main/resources/com/fr/plugin/demo.properties b/plugin-finance-function/src/main/resources/com/fr/plugin/demo.properties new file mode 100644 index 0000000..ce575b2 --- /dev/null +++ b/plugin-finance-function/src/main/resources/com/fr/plugin/demo.properties @@ -0,0 +1 @@ +Plugin-Test_Function_Abs=Test FINANCE \ No newline at end of file diff --git a/plugin-finance-function/src/main/resources/com/fr/plugin/demo_zh_CN.properties b/plugin-finance-function/src/main/resources/com/fr/plugin/demo_zh_CN.properties new file mode 100644 index 0000000..a26d0ca --- /dev/null +++ b/plugin-finance-function/src/main/resources/com/fr/plugin/demo_zh_CN.properties @@ -0,0 +1 @@ +Plugin-Test_Function_Abs=\u6D4B\u8BD5\u8D22\u52A1\u51FD\u6570 \ No newline at end of file