Browse Source

update

main
wangshaoping 8 months ago
parent
commit
acec749f23
  1. 19
      app.engine.rule/build-common.gradle
  2. 15
      app.engine.rule/build-jetty.gradle
  3. 7
      app.engine.rule/build-tomcat.gradle
  4. 15
      app.engine.rule/build-undertow.gradle
  5. 114
      app.engine.rule/build.gradle
  6. 0
      app.engine.rule/gradle.properties
  7. 16
      app.engine.rule/src/main/java/app/engine/rule/Application.java
  8. 11
      app.engine.rule/src/main/resources/META-INF/platform/plugins/frontend-module.json
  9. 5
      app.engine.rule/src/main/resources/META-INF/platform/plugins/messages.json
  10. 3
      app.engine.rule/src/main/resources/app/engine/rule/i18n/messages.properties
  11. 3
      app.engine.rule/src/main/resources/app/engine/rule/i18n/messages_tw_CN.properties
  12. 3
      app.engine.rule/src/main/resources/app/engine/rule/i18n/messages_zh_CN.properties
  13. 11
      app.engine.rule/src/main/resources/public/configure.js
  14. 5
      app.engine.rule/src/main/resources/public/favicon.svg
  15. BIN
      app.engine.rule/src/main/resources/public/login-bg.jpg
  16. 5
      app.engine.rule/src/main/resources/public/logo.svg
  17. 1
      app.engine.rule/src/main/resources/running-mode.properties
  18. 99
      app.platform/src/main/resources/mathml3/mathml3-common.xsd
  19. 684
      app.platform/src/main/resources/mathml3/mathml3-content.xsd
  20. 2151
      app.platform/src/main/resources/mathml3/mathml3-presentation.xsd
  21. 186
      app.platform/src/main/resources/mathml3/mathml3-strict-content.xsd
  22. 9
      app.platform/src/main/resources/mathml3/mathml3.xsd
  23. 2
      erm.frontend/.npmrc
  24. 102
      erm.frontend/package.json
  25. 12
      erm.frontend/webpack.config.mf.cjs
  26. 9
      erm.frontend/webpack.env.build.cjs
  27. 31
      frontend.sh
  28. 3
      gradle.properties
  29. 3
      io.sc.engine.mv.doc/README.adoc
  30. 91
      io.sc.engine.mv.doc/asciidoc/chapter-appendix/appendix.adoc
  31. 69
      io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/二项检验/二项检验.adoc
  32. 4
      io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/估值准确性.adoc
  33. 39
      io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/卡方检验/卡方检验.adoc
  34. 16
      io.sc.engine.mv.doc/asciidoc/chapter-前言/前言.adoc
  35. 96
      io.sc.engine.mv.doc/asciidoc/chapter-区分能力/cap/cap.adoc
  36. 59
      io.sc.engine.mv.doc/asciidoc/chapter-区分能力/ks/ks.adoc
  37. 113
      io.sc.engine.mv.doc/asciidoc/chapter-区分能力/roc/roc.adoc
  38. 18
      io.sc.engine.mv.doc/asciidoc/chapter-区分能力/区分能力.adoc
  39. 5
      io.sc.engine.mv.doc/asciidoc/chapter-操作手册/操作手册.adoc
  40. 6
      io.sc.engine.mv.doc/asciidoc/chapter-操作手册/样本管理/样本管理.adoc
  41. 27
      io.sc.engine.mv.doc/asciidoc/chapter-操作手册/配置/配置.adoc
  42. 41
      io.sc.engine.mv.doc/asciidoc/chapter-操作手册/验证结果/验证结果.adoc
  43. 62
      io.sc.engine.mv.doc/asciidoc/chapter-概述/概述.adoc
  44. 16
      io.sc.engine.mv.doc/asciidoc/chapter-源数据/源数据.adoc
  45. 8
      io.sc.engine.mv.doc/asciidoc/chapter-稳定性/区分能力变动曲线/区分能力变动曲线.adoc
  46. 16
      io.sc.engine.mv.doc/asciidoc/chapter-稳定性/客户群体稳定性报告/客户群体稳定性报告.adoc
  47. 10
      io.sc.engine.mv.doc/asciidoc/chapter-稳定性/稳定性.adoc
  48. 43
      io.sc.engine.mv.doc/asciidoc/chapter-稳定性/转移分析/转移分析.adoc
  49. 124
      io.sc.engine.mv.doc/asciidoc/chapter-验证样本/验证样本.adoc
  50. 40
      io.sc.engine.mv.doc/asciidoc/index.adoc
  51. 8
      io.sc.engine.mv.doc/asciidoc/resources/docinfo/docinfo-footer.html
  52. 5
      io.sc.engine.mv.doc/asciidoc/resources/docinfo/docinfo.html
  53. BIN
      io.sc.engine.mv.doc/asciidoc/resources/files/chapter-correctness-of-estimating/binomial_test.xlsx
  54. BIN
      io.sc.engine.mv.doc/asciidoc/resources/files/chapter-correctness-of-estimating/chi-square_test.xlsx
  55. BIN
      io.sc.engine.mv.doc/asciidoc/resources/files/chapter-stability/psi.xlsx
  56. BIN
      io.sc.engine.mv.doc/asciidoc/resources/fonts/FontAwesome.otf
  57. BIN
      io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.eot
  58. 2671
      io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.svg
  59. BIN
      io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.ttf
  60. BIN
      io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.woff
  61. BIN
      io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.woff2
  62. 2
      io.sc.engine.mv.doc/asciidoc/resources/highlightjs/highlight.min.js
  63. 99
      io.sc.engine.mv.doc/asciidoc/resources/highlightjs/styles/github.min.css
  64. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-appendix/001.png
  65. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-appendix/002.png
  66. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-correctness-of-estimating/001.png
  67. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-correctness-of-estimating/002.png
  68. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-datasource/001.graffle
  69. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-datasource/001.png
  70. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/ar.png
  71. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/ar2.png
  72. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap.graffle
  73. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap.png
  74. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap2.png
  75. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/ks/ks.png
  76. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/auc.png
  77. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc.graffle
  78. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc.png
  79. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc2.png
  80. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/structure.graffle
  81. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/structure.png
  82. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/psi/001.png
  83. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/sc-change/001.png
  84. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/001.png
  85. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/002.png
  86. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/003.png
  87. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/001.png
  88. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/001.pptx
  89. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/002.png
  90. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/002.pptx
  91. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/003.png
  92. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/003.pptx
  93. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/functions.graffle
  94. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/functions.png
  95. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/structure.graffle
  96. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/structure.png
  97. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/001.png
  98. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/002.png
  99. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/003.png
  100. BIN
      io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/004.png

19
app.engine.rule/build-common.gradle

@ -0,0 +1,19 @@
/**
* (target)
* : -D
* :
* 1. gradle bootwar # , target=tomcat
* 2. gradle bootwar -Dtarget=undertow # undertow, target=undertow
* 3. gradle bootwar -Dtarget=jetty # jetty, target=jetty
*/
def target =System.getProperty("target") ?: "undertow";
System.setProperty('target',target);
// targetRuntime build.gradle
apply from: "build-${target}.gradle"
//
publishPublicationPublicationToMavenRepository.enabled=false
// docker
jibBuildTar.enabled =true

15
app.engine.rule/build-jetty.gradle

@ -0,0 +1,15 @@
println "[Jetty] 环境 ......"
configurations {
all*.exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
all*.exclude group: "org.apache.tomcat.embed", module: "tomcat-embed-core"
all*.exclude group: "org.apache.tomcat.embed", module: "tomcat-embed-websocket"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-jetty")
providedRuntime(
"org.springframework.boot:spring-boot-starter-jetty",
)
}

7
app.engine.rule/build-tomcat.gradle

@ -0,0 +1,7 @@
println "[Tomcat] 环境 ......"
dependencies {
providedRuntime(
"org.springframework.boot:spring-boot-starter-tomcat",
)
}

15
app.engine.rule/build-undertow.gradle

@ -0,0 +1,15 @@
println "[Undertow] 环境 ......"
configurations {
all*.exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
all*.exclude group: "org.apache.tomcat.embed", module: "tomcat-embed-core"
all*.exclude group: "org.apache.tomcat.embed", module: "tomcat-embed-websocket"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-undertow")
providedRuntime(
"org.springframework.boot:spring-boot-starter-undertow",
)
}

114
app.engine.rule/build.gradle

@ -0,0 +1,114 @@
apply plugin: 'war'
apply plugin: 'com.google.cloud.tools.jib'
apply from: "build-common.gradle"
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web"){
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
}
dependencies {
implementation (
project(":io.sc.platform.app"),
project(":io.sc.platform.developer"),
project(":io.sc.platform.security.loginform"),
project(":io.sc.platform.scheduler.manager"),
project(":io.sc.platform.scheduler.executor"),
project(":io.sc.engine.mv"),
project(":io.sc.engine.mv.frontend"),
project(":io.sc.engine.mv.sample"),
project(":io.sc.engine.rule.client"),
project(":io.sc.engine.rule.client.spring"),
project(":io.sc.engine.rule.core"),
project(":io.sc.engine.rule.server"),
project(":io.sc.engine.rule.sample"),
)
}
/**
* replace [application.version] in i18n message file
*/
processResources {
filesMatching('**/messages*.properties') {
println 'replace ${version} in [' + it + ']'
filteringCharset = 'iso8859-1'
filter(org.apache.tools.ant.filters.ReplaceTokens, beginToken: '$version', endToken: '',tokens: [version: '' + project.version])
}
doLast{
// eclipse idea ,:
// 1. environment.properties src/main/resources
// 2. ,
delete "$buildDir/resources/main/running-mode.properties"
}
}
bootWar{
mainClass = "${project.name}.Application"
//launchScript()
manifest {
attributes 'Implementation-Version': archiveVersion,
'Implementation-Title': project.name
}
}
bootJar{
mainClass = "${project.name}.Application"
//launchScript()
manifest {
attributes 'Implementation-Version': archiveVersion,
'Implementation-Title': project.name
}
}
jib {
outputPaths {
tar = "build/libs/${project.name}-${project.version}-image.tar"
}
from {
image = "openjdk:8u342-slim"
//image = "eclipse-temurin:8u382-b05-jdk-focal"
platforms {
platform {
architecture ="arm64"
os ="linux"
}
}
}
to {
image = "${project.name}:${project.version}"
}
extraDirectories {
paths {
path {
from = "build/libs/"
into = "/opt/${project.name}/"
includes = ["${project.name}-${project.version}.war"]
}
}
}
container {
/**
* jvm的启动参数
* user.timezone - Java程序的时区问题
*/
jvmFlags = ["-Duser.timezone=Asia/Shanghai"]
creationTime = "USE_CURRENT_TIMESTAMP"
ports = ["8080"]
entrypoint = [
"java",
"-jar",
"/opt/" + project.name + "/" + project.name + "-" + project.version + ".war",
"--" + project.name + ".home.dir=" + "/opt/" + project.name
]
//entrypoint = "java -version"
//appRoot = "/usr/local/tomcat/webapps/ROOT"
}
}

0
app.engine.rule/gradle.properties

16
app.engine.rule/src/main/java/app/engine/rule/Application.java

@ -0,0 +1,16 @@
package app.engine.rule;
import io.sc.platform.core.ApplicationLauncher;
import io.sc.platform.core.PlatformSpringBootServletInitializer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.WebApplicationInitializer;
/**
* 应用程序入口
*/
@SpringBootApplication(proxyBeanMethods = false)
public class Application extends PlatformSpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) throws Exception {
ApplicationLauncher.run(Application.class,args);
}
}

11
app.engine.rule/src/main/resources/META-INF/platform/plugins/frontend-module.json

@ -0,0 +1,11 @@
{
"name": "app.platform",
"components": [
],
"resources": [
"/public/configure.js",
"/public/favicon.svg",
"/public/login-bg.jpg",
"/public/logo.svg"
]
}

5
app.engine.rule/src/main/resources/META-INF/platform/plugins/messages.json

@ -0,0 +1,5 @@
{
"includes":[
"app/engine/rule/i18n/messages"
]
}

3
app.engine.rule/src/main/resources/app/engine/rule/i18n/messages.properties

@ -0,0 +1,3 @@
application.title=Decision Engine Platform
application.version=$version
application.copyright=Copyright \u00A9 2019\u20132022

3
app.engine.rule/src/main/resources/app/engine/rule/i18n/messages_tw_CN.properties

@ -0,0 +1,3 @@
application.title=\u6C7A\u7B56\u5F15\u64CE\u7BA1\u7406\u5E73\u53F0
application.version=$version
application.copyright=Copyright \u00A9 2019\u20132022

3
app.engine.rule/src/main/resources/app/engine/rule/i18n/messages_zh_CN.properties

@ -0,0 +1,3 @@
application.title=\u51B3\u7B56\u5F15\u64CE\u7BA1\u7406\u5E73\u53F0
application.version=$version
application.copyright=Copyright \u00A9 2019\u20132022

11
app.engine.rule/src/main/resources/public/configure.js

