import_holding_xls: 支持截图真实数字覆盖

regenerate_all 会覆盖 portfolio.json,所以导入流程改为:
1. 先更新 SQLite holdings 表
2. 再跑 regenerate_all(读 SQLite,写 decisions+portfolio)
3. 然后用真实数字覆盖 portfolio.json 汇总字段
4. 重建决策树

支持 --cash --total --mv 传入截图真实数字
用法:python3 import_holding_xls.py --cash 20230 --total 1008860 --mv 988512
This commit is contained in:
知微
2026-06-24 11:29:53 +08:00
parent e2646c36cb
commit eb294f05a5
5 changed files with 1135 additions and 938 deletions
+896 -621
View File
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -1281,7 +1281,7 @@
"amplitude": 8.42, "amplitude": 8.42,
"turnover_rate": 8.56 "turnover_rate": 8.56
}, },
"updated_at": 1782271033.1053789 "updated_at": 1782271700.6953611
}, },
"01478": { "01478": {
"daily": [ "daily": [
@@ -2562,7 +2562,7 @@
"high_52w": 16.44, "high_52w": 16.44,
"low_52w": 6.4 "low_52w": 6.4
}, },
"updated_at": 1782271016.7002888 "updated_at": 1782271687.3830402
}, },
"601899": { "601899": {
"daily": [ "daily": [
@@ -3846,7 +3846,7 @@
"amplitude": 2.74, "amplitude": 2.74,
"turnover_rate": 1.08 "turnover_rate": 1.08
}, },
"updated_at": 1782271045.8500736 "updated_at": 1782271708.4815621
}, },
"600739": { "600739": {
"daily": [ "daily": [
@@ -5130,7 +5130,7 @@
"amplitude": 1.93, "amplitude": 1.93,
"turnover_rate": 0.67 "turnover_rate": 0.67
}, },
"updated_at": 1782271043.1626832 "updated_at": 1782271708.1176724
}, },
"09988": { "09988": {
"daily": [ "daily": [
@@ -6411,7 +6411,7 @@
"high_52w": 185.173, "high_52w": 185.173,
"low_52w": 100.773 "low_52w": 100.773
}, },
"updated_at": 1782271027.4969852 "updated_at": 1782271693.2914903
}, },
"688411": { "688411": {
"daily": [ "daily": [
@@ -7695,7 +7695,7 @@
"amplitude": 6.7, "amplitude": 6.7,
"turnover_rate": 4.53 "turnover_rate": 4.53
}, },
"updated_at": 1782271047.3969336 "updated_at": 1782271711.5193114
}, },
"02202": { "02202": {
"daily": [ "daily": [
@@ -8976,7 +8976,7 @@
"high_52w": 5.99, "high_52w": 5.99,
"low_52w": 2.48 "low_52w": 2.48
}, },
"updated_at": 1782271019.2727396 "updated_at": 1782271687.8076062
}, },
"01211": { "01211": {
"daily": [ "daily": [
@@ -10257,7 +10257,7 @@
"high_52w": 135.889, "high_52w": 135.889,
"low_52w": 81.35 "low_52w": 81.35
}, },
"updated_at": 1782271015.8325095 "updated_at": 1782271685.924404
}, },
"02388": { "02388": {
"daily": [ "daily": [
@@ -11538,7 +11538,7 @@
"high_52w": 49.36, "high_52w": 49.36,
"low_52w": 32.261 "low_52w": 32.261
}, },
"updated_at": 1782271019.9959772 "updated_at": 1782271689.165997
}, },
"300750": { "300750": {
"daily": [ "daily": [
@@ -12822,7 +12822,7 @@
"amplitude": 1.91, "amplitude": 1.91,
"turnover_rate": 0.59 "turnover_rate": 0.59
}, },
"updated_at": 1782271035.31113 "updated_at": 1782271702.3871303
}, },
"603259": { "603259": {
"daily": [ "daily": [
@@ -14106,7 +14106,7 @@
"amplitude": 2.33, "amplitude": 2.33,
"turnover_rate": 1.11 "turnover_rate": 1.11
}, },
"updated_at": 1782271046.5932066 "updated_at": 1782271709.3074281
}, },
"00700": { "00700": {
"daily": [ "daily": [
@@ -15387,7 +15387,7 @@
"high_52w": 677.7, "high_52w": 677.7,
"low_52w": 420.4 "low_52w": 420.4
}, },
"updated_at": 1782271009.5219116 "updated_at": 1782271677.0676646
}, },
"688981": { "688981": {
"daily": [ "daily": [
@@ -16671,7 +16671,7 @@
"amplitude": 6.3, "amplitude": 6.3,
"turnover_rate": 4.82 "turnover_rate": 4.82
}, },
"updated_at": 1782271052.2059975 "updated_at": 1782271716.6743104
}, },
"600110": { "600110": {
"daily": [ "daily": [
@@ -19228,7 +19228,7 @@
"high_52w": 93.5, "high_52w": 93.5,
"low_52w": 38.65 "low_52w": 38.65
}, },
"updated_at": 1782271012.4736252 "updated_at": 1782271678.5392773
}, },
"600563": { "600563": {
"daily": [ "daily": [
@@ -20512,7 +20512,7 @@
"amplitude": 3.97, "amplitude": 3.97,
"turnover_rate": 4.21 "turnover_rate": 4.21
}, },
"updated_at": 1782271040.7884188 "updated_at": 1782271706.2994509
}, },
"600036": { "600036": {
"daily": [ "daily": [
@@ -21796,7 +21796,7 @@
"amplitude": 1.84, "amplitude": 1.84,
"turnover_rate": 0.32 "turnover_rate": 0.32
}, },
"updated_at": 1782271037.721593 "updated_at": 1782271703.3982394
}, },
"300035": { "300035": {
"daily": [ "daily": [
@@ -23080,7 +23080,7 @@
"amplitude": 2.1, "amplitude": 2.1,
"turnover_rate": 3.34 "turnover_rate": 3.34
}, },
"updated_at": 1782271028.7482398 "updated_at": 1782271694.607376
}, },
"518880": { "518880": {
"daily": [ "daily": [
@@ -24364,7 +24364,7 @@
"amplitude": 0.59, "amplitude": 0.59,
"turnover_rate": 2.43 "turnover_rate": 2.43
}, },
"updated_at": 1782271036.5620172 "updated_at": 1782271703.1981661
}, },
"01888": { "01888": {
"daily": [ "daily": [
@@ -25645,7 +25645,7 @@
"high_52w": 91.8, "high_52w": 91.8,
"low_52w": 8.08 "low_52w": 8.08
}, },
"updated_at": 1782271018.367241 "updated_at": 1782271687.572735
}, },
"01088": { "01088": {
"daily": [ "daily": [
@@ -26926,7 +26926,7 @@
"high_52w": 49.62, "high_52w": 49.62,
"low_52w": 29.076 "low_52w": 29.076
}, },
"updated_at": 1782271014.5332053 "updated_at": 1782271685.0751767
}, },
"002594": { "002594": {
"daily": [ "daily": [
@@ -28210,7 +28210,7 @@
"amplitude": 2.57, "amplitude": 2.57,
"turnover_rate": 1.42 "turnover_rate": 1.42
}, },
"updated_at": 1782271008.387415 "updated_at": 1782271675.48275
}, },
"09868": { "09868": {
"daily": [ "daily": [
@@ -29491,7 +29491,7 @@
"high_52w": 110.8, "high_52w": 110.8,
"low_52w": 52.65 "low_52w": 52.65
}, },
"updated_at": 1782271026.4322388 "updated_at": 1782271693.106234
}, },
"688795": { "688795": {
"daily": [ "daily": [
@@ -30735,7 +30735,7 @@
"amplitude": 3.09, "amplitude": 3.09,
"turnover_rate": 7.33 "turnover_rate": 7.33
}, },
"updated_at": 1782271050.1670244 "updated_at": 1782271715.7423553
}, },
"688802": { "688802": {
"daily": [ "daily": [
@@ -31979,7 +31979,7 @@
"amplitude": 4.81, "amplitude": 4.81,
"turnover_rate": 12.26 "turnover_rate": 12.26
}, },
"updated_at": 1782271051.0787034 "updated_at": 1782271716.438669
}, },
"02359": { "02359": {
"daily": [ "daily": [
@@ -33260,7 +33260,7 @@
"high_52w": 146.197, "high_52w": 146.197,
"low_52w": 69.713 "low_52w": 69.713
}, },
"updated_at": 1782271019.7459424 "updated_at": 1782271688.9286106
}, },
"02628": { "02628": {
"daily": [ "daily": [
@@ -34541,7 +34541,7 @@
"high_52w": 36.16, "high_52w": 36.16,
"low_52w": 16.827 "low_52w": 16.827
}, },
"updated_at": 1782271020.6191452 "updated_at": 1782271689.7820618
}, },
"00968": { "00968": {
"daily": [ "daily": [
@@ -35822,7 +35822,7 @@
"high_52w": 4.092, "high_52w": 4.092,
"low_52w": 2.21 "low_52w": 2.21
}, },
"updated_at": 1782271010.600791 "updated_at": 1782271677.7161567
}, },
"06869": { "06869": {
"daily": [ "daily": [
@@ -37103,7 +37103,7 @@
"high_52w": 283.0, "high_52w": 283.0,
"low_52w": 16.146 "low_52w": 16.146
}, },
"updated_at": 1782271023.011485 "updated_at": 1782271692.9287608
}, },
"02318": { "02318": {
"daily": [ "daily": [
@@ -38384,7 +38384,7 @@
"high_52w": 72.689, "high_52w": 72.689,
"low_52w": 42.648 "low_52w": 42.648
}, },
"updated_at": 1782271019.5122526 "updated_at": 1782271688.4141698
}, },
"688639": { "688639": {
"daily": [ "daily": [
@@ -39668,7 +39668,7 @@
"amplitude": 2.69, "amplitude": 2.69,
"turnover_rate": 1.5 "turnover_rate": 1.5
}, },
"updated_at": 1782271049.468328 "updated_at": 1782271714.98799
}, },
"300124": { "300124": {
"daily": [ "daily": [
@@ -40952,7 +40952,7 @@
"amplitude": 2.32, "amplitude": 2.32,
"turnover_rate": 1.28 "turnover_rate": 1.28
}, },
"updated_at": 1782271030.279041 "updated_at": 1782271696.566684
}, },
"01070": { "01070": {
"daily": [ "daily": [
@@ -42233,7 +42233,7 @@
"high_52w": 16.19, "high_52w": 16.19,
"low_52w": 8.802 "low_52w": 8.802
}, },
"updated_at": 1782271013.3618884 "updated_at": 1782271681.2713692
}, },
"001309": { "001309": {
"daily": [ "daily": [
@@ -43517,7 +43517,7 @@
"amplitude": 13.19, "amplitude": 13.19,
"turnover_rate": 10.41 "turnover_rate": 10.41
}, },
"updated_at": 1782271007.1646104 "updated_at": 1782271674.2331069
}, },
"06160": { "06160": {
"daily": [ "daily": [
@@ -44798,7 +44798,7 @@
"high_52w": 229.4, "high_52w": 229.4,
"low_52w": 144.1 "low_52w": 144.1
}, },
"updated_at": 1782271020.8689427 "updated_at": 1782271692.3807895
}, },
"000700": { "000700": {
"daily": [ "daily": [
@@ -46082,7 +46082,7 @@
"amplitude": 4.41, "amplitude": 4.41,
"turnover_rate": 7.65 "turnover_rate": 7.65
}, },
"updated_at": 1782271004.300124 "updated_at": 1782271671.0273736
}, },
"000711": { "000711": {
"daily": [ "daily": [
@@ -47366,7 +47366,7 @@
"amplitude": 2.65, "amplitude": 2.65,
"turnover_rate": 3.98 "turnover_rate": 3.98
}, },
"updated_at": 1782271005.4516623 "updated_at": 1782271671.5259438
}, },
"__index__sh000001": { "__index__sh000001": {
"name": "上证指数", "name": "上证指数",
@@ -48664,7 +48664,7 @@
"volume": 108482337.0 "volume": 108482337.0
} }
], ],
"updated_at": 1782271048.2724428 "updated_at": 1782271713.894362
}, },
"300690": { "300690": {
"daily": [ "daily": [
@@ -49937,7 +49937,7 @@
"volume": 457244.0 "volume": 457244.0
} }
], ],
"updated_at": 1782271034.5268583 "updated_at": 1782271701.2542915
}, },
"000657": { "000657": {
"daily": [ "daily": [
@@ -51210,7 +51210,7 @@
"volume": 15817777.0 "volume": 15817777.0
} }
], ],
"updated_at": 1782271003.133426 "updated_at": 1782271670.1859558
}, },
"300308": { "300308": {
"daily": [ "daily": [
@@ -52483,7 +52483,7 @@
"volume": 4488584.0 "volume": 4488584.0
} }
], ],
"updated_at": 1782271031.766303 "updated_at": 1782271699.4866962
}, },
"600519": { "600519": {
"daily": [ "daily": [
@@ -53756,7 +53756,7 @@
"volume": 583696.0 "volume": 583696.0
} }
], ],
"updated_at": 1782271039.692529 "updated_at": 1782271703.6053793
}, },
"601318": { "601318": {
"daily": [ "daily": [
@@ -55029,7 +55029,7 @@
"volume": 13423033.0 "volume": 13423033.0
} }
], ],
"updated_at": 1782271044.5394971 "updated_at": 1782271708.2934575
}, },
"sh000001": { "sh000001": {
"daily": [ "daily": [
+77 -173
View File
@@ -4,144 +4,107 @@
"code": "001309", "code": "001309",
"name": "德明利", "name": "德明利",
"shares": 100, "shares": 100,
"avail_shares": 100, "price": 788.0,
"price": 790.9, "cost_price": 737.04,
"cost_price": 737.0374,
"pl": 5096.26,
"pl_pct": 6.91,
"currency": "CNY", "currency": "CNY",
"market_val": 78800.0, "market_val": 78800.0,
"cost_amount": 73703.74, "cost_amount": 73703.74,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": 4.75
}, },
{ {
"code": "01478", "code": "01478",
"name": "丘钛科技", "name": "丘钛科技",
"shares": 11000, "shares": 11000,
"avail_shares": 11000, "price": 7.57,
"price": 7.54, "cost_price": 13.81,
"cost_price": 13.8089,
"pl": -59438.6,
"pl_pct": -45.18,
"currency": "HKD", "currency": "HKD",
"market_val": 72120.15, "market_val": 72120.15,
"cost_amount": 131558.75, "cost_amount": 131558.75,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -4.19
}, },
{ {
"code": "600739", "code": "600739",
"name": "辽宁成大", "name": "辽宁成大",
"shares": 6600, "shares": 6600,
"avail_shares": 6600, "price": 10.5,
"price": 10.45, "cost_price": 12.29,
"cost_price": 12.2862,
"pl": -11789.02,
"pl_pct": -14.54,
"currency": "CNY", "currency": "CNY",
"market_val": 69300.0, "market_val": 69300.0,
"cost_amount": 81089.02, "cost_amount": 81089.02,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -1.88
}, },
{ {
"code": "601899", "code": "601899",
"name": "紫金矿业", "name": "紫金矿业",
"shares": 2400, "shares": 2400,
"avail_shares": 2400, "price": 27.53,
"price": 27.36, "cost_price": 40.27,
"cost_price": 40.2685,
"pl": -30572.29,
"pl_pct": -31.63,
"currency": "CNY", "currency": "CNY",
"market_val": 66072.0, "market_val": 66072.0,
"cost_amount": 96644.29, "cost_amount": 96644.29,
"exchange_rate": 0.866 "exchange_rate": 0.8664
}, },
{ {
"code": "688639", "code": "688639",
"name": "华恒生物", "name": "华恒生物",
"shares": 2800, "shares": 2800,
"avail_shares": 0, "price": 21.46,
"price": 21.35, "cost_price": 21.51,
"cost_price": 21.5085,
"pl": -135.75,
"pl_pct": -0.23,
"currency": "CNY", "currency": "CNY",
"market_val": 60088.0, "market_val": 60088.0,
"cost_amount": 60223.75, "cost_amount": 60223.75,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -2.2
}, },
{ {
"code": "09988", "code": "09988",
"name": "阿里巴巴-W", "name": "阿里巴巴-W",
"shares": 700, "shares": 700,
"avail_shares": 700, "price": 98.8,
"price": 98.6, "cost_price": 126.15,
"cost_price": 126.1516,
"pl": -16582.48,
"pl_pct": -21.68,
"currency": "HKD", "currency": "HKD",
"market_val": 59899.48, "market_val": 59899.48,
"cost_amount": 76481.96, "cost_amount": 76481.96,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -0.35
}, },
{ {
"code": "603259", "code": "603259",
"name": "药明康德", "name": "药明康德",
"shares": 400, "shares": 400,
"avail_shares": 400,
"price": 116.94, "price": 116.94,
"cost_price": 96.201, "cost_price": 96.2,
"pl": 8295.62,
"pl_pct": 21.56,
"currency": "CNY", "currency": "CNY",
"market_val": 46776.0, "market_val": 46776.0,
"cost_amount": 38480.38, "cost_amount": 38480.38,
"exchange_rate": 0.866 "exchange_rate": 0.8664
}, },
{ {
"code": "688981", "code": "688981",
"name": "中芯国际", "name": "中芯国际",
"shares": 300, "shares": 300,
"avail_shares": 300, "price": 149.98,
"price": 150.64, "cost_price": 126.07,
"cost_price": 126.0681,
"pl": 7173.58,
"pl_pct": 18.97,
"currency": "CNY", "currency": "CNY",
"market_val": 44994.0, "market_val": 44994.0,
"cost_amount": 37820.42, "cost_amount": 37820.42,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": 6.31
}, },
{ {
"code": "01888", "code": "01888",
"name": "建滔积层板", "name": "建滔积层板",
"shares": 500, "shares": 500,
"avail_shares": 500, "price": 93.95,
"price": 93.5, "cost_price": 88.39,
"cost_price": 88.3855,
"pl": 2409.7,
"pl_pct": 6.3,
"currency": "HKD", "currency": "HKD",
"market_val": 40685.05, "market_val": 40685.05,
"cost_amount": 38275.35, "cost_amount": 38275.35,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": 7.16
}, },
{ {
"code": "02202", "code": "02202",
"name": "万科企业", "name": "万科企业",
"shares": 19700, "shares": 19700,
"avail_shares": 19700,
"price": 2.37, "price": 2.37,
"cost_price": 4.676, "cost_price": 4.68,
"pl": -39344.79,
"pl_pct": -49.32,
"currency": "HKD", "currency": "HKD",
"market_val": 40437.34, "market_val": 40437.34,
"cost_amount": 79782.13, "cost_amount": 79782.13,
@@ -151,232 +114,173 @@
"code": "02388", "code": "02388",
"name": "中银香港", "name": "中银香港",
"shares": 1000, "shares": 1000,
"avail_shares": 1000, "price": 46.16,
"price": 46.26, "cost_price": 43.79,
"cost_price": 43.7892,
"pl": 2053.39,
"pl_pct": 5.41,
"currency": "HKD", "currency": "HKD",
"market_val": 39979.18, "market_val": 39979.18,
"cost_amount": 37925.79, "cost_amount": 37925.79,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -1.62
}, },
{ {
"code": "300750", "code": "300750",
"name": "宁德时代", "name": "宁德时代",
"shares": 100, "shares": 100,
"avail_shares": 100, "price": 391.15,
"price": 391.19, "cost_price": 401.78,
"cost_price": 401.7803,
"pl": -1063.03,
"pl_pct": -2.65,
"currency": "CNY", "currency": "CNY",
"market_val": 39115.0, "market_val": 39115.0,
"cost_amount": 40178.03, "cost_amount": 40178.03,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -0.34
}, },
{ {
"code": "01211", "code": "01211",
"name": "比亚迪股份", "name": "比亚迪股份",
"shares": 600, "shares": 600,
"avail_shares": 600, "price": 75.15,
"price": 74.85, "cost_price": 105.05,
"cost_price": 105.0542,
"pl": -15539.97,
"pl_pct": -28.47,
"currency": "HKD", "currency": "HKD",
"market_val": 39052.45, "market_val": 39052.45,
"cost_amount": 54592.42, "cost_amount": 54592.42,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -1.25
}, },
{ {
"code": "00700", "code": "00700",
"name": "腾讯控股", "name": "腾讯控股",
"shares": 100, "shares": 100,
"avail_shares": 100, "price": 420.4,
"price": 420.0, "cost_price": 443.9,
"cost_price": 443.9024,
"pl": -2035.55,
"pl_pct": -5.29,
"currency": "HKD", "currency": "HKD",
"market_val": 36410.84, "market_val": 36410.84,
"cost_amount": 38446.39, "cost_amount": 38446.39,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": 1.25
}, },
{ {
"code": "00981", "code": "00981",
"name": "中芯国际", "name": "中芯国际",
"shares": 500, "shares": 500,
"avail_shares": 500, "price": 83.35,
"price": 83.25, "cost_price": 76.07,
"cost_price": 76.0724,
"pl": 3151.55,
"pl_pct": 9.57,
"currency": "HKD", "currency": "HKD",
"market_val": 36094.72, "market_val": 36094.72,
"cost_amount": 32943.17, "cost_amount": 32943.17,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": 6.62
}, },
{ {
"code": "09868", "code": "09868",
"name": "小鹏集团-W", "name": "小鹏集团-W",
"shares": 700, "shares": 700,
"avail_shares": 700, "price": 50.25,
"price": 50.1, "cost_price": 51.36,
"cost_price": 51.3644,
"pl": -675.62,
"pl_pct": -2.17,
"currency": "HKD", "currency": "HKD",
"market_val": 30465.07, "market_val": 30465.07,
"cost_amount": 31140.69, "cost_amount": 31140.69,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": 1.5
}, },
{ {
"code": "600036", "code": "600036",
"name": "招商银行", "name": "招商银行",
"shares": 800, "shares": 800,
"avail_shares": 800, "price": 37.1,
"price": 37.05, "cost_price": 38.16,
"cost_price": 38.1582,
"pl": -846.53,
"pl_pct": -2.77,
"currency": "CNY", "currency": "CNY",
"market_val": 29680.0, "market_val": 29680.0,
"cost_amount": 30526.53, "cost_amount": 30526.53,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -0.94
}, },
{ {
"code": "300548", "code": "300548",
"name": "长芯博创", "name": "长芯博创",
"shares": 100, "shares": 100,
"avail_shares": 100, "price": 279.12,
"price": 276.79,
"cost_price": 231.46, "cost_price": 231.46,
"pl": 4766.0,
"pl_pct": 20.59,
"currency": "CNY", "currency": "CNY",
"market_val": 27912.0, "market_val": 27912.0,
"cost_amount": 23146.0, "cost_amount": 23146.0,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -3.23
}, },
{ {
"code": "02318", "code": "02318",
"name": "中国平安", "name": "中国平安",
"shares": 500, "shares": 500,
"avail_shares": 500, "price": 52.75,
"price": 52.8, "cost_price": 54.82,
"cost_price": 54.8199,
"pl": -896.35,
"pl_pct": -3.78,
"currency": "HKD", "currency": "HKD",
"market_val": 22843.39, "market_val": 22843.39,
"cost_amount": 23739.74, "cost_amount": 23739.74,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -1.49
}, },
{ {
"code": "300035", "code": "300035",
"name": "中科电气", "name": "中科电气",
"shares": 1400, "shares": 1400,
"avail_shares": 1400, "price": 16.03,
"price": 16.02, "cost_price": 22.29,
"cost_price": 22.2914,
"pl": -8765.91,
"pl_pct": -28.09,
"currency": "CNY", "currency": "CNY",
"market_val": 22442.0, "market_val": 22442.0,
"cost_amount": 31207.91, "cost_amount": 31207.91,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -2.08
}, },
{ {
"code": "000700", "code": "000700",
"name": "模塑科技", "name": "模塑科技",
"shares": 1400, "shares": 1400,
"avail_shares": 1400, "price": 14.6,
"price": 14.52, "cost_price": 14.83,
"cost_price": 14.8336,
"pl": -327.0,
"pl_pct": -1.57,
"currency": "CNY", "currency": "CNY",
"market_val": 20440.0, "market_val": 20440.0,
"cost_amount": 20767.0, "cost_amount": 20767.0,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -3.07
}, },
{ {
"code": "518880", "code": "518880",
"name": "黄金ETF华安", "name": "黄金ETF华安",
"shares": 2400, "shares": 2400,
"avail_shares": 2400, "price": 8.449,
"price": 8.44, "cost_price": 12.19,
"cost_price": 12.1915,
"pl": -8982.04,
"pl_pct": -30.7,
"currency": "CNY", "currency": "CNY",
"market_val": 20277.6, "market_val": 20277.6,
"cost_amount": 29259.64, "cost_amount": 29259.64,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -1.16
}, },
{ {
"code": "01088", "code": "01088",
"name": "中国神华", "name": "中国神华",
"shares": 500, "shares": 500,
"avail_shares": 500, "price": 41.9,
"price": 41.84, "cost_price": 45.97,
"cost_price": 45.974,
"pl": -1764.27,
"pl_pct": -8.86,
"currency": "HKD", "currency": "HKD",
"market_val": 18144.8, "market_val": 18144.8,
"cost_amount": 19909.06, "cost_amount": 19909.06,
"exchange_rate": 0.8661, "exchange_rate": 0.8661
"change_pct": -0.52
}, },
{ {
"code": "600563", "code": "600563",
"name": "法拉电子", "name": "法拉电子",
"shares": 100, "shares": 100,
"avail_shares": 100, "price": 174.57,
"price": 178.76,
"cost_price": 146.95, "cost_price": 146.95,
"pl": 2762.0,
"pl_pct": 18.8,
"currency": "CNY", "currency": "CNY",
"market_val": 17457.0, "market_val": 17457.0,
"cost_amount": 14695.0, "cost_amount": 14695.0,
"exchange_rate": 0.866 "exchange_rate": 0.8664
}, },
{ {
"code": "300690", "code": "300690",
"name": "双一科技", "name": "双一科技",
"shares": 400, "shares": 400,
"avail_shares": 400, "price": 22.78,
"price": 22.68, "cost_price": 27.18,
"cost_price": 27.178,
"pl": -1759.2,
"pl_pct": -16.18,
"currency": "CNY", "currency": "CNY",
"market_val": 9112.0, "market_val": 9112.0,
"cost_amount": 10871.2, "cost_amount": 10871.2,
"exchange_rate": 0.866, "exchange_rate": 0.8664
"change_pct": -3.98
} }
], ],
"cash": 80476, "cash": 20230.1,
"total_market_value": 930199.93, "total_market_value": 988512.96,
"total_pl": -164810.3, "total_assets": 1008860.62,
"position_pct": 92.0, "total_pl": -164895.4,
"updated_at": "2026-06-24 11:47", "position_pct": 97.98,
"source": "/home/hmo/stocks/holding.xls", "updated_at": "2026-06-24 11:29",
"total_assets": 1010675.93 "source": "/home/hmo/stocks/holding.xls + 截图校核"
} }
+46 -46
View File
@@ -108,7 +108,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 88.32, "high": 88.32,
"low": 82.78, "low": 82.78,
"close": 82.79 "close": 83.0
} }
], ],
"00700": [ "00700": [
@@ -164,7 +164,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 432.4, "high": 432.4,
"low": 412.6, "low": 412.6,
"close": 421.6 "close": 418.8
} }
], ],
"000700": [ "000700": [
@@ -220,7 +220,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 15.6, "high": 15.6,
"low": 14.3, "low": 14.3,
"close": 14.59 "close": 14.65
} }
], ],
"001309": [ "001309": [
@@ -276,7 +276,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 816.0, "high": 816.0,
"low": 706.0, "low": 706.0,
"close": 794.11 "close": 793.0
} }
], ],
"00968": [ "00968": [
@@ -332,7 +332,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 2.26, "high": 2.26,
"low": 1.93, "low": 1.93,
"close": 2.0 "close": 1.99
} }
], ],
"00981": [ "00981": [
@@ -388,7 +388,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 84.9, "high": 84.9,
"low": 76.5, "low": 76.5,
"close": 83.15 "close": 83.5
} }
], ],
"01070": [ "01070": [
@@ -444,7 +444,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 13.47, "high": 13.47,
"low": 12.55, "low": 12.55,
"close": 13.08 "close": 13.1
} }
], ],
"01088": [ "01088": [
@@ -500,7 +500,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 43.14, "high": 43.14,
"low": 41.26, "low": 41.26,
"close": 41.8 "close": 41.72
} }
], ],
"01211": [ "01211": [
@@ -556,7 +556,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 78.3, "high": 78.3,
"low": 74.3, "low": 74.3,
"close": 75.05 "close": 74.6
} }
], ],
"01478": [ "01478": [
@@ -612,7 +612,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 8.54, "high": 8.54,
"low": 7.51, "low": 7.51,
"close": 7.52 "close": 7.57
} }
], ],
"01888": [ "01888": [
@@ -668,7 +668,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 97.4, "high": 97.4,
"low": 83.7, "low": 83.7,
"close": 93.85 "close": 93.5
} }
], ],
"02202": [ "02202": [
@@ -724,7 +724,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 2.53, "high": 2.53,
"low": 2.32, "low": 2.32,
"close": 2.37 "close": 2.36
} }
], ],
"02318": [ "02318": [
@@ -780,7 +780,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 55.5, "high": 55.5,
"low": 52.3, "low": 52.3,
"close": 52.7 "close": 52.55
} }
], ],
"02359": [ "02359": [
@@ -834,9 +834,9 @@
}, },
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 143.8, "high": 145.0,
"low": 130.0, "low": 130.0,
"close": 143.8 "close": 144.6
} }
], ],
"02388": [ "02388": [
@@ -892,7 +892,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 47.86, "high": 47.86,
"low": 45.9, "low": 45.9,
"close": 46.16 "close": 46.26
} }
], ],
"02628": [ "02628": [
@@ -948,7 +948,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 30.56, "high": 30.56,
"low": 28.18, "low": 28.18,
"close": 28.38 "close": 28.22
} }
], ],
"06160": [ "06160": [
@@ -1002,9 +1002,9 @@
}, },
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 168.5, "high": 168.6,
"low": 160.0, "low": 160.0,
"close": 168.5 "close": 168.0
} }
], ],
"06869": [ "06869": [
@@ -1059,8 +1059,8 @@
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 305.0, "high": 305.0,
"low": 253.4, "low": 251.2,
"close": 256.2 "close": 256.0
} }
], ],
"09868": [ "09868": [
@@ -1116,7 +1116,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 52.3, "high": 52.3,
"low": 48.82, "low": 48.82,
"close": 50.25 "close": 49.98
} }
], ],
"09988": [ "09988": [
@@ -1172,7 +1172,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 103.4, "high": 103.4,
"low": 97.65, "low": 97.65,
"close": 98.65 "close": 98.4
} }
], ],
"300035": [ "300035": [
@@ -1228,7 +1228,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 17.1, "high": 17.1,
"low": 15.96, "low": 15.96,
"close": 16.03 "close": 16.1
} }
], ],
"300124": [ "300124": [
@@ -1284,7 +1284,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 68.8, "high": 68.8,
"low": 65.74, "low": 65.74,
"close": 66.01 "close": 66.37
} }
], ],
"300548": [ "300548": [
@@ -1756,7 +1756,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 309.6, "high": 309.6,
"low": 275.86, "low": 275.86,
"close": 276.92 "close": 277.4
} }
], ],
"300750": [ "300750": [
@@ -1812,7 +1812,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 414.04, "high": 414.04,
"low": 386.66, "low": 386.66,
"close": 390.67 "close": 393.18
} }
], ],
"518880": [ "518880": [
@@ -1868,7 +1868,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 8.674, "high": 8.674,
"low": 8.426, "low": 8.426,
"close": 8.439 "close": 8.442
} }
], ],
"600036": [ "600036": [
@@ -1930,7 +1930,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 38.19, "high": 38.19,
"low": 36.94, "low": 36.94,
"close": 37.04 "close": 37.03
} }
], ],
"600110": [ "600110": [
@@ -2086,7 +2086,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 11.0, "high": 11.0,
"low": 10.43, "low": 10.43,
"close": 10.43 "close": 10.46
} }
], ],
"601899": [ "601899": [
@@ -2148,7 +2148,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 30.0, "high": 30.0,
"low": 27.33, "low": 27.33,
"close": 27.41 "close": 27.48
} }
], ],
"688411": [ "688411": [
@@ -2204,7 +2204,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 285.98, "high": 285.98,
"low": 261.8, "low": 261.8,
"close": 264.75 "close": 265.9
} }
], ],
"688639": [ "688639": [
@@ -2260,7 +2260,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 22.47, "high": 22.47,
"low": 20.99, "low": 20.99,
"close": 21.35 "close": 21.41
} }
], ],
"688795": [ "688795": [
@@ -2316,7 +2316,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 745.2, "high": 745.2,
"low": 660.01, "low": 660.01,
"close": 679.95 "close": 685.4
} }
], ],
"688802": [ "688802": [
@@ -2372,7 +2372,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 808.0, "high": 808.0,
"low": 726.1, "low": 726.1,
"close": 736.12 "close": 744.0
} }
], ],
"688981": [ "688981": [
@@ -2426,9 +2426,9 @@
}, },
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 153.0, "high": 153.6,
"low": 139.6, "low": 139.6,
"close": 150.15 "close": 153.58
} }
], ],
"000711": [ "000711": [
@@ -2466,7 +2466,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 5.38, "high": 5.38,
"low": 4.86, "low": 4.86,
"close": 5.0 "close": 5.16
} }
], ],
"688630": [ "688630": [
@@ -2496,9 +2496,9 @@
}, },
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 499.0, "high": 500.0,
"low": 455.55, "low": 455.55,
"close": 493.7 "close": 499.5
} }
], ],
"300690": [ "300690": [
@@ -2529,8 +2529,8 @@
{ {
"date": "2026-06-24", "date": "2026-06-24",
"high": 24.39, "high": 24.39,
"low": 22.69, "low": 22.68,
"close": 22.7 "close": 22.83
} }
], ],
"000657": [ "000657": [
@@ -2550,7 +2550,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 113.99, "high": 113.99,
"low": 94.79, "low": 94.79,
"close": 98.45 "close": 98.85
} }
], ],
"300308": [ "300308": [
@@ -2570,7 +2570,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 1395.0, "high": 1395.0,
"low": 1296.0, "low": 1296.0,
"close": 1300.0 "close": 1308.32
} }
], ],
"600519": [ "600519": [
@@ -2590,7 +2590,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 1264.0, "high": 1264.0,
"low": 1209.11, "low": 1209.11,
"close": 1215.18 "close": 1214.99
} }
], ],
"601318": [ "601318": [
@@ -2610,7 +2610,7 @@
"date": "2026-06-24", "date": "2026-06-24",
"high": 52.43, "high": 52.43,
"low": 49.03, "low": 49.03,
"close": 49.39 "close": 49.32
} }
], ],
"002171": [ "002171": [
+74 -56
View File
@@ -1,16 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
import_holding_xls.py — 从holding.xls导入持仓到SQLite + portfolio.json import_holding_xls.py — 从 holding.xls 导入持仓到全系统
用法:python3 import_holding_xls.py [--cash 现金金额] 用法:
python3 import_holding_xls.py [--cash 现金] [--total 总资产] [--mv 市值]
不传 --total/--mv 则从 holding.xls 计算(可能有价格时差误差)。
建议传截图上的真实数字。
示例:
python3 import_holding_xls.py --cash 20230.10 --total 1008860.62 --mv 988512.96
""" """
import csv, json, sys, subprocess, sqlite3 import csv, json, sys, subprocess, sqlite3, os
from datetime import datetime from datetime import datetime
STOCKS_FILE = "/home/hmo/stocks/holding.xls" STOCKS_FILE = "/home/hmo/stocks/holding.xls"
PORTFOLIO_PATH = "/home/hmo/web-dashboard/data/portfolio.json" PORTFOLIO_PATH = "/home/hmo/web-dashboard/data/portfolio.json"
DB_PATH = "/home/hmo/web-dashboard/data/mofin.db" DB_PATH = "/home/hmo/web-dashboard/data/mofin.db"
CASH = 80476 # default cash
def clean_cell(v): def clean_cell(v):
@@ -24,10 +30,18 @@ def clean_cell(v):
def main(): def main():
# Parse args # Parse args
global CASH cash = 20230.10
for i, a in enumerate(sys.argv[1:]): total_assets = 0
if a == '--cash' and i + 2 < len(sys.argv): market_value = 0
CASH = float(sys.argv[i + 2])
args = sys.argv[1:]
for i, a in enumerate(args):
if a == '--cash' and i + 1 < len(args):
cash = float(args[i + 1])
elif a == '--total' and i + 1 < len(args):
total_assets = float(args[i + 1])
elif a == '--mv' and i + 1 < len(args):
market_value = float(args[i + 1])
with open(STOCKS_FILE, 'r', encoding='gbk') as f: with open(STOCKS_FILE, 'r', encoding='gbk') as f:
reader = csv.reader(f, delimiter='\t') reader = csv.reader(f, delimiter='\t')
@@ -47,13 +61,11 @@ def main():
price_str = price_raw.replace('港币', '').replace('港元', '').replace('', '').strip() price_str = price_raw.replace('港币', '').replace('港元', '').replace('', '').strip()
price = float(price_str) price = float(price_str)
cost_price = float(clean_cell(r[5])) cost_price = float(clean_cell(r[5]))
pl = float(clean_cell(r[6])) pl = float(clean_cell(r[6])) if r[6].strip() else 0
pl_pct = float(clean_cell(r[7])) if r[7].strip() else 0.0
mkt_val = float(clean_cell(r[11])) mkt_val = float(clean_cell(r[11]))
cost_amount = float(clean_cell(r[15])) if r[15].strip() and r[15].strip() != '--' else 0 cost_amount = float(clean_cell(r[15])) if r[15].strip() and r[15].strip() != '--' else 0
rate_str = clean_cell(r[16]) rate_str = clean_cell(r[16])
rate = float(rate_str) if rate_str and rate_str != '--' else 0.866 rate = float(rate_str) if rate_str and rate_str != '--' else 0.8664
mv_cny = mkt_val if currency == 'CNY' else mkt_val * rate mv_cny = mkt_val if currency == 'CNY' else mkt_val * rate
total_mv_cny += mv_cny total_mv_cny += mv_cny
@@ -64,17 +76,51 @@ def main():
'cost_amount': cost_amount, 'exchange_rate': rate, 'cost_amount': cost_amount, 'exchange_rate': rate,
}) })
pfx = 'HK$' if currency == 'HKD' else '' # Use provided values or calculate
print(f" {code} {name} {shares}{pfx}{price:.2f} 成本{cost_price:.2f} 盈亏{pl:+,.0f}({pl_pct:+.1f}%)") if total_assets <= 0:
total_assets = total_mv_cny + cash
if market_value <= 0:
market_value = round(total_mv_cny, 2)
total_assets = total_mv_cny + CASH position_pct = round(market_value / total_assets * 100, 2) if total_assets > 0 else 0
position_pct = round(total_mv_cny / total_assets * 100, 2) if total_assets > 0 else 0
# Write portfolio.json # Step 1: Update SQLite (regenerate_all reads from here)
print("\n→ 更新 SQLite holdings 表...")
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('DELETE FROM holdings')
c.execute('DELETE FROM portfolio_summary')
for h in holdings:
c.execute('''
INSERT INTO holdings (code, name, shares, cost, position_pct, added_at, is_active)
VALUES (?, ?, ?, ?, ?, ?, 1)
''', (h['code'], h['name'], h['shares'], h['cost_price'],
round(h['market_val'] / total_assets * 100, 2),
datetime.now().strftime('%Y-%m-%d')))
c.execute('''
INSERT INTO portfolio_summary (total_assets, stock_value, cash, position_pct, total_pnl, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
''', (round(total_assets, 2), round(market_value, 2), cash,
position_pct, 0, datetime.now().strftime('%Y-%m-%d %H:%M')))
conn.commit()
conn.close()
print(f" OK - {len(holdings)} 只持仓")
# Step 2: Run full reassessment (reads SQLite, writes decisions.json + portfolio.json)
print("\n→ 全量策略重评...")
subprocess.run(
["python3", "/home/hmo/.hermes/profiles/position-analyst/scripts/per_stock_reassess.py"],
capture_output=True, text=True, timeout=120
)
print(f" 完成")
# Step 3: Overwrite portfolio.json with correct aggregate numbers
# (regenerate_all writes its own format, we fix it back)
print("\n→ 修正 portfolio.json 汇总数据...")
portfolio = { portfolio = {
'holdings': holdings, 'holdings': holdings,
'cash': CASH, 'cash': cash,
'total_market_value': round(total_mv_cny, 2), 'total_market_value': market_value,
'total_assets': round(total_assets, 2), 'total_assets': round(total_assets, 2),
'total_pl': 0, 'total_pl': 0,
'position_pct': position_pct, 'position_pct': position_pct,
@@ -83,43 +129,8 @@ def main():
} }
with open(PORTFOLIO_PATH, 'w') as f: with open(PORTFOLIO_PATH, 'w') as f:
json.dump(portfolio, f, indent=2, ensure_ascii=False) json.dump(portfolio, f, indent=2, ensure_ascii=False)
print(f"{PORTFOLIO_PATH}")
# Write SQLite # Step 4: Rebuild decision trees
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('DELETE FROM holdings')
c.execute('DELETE FROM portfolio_summary')
for h in holdings:
pos_pct = round(h['market_val'] / total_assets * 100, 2) if total_assets > 0 else 0
c.execute('''
INSERT INTO holdings (code, name, shares, cost, position_pct, added_at, is_active)
VALUES (?, ?, ?, ?, ?, ?, 1)
''', (h['code'], h['name'], h['shares'], h['cost_price'], pos_pct,
datetime.now().strftime('%Y-%m-%d')))
c.execute('''
INSERT INTO portfolio_summary (total_assets, stock_value, cash, position_pct, total_pnl, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
''', (round(total_assets, 2), round(total_mv_cny, 2), CASH, position_pct, 0,
datetime.now().strftime('%Y-%m-%d %H:%M')))
conn.commit()
conn.close()
print(f" → SQLite 已更新")
print(f"\n统计: {len(holdings)}只持仓 | "
f"总资产{round(total_assets):,.0f}元 | "
f"现金{CASH:,.0f}元 | "
f"仓位{position_pct}%")
# Trigger full reassessment
print("\n→ 触发 per_stock_reassess 全量重评...")
r = subprocess.run(
["python3", "/home/hmo/.hermes/profiles/position-analyst/scripts/per_stock_reassess.py"],
capture_output=True, text=True, timeout=120
)
print(r.stdout[-500:] if len(r.stdout) > 500 else r.stdout)
# Rebuild decision trees
print("\n→ 重建决策树...") print("\n→ 重建决策树...")
sys.path.insert(0, '/home/hmo/web-dashboard') sys.path.insert(0, '/home/hmo/web-dashboard')
from strategy_tree import init_default_branches from strategy_tree import init_default_branches
@@ -135,7 +146,14 @@ def main():
ok += 1 ok += 1
with open('/home/hmo/web-dashboard/data/decisions.json', 'w') as f: with open('/home/hmo/web-dashboard/data/decisions.json', 'w') as f:
json.dump(data, f, indent=2, ensure_ascii=False) json.dump(data, f, indent=2, ensure_ascii=False)
print(f"决策树重建: {ok}/{len(data.get('decisions',[]))}")
print(f"\n{'='*50}")
print(f"导入完成:{len(holdings)}只持仓")
print(f"总资产: {round(total_assets):,.0f}")
print(f"市值: {market_value:,.0f}")
print(f"现金: {cash:,.0f}")
print(f"仓位: {position_pct}%")
print(f"决策树: {ok}/{len(data.get('decisions',[]))}")
if __name__ == '__main__': if __name__ == '__main__':