st-comp 0.0.171 → 0.0.173

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "st-comp",
3
3
  "public": true,
4
- "version": "0.0.171",
4
+ "version": "0.0.173",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -19,6 +19,7 @@
19
19
  "echarts": "^5.4.3",
20
20
  "element-plus": "^2.9.4",
21
21
  "interactjs": "^1.10.27",
22
+ "markdown-it": "^14.1.0",
22
23
  "monaco-editor": "0.47.0",
23
24
  "pinia": "^2.1.6",
24
25
  "st-func": "^0.0.59",
@@ -19,9 +19,9 @@ const props = defineProps({
19
19
  // 品种市场类型: 0-期货, 1-股票, 2-期权
20
20
  varietyStock: { type: Number, required: true },
21
21
  // 复权选项 [股票]
22
- rightType: { type: Number, required: true },
22
+ rightType: { type: [Number, null], default: null },
23
23
  // 常用选项 [期货]
24
- klineType: { type: Number, required: true },
24
+ klineType: { type: [Number, null], default: null },
25
25
 
26
26
  // 业务系统相关
27
27
  deleteFirstNumber: { type: Number, default: 1 }, // 绩效标记
@@ -39,28 +39,13 @@ const getData = async () => {
39
39
  const params = {
40
40
  varietyCode: props.varietyCode,
41
41
  cycle: "6",
42
+ right: props.varietyStock === 1 ? props.rightType : 0, // 复权方式
43
+ contractType: props.varietyStock ? null : props.klineType, // 合约类型
42
44
  startTime: "1999-01-01 00:00:00",
43
45
  endTime: dayjs().format("YYYY-MM-DD 23:59:59"),
44
46
  deleteFirstNumber: props.deleteFirstNumber,
45
47
  withoutPermission: props.withoutPermission,
46
48
  };
47
- // 参数加工
48
- switch (props.varietyStock) {
49
- // 期货
50
- case 0: {
51
- Object.assign(params, {
52
- contractType: props.klineType,
53
- });
54
- break;
55
- }
56
- // 股票
57
- case 1: {
58
- Object.assign(params, {
59
- right: props.rightType,
60
- });
61
- break;
62
- }
63
- }
64
49
  const { body } = await request.post("/middleLayer/kline/getKline", params);
65
50
  if (body) {
66
51
  klineData.value = body;
@@ -12,7 +12,6 @@ import {
12
12
  handleMarkPointTradeLog,
13
13
  handleNetPositionLine,
14
14
  handleTradeIncomeRateLine,
15
- handleMarkPointOffset,
16
15
  normalizeToKlineTimeByMatch,
17
16
  } from "./utils.js";
18
17
  import Tips from "./components/Tips.vue";
@@ -33,8 +32,8 @@ const props = defineProps({
33
32
  // 交互选项
34
33
  cycle: { type: String, required: true },
35
34
  sellBuy: { type: Number, default: 1 },
36
- rightType: { type: Number, default: null }, // 复权类型
37
- klineType: { type: Number, default: null }, // 合约类型
35
+ rightType: { type: [Number, null], default: null }, // 复权类型
36
+ klineType: { type: [Number, null], default: null }, // 合约类型
38
37
  incomeType: { type: Number, default: 0 }, // 收益计算类型 0-单利, 1-复利
39
38
  initTimeRange: { type: Array, required: true },
40
39
  mainIndicator: { type: String, required: true },
@@ -185,7 +184,7 @@ const getMainData = async ({ startTime, endTime }) => {
185
184
  const params = {
186
185
  varietyCode: props.varietyCode,
187
186
  cycle: props.cycle, // 周期
188
- right: props.rightType, // 复权方式
187
+ right: props.varietyStock === 1 ? props.rightType : 0, // 复权方式
189
188
  contractType: props.varietyStock ? null : props.klineType, // 合约类型
190
189
  mainIndicatorList: props.indicatorStore.getIndicatorParams(props.mainIndicator),
191
190
  subIndicator: subIndicator.value,
@@ -248,7 +247,7 @@ const getMoreData = async (type) => {
248
247
  const params = {
249
248
  varietyCode: props.varietyCode,
250
249
  cycle: props.cycle,
251
- right: props.rightType,
250
+ right: props.varietyStock === 1 ? props.rightType : 0, // 复权方式
252
251
  contractType: props.varietyStock ? null : props.klineType,
253
252
  endTime: klineData.value.time[0],
254
253
  limit: loadAddCount,
@@ -273,7 +272,7 @@ const getMoreData = async (type) => {
273
272
  const params = {
274
273
  varietyCode: props.varietyCode,
275
274
  cycle: props.cycle, // 周期
276
- right: props.rightType, // 复权方式
275
+ right: props.varietyStock === 1 ? props.rightType : 0, // 复权方式
277
276
  contractType: props.varietyStock ? null : props.klineType, // 合约类型
278
277
  startTime: klineData.value.time[klineData.value.time.length - 1], // 开始时间
279
278
  limit: loadAddCount, // 查询K线数量
@@ -513,7 +512,7 @@ const draw = (params = { startValue: 0, endValue: 0 }) => {
513
512
  {
514
513
  type: "candlestick",
515
514
  data,
516
- markPoint: { data: handleMarkPointOffset([...tradePointData]) },
515
+ markPoint: { data: [...tradePointData] },
517
516
  markLine: { data: [...tradeLineData] },
518
517
  itemStyle: {
519
518
  color: "transparent",
@@ -489,28 +489,6 @@ export const normalizeToKlineTimeByMatch = (klineTimeArray, timeRange, cycle) =>
489
489
  return [klineStart, klineEnd];
490
490
  };
491
491
 
492
- // 统一处理markPoint标记点的偏移量
493
- export const handleMarkPointOffset = (markPointData) => {
494
- const data = [];
495
- markPointData.reduce((result, item) => {
496
- // 1. 找到当前标记的位置
497
- const key = `${item.coord[0]}+${item.label.position}`;
498
- const baseOffset = item.label.position === "top" ? -16 : 16;
499
- // 2. 找到先前处于相同位置 [key] 标记的数量
500
- const sameNum = result.get(key);
501
- if (sameNum) {
502
- result.set(key, sameNum + 1);
503
- item.symbolOffset[1] = baseOffset * (sameNum + 1);
504
- } else {
505
- result.set(key, 1);
506
- item.symbolOffset[1] = item.label.position === "top" ? -7 : 7;
507
- }
508
- data.push(item);
509
- return result;
510
- }, new Map());
511
- return data;
512
- };
513
-
514
492
  // 生成图表配置所需数据: 成交点位, 成交点位连线
515
493
  export const handleMarkPointTradeLog = (tradeLog, cycle, sellBuy, klineTimeArray, klineDataArray) => {
516
494
  // 获取交易类型
@@ -588,8 +566,7 @@ export const handleMarkPointTradeLog = (tradeLog, cycle, sellBuy, klineTimeArray
588
566
  // 2.图标位置 [开在K线上方,平在K线下方]
589
567
  const position = ["开多", "开空"].includes(tradeType) ? "top" : "bottom";
590
568
  // 3.获取对应Y轴值
591
- const klineIndex = binarySearch(klineTimeAry, next.klineTime);
592
- const yAxisValue = position === "top" ? klineDataAry[klineIndex]?.[3] : klineDataAry[klineIndex]?.[2];
569
+ const yAxisValue = position === "top" ? (next.openPriceAll / next.amount)?.toFixed(2) : (next.closePriceAll / next.amount)?.toFixed(2);
593
570
  // 4.将Y轴值存到连线的数据中, 提供给连线配置使用
594
571
  baseLineData.forEach((item) => {
595
572
  const rangeTime = position === "top" ? item.range[0] : item.range[1];
@@ -624,50 +601,45 @@ export const handleMarkPointTradeLog = (tradeLog, cycle, sellBuy, klineTimeArray
624
601
  };
625
602
  // 交易点: 生成基础点位, 连线数据
626
603
  const handleTradePointLineData = (tradeLog, cycle, klineTimeArray) => {
627
- /**
628
- * @description: 点位, 连线
629
- * 1.生成点位, 连线基础数据
630
- * 2.点位处理: 合并同一根K线上的数据, 整体按照时间排序
631
- * 3.连线处理: 过滤相同范围的连线
632
- */
604
+ // 1. 生成基础数据 { 点位数据, 连线数据 }
633
605
  const { basePointData, baseLineData } = tradeLog.reduce(
634
606
  (result, item) => {
635
- // 开仓点位的数据
607
+ // 开仓点
636
608
  const startPoint = {
637
- pointTime: item.openTime, // 交易点位的真实时间
638
- klineTime: normalizeToKlineTime(klineTimeArray, item.openTime, cycle), // 交易点位的K线时间
639
-
609
+ pointTime: item.openTime, // 交易时间
640
610
  tradeAction: "开", // 交易行为
641
611
  tradeDirection: item.tradeDirection ? "空" : "多", // 交易方向 1:空, 0:多
642
612
  tradeType: `开${item.tradeDirection ? "空" : "多"}`,
643
-
644
613
  part: null, // 份数,
645
614
  amount: item.tradeVolume, // 手数
646
615
  profitAndLoss: item.profitAndLoss, // 盈亏
647
- openPriceAll: item.openPrice * item.tradeVolume, // 开仓价格
648
- closePriceAll: item.closePrice * item.tradeVolume, // 平仓价格
616
+ openPriceAll: item.openPrice * item.tradeVolume, // 开仓总价
617
+ closePriceAll: item.closePrice * item.tradeVolume, // 平仓总价
618
+
619
+ klineTime: normalizeToKlineTime(klineTimeArray, item.openTime, cycle), // K线时间
649
620
  };
650
- // 平仓点位的数据
621
+ // 平仓点
651
622
  const endPoint = {
652
- pointTime: item.closeTime, // 交易点位的真实时间
653
- klineTime: normalizeToKlineTime(klineTimeArray, item.closeTime, cycle), // 交易点位的K线时间
654
-
623
+ pointTime: item.closeTime, // 交易时间
655
624
  tradeAction: "平", // 交易行为
656
625
  tradeDirection: item.tradeDirection ? "空" : "多", // 交易方向 1:空, 0:多
657
626
  tradeType: `平${item.tradeDirection ? "空" : "多"}`,
658
-
659
627
  part: null, // 份数,
660
628
  amount: item.tradeVolume, // 手数
661
629
  profitAndLoss: item.profitAndLoss, // 盈亏
662
- openPriceAll: item.openPrice * item.tradeVolume, // 开仓价格
663
- closePriceAll: item.closePrice * item.tradeVolume, // 平仓价格
630
+ openPriceAll: item.openPrice * item.tradeVolume, // 开仓总价
631
+ closePriceAll: item.closePrice * item.tradeVolume, // 平仓总价
632
+
633
+ klineTime: normalizeToKlineTime(klineTimeArray, item.closeTime, cycle), // K线时间
664
634
  };
665
- result.basePointData.push(startPoint, endPoint);
666
- result.baseLineData.push({
635
+ // 连线
636
+ const line = {
667
637
  range: [startPoint.klineTime, endPoint.klineTime],
668
638
  rangeValue: [null, null],
669
639
  color: endPoint.profitAndLoss > 0 ? "#FF0000" : "#389e0d",
670
- });
640
+ };
641
+ result.basePointData.push(startPoint, endPoint);
642
+ result.baseLineData.push(line);
671
643
  return result;
672
644
  },
673
645
  {
@@ -675,12 +647,12 @@ export const handleMarkPointTradeLog = (tradeLog, cycle, sellBuy, klineTimeArray
675
647
  baseLineData: [], // 连线数据
676
648
  }
677
649
  );
650
+ // 2. 合并同一根K线上的数据, 整体按照时间排序
678
651
  const basePointDataMergeMap = basePointData.reduce((result, item) => {
679
652
  const newItem = JSON.parse(JSON.stringify(item));
653
+ // 判断这个节点是否已存在数据, 若存在说明重复, 进行数据合并
680
654
  const key = newItem.klineTime + newItem.tradeType;
681
- // 判断这个节点是否已存在数据
682
655
  if (result.has(key)) {
683
- // 已存在,说明节点重复,进行数据合并
684
656
  const oldItem = result.get(key);
685
657
  newItem.amount += oldItem.amount;
686
658
  newItem.part += oldItem.part;
@@ -692,6 +664,7 @@ export const handleMarkPointTradeLog = (tradeLog, cycle, sellBuy, klineTimeArray
692
664
  return result;
693
665
  }, new Map([]));
694
666
  const pointData = [...basePointDataMergeMap.values()].sort((a, b) => new Date(a.klineTime) - new Date(b.klineTime));
667
+ // 3. 过滤掉开始和结束都在一根K线上的连线
695
668
  const lineData = baseLineData.filter(({ range }) => range[0] !== range[1]);
696
669
  return { pointData, lineData };
697
670
  };
@@ -1,37 +1,71 @@
1
1
  <!-- 因子说明 -->
2
2
  <script setup name="FactorDescription">
3
+ import { ref, onMounted, inject } from "vue";
4
+ import MarkdownIt from "markdown-it";
5
+
6
+ const { env } = inject("stConfig"); // 组件库全局配置
7
+
8
+ // 选股变量说明文档
9
+ const md = new MarkdownIt();
10
+ const markdownContent = ref("");
11
+
3
12
  const props = defineProps({
13
+ factorType: { type: String, default: "模版" },
4
14
  data: { type: Array, default: [] },
5
15
  });
6
16
  const visible = defineModel("visible", { default: false });
17
+
18
+ onMounted(async () => {
19
+ let res = "";
20
+ if (env.MODE === "production") {
21
+ res = await fetch("//47.99.165.133/factor_select.md");
22
+ } else {
23
+ res = await fetch("//192.168.12.38/factor_select.md");
24
+ }
25
+ const markdownText = await res.text();
26
+ markdownContent.value = md.render(markdownText);
27
+ });
7
28
  </script>
8
29
 
9
30
  <template>
10
31
  <el-dialog
11
32
  v-model="visible"
12
- title="因子使用说明"
33
+ :title="factorType === '模版' ? '因子说明' : '脚本编写说明'"
13
34
  width="1000"
14
35
  align-center
36
+ draggable
37
+ overflow
38
+ append-to-body
15
39
  destroy-on-close
16
40
  >
17
- <el-table
18
- :data="data"
19
- border
20
- height="500"
21
- >
22
- <el-table-column
23
- prop="factorName"
24
- label="因子名称"
25
- width="200"
26
- />
27
- <el-table-column
28
- prop="factorDesc"
29
- label="因子说明"
30
- />
31
- <el-table-column
32
- prop="factorScoreDefine"
33
- label="分值定义"
34
- />
35
- </el-table>
41
+ <el-scrollbar height="500px">
42
+ <template v-if="factorType === '模版'">
43
+ <el-table
44
+ :data="data"
45
+ border
46
+ height="500"
47
+ >
48
+ <el-table-column
49
+ prop="factorName"
50
+ label="因子名称"
51
+ width="200"
52
+ />
53
+ <el-table-column
54
+ prop="factorDesc"
55
+ label="因子说明"
56
+ />
57
+ <el-table-column
58
+ prop="factorScoreDefine"
59
+ label="分值定义"
60
+ />
61
+ </el-table>
62
+ </template>
63
+ <template v-if="factorType === '脚本'">
64
+ <div
65
+ class="markdown-body"
66
+ v-html="markdownContent"
67
+ />
68
+ </template>
69
+ </el-scrollbar>
36
70
  </el-dialog>
37
71
  </template>
@@ -4,7 +4,7 @@ import { nextTick, ref, watch } from "vue";
4
4
  import { Plus, CircleCloseFilled, InfoFilled } from "@element-plus/icons-vue";
5
5
  import { handleVerifyScore, extractConditionDetails } from "./tools.js";
6
6
  import FactorDescription from "./FactorDescription.vue";
7
- import MonacoEditor from "../../../MonacoEditor/index.vue"
7
+ import MonacoEditor from "../../../MonacoEditor/index.vue";
8
8
 
9
9
  const props = defineProps({
10
10
  config: {
@@ -49,14 +49,18 @@ const dialogForm = ref({
49
49
  // SQL语句
50
50
  sqlValue: null,
51
51
  // 脚本语句
52
- factorSelectExpr: '',
52
+ factorSelectExpr: "",
53
53
  });
54
54
 
55
- watch(() => [factorType.value, visible.value], () => {
56
- if (factorType.value === '模版' || visible.value === false) {
57
- stVarSelectDialogRef.value.close();
58
- }
59
- }, { deep: true })
55
+ watch(
56
+ () => [factorType.value, visible.value],
57
+ () => {
58
+ if (factorType.value === "模版" || visible.value === false) {
59
+ stVarSelectDialogRef.value.close();
60
+ }
61
+ },
62
+ { deep: true }
63
+ );
60
64
 
61
65
  // 打开
62
66
  const handleOpenDialog = () => {
@@ -73,15 +77,15 @@ const handleOpenDialog = () => {
73
77
  // 解决初次访问样式错乱问题
74
78
  nextTick(() => {
75
79
  if (factorSelectExpr) {
76
- factorType.value = '脚本'
80
+ factorType.value = "脚本";
77
81
  monacoEditorRef.value.setValue(factorSelectExpr);
78
- } else factorType.value = '模版';
82
+ } else factorType.value = "模版";
79
83
  monacoEditorRef.value.resize();
80
- })
84
+ });
81
85
  };
82
86
  // 确定
83
87
  const handleSubmit = () => {
84
- if (factorType.value === '模版') {
88
+ if (factorType.value === "模版") {
85
89
  dialogFormRef.value.validate((valid) => {
86
90
  const { list, sqlEnable, sqlValue } = dialogForm.value;
87
91
  if (!valid) return;
@@ -100,7 +104,7 @@ const handleSubmit = () => {
100
104
  list: JSON.parse(JSON.stringify(list)),
101
105
  sqlEnable,
102
106
  sqlValue,
103
- factorSelectExpr: '',
107
+ factorSelectExpr: "",
104
108
  };
105
109
  visible.value = false;
106
110
  });
@@ -224,7 +228,7 @@ const handleDeleteTag = (aciton, index) => {
224
228
  list: [],
225
229
  sqlEnable: 0,
226
230
  sqlValue: null,
227
- factorSelectExpr: '',
231
+ factorSelectExpr: "",
228
232
  };
229
233
  break;
230
234
  }
@@ -233,10 +237,10 @@ const handleDeleteTag = (aciton, index) => {
233
237
 
234
238
  // 打开变量选择器
235
239
  const open = () => {
236
- const zIndex = document.getElementsByClassName('factor-dialog')?.[0]?.style?.zIndex;
237
- console.log(zIndex)
240
+ const zIndex = document.getElementsByClassName("factor-dialog")?.[0]?.style?.zIndex;
241
+ console.log(zIndex);
238
242
  stVarSelectDialogRef.value.open(monacoEditorRef.value, zIndex);
239
- }
243
+ };
240
244
  </script>
241
245
 
242
246
  <template>
@@ -277,7 +281,7 @@ const open = () => {
277
281
  width="500"
278
282
  placement="bottom-start"
279
283
  >
280
- <div style="white-space: pre-line;">
284
+ <div style="white-space: pre-line">
281
285
  {{ data.factorSelectExpr }}
282
286
  </div>
283
287
  <template #reference>
@@ -328,19 +332,29 @@ const open = () => {
328
332
  <el-radio-group
329
333
  v-model="factorType"
330
334
  size="small"
331
- style="margin-left: 12px;"
335
+ style="margin-left: 12px"
332
336
  >
333
- <el-radio-button label="模版" value="模版" />
334
- <el-radio-button label="脚本" value="脚本" />
337
+ <el-radio-button
338
+ label="模版"
339
+ value="模版"
340
+ />
341
+ <el-radio-button
342
+ label="脚本"
343
+ value="脚本"
344
+ />
335
345
  </el-radio-group>
336
346
  <!-- 变量选择器 -->
337
- <div style="flex: 1; text-align: right;" v-if="factorType === '脚本'">
347
+ <div
348
+ style="flex: 1; text-align: right"
349
+ v-if="factorType === '脚本'"
350
+ >
338
351
  <el-button
339
352
  size="small"
340
353
  type="primary"
341
354
  @click="open"
342
- >变量选择器</el-button>
343
- </div>
355
+ >变量选择器</el-button
356
+ >
357
+ </div>
344
358
  </div>
345
359
  </template>
346
360
  <!-- 模版模式 -->
@@ -576,11 +590,7 @@ const open = () => {
576
590
  <!-- 脚本模式 -->
577
591
  <div
578
592
  style="width: 968px; height: 400px"
579
- :style="
580
- factorType === '脚本' ?
581
- null :
582
- { position: 'fixed', top: '-400px', zIndex: -1 }
583
- "
593
+ :style="factorType === '脚本' ? null : { position: 'fixed', top: '-400px', zIndex: -1 }"
584
594
  >
585
595
  <MonacoEditor
586
596
  ref="monacoEditorRef"
@@ -601,6 +611,7 @@ const open = () => {
601
611
  <!-- 因子说明: 机器打分, 人工打分因子 -->
602
612
  <FactorDescription
603
613
  v-model:visible="visibleDescriptions"
614
+ :factorType="factorType"
604
615
  :data="config.factorDescriptions?.filter((item) => [1, 3].includes(item.type))"
605
616
  />
606
617
  <!-- 变量选择器 -->
@@ -2,6 +2,20 @@
2
2
  <div>
3
3
  <div style="display: flex; align-items: center; gap: 10px">
4
4
  <el-button @click="stKlineConfigRef?.open">打开K线自定义配置</el-button>
5
+ <el-select
6
+ v-model="sellBuy"
7
+ style="width: 100px"
8
+ >
9
+ <el-option
10
+ v-for="item in [
11
+ { label: '买卖', value: 0 },
12
+ { label: '开平', value: 1 },
13
+ ]"
14
+ :key="item.value"
15
+ :value="item.value"
16
+ :label="item.label"
17
+ />
18
+ </el-select>
5
19
  <el-select
6
20
  v-model="cycle"
7
21
  style="width: 100px"
@@ -43,6 +57,7 @@
43
57
 
44
58
  <script setup>
45
59
  import dayjs from "dayjs";
60
+ import tradeLogMock from "./tradeLogMock";
46
61
  import { onMounted, ref, reactive, provide, inject, nextTick } from "vue";
47
62
 
48
63
  const { request } = inject("stConfig"); // 组件库全局配置
@@ -54,14 +69,15 @@ provide("userKlineConfig", userKlineConfig);
54
69
 
55
70
  const cycleOptions = [
56
71
  { label: "1m", value: "1" },
72
+ { label: "60m", value: "5" },
57
73
  { label: "1d", value: "6" },
58
74
  { label: "1mon", value: "8" },
59
75
  ];
60
76
 
61
- const varietyName = ref(null);
62
- const varietyCode = ref("000016");
63
- const varietyStock = ref(1); // 0: 期货, 1: 股票, 2: 期权
64
- const tradeLog = ref([]);
77
+ const varietyName = ref("棉花");
78
+ const varietyCode = ref("CF");
79
+ const varietyStock = ref(0); // 0: 期货, 1: 股票, 2: 期权
80
+ const tradeLog = ref(tradeLogMock);
65
81
  const netPositionData = ref([]);
66
82
  const cycle = ref("6");
67
83
  const sellBuy = ref(1);