@ -0,0 +1,11 @@
// 在浏览器 window 对象中新建名为 APP 的容器变量, 用于存放平台的全局变量
window.APP = {};
// 全局配置
window.APP.configure ={
// 应用上下文路径
webContextPath: '[(@{/})]'.startsWith('[')? '/' : '[(@{/})]',
// 默认后端 API 请求的服务地址前缀
apiContextPaths: {
DEFAULT: '[(@{/})]'.startsWith('[') ? 'http://localhost:8080/' : '[(@{/})]'
}
};

5
app.engine.rule/src/main/resources/public/favicon.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="blue" class="bi bi-browser-edge" viewBox="0 0 16 16">
<path d="M9.482 9.341c-.069.062-.17.153-.17.309 0 .162.107.325.3.456.877.613 2.521.54 2.592.538h.002c.667 0 1.32-.18 1.894-.519A3.838 3.838 0 0 0 16 6.819c.018-1.316-.44-2.218-.666-2.664l-.04-.08C13.963 1.487 11.106 0 8 0A8 8 0 0 0 .473 5.29C1.488 4.048 3.183 3.262 5 3.262c2.83 0 5.01 1.885 5.01 4.797h-.004v.002c0 .338-.168.832-.487 1.244l.006-.006a.594.594 0 0 1-.043.041Z"/>
<path d="M.01 7.753a8.137 8.137 0 0 0 .753 3.641 8 8 0 0 0 6.495 4.564 5.21 5.21 0 0 1-.785-.377h-.01l-.12-.075a5.45 5.45 0 0 1-1.56-1.463A5.543 5.543 0 0 1 6.81 5.8l.01-.004.025-.012c.208-.098.62-.292 1.167-.285.129.001.257.012.384.033a4.037 4.037 0 0 0-.993-.698l-.01-.005C6.348 4.282 5.199 4.263 5 4.263c-2.44 0-4.824 1.634-4.99 3.49Zm10.263 7.912c.088-.027.177-.054.265-.084-.102.032-.204.06-.307.086l.042-.002Z"/>
<path d="M10.228 15.667a5.21 5.21 0 0 0 .303-.086l.082-.025a8.019 8.019 0 0 0 4.162-3.3.25.25 0 0 0-.331-.35c-.215.112-.436.21-.663.294a6.367 6.367 0 0 1-2.243.4c-2.957 0-5.532-2.031-5.532-4.644.002-.135.017-.268.046-.399a4.543 4.543 0 0 0-.46 5.898l.003.005c.315.441.707.821 1.158 1.121h.003l.144.09c.877.55 1.721 1.078 3.328.996Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app.engine.rule/src/main/resources/public/login-bg.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 KiB

5
app.engine.rule/src/main/resources/public/logo.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-browser-edge" viewBox="0 0 16 16">
<path d="M9.482 9.341c-.069.062-.17.153-.17.309 0 .162.107.325.3.456.877.613 2.521.54 2.592.538h.002c.667 0 1.32-.18 1.894-.519A3.838 3.838 0 0 0 16 6.819c.018-1.316-.44-2.218-.666-2.664l-.04-.08C13.963 1.487 11.106 0 8 0A8 8 0 0 0 .473 5.29C1.488 4.048 3.183 3.262 5 3.262c2.83 0 5.01 1.885 5.01 4.797h-.004v.002c0 .338-.168.832-.487 1.244l.006-.006a.594.594 0 0 1-.043.041Z"/>
<path d="M.01 7.753a8.137 8.137 0 0 0 .753 3.641 8 8 0 0 0 6.495 4.564 5.21 5.21 0 0 1-.785-.377h-.01l-.12-.075a5.45 5.45 0 0 1-1.56-1.463A5.543 5.543 0 0 1 6.81 5.8l.01-.004.025-.012c.208-.098.62-.292 1.167-.285.129.001.257.012.384.033a4.037 4.037 0 0 0-.993-.698l-.01-.005C6.348 4.282 5.199 4.263 5 4.263c-2.44 0-4.824 1.634-4.99 3.49Zm10.263 7.912c.088-.027.177-.054.265-.084-.102.032-.204.06-.307.086l.042-.002Z"/>
<path d="M10.228 15.667a5.21 5.21 0 0 0 .303-.086l.082-.025a8.019 8.019 0 0 0 4.162-3.3.25.25 0 0 0-.331-.35c-.215.112-.436.21-.663.294a6.367 6.367 0 0 1-2.243.4c-2.957 0-5.532-2.031-5.532-4.644.002-.135.017-.268.046-.399a4.543 4.543 0 0 0-.46 5.898l.003.005c.315.441.707.821 1.158 1.121h.003l.144.09c.877.55 1.721 1.078 3.328.996Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
app.engine.rule/src/main/resources/running-mode.properties

@ -0,0 +1 @@
development=true

99
app.platform/src/main/resources/mathml3/mathml3-common.xsd

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:m="http://www.w3.org/1998/Math/MathML"
elementFormDefault="qualified"
targetNamespace="http://www.w3.org/1998/Math/MathML">
<xs:element name="math">
<xs:complexType>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:MathExpression"/>
<xs:attributeGroup ref="m:math.attributes"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="CommonDeprecatedAtt">
<xs:attribute name="other"/>
</xs:attributeGroup>
<xs:attributeGroup name="CommonAtt">
<xs:attribute name="id" type="xs:ID"/>
<xs:attribute name="xref"/>
<xs:attribute name="class" type="xs:NMTOKENS"/>
<xs:attribute name="style" type="xs:string"/>
<xs:attribute name="href" type="xs:anyURI"/>
<xs:attributeGroup ref="m:CommonDeprecatedAtt"/>
<xs:anyAttribute namespace="##other" processContents="skip"/>
</xs:attributeGroup>
<xs:attributeGroup name="math.deprecatedattributes">
<xs:attribute name="mode" type="xs:string"/>
<xs:attribute name="macros" type="xs:string"/>
</xs:attributeGroup>
<xs:attributeGroup name="name">
<xs:attribute name="name" use="required" type="xs:NCName"/>
</xs:attributeGroup>
<xs:attributeGroup name="cd">
<xs:attribute name="cd" use="required" type="xs:NCName"/>
</xs:attributeGroup>
<xs:attributeGroup name="src">
<xs:attribute name="src" type="xs:anyURI"/>
</xs:attributeGroup>
<xs:element name="annotation">
<xs:complexType mixed="true">
<xs:attributeGroup ref="m:annotation.attributes"/>
</xs:complexType>
</xs:element>
<xs:complexType name="annotation-xml.model"><!--content model altered for libxml (annotation-xml)--><xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
<xs:group name="anyElement">
<xs:choice>
<xs:any namespace="##other" processContents="skip"/>
<xs:any namespace="##local" processContents="skip"/>
</xs:choice>
</xs:group>
<xs:element name="annotation-xml">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:annotation-xml.model">
<xs:attributeGroup ref="m:annotation.attributes"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="annotation.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attribute name="cd" type="xs:NCName"/>
<xs:attribute name="name" type="xs:NCName"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attributeGroup ref="m:src"/>
</xs:attributeGroup>
<xs:attributeGroup name="DefEncAtt">
<xs:attribute name="encoding" type="xs:string"/>
<xs:attribute name="definitionURL" type="xs:anyURI"/>
</xs:attributeGroup>
<xs:group name="semantics">
<xs:sequence>
<xs:element name="semantics">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:MathExpression"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:annotation"/>
<xs:element ref="m:annotation-xml"/>
</xs:choice>
</xs:sequence>
<xs:attributeGroup ref="m:semantics.attributes"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:group>
<xs:attributeGroup name="semantics.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="cd" type="xs:NCName"/>
<xs:attribute name="name" type="xs:NCName"/>
</xs:attributeGroup>
<xs:simpleType name="length">
<xs:restriction base="xs:string">
<xs:pattern value="\s*((-?[0-9]*([0-9]\.?|\.[0-9])[0-9]*(e[mx]|in|cm|mm|p[xtc]|%)?)|(negative)?((very){0,2}thi(n|ck)|medium)mathspace)\s*"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

684
app.platform/src/main/resources/mathml3/mathml3-content.xsd

@ -1,684 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:m="http://www.w3.org/1998/Math/MathML"
elementFormDefault="qualified"
targetNamespace="http://www.w3.org/1998/Math/MathML">
<xs:include schemaLocation="mathml3-strict-content.xsd"/>
<xs:complexType name="cn.content" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:mglyph"/>
<xs:element ref="m:sep"/>
<xs:element ref="m:PresentationExpression"/>
</xs:choice>
</xs:complexType>
<xs:attributeGroup name="cn.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="type"/>
<xs:attribute name="base"/>
</xs:attributeGroup>
<xs:attributeGroup name="ci.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="type"/>
</xs:attributeGroup>
<xs:attributeGroup name="ci.type">
<xs:attribute name="type" use="required"/>
</xs:attributeGroup>
<xs:complexType name="ci.content" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:mglyph"/>
<xs:element ref="m:PresentationExpression"/>
</xs:choice>
</xs:complexType>
<xs:attributeGroup name="csymbol.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="type"/>
<xs:attribute name="cd" type="xs:NCName"/>
</xs:attributeGroup>
<xs:complexType name="csymbol.content" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:mglyph"/>
<xs:element ref="m:PresentationExpression"/>
</xs:choice>
</xs:complexType>
<xs:element name="bvar">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:choice>
<xs:element ref="m:ci"/>
<xs:group ref="m:semantics-ci"/>
</xs:choice>
<xs:element ref="m:degree"/>
</xs:choice>
<xs:attributeGroup ref="m:CommonAtt"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cbytes.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:attributeGroup>
<xs:attributeGroup name="cs.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:attributeGroup>
<!--Ambiguous content model altered (apply.content)-->
<xs:complexType name="apply.content">
<xs:sequence>
<xs:group ref="m:ContExp"/>
<xs:group ref="m:BvarQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:Qualifier"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="bind.content">
<xs:complexContent>
<xs:extension base="m:apply.content"/>
</xs:complexContent>
</xs:complexType>
<xs:attributeGroup name="base">
<xs:attribute name="base" use="required"/>
</xs:attributeGroup>
<xs:element name="sep">
<xs:complexType/>
</xs:element>
<xs:element name="PresentationExpression" abstract="true"/>
<xs:group name="DomainQ">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:domainofapplication"/>
<xs:element ref="m:condition"/>
<!--Ambiguous content model altered (interval)--><xs:sequence>
<xs:element ref="m:lowlimit"/>
<xs:element minOccurs="0" ref="m:uplimit"/>
</xs:sequence>
</xs:choice>
</xs:sequence>
</xs:group>
<xs:element name="domainofapplication">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="condition">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="uplimit">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="lowlimit">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:group name="Qualifier">
<xs:choice>
<xs:group ref="m:DomainQ"/>
<xs:element ref="m:degree"/>
<xs:element ref="m:momentabout"/>
<xs:element ref="m:logbase"/>
</xs:choice>
</xs:group>
<xs:element name="degree">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="momentabout">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="logbase">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="type">
<xs:attribute name="type" use="required"/>
</xs:attributeGroup>
<xs:attributeGroup name="order">
<xs:attribute name="order" use="required">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="numeric"/>
<xs:enumeration value="lexicographic"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="closure">
<xs:attribute name="closure" use="required"/>
</xs:attributeGroup>
<xs:element name="piecewise">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:piece"/>
<xs:element ref="m:otherwise"/>
</xs:choice>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="piece">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:ContExp"/>
<xs:group ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="otherwise">
<xs:complexType>
<xs:group ref="m:ContExp"/>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="DeprecatedContExp" abstract="true"/>
<xs:element name="reln" substitutionGroup="m:DeprecatedContExp">
<xs:complexType>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="fn" substitutionGroup="m:DeprecatedContExp">
<xs:complexType>
<xs:group ref="m:ContExp"/>
</xs:complexType>
</xs:element>
<xs:element name="declare" substitutionGroup="m:DeprecatedContExp">
<xs:complexType>
<xs:group maxOccurs="unbounded" ref="m:ContExp"/>
<xs:attribute name="type" type="xs:string"/>
<xs:attribute name="scope" type="xs:string"/>
<xs:attribute name="nargs" type="xs:nonNegativeInteger"/>
<xs:attribute name="occurrence">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="prefix"/>
<xs:enumeration value="infix"/>
<xs:enumeration value="function-model"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="interval.class" abstract="true">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:ContExp"/>
<xs:group ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="closure"/>
</xs:complexType>
</xs:element>
<xs:element name="interval" substitutionGroup="m:interval.class"/>
<xs:element name="unary-functional.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="inverse" substitutionGroup="m:unary-functional.class"/>
<xs:element name="ident" substitutionGroup="m:unary-functional.class"/>
<xs:element name="domain" substitutionGroup="m:unary-functional.class"/>
<xs:element name="codomain" substitutionGroup="m:unary-functional.class"/>
<xs:element name="image" substitutionGroup="m:unary-functional.class"/>
<xs:element name="ln" substitutionGroup="m:unary-functional.class"/>
<xs:element name="log" substitutionGroup="m:unary-functional.class"/>
<xs:element name="moment" substitutionGroup="m:unary-functional.class"/>
<xs:element name="lambda.class" abstract="true">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:BvarQ"/>
<xs:group ref="m:DomainQ"/>
<xs:group ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="lambda" substitutionGroup="m:lambda.class"/>
<xs:element name="nary-functional.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="compose" substitutionGroup="m:nary-functional.class"/>
<xs:group name="binary-arith.class">
<xs:choice>
<xs:element ref="m:quotient"/>
<xs:element ref="m:divide"/>
<xs:element ref="m:minus"/>
<xs:element ref="m:power"/>
<xs:element ref="m:rem"/>
<xs:element ref="m:root"/>
</xs:choice>
</xs:group>
<xs:element name="quotient">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="divide">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="minus">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="power">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="rem">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:group name="unary-arith.class">
<xs:choice>
<xs:element ref="m:factorial"/>
<!--Ambiguous content model altered (minus)--><!--Ambiguous content model altered (root)--><xs:element ref="m:abs"/>
<xs:element ref="m:conjugate"/>
<xs:element ref="m:arg"/>
<xs:element ref="m:real"/>
<xs:element ref="m:imaginary"/>
<xs:element ref="m:floor"/>
<xs:element ref="m:ceiling"/>
<xs:element ref="m:exp"/>
</xs:choice>
</xs:group>
<xs:element name="factorial">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="abs">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="conjugate">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="arg">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="real">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="imaginary">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="floor">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="ceiling">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="exp">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="nary-minmax.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="max" substitutionGroup="m:nary-minmax.class"/>
<xs:element name="min" substitutionGroup="m:nary-minmax.class"/>
<xs:element name="nary-arith.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="plus" substitutionGroup="m:nary-arith.class"/>
<xs:element name="times" substitutionGroup="m:nary-arith.class"/>
<xs:element name="gcd" substitutionGroup="m:nary-arith.class"/>
<xs:element name="lcm" substitutionGroup="m:nary-arith.class"/>
<xs:element name="nary-logical.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="and" substitutionGroup="m:nary-logical.class"/>
<xs:element name="or" substitutionGroup="m:nary-logical.class"/>
<xs:element name="xor" substitutionGroup="m:nary-logical.class"/>
<xs:element name="unary-logical.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="not" substitutionGroup="m:unary-logical.class"/>
<xs:element name="binary-logical.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="implies" substitutionGroup="m:binary-logical.class"/>
<xs:element name="equivalent" substitutionGroup="m:binary-logical.class"/>
<xs:element name="quantifier.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="forall" substitutionGroup="m:quantifier.class"/>
<xs:element name="exists" substitutionGroup="m:quantifier.class"/>
<xs:element name="nary-reln.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="eq" substitutionGroup="m:nary-reln.class"/>
<xs:element name="gt" substitutionGroup="m:nary-reln.class"/>
<xs:element name="lt" substitutionGroup="m:nary-reln.class"/>
<xs:element name="geq" substitutionGroup="m:nary-reln.class"/>
<xs:element name="leq" substitutionGroup="m:nary-reln.class"/>
<xs:element name="binary-reln.class" abstract="true"/>
<xs:element name="neq" substitutionGroup="m:binary-reln.class">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="approx" substitutionGroup="m:binary-reln.class">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="factorof" substitutionGroup="m:binary-reln.class">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="tendsto" substitutionGroup="m:binary-reln.class">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="type"/>
</xs:complexType>
</xs:element>
<xs:element name="int.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="int" substitutionGroup="m:int.class"/>
<xs:element name="Differential-Operator.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="diff" substitutionGroup="m:Differential-Operator.class"/>
<xs:element name="partialdiff.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="partialdiff" substitutionGroup="m:partialdiff.class"/>
<xs:element name="unary-veccalc.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="divergence" substitutionGroup="m:unary-veccalc.class"/>
<xs:element name="grad" substitutionGroup="m:unary-veccalc.class"/>
<xs:element name="curl" substitutionGroup="m:unary-veccalc.class"/>
<xs:element name="laplacian" substitutionGroup="m:unary-veccalc.class"/>
<xs:element name="nary-setlist-constructor.class" abstract="true"/>
<xs:element name="set" substitutionGroup="m:nary-setlist-constructor.class">
<xs:complexType>
<xs:sequence>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:BvarQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:DomainQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="type"/>
</xs:complexType>
</xs:element>
<xs:element name="list" substitutionGroup="m:nary-setlist-constructor.class">
<xs:complexType>
<xs:sequence>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:BvarQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:DomainQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
<xs:attribute name="order">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="numeric"/>
<xs:enumeration value="lexicographic"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="nary-set.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="union" substitutionGroup="m:nary-set.class"/>
<xs:element name="intersect" substitutionGroup="m:nary-set.class"/>
<xs:element name="cartesianproduct" substitutionGroup="m:nary-set.class"/>
<xs:element name="binary-set.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="in" substitutionGroup="m:binary-set.class"/>
<xs:element name="notin" substitutionGroup="m:binary-set.class"/>
<xs:element name="notsubset" substitutionGroup="m:binary-set.class"/>
<xs:element name="notprsubset" substitutionGroup="m:binary-set.class"/>
<xs:element name="setdiff" substitutionGroup="m:binary-set.class"/>
<xs:element name="nary-set-reln.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="subset" substitutionGroup="m:nary-set-reln.class"/>
<xs:element name="prsubset" substitutionGroup="m:nary-set-reln.class"/>
<xs:element name="unary-set.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="card" substitutionGroup="m:unary-set.class"/>
<xs:element name="sum.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="sum" substitutionGroup="m:sum.class"/>
<xs:element name="product.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="product" substitutionGroup="m:product.class"/>
<xs:element name="limit.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="limit" substitutionGroup="m:limit.class"/>
<xs:element name="unary-elementary.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="sin" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="cos" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="tan" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="sec" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="csc" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="cot" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="sinh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="cosh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="tanh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="sech" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="csch" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="coth" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arcsin" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccos" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arctan" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccosh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccot" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccoth" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccsc" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arccsch" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arcsec" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arcsech" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arcsinh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="arctanh" substitutionGroup="m:unary-elementary.class"/>
<xs:element name="nary-stats.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="mean" substitutionGroup="m:nary-stats.class"/>
<xs:element name="sdev" substitutionGroup="m:nary-stats.class"/>
<xs:element name="variance" substitutionGroup="m:nary-stats.class"/>
<xs:element name="median" substitutionGroup="m:nary-stats.class"/>
<xs:element name="mode" substitutionGroup="m:nary-stats.class"/>
<xs:element name="nary-constructor.class" abstract="true">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:BvarQ"/>
<xs:group ref="m:DomainQ"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="vector" substitutionGroup="m:nary-constructor.class"/>
<xs:element name="matrix" substitutionGroup="m:nary-constructor.class"/>
<xs:element name="matrixrow" substitutionGroup="m:nary-constructor.class"/>
<xs:element name="unary-linalg.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="determinant" substitutionGroup="m:unary-linalg.class"/>
<xs:element name="transpose" substitutionGroup="m:unary-linalg.class"/>
<xs:element name="nary-linalg.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="selector" substitutionGroup="m:nary-linalg.class"/>
<xs:element name="binary-linalg.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="vectorproduct" substitutionGroup="m:binary-linalg.class"/>
<xs:element name="scalarproduct" substitutionGroup="m:binary-linalg.class"/>
<xs:element name="outerproduct" substitutionGroup="m:binary-linalg.class"/>
<xs:element name="constant-set.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="integers" substitutionGroup="m:constant-set.class"/>
<xs:element name="reals" substitutionGroup="m:constant-set.class"/>
<xs:element name="rationals" substitutionGroup="m:constant-set.class"/>
<xs:element name="naturalnumbers" substitutionGroup="m:constant-set.class"/>
<xs:element name="complexes" substitutionGroup="m:constant-set.class"/>
<xs:element name="primes" substitutionGroup="m:constant-set.class"/>
<xs:element name="emptyset" substitutionGroup="m:constant-set.class"/>
<xs:element name="constant-arith.class" abstract="true">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:DefEncAtt"/>
</xs:complexType>
</xs:element>
<xs:element name="exponentiale" substitutionGroup="m:constant-arith.class"/>
<xs:element name="imaginaryi" substitutionGroup="m:constant-arith.class"/>
<xs:element name="notanumber" substitutionGroup="m:constant-arith.class"/>
<xs:element name="true" substitutionGroup="m:constant-arith.class"/>
<xs:element name="false" substitutionGroup="m:constant-arith.class"/>
<xs:element name="pi" substitutionGroup="m:constant-arith.class"/>
<xs:element name="eulergamma" substitutionGroup="m:constant-arith.class"/>
<xs:element name="infinity" substitutionGroup="m:constant-arith.class"/>
</xs:schema>

2151
app.platform/src/main/resources/mathml3/mathml3-presentation.xsd

File diff suppressed because it is too large

186
app.platform/src/main/resources/mathml3/mathml3-strict-content.xsd

@ -1,186 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:m="http://www.w3.org/1998/Math/MathML"
elementFormDefault="qualified"
targetNamespace="http://www.w3.org/1998/Math/MathML">
<xs:group name="ContExp">
<xs:choice>
<!--Ambiguous content model altered (ContExp)-->
<xs:element ref="m:apply"/>
<xs:element ref="m:bind"/>
<xs:element ref="m:ci"/>
<xs:element ref="m:cn"/>
<xs:element ref="m:csymbol"/>
<xs:element ref="m:cbytes"/>
<xs:element ref="m:cerror"/>
<xs:element ref="m:cs"/>
<xs:element ref="m:share"/>
<xs:element ref="m:piecewise"/>
<xs:element ref="m:DeprecatedContExp"/>
<xs:element ref="m:interval.class"/>
<xs:element ref="m:unary-functional.class"/>
<xs:element ref="m:lambda.class"/>
<xs:element ref="m:nary-functional.class"/>
<xs:group ref="m:binary-arith.class"/>
<xs:group ref="m:unary-arith.class"/>
<xs:element ref="m:nary-minmax.class"/>
<xs:element ref="m:nary-arith.class"/>
<xs:element ref="m:nary-logical.class"/>
<xs:element ref="m:unary-logical.class"/>
<xs:element ref="m:binary-logical.class"/>
<xs:element ref="m:quantifier.class"/>
<xs:element ref="m:nary-reln.class"/>
<xs:element ref="m:binary-reln.class"/>
<xs:element ref="m:int.class"/>
<xs:element ref="m:Differential-Operator.class"/>
<xs:element ref="m:partialdiff.class"/>
<xs:element ref="m:unary-veccalc.class"/>
<xs:element ref="m:nary-setlist-constructor.class"/>
<xs:element ref="m:nary-set.class"/>
<xs:element ref="m:binary-set.class"/>
<xs:element ref="m:nary-set-reln.class"/>
<xs:element ref="m:unary-set.class"/>
<xs:element ref="m:sum.class"/>
<xs:element ref="m:product.class"/>
<xs:element ref="m:limit.class"/>
<xs:element ref="m:unary-elementary.class"/>
<xs:element ref="m:nary-stats.class"/>
<xs:element ref="m:nary-constructor.class"/>
<xs:element ref="m:unary-linalg.class"/>
<xs:element ref="m:nary-linalg.class"/>
<xs:element ref="m:binary-linalg.class"/>
<xs:element ref="m:constant-set.class"/>
<xs:element ref="m:constant-arith.class"/>
</xs:choice>
</xs:group>
<xs:element name="cn">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:cn.content">
<xs:attributeGroup ref="m:cn.attributes"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:group name="semantics-ci">
<xs:sequence>
<xs:element name="semantics">
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element ref="m:ci"/>
<xs:group ref="m:semantics-ci"/>
</xs:choice>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:annotation"/>
<xs:element ref="m:annotation-xml"/>
</xs:choice>
</xs:sequence>
<xs:attributeGroup ref="m:semantics.attributes"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:group>
<xs:group name="semantics-contexp">
<xs:sequence>
<xs:element name="semantics">
<xs:complexType>
<xs:sequence>
<xs:group ref="m:ContExp"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="m:annotation"/>
<xs:element ref="m:annotation-xml"/>
</xs:choice>
</xs:sequence>
<xs:attributeGroup ref="m:semantics.attributes"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:group>
<xs:element name="ci">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:ci.content">
<xs:attributeGroup ref="m:ci.attributes"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="csymbol">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:csymbol.content">
<xs:attributeGroup ref="m:csymbol.attributes"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:simpleType name="SymbolName">
<xs:restriction base="xs:NCName"/>
</xs:simpleType>
<xs:group name="BvarQ">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="m:bvar"/>
</xs:sequence>
</xs:group>
<xs:element name="apply">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:apply.content">
<xs:attributeGroup ref="m:CommonAtt"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="bind">
<xs:complexType>
<xs:complexContent>
<xs:extension base="m:bind.content">
<xs:attributeGroup ref="m:CommonAtt"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="share">
<xs:complexType>
<xs:attributeGroup ref="m:CommonAtt"/>
<xs:attributeGroup ref="m:src"/>
</xs:complexType>
</xs:element>
<xs:element name="cerror">
<xs:complexType>
<xs:sequence>
<xs:element ref="m:csymbol"/>
<xs:group minOccurs="0" maxOccurs="unbounded" ref="m:ContExp"/>
</xs:sequence>
<xs:attributeGroup ref="m:cerror.attributes"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cerror.attributes">
<xs:attributeGroup ref="m:CommonAtt"/>
</xs:attributeGroup>
<xs:element name="cbytes">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="m:base64">
<xs:attributeGroup ref="m:cbytes.attributes"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:simpleType name="base64">
<xs:restriction base="xs:base64Binary"/>
</xs:simpleType>
<xs:element name="cs">
<xs:complexType mixed="true">
<xs:attributeGroup ref="m:cs.attributes"/>
</xs:complexType>
</xs:element>
<xs:group name="MathExpression">
<xs:choice>
<xs:group ref="m:ContExp"/>
<xs:element ref="m:PresentationExpression"/>
<xs:group ref="m:semantics"/>
</xs:choice>
</xs:group>
</xs:schema>

9
app.platform/src/main/resources/mathml3/mathml3.xsd

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:m="http://www.w3.org/1998/Math/MathML"
elementFormDefault="qualified"
targetNamespace="http://www.w3.org/1998/Math/MathML">
<xs:include schemaLocation="mathml3-content.xsd"/>
<xs:include schemaLocation="mathml3-presentation.xsd"/>
<xs:include schemaLocation="mathml3-common.xsd"/>
</xs:schema>

2
erm.frontend/.npmrc

@ -3,6 +3,8 @@ registry=http://nexus.sc.io:8000/repository/npm-public/
# 用户邮箱
email=
# publish 时无需先进行 git 代码同步检查, 可避免 publish 时使用 --no-git-checks 选项
git-checks=false
# 注意: 以下 // 不是注释,不能去掉哦
# 登录 npm 仓库的用户认证信息, 在 npm publish 时使用, publish 的 npm registry 在 package.json 文件中 publishConfig 部分配置

102
erm.frontend/package.json

@ -23,92 +23,94 @@
"access": "public"
},
"devDependencies": {
"@babel/core": "7.24.4",
"@babel/preset-env": "7.24.4",
"@babel/preset-typescript": "7.24.1",
"@babel/plugin-transform-class-properties": "7.24.1",
"@babel/plugin-transform-object-rest-spread": "7.24.1",
"@quasar/app-webpack": "3.12.5",
"@quasar/cli": "2.4.0",
"@babel/core": "7.24.7",
"@babel/preset-env": "7.24.7",
"@babel/preset-typescript": "7.24.7",
"@babel/plugin-transform-class-properties": "7.24.7",
"@babel/plugin-transform-object-rest-spread": "7.24.7",
"@quasar/app-webpack": "3.13.2",
"@quasar/cli": "2.4.1",
"@types/mockjs": "1.0.10",
"@types/node": "20.12.7",
"@typescript-eslint/eslint-plugin": "7.7.1",
"@typescript-eslint/parser": "7.7.1",
"@vue/compiler-sfc": "3.4.24",
"@types/node": "20.14.10",
"@typescript-eslint/eslint-plugin": "7.15.0",
"@typescript-eslint/parser": "7.15.0",
"@vue/compiler-sfc": "3.4.31",
"@webpack-cli/serve": "2.0.5",
"autoprefixer": "10.4.19",
"babel-loader": "9.1.3",
"clean-webpack-plugin": "4.0.0",
"copy-webpack-plugin": "12.0.2",
"cross-env": "7.0.3",
"css-loader": "7.1.1",
"css-loader": "7.1.2",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.3",
"eslint-plugin-vue": "9.25.0",
"eslint-webpack-plugin": "4.1.0",
"eslint-plugin-vue": "9.27.0",
"eslint-webpack-plugin": "4.2.0",
"html-webpack-plugin": "5.6.0",
"json5": "2.2.3",
"mini-css-extract-plugin": "2.9.0",
"nodemon": "3.1.0",
"postcss": "8.4.38",
"nodemon": "3.1.4",
"postcss": "8.4.39",
"postcss-import": "16.1.0",
"postcss-loader": "8.1.1",
"postcss-preset-env": "9.5.9",
"prettier": "3.2.5",
"sass": "1.75.0",
"postcss-preset-env": "9.6.0",
"prettier": "3.3.2",
"sass": "1.77.6",
"sass-loader": "14.2.1",
"typescript": "5.4.5",
"typescript": "5.5.3",
"vue-loader": "17.4.2",
"webpack": "5.91.0",
"webpack": "5.92.1",
"webpack-bundle-analyzer": "4.10.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4",
"webpack-merge": "5.10.0",
"webpack-merge": "6.0.1",
"@vue/babel-plugin-jsx": "1.2.2"
},
"dependencies": {
"@codemirror/autocomplete": "6.16.0",
"@codemirror/commands": "6.5.0",
"@codemirror/autocomplete": "6.17.0",
"@codemirror/commands": "6.6.0",
"@codemirror/lang-html": "6.4.9",
"@codemirror/lang-java": "6.0.1",
"@codemirror/lang-javascript": "6.2.2",
"@codemirror/lang-json": "6.0.1",
"@codemirror/lang-sql": "6.6.3",
"@codemirror/lang-sql": "6.7.0",
"@codemirror/lang-xml": "6.1.0",
"@codemirror/language": "6.10.1",
"@codemirror/language": "6.10.2",
"@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.26.3",
"@maxgraph/core": "0.10.0",
"@quasar/extras": "1.16.11",
"@vueuse/core": "10.9.0",
"axios": "1.6.8",
"@codemirror/view": "6.28.4",
"@maxgraph/core": "0.12.0",
"@quasar/extras": "1.16.12",
"@vueuse/core": "10.11.0",
"axios": "1.7.2",
"codemirror": "6.0.1",
"dayjs": "1.11.10",
"echarts": "5.5.0",
"dayjs": "1.11.11",
"echarts": "5.5.1",
"exceljs": "4.4.0",
"file-saver": "2.0.5",
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.246",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",
"vue-dompurify-html": "5.0.1",
"platform-core": "8.1.272",
"quasar": "2.15.4",
"tailwindcss": "3.4.4",
"vue": "3.4.31",
"vue-dompurify-html": "5.1.0",
"vue-i18n": "9.13.1",
"vue-router": "4.3.2",
"@univerjs/core": "0.1.13",
"@univerjs/design": "0.1.13",
"@univerjs/docs": "0.1.13",
"@univerjs/docs-ui": "0.1.13",
"@univerjs/engine-formula": "0.1.13",
"@univerjs/engine-render": "0.1.13",
"@univerjs/facade": "0.1.13",
"@univerjs/sheets": "0.1.13",
"@univerjs/sheets-formula": "0.1.13",
"@univerjs/sheets-ui": "0.1.13",
"@univerjs/ui": "0.1.13"
"vue-router": "4.4.0",
"@univerjs/core": "0.2.0",
"@univerjs/design": "0.2.0",
"@univerjs/docs": "0.2.0",
"@univerjs/docs-ui": "0.2.0",
"@univerjs/engine-formula": "0.2.0",
"@univerjs/engine-render": "0.2.0",
"@univerjs/facade": "0.2.0",
"@univerjs/sheets": "0.2.0",
"@univerjs/sheets-formula": "0.2.0",
"@univerjs/sheets-ui": "0.2.0",
"@univerjs/ui": "0.2.0",
"pinia-undo": "0.2.4",
"xml-formatter": "3.6.3"
}
}

12
erm.frontend/webpack.config.mf.cjs

@ -60,6 +60,18 @@ module.exports = {
'vue-dompurify-html':{ requiredVersion: deps['vue-dompurify-html'], singleton: true },
'vue-i18n': { requiredVersion: deps['vue-i18n'], singleton: true },
'vue-router': { requiredVersion: deps['vue-router'], singleton: true },
"xml-formatter": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/core": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/design": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/docs": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/docs-ui": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/engine-formula": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/engine-render": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/facade": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/sheets": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/sheets-formula": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/sheets-ui": { requiredVersion: deps['vue-router'], singleton: true },
"@univerjs/ui": { requiredVersion: deps['vue-router'], singleton: true }
}
}),
]

9
erm.frontend/webpack.env.build.cjs

@ -24,7 +24,7 @@ module.exports = merge(common, mf, {
cacheGroups: {
'shared': {
name: 'vue',
test: /[\\/]node_modules[\\/](axios|dayjs|exceljs|file-saver|luckyexcel|mockjs)[\\/]/,
test: /[\\/]node_modules[\\/](axios|dayjs|exceljs|file-saver|luckyexcel|mockjs|xml-formatter)[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
@ -71,6 +71,13 @@ module.exports = merge(common, mf, {
chunks: 'all',
enforce: true
},
'@univerjs': {
name: '@univerjs',
test: /[\\/]node_modules[\\/]@univerjs[\\/]/,
priority: 20,
chunks: 'all',
enforce: true
},
'view': {
name: 'view',
test: /[\\/]view[\\/]/,

31
frontend.sh

@ -0,0 +1,31 @@
#!/bin/bash
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.engine.mv.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.engine.rule.frontend
gradle frontend
cd cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.engine.st.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.developer.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.lcdp.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.lcdp.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.mvc.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.security.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.platform.system.frontend
gradle frontend
cd /Users/wangshaoping/wspsc/workspace/wangshaoping/v8/platform/io.sc.standard.frontend
gradle frontend

3
gradle.properties

@ -38,7 +38,7 @@ application_version=1.0.0
platform_group=io.sc
platform_version=8.1.44
platform_plugin_version=8.1.44
platform_core_frontend_version=8.1.246
platform_core_frontend_version=8.1.273
###########################################################
# dependencies version
@ -48,6 +48,7 @@ asm_version=9.7
checker_version=3.43.0
commons_fileupload_version=1.4
commons_io_version=2.16.1
commons_text_version=1.12.0
cxf_version=3.2.7
dm_hibernate_version=8.1.2.192
flowable_version=6.8.0

3
io.sc.engine.mv.doc/README.adoc

@ -0,0 +1,3 @@
= 项目介绍

91
io.sc.engine.mv.doc/asciidoc/chapter-appendix/appendix.adoc

@ -0,0 +1,91 @@
[appendix]
= 附录
[#psi-custom-distribution-table]
== 群体稳定性报告需要开发样本数据(建模时的客户群体分布情况)
|===
|模型标识(FD_MODEL_ID) | 模型名称(FD_MODEL_NAME) | 分数段开始值(含)(FD_SCORE_SEG_START) | 分数段结束值(含)(FD_SCORE_SEG_END) | 客户个数(FD_COUNT)
|M1 | 模型1 | 0 | 154 | 2200
|M1 | 模型1 | 155 | 194 | 1583
|M1 | 模型1 | 195 | 234 | 1970
|M1 | 模型1 | 235 | 274 | 2540
|M1 | 模型1 | 275 | 314 | 2450
|M1 | 模型1 | 315 | 354 | 1620
|M1 | 模型1 | 355 | 394 | 1620
|M1 | 模型1 | 395 | 434 | 2100
|M1 | 模型1 | 434 | 1000 | 1250
|===
TIP: 注意:上述分段看上去好像是按照分数进行等分的,其实并非如此,正确的分段方法是按照客户个数进行等分(当然,实际情况不可能按客户个数真正做到等分,通常通过人工进行基本等分即可),而分割的段数一般为 10 个分段。
系统提供界面给使用者录入上述表格数据,或者提供 Excel 模板,用户整理好后,由系统界面导入到后台数据库中。
在有了上述建模时的客户分布数据情况后,系统可以定期自动产生客户群体稳定性报告,在生成报告时,其分数段的划分和建模时导入的数据分数段划分保持一致。
TIP: 如果不能获取到建模时的客户分布情况数据,系统可以采用从上线后一定时间段的客户分布情况作为初始数据,其分数段划段原则为:尽可能平分客户个数,一般分10个段即可。
[#binomial_test_const_table]
== 二项检验常量表
image::chapter-appendix/001.png[]
[%autowidth]
|===
|显著水平 | 置信水平 | 正态分布Z值上界 | 正态分布Z值下界
|0.01 | 0.99 | 2.576 | -2.576
|0.05 | 0.95 | 1.96 | -1.96
|0.1 | 0.9 | 1.645 | -1.645
|===
TIP: 显著性水平的值越大,表示对模型的要求越高。
[#square_test_const_table]
== 卡方分布临界值常量表
[%autowidth]
|===
|自由度 | 显著水平 | |
| | 0.1 | 0.05 | 0.01
|1 | 2.706 | 3.841 | 6.635
|2 | 4.605 | 5.991 | 9.21
|3 | 6.251 | 7.815 | 11.345
|4 | 7.779 | 9.488 | 13.277
|5 | 9.236 | 11.07 | 15.086
|6 | 10.645 | 12.592 | 16.812
|7 | 12.017 | 14.067 | 18.475
|8 | 13.362 | 15.507 | 20.09
|9 | 14.684 | 16.919 | 21.666
|10 | 15.987 | 18.307 | 23.209
|11 | 17.275 | 19.675 | 24.725
|12 | 18.549 | 21.026 | 26.217
|13 | 19.812 | 22.362 | 27.688
|14 | 21.064 | 23.685 | 29.141
|15 | 22.307 | 24.996 | 30.578
|16 | 23.542 | 26.296 | 32
|===
TIP: 显著性水平的值越大,表示对模型的要求越高。
== 模型验证系统数据源接口
模型验证系统作为一个独立的系统,从系统角度讲具有高内聚性,但模型验证系统的验证样本需要外部系统供给,这些外部系统我们称之为数据源,
模型验证系统为数据源提供两个数据接口,只要数据源为模型验证系统提供了这两个接口的数据,模型验证系统即可正常工作。
. <<MV_SCORE_RECORD>>
. <<MV_DEFAULT_RECORD>>
在实际进行系统集成时,只要按要求填充好以上两个数据表即可。
== 模型验证系统实现执行过程
理解模型验证的系统实现过程,有利于理解系统的工作原理和结构,参考以下图示:
image::chapter-appendix/002.png[]
具体说明如下:
. 图中每一框代表系统中的一个类或接口
. 模型验证执行由 Validator 类(作为 spring bean)负责
. Validator 类在执行时会先执行 DataExtractorManager 进行源数据抽取工作
. Validator 类在执行时会然后执行 ExecutorManager 进行验证工作
. DataExtractorManager 管理了多个 DataExtractor,会按照每个 DataExtractor 设定的执行顺序进行依次调用,顺序号越小,执行顺序越靠前。
. ExecutorManager 管理了多个 Executor,会按照每个 Executor 设定的执行顺序进行依次调用,顺序号越小,执行顺序越靠前。
. DataExtractor 和 Executor 都有一个或多个具体实现,负责完成具体的执行操作。

69
io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/二项检验/二项检验.adoc

@ -0,0 +1,69 @@
= 二项检验(保守度检验)
二项检验是在一个时期对模型单个等级的检验,即分别检验模型的每个等级所采用的违约概率是否合适,检验的步骤如下:
[#binomial_test_data_repared]
== 准备建模时确定的各个等级对应的违约概率表
通常在评级系统中,对标尺的划分是根据分数段来确定的,即当某个客户的评分处在[80,90]分数段内,对应的等级为 AAA,也就是说只要评分在 80 到 90 分之间,都对应等级 AAA。
在二项检验时,需要的是某个等级的违约概率,而这个概率是在建模时确定好的,所以需要初始化所有模型所有等级下的违约概率,和分数段对应等级不同的是,对于同一个等级下,其违约概率相同。
因此,为了能够进行二项检验,需要系统事先准备好模型等级的违约概率表(通常采用系统录入和导入方式实现),其示例及结构如下:
|===
|模型标识(FD_MODEL_ID) | 模型名称(FD_MODEL_NAME) | 等级(FD_LEVEL) | 违约概率(FD_PD)
|M1 | 模型1 | AAA+ | 0.001
|M1 | 模型1 | AAA | 0.005
|M1 | 模型1 | AAA- | 0.01
|M1 | 模型1 | ... | ...
|M1 | 模型1 | C- | 0.1
|M1 | 模型1 | ... | ...
|M2 | 模型2 | AAA+ | 0.005
|M2 | 模型2 | AAA | 0.01
|M2 | 模型2 | AAA- | 0.015
|M2 | 模型2 | ... | ...
|M2 | 模型2 | C- | 0.1
|... | ... | ... | ...
|===
上表中每一行代表某个模型的某个等级的建模时确定的违约概率。其字段意义说明如下:
|===
|字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
|模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
|模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
|等级 | FD_LEVEL | varchar(10) | 不能为空 |
|建模时确定的违约概率 | FD_PD | 小数(精度 6) | 不能为空 |
|===
== 统计并计算出用于二项检验的各个指标,生成以下数据表
image::chapter-correctness-of-estimating/001.png[]
link:resources/files/chapter-correctness-of-estimating/binomial_test.xlsx[二项检验指标 Excel 模板]
其字段意义说明如下:
|===
|字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
|模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
|模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
|等级 | FD_LEVEL | varchar(10) | 不能为空 |
|建模时确定的违约概率 | FD_PD | 小数(精度 6) | 不能为空 |
|评级客户个数 | FD_COUNT | 整数 | 不能为空 |
|事实违约客户个数 | FD_DEFAULT_COUNT | 整数 | 不能为空 |
|正态分布平均数 | FD_ND_AVG | 小数(精度 6) | 不能为空 |
|正态分布标准差 | FD_ND_SD | 小数(精度 6) | 不能为空 |
|显著水平 | FD_SL | 小数(精度 6) | 不能为空 | <<binomial_test_const_table>>
|置信水平 | FD_CL | 小数(精度 6) | 不能为空 | <<binomial_test_const_table>>
|正态分布Z值上界 | FD_Z_UPPER | 小数(精度 6) | 不能为空 |<<binomial_test_const_table>>
|正态分布Z值下界 | FD_Z_LOWER | 小数(精度 6) | 不能为空 | <<binomial_test_const_table>>
|临界值上界 | FD_D_UPPER | 小数(精度 6) | 不能为空 |
|临界值下界 | FD_D_LOWER | 小数(精度 6) | 不能为空 |
|是否小于等于上界 | FD_LE_UPPER | 整数 | 不能为空 | 1:满足条件;0:不满足条件
|是否大于等于下界 | FD_GE_LOWER | 整数 | 不能为空 | 1:满足条件;0:不满足条件
|===
[TIP]
====
. <<binomial_test_const_table>>中列出了可选的三种情况(三行),在进行二项检验时,根据客户的需求选取一行即可。
. 在进行二项检验时,可以采用双边检验,也可以采用单边检验。对于双边检验而言,需要在上下界均满足条件,即满足“小于等于上界”同时满足“大于等于下界”
====

4
io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/估值准确性.adoc

@ -0,0 +1,4 @@
= 估值准确性验证
include::卡方检验/卡方检验.adoc[leveloffset=+1]
include::二项检验/二项检验.adoc[leveloffset=+1]

39
io.sc.engine.mv.doc/asciidoc/chapter-估值准确性/卡方检验/卡方检验.adoc

@ -0,0 +1,39 @@
= 卡方检验(适合度检验)
卡方检验是在一个时期对模型所有等级的检验,即检验模型的所有等级及违约概率是否合适,检验的步骤如下:
== 准备建模时确定的各个等级对应的违约概率表
该表同 <<binomial_test_data_repared>> 二项检验准备建模时确定的各个等级对应的违约概率表
== 统计并计算出用于卡方检验的各个指标,生成以下数据表
image::chapter-correctness-of-estimating/002.png[]
link:resources/files/chapter-correctness-of-estimating/chi-square_test.xlsx[卡方检验指标 Excel 模板]
其字段意义说明如下:
|===
|字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
|模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
|模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
|等级 | FD_LEVEL | varchar(10) | 不能为空 |
|建模时确定的违约概率 | FD_PD | 小数(精度 6) | 不能为空 |
|评级客户个数 | FD_COUNT | 整数 | 不能为空 |
|事实违约客户个数 | FD_DEFAULT_COUNT | 整数 | 不能为空 |
|卡方值 | FD_CHI_SQUARE | 小数(精度 6) | 不能为空 |
|===
== 汇总一个模型所有级别的卡方值,参照“卡方分布临界值常量表”进行对比
将上述指标表按照模型对所有级别的卡方值求和,得到模型的卡方检验值,和“卡方分布临界值常量表”中对应的值进行比较,
如果模型的卡方检验值小于等于“卡方分布临界值常量表”中的值,表示该模型通过卡方检验,否则表示没有通过卡方检验。
对于上述示例来说,模型1的卡方检验值为 21.36777057,由于该模型包含3个等级,从 <<square_test_const_table>> 中查找“自由度”为 3 对应的那行数据,
----
显著水平 0.1 0.05 0.01
------------------------------
1 2.706 3.841 6.635
2 4.605 5.991 9.21
3 6.251 7.815 11.345(自由度为3,选择此行最为参照数据)
----
通过比较,发现模型1的卡方检验值 21.36777057 不满足卡方分布临界值,所以该模型没有通过卡方检验。

16
io.sc.engine.mv.doc/asciidoc/chapter-前言/前言.adoc

@ -0,0 +1,16 @@
= 前言
模型的主要作用是通过历史预测未来,采用历史数据构建模型,并利用模型对未来进行预测。
通过历史数据构建的模型反应的是历史的情况,
能够用于对未来进行预测的前提是假设未来一定时间内影响预测结果的因变量不会发生较大变化,
即未来一定时间内会延续历史。
随着时间和相关环境的变化,根据历史数据构建的模型是否还能具有较好的预测能力呢?这需要一些验证方法来鉴别。
在继续说明模型验证之前,我们先对“模型”进行一定的限定,对于衡量好坏客户(坏客户通常是指发生事实违约的客户)的风险模型来讲,
其预测的是未来一段时间内客户的违约概率(即违约的可能性),这种风险模型在构建时采用的目标样本是坏客户,即找出这些坏客户具有的共同特征,
所以在评判一个风险模型的好坏时,是看这个模型是否能够准确地分辨出坏客户,而不是该模型是否能够准确地分辨出好客户。
常见的对模型整体情况验证的方法主要包括:
* 定量模型验证:模型区分能力验证、模型稳定性验证、估值准确性验证等。
* 定性模型验证:治理结构、政策、流程、控制、文档管理、结果运用等。

96
io.sc.engine.mv.doc/asciidoc/chapter-区分能力/cap/cap.adoc

@ -0,0 +1,96 @@
= CAP 曲线及 AR 值
CAP 曲线,也称作为累积准确曲线,AR 值,也称作为准确性比率
== CAP 曲线及 AR 值解读
image::chapter-sc/cap/cap.png[]
=== CAP 曲线坐标轴
. X轴(客户个数百分比): 评分小于等于某个分数的客户个数占整个客户个数的百分比
. Y轴(违约个数百分比): 评分小于等于某个分数的实事违约客户个数占整个实事违约客户个数百分比
. Z轴(截断点): 按评分进行分段
在实际绘制 CAP 曲线时,只包含 X 和 Y 轴,没有 Z 轴,这里为了便于理解,引入 Z 轴。那么 ROC 曲线是如何绘制的呢?
同样需要引入截断点,截断点是将模型预测的得分进行等分,评级得分是一个整数,其取值范围可以是从 0 到 100,或者其他。假如我们将从 0 到 100 的得分等分为 100 个段,那么组成这些截断点的数列为(共101个): 1,2,3,...,99,100。这些截断点就构成了 Z 轴。
[%autowidth]
|===
|截断点序号 | 截断点得分(小于等于)
| 1 | 0
| 2 | 1
| 3 | 2
| ... |
| 100 | 99
| 101 | 100
|===
那么这些截断点在 CAP 曲线上所代表的含义是什么呢?在 CAP 曲线的 Z 轴(虚拟的)代表客户评分小于等于截断点的值。
. 截断点 0: 评分小于等于 0 分
. 截断点 1: 评分小于等于 1 分
. 截断点 100: 评分小于等于 100 分
. 依次类推
=== CAP 曲线绘制
在有了上述截断点的概念的情况下,我们就可以分别计算出在某个截断点(小于等于某个得分)的 X 轴和 Y 轴的坐标值,
这样就能根据截断点的个数绘制出多个坐标点,将这些坐标点通过平滑曲线连接,
便绘制出 CAP 曲线。
=== 随机模型(完全没有预测能力的模型)的表现
==== 完美模型(理想模型)的表现
==== 现实中较好模型的表现
现实中较好的模型表现为介于完美模型和随机模型之间的曲线,曲线越接近于完美曲线越好。
==== AR 值
直观的讲,AR 值就是下面面 CAP 曲线中两块面积的比值。
image::chapter-sc/cap/cap2.png[]
image::chapter-sc/cap/ar.png[]
其中 aR 为褐色部分的面积,aP 为褐色部分和黄色部分面积之和
. 完美模型 AR 值: 1
. 随机模型 AR 值: 0
. 现实中较好模型 AR 值: 介于完美模型和随机模型 AR 值之间,越接近 1 越好。
== CAP 曲线及 AR 值系统实现
[#CAP_KPI]
=== CAP 指标表
对每个需要验证的模型根据截断点分别对 <<MV_GENERAL_SAMPLE>> 进行样本个数统计。
|===
|模型标识 | 模型名称 | 截断点 | 评分小于等于截断点的客户个数 | 客户总数 | 评分小于等于截断点的事实违约客户个数 | 事实违约的总客户数 | 违约个数百分比(Y) | 客户个数百分比(X)
| | |评分小于等于 | TS | TT | TDS | TDT | TDS/TDT | TS/TT
|M1 | 模型1 | 0 | 1 | 100 | 1 | 10 | 0.1 | 0.01
|M1 | 模型1 | 1 | 1 | 100 | 1 | 10 | 0.1 | 0.01
|... | ... | ... | ... | ... | ... | ... | ... | ...
|M1 | 模型1 | 100 | 100 | 100 | 10 | 10 | 1 | 1
|===
上表中每一行代表某个模型的一个截断点下的违约客户个数百分比和客户个数百分比。其字段意义说明如下:
|===
| 字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
| 模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
| 模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
| 平分率截断点 | FD_SCORE_CUT_OFF_POINT | 整数 | 不能为空 | 对平分进行平分,代表在小于等于截断点时的数据
| 评分小于等于截断点的客户个数 | FD_TS | 整数 | 不能为空 |
| 客户总数 | FD_TT | 整数 | 不能为空 |
| 评分小于等于截断点的事实违约客户个数 | FD_TDS | 整数 | 不能为空 |
| 事实违约的总客户数 | FD_TDT | 整数 | 不能为空 |
| 违约个数百分比(Y) | FD_Y | 小数(精度 6) | 不能为空 | FD_Y=FD_TDS/FD_TDT
| 客户个数百分比(X) | FD_X | 小数(精度 6) | 不能为空 | FD_X=FD_TS/FD_TT
|===
=== CAP 曲线绘制
通过 <<CAP_KPI>> 中的 X 和 Y 值 绘制并连接各个点
=== AR 值计算
在具体的实现时,可以采用分割成多个梯形进行面积相加。
计算公式:
image::chapter-sc/cap/ar2.png[]

59
io.sc.engine.mv.doc/asciidoc/chapter-区分能力/ks/ks.adoc

@ -0,0 +1,59 @@
= KS 曲线及 KS 值
== KS 曲线及 KS 值解读
image::chapter-sc/ks/ks.png[]
=== KS 曲线坐标轴
* X轴(模型预测得分): 模型预测得分小于等于某个分数
* Y轴(正常/违约客户占总正常/违约客户百分比): 模型预测得分小于等于某个分数的情况下,分别统计占比
在 KS 曲线中包含两条曲线:
* 正常客户KS曲线:模型预测得分小于等于某个分数时,正常的客户占所有正常客户的百分比
* 违约客户KS曲线:模型预测得分小于等于某个分数时,违约的客户占所有违约客户的百分比
=== KS 曲线绘制
=== KS 值
直观的讲,就是两条 KS 曲线相距最远的线段的长度,即上图中红色的线段的长度。
== KS 曲线及 KS 值系统实现
[#KS_KPI]
=== KS 指标表
对每个需要验证的模型根据模型预测评分分别对 <<MV_GENERAL_SAMPLE>> 进行样本个数统计。
. KS 指标表: MV_SC_KS_KPI
|===
|模型标识 | 模型名称 | 评分截断点(X) | 评分小于等于截断点事实正常的客户个数 | 事实正常的总客户个数 | 评分小于等于截断点事实违约客户个数 | 事实违约的总客户个数 | 正常客户占比(Y1) | 违约客户占比(Y2)
| | | 小于等于 | N | TN | D | TD | N/TN | D/TD
|M1 | 模型1 | 0 | 0 | 90 | 0 | 10 | 0 | 0
|... | ... | ... | ... | ... | ... | ... | ... | ...
|M1 | 模型1 | 50 | 3 | 90 | 3 | 10 | 0.033 | 0.3
|... | ... | ... | ... | ... | ... | ... | ... | ...
|M1 | 模型1 | 100 | 90 | 90 | 10 | 10 | 1 | 1
|===
上表中每一行代表某个模型的一个截断点下的正常客户占比和违约客户占比。其字段意义说明如下:
|===
|字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
|模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
|模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
|平分率截断点(X) | FD_SCORE_CUT_OFF_POINT | 整数 | 不能为空 | 对平分进行平分,代表在小于等于截断点时的数据
|评分小于等于截断点事实正常的客户个数 | FD_N | 整数 | 不能为空 |
|事实正常的总客户个数 | FD_TN | 整数 | 不能为空 |
|评分小于等于截断点事实违约客户个数 | FD_D | 整数 | 不能为空 |
|事实违约的总客户个数 | FD_TD | 整数 | 不能为空 |
|正常客户占比(Y1) | FD_Y1 | 小数(精度 6) | 不能为空 | FD_Y1=FD_N/FD_TN
|违约客户占比(Y2) | FD_Y2 | 小数(精度 6) | 不能为空 | FD_Y2=FD_D/FD_TD
|===
=== KS 曲线绘制
* 通过 <<KS_KPI>> 中的 X 和 Y1 值 绘制并连接各个点形成正常客户的 KS 曲线。
* 通过 <<KS_KPI>> 中的 X 和 Y2 值 绘制并连接各个点形成违约客户的 KS 曲线。
* 将两条曲线绘制在同一张坐标系中
=== KS 值计算
max(Y1-Y2)

113
io.sc.engine.mv.doc/asciidoc/chapter-区分能力/roc/roc.adoc

@ -0,0 +1,113 @@
= ROC 曲线及 AUC 值
== ROC 曲线及 AUC 值解读
image::chapter-sc/roc/roc.png[]
=== ROC 曲线坐标轴
. X轴(误警率): 模型在某个截断点的误警率
. Y轴(命中率): 模型在某个截断点的命中率
. Z轴(截断点): 按预测违约概率进行分段
在实际绘制 ROC 曲线时,只包含 X 和 Y 轴,没有 Z 轴,这里为了便于理解,引入 Z 轴。那么 ROC 曲线是如何绘制的呢?
模型区分能力验证主要验证模型预测情况和实际表现情况的关系。对于风险模型,模型预测的是一个客户的违约概率,模型的实际表现情况是一个客户是否违约。
两者是不能直接进行比较的,所以需要引入截断点,截断点是将模型预测的违约概率进行等分,我们知道违约概率是一个介于 0 到 1 的百分比数,
假如我们将其等分为 20 个段,那么组成这些截断点的数列为(共21个): 1,0.95,0.90,0.85,...,0.05,0。这些截断点就构成了 Z 轴。
[%autowidth]
|===
| 截断点序号 | 截断点违约概率(大于等于)
| 1 | 1.00
| 2 | 0.95
| 3 | 0.90
| 4 | 0.85
| 5 | 0.80
| 6 | 0.75
| 7 | 0.70
| 8 | 0.65
| 9 | 0.60
| 10 | 0.55
| 11 | 0.50
| 12 | 0.45
| 13 | 0.40
| 14 | 0.35
| 15 | 0.30
| 16 | 0.25
| 17 | 0.20
| 18 | 0.15
| 19 | 0.10
| 20 | 0.05
| 21 | 0.00
|===
那么这些截断点在 ROC 曲线上所代表的含义是什么呢?在 ROC 曲线的 Z 轴(虚拟的)代表当模型预测的违约概率大于等于其截断点值时被认为预测此客户为违约。
. 截断点 1 : 把模型预测违约概率为 1 的客户认定为模型预测此客户将违约
. 截断点 0.95: 把模型预测违约概率大于等于 0.95 的客户认定为模型预测此客户将违约
. 截断点 0.90: 把模型预测违约概率大于等于 0.90 的客户认定为模型预测此客户将违约
. 依次类推
=== ROC 曲线绘制
在有了上述截断点的概念的情况下,我们就可以分别计算出在某个截断点(大于等于某个违约概率被认为预测为违约)模型的命中率和误警率,
然后在基于命中率和误警率坐标系中绘制一个点,这样就能根据截断点的个数绘制出多个坐标点,将这些坐标点通过平滑曲线连接,便绘制出 ROC 曲线。
=== 随机模型(完全没有预测能力的模型)的表现
此类模型在任何一个截断点下,其命中率(预测成功率)和误警率(预测失败率)相同,那就等于用扔硬币猜正反面来预测,所以此类模型不具有预测能力。在上图中表现为正方形的对角线。
=== 完美模型(理想模型)的表现
如果一个模型预测为违约客户,而实际上这些客户就发生了违约,那么这个模型就是完美的,即和实际一致。在上图中表现为一条水平线段(命中率为 1 的水平线段)。
=== 现实中较好模型的表现
现实中较好的模型表现为介于完美模型和随机模型之间的曲线,曲线越接近于完美曲线越好。
=== AUC 值
直观的讲,AUC 的值就是上面 ROC 曲线和 X 轴构成的图像的面积,即小图中黄色部分的面积。
image::chapter-sc/roc/roc2.png[]
. 完美模型 AUC 值: 1*1 =1
. 随机模型 AUC 值: 1*1/2 =0.5
. 现实中较好模型 AUC 值: 介于完美模型和随机模型 AUC 值之间,越接近 1 越好。
== ROC 曲线及 AUC 值系统实现
[#ROC_KPI]
=== ROC 指标表
对每个需要验证的模型根据截断点分别对 <<MV_GENERAL_SAMPLE>> 进行样本个数统计。
|===
| 模型标识 | 模型名称 | 截断点 | 实际违约且预测违约样本总数 | 实际违约且预测正常样本总数 | 实际正常且预测违约样本总数 | 实际正常且预测正常样本总数 | 实际违约样本总数 | 实际正常样本总数 | 违约预测命中率(Y) | 违约预测误警率(X)
| | | 大于等于 | DD | DN | ND | NN | TD=DD+DN | TN=ND+NN | DD/TD | ND/TN
| M1 | 模型1 | 1 | 10 | 1 | 2 | 20 | 11 | 22 | 0.9091 | 0.0909
| M1 | 模型1 | 0.95 | 10 | 1 | 2 | 20 | 11 | 22 | 0.9091 | 0.0909
| M1 | 模型1 | 0.90 | 10 | 1 | 2 | 20 | 11 | 22 | 0.9091 | 0.0909
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ...
| M1 | 模型1 | 0.05 | 10 | 1 | 2 | 20 | 11 | 22 | 0.9091 | 0.0909
| M1 | 模型1 | 0 | 10 | 1 | 2 | 20 | 11 | 22 | 0.9091 | 0.0909
|===
上表中每一行代表某个模型的一个截断点下的命中率和误警率。其字段意义说明如下:
|===
| 字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
| 模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
| 模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
| 违约概率截断点 | FD_PD_CUT_OFF_POINT | 小数(精度 6) | 不能为空 |对违约概率进行平分,代表在大于等于截断点时的数据
| 实际违约且预测违约样本总数 | FD_DD | 整数 | 不能为空 |
| 实际违约且预测正常样本总数 | FD_DN | 整数 | 不能为空 |
| 实际正常且预测违约样本总数 | FD_ND | 整数 | 不能为空 |
| 实际正常且预测正常样本总数 | FD_NN | 整数 | 不能为空 |
| 实际违约样本总数 | FD_DT | 整数 | 不能为空 | FD_DT=FD_DD+FD_DN
| 实际正常样本总数 | FD_NT | 整数 | 不能为空 | FD_NT=FD_ND+FD_NN
| 违约预测命中率(Y) | FD_Y | 小数(精度 6) | 不能为空 | FD_Y=FD_DD/FD_TD
| 违约预测误警率(X) | FD_X | 小数(精度 6) | 不能为空 | FD_X=FD_ND/FD_TN
|===
=== ROC 曲线绘制
通过 <<ROC_KPI>> 中的 X 和 Y 值 绘制并连接各个点
=== AUC 值计算
在具体的实现时,可以采用分割成多个梯形进行面积相加。
计算公式:
image::chapter-sc/roc/auc.png[]

18
io.sc.engine.mv.doc/asciidoc/chapter-区分能力/区分能力.adoc

@ -0,0 +1,18 @@
= 模型区分能力验证
通过模型区分能力的验证,可以鉴别模型区分好坏客户的能力(即可以鉴别模型预测的效果),
常用方法:ROC曲线(AUC值), CAP曲线(AR值), KS曲线(KS值)等。
模型的区分能力高低决定了模型预测效果的好坏,理论上讲,一个完美(理想)的模型将会是这样的:模型预测某个客户在将来一段时间内会违约,实际上这个客户在将来一段时间内就发生了事实违约。
事实上一个风险模型预测的是一个客户在未来一段时间内的违约概率,注意是概率而不是预测客户是否违约。在实际使用中,我们可以对预测的违约概率进行截断点处理,即当预测某个客户的违约概率大于某一个固定值(例如: 90%),那么就认为模型预测某个客户在未来一段时间内会违约。
[TIP]
====
一个风险模型的主要目的是管控风险,一个好的风险模型应该能够鉴别坏的客户,通俗的讲就是这个模型能够尽可能地找到可能为坏的客户,即便将部分事实上好的客户判断为坏的客户也是可以接受的。
对于银行来说,即便误判了一些好的客户,减少了一定的业务利润,但却拒绝了大多数坏的客户,减少了大的损失,这一点对于对大额贷款的公司类客户显得更为重要。当然这也不能过分,过分的拒绝客户,就影响业绩,极端的情况就是什么业务也不做,自然也就没有风险,但也没有任何利润。
====
include::roc/roc.adoc[leveloffset=+1]
include::cap/cap.adoc[leveloffset=+1]
include::ks/ks.adoc[leveloffset=+1]

5
io.sc.engine.mv.doc/asciidoc/chapter-操作手册/操作手册.adoc

@ -0,0 +1,5 @@
= 操作手册
include::验证结果/验证结果.adoc[leveloffset=+1]
include::样本管理/样本管理.adoc[leveloffset=+1]
include::配置/配置.adoc[leveloffset=+1]

6
io.sc.engine.mv.doc/asciidoc/chapter-操作手册/样本管理/样本管理.adoc

@ -0,0 +1,6 @@
= 样本管理
== 评分记录
image::chapter-use-help/sample/001.png[]
== 违约记录
image::chapter-use-help/sample/002.png[]

27
io.sc.engine.mv.doc/asciidoc/chapter-操作手册/配置/配置.adoc

@ -0,0 +1,27 @@
= 配置
== 执行器
image::chapter-use-help/configure/001.png[]
== 评分截断点
image::chapter-use-help/configure/002.png[]
== 预警阈值
image::chapter-use-help/configure/003.png[]
== 二项检验 Z 值常量
image::chapter-use-help/configure/004.png[]
== 卡方分布临界值常量
image::chapter-use-help/configure/005.png[]
== 模型
image::chapter-use-help/configure/006.png[]
== 建模时评分分布
image::chapter-use-help/configure/007.png[]
== 标尺
image::chapter-use-help/configure/008.png[]
== 数据抽取器
image::chapter-use-help/configure/009.png[]

41
io.sc.engine.mv.doc/asciidoc/chapter-操作手册/验证结果/验证结果.adoc

@ -0,0 +1,41 @@
= 验证结果
== 功能清单
[cols="1,2",options="header"]
|===
| 功能项 | 功能说明
| 刷新 | 重新验证结果
| 执行验证 | 执行验证
| 详情 | 查看验证结果详细信息
| 删除 | 删除验证结果
| 查看 | 查看当前选择的验证结果的详细信息
| 导出 | 将当前验证结果导出成 csv 文件
|===
== 执行验证
image::chapter-use-help/result/001.png[]
点击 "执行验证" 按钮, 系统弹出对话框, 输入执行参数, 点击 "立即执行" 按钮。
image::chapter-use-help/result/002.png[]
执行完成后, 系统生成验证结果。
image::chapter-use-help/result/003.png[]
== 查看验证结果摘要
image::chapter-use-help/result/004.png[]
== 查看验证结果详情
选择要查看的验证结果摘要, 点击 "详情" 按钮, 打开验证结果详情对话框。
区分能力:
image::chapter-use-help/result/005.png[]
稳定性:
image::chapter-use-help/result/006.png[]
标尺检验
image::chapter-use-help/result/007.png[]

62
io.sc.engine.mv.doc/asciidoc/chapter-概述/概述.adoc

@ -0,0 +1,62 @@
= 模型验证概述
上述提到的常见的三种验证方法(模型区分能力验证、模型稳定性验证、估值准确性验证),
均是对模型的整体情况进行验证,也就是从三个方面(维度)来检验模型的总体情况如何。
== 模型验证阶段、验证内容及方法、验证主体约束
image::chapter-summary/001.png[]
以上是根据银监会的监管框架要求总结了关于模型验证阶段、验证内容及方法、验证主体相关约束,具体说明如下:
. 返回检验: 是指模型定量验证的方法,简单来说是通过实际表现和预测结果进行比较进行验证的一种方法。
. 按模型验证阶段主要分为:投产前、投产后两个主要阶段,所谓投产前是指咨询建模完成后,模型还没有应用到实际的业务系统中(即系统还未落地实施);而投产后是指模型已经应用到实际业务系统中(即咨询建模的成果已经通过系统实施完成,并在实际业务中使用模型)
. 按模型验证内容主要分为:定量验证和定性验证,请注意,这里所说的定量、定性与指标类型中的定量、定性不是一个概念。模型定量验证部分主要是通过标准的数理统计验证算法(区分能力验证、稳定性验证、估值准确性验证等)对模型进行验证(通常可以获得定量的验证结果),而模型定性验证部分则是从治理结构、政策、流程、文档等方面进行验证。
. 按模型验证内容还可以分为:全面验证和持续监控,全面验证同时包含定量和定性验证,而持续监控只包含定量验证。
. 模型开发主体:指咨询建模的公司或组织(国际著名的四大会计公司:普华永道、毕马威、德勤、安永,其他咨询公司:SAS、FICO等)
. 模型应用主体:指实际使用模型的公司或组织(通常指银行)
. 主体保持独立:指主体间不能相同或相关,例如投产前全面验证需要满足:“与模型开发主体和模型应用主体保持独立”,表示进行验证的单位或组织不能是模型的开发主体,也不能是模型的应用主体,即进行验证的单位或组织是第三方(不能是开发时的咨询公司,也不能是银行应用、开发模型的团队)
== 定量模型验证的三个维度
image::chapter-summary/002.png[]
具体说明如下:
. 定量模型验证的三个维度(即三个主要验证方面):模型区分能力、模型稳定性、模型估值准确性
. 模型区分能力: 对于模型整体定量验证而言,模型区分能力验证是验证模型对坏客户的鉴别能力(模型总得分越低,客户变为坏客户的可能性越大),对于单指标的区分能力验证,模型区分能力验证是验证单个指标对鉴别坏客户的能力(通常表现为单指标的单调性,即单指标的得分越低,客户变坏(违约)的可能性越大)
. 模型稳定性: 主要用于分析客户群体是否发生较大变化,通常在咨询建模期间,建模者会对客户群体进行划分,分别计算出各个群体在整个群体中的占比,在实际应用模型过程中,如果这种占比发生了较大变化,说明模型变得不够稳定了。
. 估值准确性: 估值准确性的验证,通常包含卡方检验和二项检验,卡方检验主要检验的是模型标尺的整体划分是否符合要求,而二项检验主要检验的是模型标尺单级别的违约概率设置是否符合要求。
== 定量模型验证三个维度的验证顺序
通常在对模型进行定量验证时采用的方法(即验证维度)分为三种:模型区分能力、模型稳定性、模型估值准确性,
那这三种验证方法的顺序和重要性又是怎样的呢?以下给出在实际模型定量验证采用的通用工作流程。
image::chapter-summary/003.png[]
具体说明如下:
. 首先进行模型整体区分能力验证,如果没有通过,则表明模型在整体预测方面已经出现问题(即当前实际情况与建模时的情况发生了较大变化),通常需要考虑重新建模,即对组成模型的各个指标以及权重进行重新调整,如果条件允许,可以对单指标进行区分能力检验,分析具体是哪些指标对模型区分好坏客户能力失去了作用,为模型调整提供依据。
. 如果通过了模型的区分能力验证,再进行模型稳定性验证,如果没有通过模型稳定性验证,通常也需要考虑对模型进行调整,通过分析单个特征变量客户群的变化,能够为模型调整提供依据。
. 如果通过了区分能力和稳定性验证,再进行模型估值准确性验证,在进行模型估值准确性验证时,通常采用两种检验方法:卡方检验和二项检验。在实际操作时,一般先进行卡方检验,该检验验证的是模型的标尺划分是否合理,如果没有通过,需要考虑对标尺进行重新划分,在通过卡方检验之后,再进行二项检验,该检验验证的是标尺中单个级别的违约概率设置是否合适,如果没有通过二项检验,需要考虑重新调整单级别的违约概率。
== 模型验证总体功能
image::chapter-summary/functions.png[]
具体说明如下:
. 在进行模型验证之前需要准备必须的数据,这些数据主要包含两方面:评级及违约相关的数据;咨询建模数据。
.. 评级及违约相关的数据是最重要的,也是必须的。
.. 咨询建模数据,仅用于模型稳定性验证中计算 PSI 时使用(如果无法获取,可采用评级系统上线后一段时间内的数据代替)。
. 模型验证系统所需的数据可通过数据抽取模块将其加载到模型验证系统中。此模块的具体实现可根据情况而定(可以通过 SQL 语句直接从源系统中抽取,也可以通过 ETL 方式加载到系统中,甚至也可以通过 xls 导入到系统中),目的是将这些需要的数据导入到模型验证系统中固定的数据表和字段中
. 在所需的数据导入到模型验证系统后,首先需要进行的就是对这些数据(我们称之为验证样本)进行一些过滤,生成合格的验证样本用于后续的验证操作。
. 在有了合格的验证样本后,通过验证算法对模型进行实际的验证处理,并将验证处理的结果保存到模型验系统的数据库中,以便后续展示给使用者。
. 模型验证实际处理逻辑执行完毕后,就可以查看验证结果了。
.. “模型区分能力验证结果”,“模型稳定性验证结果”,“模型估值准确性验证结果”分别对应的是模型验证的三个维度的详细结果信息;
.. “模型验证结果摘要”是展示对一个模型的总体验证结果摘要信息,包含上述三个维度验证结果:AUC,AR,KS,PSI,是否通过卡方检验,是否通过二项检验;查看参与本次模型验证验证样本相关数据信息
== 模型验证系统数据架构
image::chapter-summary/structure.png[]
具体说明如下:
. 模型验证数据来源于“源系统”,主要分为评级相关、违约相关和咨询建模相关三部分
. 数据表分为计算相关和历史相关两大类
. 验证结果分为单项和总体两个层面

16
io.sc.engine.mv.doc/asciidoc/chapter-源数据/源数据.adoc

@ -0,0 +1,16 @@
= 模型验证源数据
模型验证源数据作为模型验证的必要条件,是完成模型验证的第一个也是最重要的一个环节,主要涉及的源数据包括:评级及违约相关数据和咨询建模相关数据。
== 评级及违约相关数据
模型验证中一个最重要的验证就是模型的区分能力验证,该验证主要是通过模型的预测结果和实际结果来进行,其中模型的预测结果通常对应客户的评级结果,而模型的实际结果通常对应客户的实际违约情况。
== 咨询建模相关数据
=== 群体稳定性报告需要开发样本数据(建模时的客户群体分布情况)
在生成客户群体稳定性报告时,需要用到建模时的客户群体分布情况,详情参见 <<psi-custom-distribution-table>>
== 常量和配置相关数据
=== 二项检验常量表
在进行二项检验时,需要用到二项检验常量表,详情参见 <<binomial_test_const_table>>
=== 卡方分布临界值常量表
在进行卡方检验时,需要用到卡方分布临界值常量表,详情参见 <<square_test_const_table>>

8
io.sc.engine.mv.doc/asciidoc/chapter-稳定性/区分能力变动曲线/区分能力变动曲线.adoc

@ -0,0 +1,8 @@
== 区分能力变动曲线
根据银监会的监管要求,“商业银行应当对不同时间段模型区分能力的稳定性进行验证,并确保模型区分能力超过设定时限后随时间段长度的增大而逐渐减弱而非骤降。”
因此,考虑对模型区分能力验证工具的AR值、KS值的稳定性进行验证,
展示一年或三年内模型的AR值、KS值的变动曲线,如果有骤降的情况发生,需检查原因,考虑建立更长时期的模型。
image::chapter-stability/sc-change/001.png[]
在系统实现时,采用系统定期跑批数据(排除人工运行产生的数据)绘制变化曲线。

16
io.sc.engine.mv.doc/asciidoc/chapter-稳定性/客户群体稳定性报告/客户群体稳定性报告.adoc

@ -0,0 +1,16 @@
= 客户群体稳定性报告
通过对模型发展群体(开发样本)的评分分布百分比和模型实施群体(实施样本)评分百分比的比较,转换,统计出一个反映从模型发展到模型实施的客户群体变化幅度大小的稳定性指数PSI(Population Shift Index)。指数越高,稳定性越低,客户群体的评分分布变化越大。一般来说:
* PSI 在 0.1 以下意味着从模型发展群体到模型实施群体的评分分布之间的变化很小,稳定性较高。
* PSI 在 0.1-0.25 之间意味着从模型发展群体到模型实施群体的评分分布之间发生了变化,需要关注。
* PSI 0.25 以上意味着从模型发展群体到模型实施群体的评分分布之间发生了较大变化。
如果稳定指数在 0.25 以上,银行应该进一步通过变量分析报告来分析客户群体变化的趋势,以找到导致评分大幅度变化的原因,并决定是否需要作适当调整。
== 客户群体稳定性报告模板及其计算方法
image::chapter-stability/psi/001.png[]
link:resources/files/chapter-stability/psi.xlsx[客户群体稳定性报告 Excel 模板]
== 客户群体稳定性报告系统实现
在生成客户群体稳定性报告时,需要用到建模时的客户群体分布情况,详情参见 <<psi-custom-distribution-table>>

10
io.sc.engine.mv.doc/asciidoc/chapter-稳定性/稳定性.adoc

@ -0,0 +1,10 @@
= 稳定性验证
稳定性是指模型对时间和宏观环境的变化保持稳定。具体的模型验证工具主要包括:
. 转移分析
. 客户群体稳定性报告
. 区分能力变动曲线
include::转移分析/转移分析.adoc[leveloffset=+1]
include::客户群体稳定性报告/客户群体稳定性报告.adoc[leveloffset=+1]
include::区分能力变动曲线/区分能力变动曲线.adoc[leveloffset=+1]

43
io.sc.engine.mv.doc/asciidoc/chapter-稳定性/转移分析/转移分析.adoc

@ -0,0 +1,43 @@
= 转移分析
转移分析是通过构建评级等级的转移矩阵,观察等级的变化情况,从而判断模型是否稳定。
转移矩阵分析是假设在最稳定的模型中,其它条件不变的情况下,同一评级客户前后期的评级等级应当维持不变。由此可知在最稳定的模型情况下,其转移矩阵表现为如下状态:
image::chapter-stability/transition_matrix/001.png[]
但在实际情况下,这种理论上的稳定模型是不存在的,一定会出现等级转移的情况。
在进行转移分析来判断模型的稳定性时,可通过以下步骤进行:
== 建立转移矩阵
转移矩阵实际上是转置概率矩阵,这里出转置概率的定义及其含义:
image::chapter-stability/transition_matrix/002.png[]
以下通过实例说明如何建立转移矩阵:
假定 1 年前评级等级为 5 的客户个数总共有 10 个,1 年后这 10 个客户的评级等级发生如下变化:
* 这 10 个客户中有 2 个的评级等级变成了 4
* 这 10 个客户中有 3 个的评级等级变成了 6
* 这 10 个客户中有 5 个的评级等级与 1 年前相同,等级为 5
那么对于评级等级为 5 的转移矩阵为
image::chapter-stability/transition_matrix/003.png[]
== 观察分析转移矩阵
在观察和分析转移矩阵时,我们可以从以下两个方面进行分析
. 转移概率是否随着等级变动的幅度加大而递减,其业务含义为:即便等级有发生变化,但变化越小,模型的稳定性才越好。
. 等级大幅度的变动是否属于合理范围,其业务含义为:当发现等级的变化幅度较大时,需要判断是否从业务角度有合理的解释。
== 观察多个连续的转移矩阵
通过观察多个连续的转移矩阵(至少跨2个时间周期,产生2个转移矩阵),即对评级等级变化情况进行多次持续跟踪,
观察是否出现评级等级变化回复的情况(例如:评级等级在第一年到第二年发生了调高,而第二年到第三年又回调到第一年的等级;或评级等级在第一年到第二年发生了调低,而第二年到第三年又回调到第一年的等级)。
回复的比率越高,则代表模型的稳定性越高。
== 计算转移矩阵的SVD值(Singular Value Decomposition:奇异值分解方法),衡量评级变动的程度
SVD值可以作为评估评级稳定性的量化指标,其值越小,表示评级等级变动的幅度越小,说明模型越稳定。
SVD值的计算方法如下:

124
io.sc.engine.mv.doc/asciidoc/chapter-验证样本/验证样本.adoc

@ -0,0 +1,124 @@
= 模型验证合格验证样本
模型验证的准确性在很大程度上依赖于验证样本选取的正确性和合理性,
一个可用于模型验证的样本数据应该包含两个方面的信息:
* 模型的预测信息(评级信息)
* 模型的实际表现信息(违约确定信息)
我们先对这两个部分的信息进行适当的解释,之后,我们再给出如何选取一个合格的模型验证样本。
[#MV_SCORE_RECORD]
== 模型的预测信息(评级信息)
通常一个模型对客户的预测信息存在于评级系统中,一个最基本的预测信息包含的数据结构如下:
. 评级预测记录表(模型预测表):MV_SCORE_RECORD
|===
| 客户标识 | 模型标识 | 模型名称 | 评级结果(违约概率) | 评级结果(得分) | 评级结果(等级) | 评级有效期开始日期(评级生效日期) | 评级有效期结束日期(评级失效日期)
| A | M1 | 模型1 | 0.001 | 90 | 5 | 2013-01-01 | 2013-12-30
| A | M1 | 模型1 | 0.0015 | 80 | 6 | 2014-01-01 | 2014-12-30
| A | M1 | 模型1 | 0.0012 | 85 | 6 | 2014-06-01 | 2015-05-31
| A | M1 | 模型1 | 0.005 | 60 | 10 | 2015-09-01 | 2016-08-31
|===
上表中每一行代表一次有效评级。其字段意义说明如下:
|===
| 字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
| 客户标识 | FD_CUSTOM_ID | varchar(32) | 不能为空 |
| 模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
| 模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
| 评级结果(违约概率) | FD_PD | 小数(精度 6) | 不能为空 | 通过模型计算出的违约概率,非经人工调整后的违约概率
| 评级结果(得分) | FD_SCORE | 整数 | 不能为空 | 通过模型计算出的得分,非经人工调整后的得分
| 评级结果(等级) | FD_LEVEL | varchar(10) | 不能为空 | 通过模型计算出的等级,非经人工调整后的等级
| 评级有效期开始日期(评级生效日期) | FD_SCORE_BEGIN_DATE | date | 不能为空 |
| 评级有效期结束日期(评级失效日期) | FD_SCORE_END_DATE | date | 不能为空 |
|===
我们先看一看一个通用的评级及信贷业务流程
image::chapter-datasource/001.png[]
一个可以用于模型验证的评级及违约相关数据,对于一个客户而言,必须存在一个有效的评级记录(模型预测结果),同时也必须存在与之对应的一个违约情况记录(模型实际结果,存在违约记录表示事实违约,不存在违约记录表示事实正常),
一个合格的可以作为模型验证样本的评级记录需要满足以下条件:
. 评级记录的状态必须是有效的,即一个客户的评级步骤必须全部走完,并且通过审批。(排除那些未完成的评级记录、未审批通过的评级记录、状态为非有效的评级记录)
. 该评级客户是银行或金融机构的客户,即该客户和银行或金融机构发生了信贷关系。(排除虽然有效评级,但没有发放贷款,通常表现为客户的评级结果没有达到信贷的条件,即被拒绝的客户),也就是说需要对满足条件 1 的评级记录进行部分排除,排除条件为评级记录对应的客户不在银行或金融机构的客户名单中的那些评级记录。
[#MV_DEFAULT_RECORD]
== 模型的实际表现信息(违约确定信息)
[%autowidth]
|===
| 客户标识 | 客户违约确定(认定)日期
| A | 2015-11-02
|===
上表中每一行代表一次确认(认定)客户违约。其字段意义说明如下:
[%autowidth]
|===
| 字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
| 客户标识 | FD_CUSTOM_ID | varchar(32) | 不能为空 |
| 违约认定日期 | FD_DEFAULT_CONFIRM_DATE | date | 不能为空 |
|===
TIP: 模型的实际表现情况,对于信用风险内评模型而言,主要考察的是客户的实际违约情况,
对于在表现期(对公评级通常为自评级生效后1年)内没有发生事实违约的我们称之为好客户样本,
而在表现期内发生了事实违约的我们称之为坏样本。
原则上只有在最终确认为违约时才能作为坏样本,即在对坏样本“违约认定日期”定为违约认定日期。
== 合格验证样本数据结构
[#MV_GENERAL_SAMPLE]
=== 用于模型验证的合格验证样本表: MV_GENERAL_SAMPLE
|===
| 客户标识 | 模型标识 | 模型名称 | 评级结果(违约概率) | 评级结果(得分) | 评级结果(等级) | 评级有效期开始日期(评级生效日期) | 评级有效期结束日期(评级失效日期) | 实际违约状态
| A | M1 | 模型1 | 0.001 | 90 | 5 | 2013-01-01 | 2013-12-30 | D
| A | M1 | 模型1 | 0.0005 | 80 | 6 | 2014-01-01 | 2014-12-30 | N
| B | M2 | 模型2 | 0.0005 | 80 | 6 | 2014-06-01 | 2015-05-31 | D
| B | M2 | 模型2 | 0.0005 | 80 | 6 | 2015-09-01 | 2016-08-31 | N
|===
上表中每一行代表一条合格的验证样本。其字段意义说明如下:
|===
|字段中文名称 | 字段名称 | 字段类型 | 字段约束 | 备注
|客户标识 | FD_CUSTOM_ID | varchar(32) | 不能为空 |
|模型标识 | FD_MODEL_ID | varchar(32) | 不能为空 |
|模型名称 | FD_MODEL_NAME | varchar(100) | 不能为空 |
|评级结果(违约概率) | FD_PD | 小数(精度 6) | 不能为空 | 通过模型计算出的违约概率,非经人工调整后的违约概率
|评级结果(得分) | FD_SCORE | 整数 | 不能为空 | 通过模型计算出的得分,非经人工调整后的得分
|评级结果(等级) | FD_LEVEL | varchar(10) | 不能为空 | 通过模型计算出的等级,非经人工调整后的等级
|评级有效期开始日期(评级生效日期) | FD_SCORE_BEGIN_DATE | date |不能为空 |
|评级有效期结束日期(评级失效日期) | FD_SCORE_END_DATE | date |不能为空 |
|实际违约状态 | FD_DEFAULT_STATUS | varchar(1) |不能为空 | D:表示实际发生违约N:表示实际未发生违约
|===
== 合格样本选取规则
在有了合格的模型预测信息(评级记录)和模型的实际表现信息(违约情况)后,我们需要结合两者信息根据合适的过滤规则生成可以真正用于模型验证的合格样本集。这些规则包括:
. 客户在评级系统中存在有效评级记录(存在有效的评级结果,包括:评分,违约概率,等级等),且存在评级有效期
. 如果客户在评级有效期内,被确定(认定)为违约,则该评级及其违约状态合并为一条有效验证样本
. 如果客户在整个评级预测有效期内,没有发生违约,则该评级及其非违约状态(正常状态)合并为一条有效验证样本
. 如果客户存在多条评级记录,对每一条评级记录采用上述策略
== 模型验证样本时间范围选取规则
采用评级有效期开始日期范围作为样本选取的约束条件,具体规则如下:
[%autowidth]
|===
| S | E | 样本的评级有效期开始日期介于从 S 到 E 之间
| | E | 样本的评级有效期开始日期介于从上线以来到 E 之间
| S | | 样本的评级有效期开始日期介于从 S 到 当前日期之间
| | | 样本的评级有效期开始日期介于从上线以来到当前日期之间
|===
[TIP]
.虽然模型验证样本的时间选择窗口基于评级有效期开始日期或评级有效期结束日期,但系统还会根据建模时确定的观察期或表现期来过滤样本,即,虽然选择的样本的评级有效期开始日期位于约束条件范围内,还需查看表现期(对公评级通常为1年)内的情况而定:
====
. 如果表现期内发生事实违约,则该样本被采纳,作为坏样本。
. 如果整个表现期结束,如果没有发生事实违约,则该样本被采纳,作为好样本。
. 如果表现期未结束,且没有发生事实违约,则该样本不被采纳(需在验证样本集中排除),因为还未到表现期,客户是否违约还未定。
====

40
io.sc.engine.mv.doc/asciidoc/index.adoc

@ -0,0 +1,40 @@
:doctype: book
:backend: html5
:toc: right
:toc-title: 目录
:toclevels: 5
:sectnums:
:sectnumlevels: 5
:sectanchors:
:linkcss:
:webfonts!:
:icons: font
:iconfont-remote!:
:source-highlighter: highlightjs
:highlightjsdir: ./resources/highlightjs
:imagesdir: ./resources/images
:stylesdir: ./resources/styles
:scriptsdir: ./resources/javascript
:docinfodir: ./resources/docinfo
:docinfo: shared
= 模型验证参考手册
2022-12-09 : 迭代中
include::chapter-前言/前言.adoc[leveloffset=+1]
include::chapter-概述/概述.adoc[leveloffset=+1]
include::chapter-操作手册/操作手册.adoc[leveloffset=+1]
include::chapter-源数据/源数据.adoc[leveloffset=+1]
include::chapter-验证样本/验证样本.adoc[leveloffset=+1]
include::chapter-区分能力/区分能力.adoc[leveloffset=+1]
include::chapter-稳定性/稳定性.adoc[leveloffset=+1]
include::chapter-估值准确性/估值准确性.adoc[leveloffset=+1]
include::chapter-appendix/appendix.adoc[leveloffset=+1]

8
io.sc.engine.mv.doc/asciidoc/resources/docinfo/docinfo-footer.html

@ -0,0 +1,8 @@
<script src="./resources/javascript/tocbot.min.js"></script>
<script>
tocbot.init({
tocSelector: '#toc',
contentSelector: '#content',
headingSelector: 'h1, h2, h3, h4, h5'
});
</script>

5
io.sc.engine.mv.doc/asciidoc/resources/docinfo/docinfo.html

@ -0,0 +1,5 @@
<link rel="stylesheet" href="./resources/styles/tocbot.css"/>
<link rel="stylesheet" href="./resources/styles/framework.css"/>
<!-- 自定义样式 -->
<link rel="stylesheet" href="./resources/css/customize.css"/>

BIN
io.sc.engine.mv.doc/asciidoc/resources/files/chapter-correctness-of-estimating/binomial_test.xlsx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/files/chapter-correctness-of-estimating/chi-square_test.xlsx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/files/chapter-stability/psi.xlsx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/fonts/FontAwesome.otf

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.eot

Binary file not shown.

2671
io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 434 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.ttf

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.woff

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/fonts/fontawesome-webfont.woff2

Binary file not shown.

2
io.sc.engine.mv.doc/asciidoc/resources/highlightjs/highlight.min.js

File diff suppressed because one or more lines are too long

99
io.sc.engine.mv.doc/asciidoc/resources/highlightjs/styles/github.min.css

@ -0,0 +1,99 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}
.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}
.hljs-string,
.hljs-doctag {
color: #d14;
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}
.hljs-subst {
font-weight: normal;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}
.hljs-regexp,
.hljs-link {
color: #009926;
}
.hljs-symbol,
.hljs-bullet {
color: #990073;
}
.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}
.hljs-meta {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-appendix/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-appendix/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-correctness-of-estimating/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-correctness-of-estimating/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-datasource/001.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-datasource/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/ar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/ar2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/cap/cap2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/ks/ks.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/auc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/roc/roc2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/structure.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-sc/structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/psi/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/sc-change/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-stability/transition_matrix/003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/001.pptx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/002.pptx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/003.pptx

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/functions.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/functions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/structure.graffle

Binary file not shown.

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-summary/structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 KiB

BIN
io.sc.engine.mv.doc/asciidoc/resources/images/chapter-use-help/configure/004.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save