diff --git a/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本 (2).csv b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本 (2).csv
new file mode 100644
index 0000000..3496cba
--- /dev/null
+++ b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本 (2).csv
@@ -0,0 +1,3 @@
+datetime,symbol,open,high,low,close,volume,turnover,open_interest
+2025-10-09 09:30:00,IM2512,7444.0,7445.2,7443.8,7445.2,406,604503760,-176
+2025-10-09 09:45:00,IM2512,7448.0,7497.8,7430.8,7482.6,20461,30561185000,-6689
\ No newline at end of file
diff --git a/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本.csv b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本.csv
new file mode 100644
index 0000000..dc90248
--- /dev/null
+++ b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m - 副本.csv
@@ -0,0 +1,37 @@
+datetime,symbol,open,high,low,close,volume,turnover,open_interest
+2025/10/9 9:30:00,IM2512,7444,7445.2,7443.8,7445.2,406,604503760,-176
+2025/10/9 9:45:00,IM2512,7448,7497.8,7430.8,7482.6,20461,30561185000,-6689
+2025/10/9 10:00:00,IM2512,7482.6,7486.6,7413.2,7438.2,16226,24136810440,-3489
+2025/10/9 10:15:00,IM2512,7436.6,7495.4,7425,7485,11496,17154464040,-1823
+2025/10/9 10:30:00,IM2512,7485.4,7490,7472.2,7488.6,7543,11285654240,-324
+2025/10/9 10:45:00,IM2512,7500,7517.8,7481.4,7508.6,9391,14084729600,584
+2025/10/9 11:00:00,IM2512,7508,7519,7484,7497.6,7377,11069550680,-184
+2025/10/9 11:15:00,IM2512,7498.2,7512.6,7493.2,7509.4,5153,7733800280,330
+2025/10/9 11:30:00,IM2512,7511.8,7515,7491.8,7497.8,4425,6640375520,639
+2025/10/9 13:00:00,IM2512,7496.2,7496.8,7496.2,7496.8,32,47974960,-1
+2025/10/9 13:15:00,IM2512,7497.2,7499.2,7475.2,7485.8,6500,9733899920,589
+2025/10/9 13:30:00,IM2512,7486,7490.4,7466.6,7486,5213,7795326120,634
+2025/10/9 13:45:00,IM2512,7487.4,7494.4,7477,7477,3671,5497097120,740
+2025/10/9 14:00:00,IM2512,7476.2,7489,7456.4,7486.2,7696,11501011000,1633
+2025/10/9 14:15:00,IM2512,7485,7502.6,7477,7481.8,5525,8276203640,1217
+2025/10/9 14:30:00,IM2512,7481,7492,7447.4,7455.8,7169,10703287880,1738
+2025/10/9 14:45:00,IM2512,7455.8,7468,7454.2,7461.6,4891,7299102080,1399
+2025/10/9 15:00:00,IM2512,7461,7468,7449.6,7449.6,7203,10744653160,1766
+2025/10/10 9:30:00,IM2512,7415,7417.6,7415,7417.4,595,882483920,-241
+2025/10/10 9:45:00,IM2512,7416.8,7433,7353.2,7353.2,26824,39698311640,-12378
+2025/10/10 10:00:00,IM2512,7352.6,7379.8,7339,7359.8,18742,27579709040,-3461
+2025/10/10 10:15:00,IM2512,7357.8,7375.6,7340,7343,9092,13374526160,-296
+2025/10/10 10:30:00,IM2512,7343.8,7373,7331.6,7373,8344,12267357720,263
+2025/10/10 10:45:00,IM2512,7374,7397.8,7373,7374.4,8100,11962701680,689
+2025/10/10 11:00:00,IM2512,7373.8,7391.6,7369.6,7375.8,4499,6640018080,130
+2025/10/10 11:15:00,IM2512,7375.6,7379,7357.4,7361.2,5339,7867726520,783
+2025/10/10 11:30:00,IM2512,7361.6,7367.2,7337,7349.2,6178,9084385760,679
+2025/10/10 13:00:00,IM2512,7350.8,7350.8,7349,7349,37,54391480,7
+2025/10/10 13:15:00,IM2512,7349,7366.6,7341.8,7361.2,5066,7453994640,1220
+2025/10/10 13:30:00,IM2512,7360.8,7365,7349.8,7358.6,3472,5108998600,1031
+2025/10/10 13:45:00,IM2512,7358,7371.4,7335,7345.4,6372,9368157960,1088
+2025/10/10 14:00:00,IM2512,7346.4,7380.6,7342,7380.6,7364,10842634240,2320
+2025/10/10 14:15:00,IM2512,7381.8,7386.8,7340.6,7340.6,7534,11087884440,2262
+2025/10/10 14:30:00,IM2512,7341.4,7353,7330.2,7347.6,6980,10249484440,2363
+2025/10/10 14:45:00,IM2512,7348.4,7364.4,7335.4,7342.4,5690,8364008080,2237
+2025/10/10 15:00:00,IM2512,7342.4,7348.2,7327.2,7340.4,9841,14443336560,2355
diff --git a/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m.csv b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m.csv
new file mode 100644
index 0000000..93018ba
--- /dev/null
+++ b/2.数据下载与处理/ssquant_download/IM888_2025-10-01_2025-10-10_1m.csv
@@ -0,0 +1,37 @@
+datetime,symbol,open,high,low,close,volume,amount,openint,cumulative_openint,open_askp,open_bidp,close_askp,close_bidp,开仓,平仓,多开,空开,多平,空平,双开,双平,双换,B,S,未知
+2025-10-09 09:30:00,IM2512,7444.0,7445.2,7443.8,7445.2,406,604503760,-176,183346,7445.0,7443.2,7448.0,7447.2,0,406,0,0,119,230,0,57,0,230,119,0
+2025-10-09 09:45:00,IM2512,7448.0,7497.8,7430.8,7482.6,20461,30561185000,-6689,176657,7448.0,7447.2,7484.0,7482.6,2468,16620,1079,932,6738,7334,457,2548,261,8413,7670,1112
+2025-10-09 10:00:00,IM2512,7482.6,7486.6,7413.2,7438.2,16226,24136810440,-3489,173168,7484.8,7482.6,7438.0,7435.6,3309,11270,1281,1356,4765,4298,672,2207,375,5579,6121,1272
+2025-10-09 10:15:00,IM2512,7436.6,7495.4,7425.0,7485.0,11496,17154464040,-1823,171345,7436.8,7435.8,7486.2,7485.6,3312,6782,1154,1312,2567,2905,846,1310,322,4059,3879,1080
+2025-10-09 10:30:00,IM2512,7485.4,7490.0,7472.2,7488.6,7543,11285654240,-324,171019,7486.4,7485.0,7488.8,7488.6,2875,3611,1184,1271,1454,1468,420,689,218,2652,2725,839
+2025-10-09 10:45:00,IM2512,7500.0,7517.8,7481.4,7508.6,9391,14084729600,584,171653,7500.0,7498.8,7509.0,7508.2,4459,3507,1930,1822,1420,1590,707,497,389,3520,3242,1036
+2025-10-09 11:00:00,IM2512,7508.0,7519.0,7484.0,7497.6,7377,11069550680,-184,171469,7508.8,7508.0,7498.0,7497.6,2836,3227,1194,1269,1396,1220,373,611,272,2414,2665,1042
+2025-10-09 11:15:00,IM2512,7498.2,7512.6,7493.2,7509.4,5153,7733800280,330,171795,7498.8,7498.2,7510.0,7509.4,2393,1709,1180,899,626,763,314,320,224,1943,1525,827
+2025-10-09 11:30:00,IM2512,7511.8,7515.0,7491.8,7497.8,4425,6640375520,639,172438,7511.8,7510.8,7497.8,7496.2,2351,1271,991,1046,554,554,314,163,184,1545,1600,619
+2025-10-09 13:00:00,IM2512,7496.2,7496.8,7496.2,7496.8,32,47974960,-1,172437,7496.2,7495.6,7497.4,7496.8,0,8,0,0,8,0,0,0,0,0,8,24
+2025-10-09 13:15:00,IM2512,7497.2,7499.2,7475.2,7485.8,6500,9733899920,589,173026,7498.6,7497.4,7486.4,7486.0,3293,2109,1341,1403,820,868,549,421,259,2209,2223,839
+2025-10-09 13:30:00,IM2512,7486.0,7490.4,7466.6,7486.0,5213,7795326120,634,173660,7486.0,7485.4,7486.6,7486.0,2802,1490,1049,1349,604,712,404,174,156,1761,1953,765
+2025-10-09 13:45:00,IM2512,7487.4,7494.4,7477.0,7477.0,3671,5497097120,740,174408,7488.0,7487.6,7476.8,7476.2,2076,864,783,958,361,366,335,137,132,1149,1319,599
+2025-10-09 14:00:00,IM2512,7476.2,7489.0,7456.4,7486.2,7696,11501011000,1633,176036,7476.8,7476.2,7486.2,7485.6,4641,1882,1691,2203,647,891,747,344,194,2582,2850,979
+2025-10-09 14:15:00,IM2512,7485.0,7502.6,7477.0,7481.8,5525,8276203640,1217,177254,7485.6,7485.2,7481.2,7481.0,3369,1164,1338,1537,476,489,494,199,191,1827,2013,801
+2025-10-09 14:30:00,IM2512,7481.0,7492.0,7447.4,7455.8,7169,10703287880,1738,178996,7481.0,7480.8,7456.4,7455.8,4537,1580,1855,2105,789,530,577,261,153,2385,2894,899
+2025-10-09 14:45:00,IM2512,7455.8,7468.0,7454.2,7461.6,4891,7299102080,1399,180390,7456.0,7454.6,7462.0,7460.4,3146,881,1400,1315,317,483,431,81,144,1883,1632,720
+2025-10-09 15:00:00,IM2512,7461.0,7468.0,7449.6,7449.6,7203,10744653160,1766,182161,7461.8,7461.2,7450.6,7449.6,4603,1573,1749,2375,843,578,479,152,114,2327,3218,913
+2025-10-10 09:30:00,IM2512,7415.0,7417.6,7415.0,7417.4,595,882483920,-241,181920,7417.4,7415.0,7416.8,7412.0,91,504,91,0,245,259,0,0,0,350,245,0
+2025-10-10 09:45:00,IM2512,7416.8,7433.0,7353.2,7353.2,26824,39698311640,-12378,169542,7416.8,7415.6,7353.4,7353.2,1483,24554,602,488,11019,9562,393,3973,243,10164,11507,544
+2025-10-10 10:00:00,IM2512,7352.6,7379.8,7339.0,7359.8,18742,27579709040,-3461,166081,7352.4,7352.0,7359.4,7358.0,4447,12800,1570,1977,5331,5334,900,2135,310,6904,7308,1185
+2025-10-10 10:15:00,IM2512,7357.8,7375.6,7340.0,7343.0,9092,13374526160,-296,165785,7357.8,7357.0,7344.0,7343.0,3853,3967,1580,1685,1472,1646,588,849,321,3226,3157,951
+2025-10-10 10:30:00,IM2512,7343.8,7373.0,7331.6,7373.0,8344,12267357720,263,166048,7343.8,7342.6,7373.4,7373.0,3821,3190,1545,1627,1183,1435,649,572,333,2980,2810,1000
+2025-10-10 10:45:00,IM2512,7374.0,7397.8,7373.0,7374.4,8100,11962701680,689,166772,7374.4,7374.0,7374.2,7373.4,4123,2630,1941,1626,1072,1149,556,409,237,3090,2698,1110
+2025-10-10 11:00:00,IM2512,7373.8,7391.6,7369.6,7375.8,4499,6640018080,130,166902,7374.6,7374.4,7376.4,7375.6,2022,1627,927,866,671,684,229,272,179,1611,1537,671
+2025-10-10 11:15:00,IM2512,7375.6,7379.0,7357.4,7361.2,5339,7867726520,783,167685,7375.4,7375.2,7363.0,7361.2,2806,1619,1097,1319,821,518,390,280,183,1615,2140,731
+2025-10-10 11:30:00,IM2512,7361.6,7367.2,7337.0,7349.2,6178,9084385760,679,168364,7362.8,7361.4,7350.6,7349.0,3068,1943,1295,1218,913,676,555,354,289,1971,2131,878
+2025-10-10 13:00:00,IM2512,7350.8,7350.8,7349.0,7349.0,37,54391480,7,168371,7350.2,7349.2,7350.6,7349.0,12,25,12,0,25,0,0,0,0,12,25,0
+2025-10-10 13:15:00,IM2512,7349.0,7366.6,7341.8,7361.2,5066,7453994640,1220,169591,7352.2,7349.0,7362.0,7361.2,3031,1103,1280,1195,415,507,556,181,193,1787,1610,739
+2025-10-10 13:30:00,IM2512,7360.8,7365.0,7349.8,7358.6,3472,5108998600,1031,170622,7361.2,7360.8,7359.4,7358.6,2205,560,1068,785,245,206,352,109,167,1274,1030,540
+2025-10-10 13:45:00,IM2512,7358.0,7371.4,7335.0,7345.4,6372,9368157960,1088,171730,7358.0,7357.4,7346.4,7345.2,3566,1729,1379,1719,798,697,468,234,205,2076,2517,872
+2025-10-10 14:00:00,IM2512,7346.4,7380.6,7342.0,7380.6,7364,10842634240,2320,174050,7346.8,7345.6,7380.8,7380.6,5174,1160,2230,2152,413,612,792,135,217,2842,2565,813
+2025-10-10 14:15:00,IM2512,7381.8,7386.8,7340.6,7340.6,7534,11087884440,2262,176312,7382.6,7381.8,7341.4,7340.4,5204,1365,2031,2125,638,479,1048,248,172,2510,2763,793
+2025-10-10 14:30:00,IM2512,7341.4,7353.0,7330.2,7347.6,6980,10249484440,2363,178675,7341.8,7340.4,7348.4,7347.6,5028,1122,2187,2004,360,632,837,130,131,2819,2364,699
+2025-10-10 14:45:00,IM2512,7348.4,7364.4,7335.4,7342.4,5690,8364008080,2237,180909,7349.8,7348.4,7343.6,7342.4,4179,659,1603,1952,338,258,624,63,163,1861,2290,689
+2025-10-10 15:00:00,IM2512,7342.4,7348.2,7327.2,7340.4,9841,14443336560,2355,183267,7344.0,7343.2,7341.8,7340.6,6345,2260,2637,2915,1237,836,793,187,154,3473,4152,1082
diff --git a/2.数据下载与处理/ssquant_download/ssquant_download.ipynb b/2.数据下载与处理/ssquant_download/ssquant_download.ipynb
index 2d3a1a0..ca46c9f 100644
--- a/2.数据下载与处理/ssquant_download/ssquant_download.ipynb
+++ b/2.数据下载与处理/ssquant_download/ssquant_download.ipynb
@@ -86,9 +86,21 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "ename": "ModuleNotFoundError",
+ "evalue": "No module named 'ssquant.SQDATA'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
+ "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)",
+ "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mssquant\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mSQDATA\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m TakeData\n",
+ "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'ssquant.SQDATA'"
+ ]
+ }
+ ],
"source": [
"from ssquant.SQDATA import TakeData"
]
@@ -130,164 +142,9 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "头部文件为:--------------------\n"
- ]
- },
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " datetime | \n",
- " symbol | \n",
- " open | \n",
- " high | \n",
- " low | \n",
- " close | \n",
- " volume | \n",
- " amount | \n",
- " cumulative_openint | \n",
- " openint | \n",
- " open_bidp | \n",
- " open_askp | \n",
- " close_bidp | \n",
- " close_askp | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " | 0 | \n",
- " 2019-01-02 09:01:00 | \n",
- " rb1905 | \n",
- " 3399 | \n",
- " 3405 | \n",
- " 3389 | \n",
- " 3401 | \n",
- " 69562 | \n",
- " 2362607160 | \n",
- " 2383714 | \n",
- " 16864 | \n",
- " 3399.0 | \n",
- " 3400.0 | \n",
- " 3400.0 | \n",
- " 3401.0 | \n",
- "
\n",
- " \n",
- " | 1 | \n",
- " 2019-01-02 09:02:00 | \n",
- " rb1905 | \n",
- " 3401 | \n",
- " 3430 | \n",
- " 3401 | \n",
- " 3410 | \n",
- " 88696 | \n",
- " 3034283200 | \n",
- " 2399530 | \n",
- " -12248 | \n",
- " 3401.0 | \n",
- " 3402.0 | \n",
- " 3409.0 | \n",
- " 3410.0 | \n",
- "
\n",
- " \n",
- " | 2 | \n",
- " 2019-01-02 09:03:00 | \n",
- " rb1905 | \n",
- " 3409 | \n",
- " 3414 | \n",
- " 3409 | \n",
- " 3412 | \n",
- " 22828 | \n",
- " 778740580 | \n",
- " 2387356 | \n",
- " 1180 | \n",
- " 3409.0 | \n",
- " 3410.0 | \n",
- " 3411.0 | \n",
- " 3412.0 | \n",
- "
\n",
- " \n",
- " | 3 | \n",
- " 2019-01-02 09:04:00 | \n",
- " rb1905 | \n",
- " 3412 | \n",
- " 3413 | \n",
- " 3403 | \n",
- " 3404 | \n",
- " 17378 | \n",
- " 592413220 | \n",
- " 2388158 | \n",
- " 54 | \n",
- " 3411.0 | \n",
- " 3412.0 | \n",
- " 3404.0 | \n",
- " 3405.0 | \n",
- "
\n",
- " \n",
- " | 4 | \n",
- " 2019-01-02 09:05:00 | \n",
- " rb1905 | \n",
- " 3405 | \n",
- " 3409 | \n",
- " 3405 | \n",
- " 3405 | \n",
- " 15770 | \n",
- " 537276980 | \n",
- " 2388190 | \n",
- " 1674 | \n",
- " 3405.0 | \n",
- " 3406.0 | \n",
- " 3405.0 | \n",
- " 3406.0 | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " datetime symbol open high low close volume amount \\\n",
- "0 2019-01-02 09:01:00 rb1905 3399 3405 3389 3401 69562 2362607160 \n",
- "1 2019-01-02 09:02:00 rb1905 3401 3430 3401 3410 88696 3034283200 \n",
- "2 2019-01-02 09:03:00 rb1905 3409 3414 3409 3412 22828 778740580 \n",
- "3 2019-01-02 09:04:00 rb1905 3412 3413 3403 3404 17378 592413220 \n",
- "4 2019-01-02 09:05:00 rb1905 3405 3409 3405 3405 15770 537276980 \n",
- "\n",
- " cumulative_openint openint open_bidp open_askp close_bidp close_askp \n",
- "0 2383714 16864 3399.0 3400.0 3400.0 3401.0 \n",
- "1 2399530 -12248 3401.0 3402.0 3409.0 3410.0 \n",
- "2 2387356 1180 3409.0 3410.0 3411.0 3412.0 \n",
- "3 2388158 54 3411.0 3412.0 3404.0 3405.0 \n",
- "4 2388190 1674 3405.0 3406.0 3405.0 3406.0 "
- ]
- },
- "execution_count": 49,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
+ "outputs": [],
"source": [
"print('头部文件为:--------------------')\n",
"data.head()"
@@ -363,7 +220,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.9"
+ "version": "3.13.2"
}
},
"nbformat": 4,
diff --git a/2.数据下载与处理/ssquant_download/专属数据库请求数据API示例.py b/2.数据下载与处理/ssquant_download/专属数据库请求数据API示例.py
new file mode 100644
index 0000000..ede616e
--- /dev/null
+++ b/2.数据下载与处理/ssquant_download/专属数据库请求数据API示例.py
@@ -0,0 +1,109 @@
+import requests
+import pandas as pd
+from datetime import datetime, timedelta
+from io import StringIO
+
+def get_futures_data(symbol, start_date, end_date, kline_period='1m', adjust_type='0', depth='no'):
+ """
+ 获取期货数据
+ """
+ # 构建请求参数
+ params = {
+ 'username': username,
+ 'password': password,
+ 'symbol': symbol,
+ 'start_date': start_date,
+ 'end_date': end_date,
+ 'kline_period': kline_period,
+ 'adjust_type': adjust_type
+ }
+
+ if depth:
+ params['Depth'] = depth
+
+ print("请求参数:", params) # 打印请求参数,便于调试
+
+ try:
+ # 发送请求,设置超时时间为30秒
+ response = requests.get(base_url, params=params, timeout=300)
+
+ # 检查响应状态
+ if response.status_code == 200:
+ # 检查响应是否为JSON格式
+ if 'application/json' in response.headers.get('Content-Type', ''):
+ data = pd.read_json(StringIO(response.text), orient='records')
+ data=data.reset_index()
+ #列名排序
+ if depth=='yes':
+ columns=['datetime','symbol','open','high','low','close','volume','amount','openint','cumulative_openint','open_askp','open_bidp','close_askp','close_bidp','开仓','平仓','多开','空开','多平','空平','双开','双平','双换','B','S','未知']
+ else:
+ columns=['datetime','symbol','open','high','low','close','volume','amount','openint','cumulative_openint','open_askp','open_bidp','close_askp','close_bidp']
+ # 重新排列列名
+ data = data.reindex(columns=columns)
+ data['datetime'] = pd.to_datetime(data['datetime'])
+ # 更改时间显示的格式,例如 "YYYY-MM-DD HH:MM:SS"
+ # 将 UTC 时间转换为本地时区,例如 'Asia/Shanghai'
+ data['datetime'] = data['datetime'].dt.tz_convert('Asia/Shanghai')
+ data['datetime'] = data['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
+ return data
+ else:
+ print("响应不是JSON格式:", response.text[:1000])
+ return None
+ elif response.status_code == 401:
+ print("认证失败:用户名和密码不能为空")
+ return None
+ elif response.status_code == 402:
+ print("认证失败:账号不存在,请检查账号后重新输入......如还有问题联系管理员微信:viquant01")
+ return None
+ elif response.status_code == 405:
+ print("认证失败:账号已过期,请联系管理员微信:viquant01")
+ return None
+ elif response.status_code == 406:
+ print("认证失败:密码错误,请检查密码后重新输入......如还有问题联系管理员微信:viquant01")
+ return None
+ else:
+ print(f"请求失败,状态码:{response.status_code}")
+ print(f"错误信息:{response.json().get('error', '未知错误')}")
+ return None
+
+ except Exception as e:
+ print(f"发生错误:{e}")
+ print("请求URL:", response.url)
+ print("响应内容:", response.text[:1000]) # 打印前1000个字符
+ return None
+
+# 使用示例
+if __name__ == "__main__":
+ # API配置
+ base_url = 'http://kanpan789.com:8086/ftdata'
+ # 用户认证信息
+ username = '240884432@qq.com' # 替换为你的手机号或者邮箱
+ password = 'Zj123!@#' # 替换为你的密码
+ # 示例参数
+ symbol = "IM888" #
+ start_date = "2025-10-01" #start_date : 开始时间
+ end_date = "2025-10-10" #end_date(包含当天):结束时间
+ kline_period="15M" #周期:1M..5M..NM(分钟),1D(天),1W(周),1Y(月)
+ adjust_type= 0 #复权开关 :0(不复权)1(后复权)
+ depth='yes' # 获取交易数据统计: yes(获取),no(不获取)
+
+ # 获取K线数据
+ df_data = get_futures_data(
+ symbol=symbol,
+ start_date=start_date,
+ end_date=end_date,
+ kline_period=kline_period,
+ adjust_type=adjust_type,
+ depth=depth
+ )
+
+ if df_data is not None:
+ print("\nK线数据示例:")
+ print(df_data)
+ print(f"\n数据条数:{len(df_data)}")
+
+ # 保存到CSV文件(可选)
+ csv_filename = f"{symbol}_{start_date}_{end_date}_1m.csv"
+ df_data.to_csv(csv_filename, index=False)
+ print(f"\n数据已保存到文件:{csv_filename}")
+
\ No newline at end of file
diff --git a/2.数据下载与处理/ssquant_download/松鼠数据下载脚本.ipynb b/2.数据下载与处理/ssquant_download/松鼠数据下载脚本.ipynb
index 6f75e02..c016f30 100644
--- a/2.数据下载与处理/ssquant_download/松鼠数据下载脚本.ipynb
+++ b/2.数据下载与处理/ssquant_download/松鼠数据下载脚本.ipynb
@@ -88,10 +88,22 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 1,
"id": "65b4b7aa",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "ename": "ModuleNotFoundError",
+ "evalue": "No module named 'ssquant.SQDATA'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
+ "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)",
+ "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mssquant\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mSQDATA\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m TakeData\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpandas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpd\u001b[39;00m\n",
+ "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'ssquant.SQDATA'"
+ ]
+ }
+ ],
"source": [
"from ssquant.SQDATA import TakeData\n",
"import pandas as pd"
@@ -227,7 +239,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "Python 3",
"language": "python",
"name": "python3"
},
@@ -241,7 +253,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.9"
+ "version": "3.13.2"
}
},
"nbformat": 4,
diff --git a/temp/dingdanliu_nb_option.py b/temp/dingdanliu_nb_option.py
new file mode 100644
index 0000000..f6bddda
--- /dev/null
+++ b/temp/dingdanliu_nb_option.py
@@ -0,0 +1,1735 @@
+"""
+该代码的主要目的是处理Tick数据并生成交易信号。代码中定义了一个tickcome函数,它接收到Tick数据后会进行一系列的处理,包括构建Tick字典、更新上一个Tick的成交量、保存Tick数据、生成K线数据等。其中涉及到的一些函数有:
+on_tick(tick): 处理单个Tick数据,根据Tick数据生成K线数据。
+tickdata(df, symbol): 处理Tick数据,生成K线数据。
+orderflow_df_new(df_tick, df_min, symbol): 处理Tick和K线数据,生成订单流数据。F
+GetOrderFlow_dj(kData): 计算订单流的信号指标。
+除此之外,代码中还定义了一个MyTrader类,继承自TraderApiBase,用于实现交易相关的功能。
+"""
+
+# from concurrent.futures import ThreadPoolExecutor
+from multiprocessing import Process, Queue
+import queue
+import threading
+# from AlgoPlus.CTP.MdApi import run_tick_engine
+# from AlgoPlus.CTP.FutureAccount import get_simulate_account
+# from AlgoPlus.CTP.FutureAccount import FutureAccount
+# from AlgoPlus.CTP.TraderApiBase import TraderApiBase
+
+from CtpPlus.CTP.MdApi import run_tick_engine
+from CtpPlus.CTP.FutureAccount import get_simulate_account
+from CtpPlus.CTP.FutureAccount import FutureAccount
+from CtpPlus.CTP.TraderApiBase import TraderApiBase
+
+# from AlgoPlus.ta.time_bar import tick_to_bar
+import pandas as pd
+from datetime import datetime, timedelta
+from datetime import time as s_time
+import operator
+import time
+import numpy as np
+import os
+import re
+
+# import talib as tb
+
+import akshare as ak
+import ast
+
+# 加入邮件通知
+import smtplib
+from email.mime.text import MIMEText # 导入 MIMEText 类发送纯文本邮件
+from email.mime.multipart import (
+ MIMEMultipart,
+)
+
+# from email.mime.application import MIMEApplication
+
+# 配置邮件信息
+receivers = ["240884432@qq.com"] # 设置邮件接收人地址
+subject = "TD_Simnow_Signal" # 设置邮件主题 订单流策略交易信号
+
+# 配置邮件服务器信息
+smtp_server = "smtp.qq.com" # 设置发送邮件的 SMTP 服务器地址
+smtp_port = 465 # 设置发送邮件的 SMTP 服务器端口号,一般为 25 端口 465
+sender = "240884432@qq.com" # 设置发送邮件的邮箱地址
+username = "240884432@qq.com" # 设置发送邮件的邮箱用户名
+password = "osjyjmbqrzxtbjbf" # zrmpcgttataabhjh,设置发送邮件的邮箱密码或授权码
+
+tickdatadict = {} # 存储Tick数据的字典
+quotedict = {} # 存储行情数据的字典
+ofdatadict = {} # 存储K线数据的字典
+trade_dfs = {} # pd.DataFrame({}) # 存储交易数据的DataFrame对象
+previous_volume = {} # 上一个Tick的成交量
+tsymbollist = {}
+
+
+time_period = 30
+delta_rate = 0.8
+dj_rate = 0.8
+
+clearing_time_dict = {
+ "sc": s_time(2, 30),
+ "bc": s_time(1, 0),
+ "lu": s_time(23, 0),
+ "nr": s_time(23, 0),
+ "au": s_time(2, 30),
+ "ag": s_time(2, 30),
+ "ss": s_time(1, 0),
+ "sn": s_time(1, 0),
+ "ni": s_time(1, 0),
+ "pb": s_time(1, 0),
+ "zn": s_time(1, 0),
+ "al": s_time(1, 0),
+ "cu": s_time(1, 0),
+ "ru": s_time(23, 0),
+ "rb": s_time(23, 0),
+ "hc": s_time(23, 0),
+ "fu": s_time(23, 0),
+ "bu": s_time(23, 0),
+ "sp": s_time(23, 0),
+ "PF": s_time(23, 0),
+ "SR": s_time(23, 0),
+ "CF": s_time(23, 0),
+ "CY": s_time(23, 0),
+ "RM": s_time(23, 0),
+ "MA": s_time(23, 0),
+ "TA": s_time(23, 0),
+ "ZC": s_time(23, 0),
+ "FG": s_time(23, 0),
+ "OI": s_time(23, 0),
+ "SA": s_time(23, 0),
+ "p": s_time(23, 0),
+ "j": s_time(23, 0),
+ "jm": s_time(23, 0),
+ "i": s_time(23, 0),
+ "l": s_time(23, 0),
+ "v": s_time(23, 0),
+ "pp": s_time(23, 0),
+ "eg": s_time(23, 0),
+ "c": s_time(23, 0),
+ "cs": s_time(23, 0),
+ "y": s_time(23, 0),
+ "m": s_time(23, 0),
+ "a": s_time(23, 0),
+ "b": s_time(23, 0),
+ "rr": s_time(23, 0),
+ "eb": s_time(23, 0),
+ "pg": s_time(23, 0),
+}
+
+fees_df = pd.read_csv('./futures_fees_info.csv', header = 0, usecols= [1, 3, 5, 13, 15],names=['合约代码', '品种代码', '合约乘数', '做多保证金率(按金额)', '做空保证金率(按金额)'])
+contacts_df = pd.read_csv('./main_contacts.csv', header = 0, usecols= [16, 17],names=['主连代码', '品种代码'])
+
+def get_main_contact_on_time(main_symbol_code,contacts_df):
+ main_symbol = contacts_df[contacts_df['品种代码'] == main_symbol_code]['主连代码'].iloc[0]
+ # print("最终使用的主连代码:",main_symbol)
+ return main_symbol#.encode('ascii')
+
+def send_mail(text):
+ msg = MIMEMultipart()
+ msg["From"] = sender
+ msg["To"] = ";".join(receivers)
+ msg["Subject"] = subject
+ msg.attach(MIMEText(text, "plain", "utf-8"))
+ smtp = smtplib.SMTP_SSL(smtp_server, smtp_port)
+ smtp.login(username, password)
+ smtp.sendmail(sender, receivers, msg.as_string())
+ smtp.quit()
+
+
+
+# def get_otm_put_strike_price(option_finance_board_df, future_price):
+# # 计算距离当前期货价格最近的行权价
+# option_finance_board_df['strike_diff'] = abs(option_finance_board_df['行权价'] - future_price)
+# closest_row = option_finance_board_df.loc[option_finance_board_df['strike_diff'].idxmin()]
+# otm_put_strike_price = closest_row['行权价']
+# return otm_put_strike_price
+
+
+def send_feishu_message(text):
+ headers = {
+ "Content-Type": "application/json"
+ }
+ data = {
+ "msg_type": "text",
+ "content": {
+ "text": text
+ }
+ }
+ # response = requests.post("https://open.feishu.cn/open-apis/bot/v2/hook/8608dfa4-e599-462a-8dba-6ac72873dd27", headers=headers, json=data) #AI策略飞书地址
+ response = requests.post("https://open.feishu.cn/open-apis/bot/v2/hook/fae322eb-1ff7-4133-ba00-0ca4895d205e", headers=headers, json=data) #订单流策略飞书地址
+ if response.status_code != 200:
+ print(f"飞书消息发送失败,状态码: {response.status_code}, 响应内容: {response.text}")
+# def futures_main_day(future_symbol, delta_days):
+# # 获取当前日期的数据
+# today = datetime.now().strftime("%Y%m%d")
+# # 计算多少日前的日期
+# start_day = (datetime.now() - timedelta(days=delta_days)).strftime("%Y%m%d")
+
+# futures_main_sina_hist = ak.futures_main_sina(
+# symbol=future_symbol, start_date=start_day, end_date=today
+# )
+# return futures_main_sina_hist
+
+
+# 交易程序---------------------------------------------------------------------------------------------------------------------------------------------------------------------
+class ParamObj:
+
+ symbol = None # 合约名称
+ Lots = None # 下单手数
+ py = None # 设置委托价格的偏移,更加容易促成成交
+ trailing_stop_percent = None # 跟踪出场参数
+ fixed_stop_loss_percent = None # 固定出场参数
+ dj_X = None # 开仓的堆积参数
+ delta = None # 开仓的delta参数
+ sum_delta = None # 开仓的delta累积参数
+ 失衡 = None
+ 堆积 = None
+ 周期 = None
+
+ # 策略需要用到的变量
+ cont_df = 0
+ pos = 0
+ short_trailing_stop_price = 0
+ long_trailing_stop_price = 0
+ sl_long_price = 0
+ sl_shor_price = 0
+ out_long = 0
+ out_short = 0
+ clearing_executed = False
+ kgdata = True
+
+ def __init__(
+ self,
+ symbol,
+ Lots,
+ py,
+ trailing_stop_percent,
+ fixed_stop_loss_percent,
+ dj_X,
+ delta,
+ sum_delta,
+ 失衡,
+ 堆积,
+ 周期,
+ ):
+ self.symbol = symbol
+ self.Lots = Lots
+ self.py = py
+ self.trailing_stop_percent = trailing_stop_percent
+ self.fixed_stop_loss_percent = fixed_stop_loss_percent
+ self.dj_X = dj_X
+ self.delta = delta
+ self.sum_delta = sum_delta
+ self.失衡 = 失衡
+ self.堆积 = 堆积
+ self.周期 = 周期
+
+
+class MyTrader(TraderApiBase):
+
+ def __init__(
+ self,
+ broker_id,
+ td_server,
+ investor_id,
+ password,
+ app_id,
+ auth_code,
+ md_queue=None,
+ page_dir="",
+ private_resume_type=2,
+ public_resume_type=2,
+ ):
+ self.param_dict = {}
+ self.queue_dict = {}
+ self.品种 = " "
+
+ def tickcome(self, md_queue):
+ global previous_volume
+ data = md_queue
+ instrument_id = data["InstrumentID"].decode() # 品种代码
+ ActionDay = data["ActionDay"].decode() # 交易日日期
+ update_time = data["UpdateTime"].decode() # 更新时间
+
+ update_millisec = str(data["UpdateMillisec"]) # 更新毫秒数
+ created_at = (
+ ActionDay[:4]
+ + "-"
+ + ActionDay[4:6]
+ + "-"
+ + ActionDay[6:]
+ + " "
+ + update_time
+ + "."
+ + update_millisec
+ ) # 创建时间
+ # 构建tick字典
+ tick = {
+ "symbol": instrument_id, # 品种代码和交易所ID
+ "created_at": datetime.strptime(created_at, "%Y-%m-%d %H:%M:%S.%f"),
+ # "created_at": datetime.strptime(created_at, "-- %H:%M:%S.%f"),
+ "price": float(data["LastPrice"]), # 最新价
+ "last_volume": (
+ int(data["Volume"]) - previous_volume.get(instrument_id, 0)
+ if previous_volume.get(instrument_id, 0) != 0
+ else 0
+ ), # 瞬时成交量
+ "bid_p": float(data["BidPrice1"]), # 买价
+ "bid_v": int(data["BidVolume1"]), # 买量
+ "ask_p": float(data["AskPrice1"]), # 卖价
+ "ask_v": int(data["AskVolume1"]), # 卖量
+ "UpperLimitPrice": float(data["UpperLimitPrice"]), # 涨停价
+ "LowerLimitPrice": float(data["LowerLimitPrice"]), # 跌停价
+ "TradingDay": data["TradingDay"].decode(), # 交易日日期
+ "cum_volume": int(data["Volume"]), # 最新总成交量
+ "cum_amount": float(data["Turnover"]), # 最新总成交额
+ "cum_position": int(data["OpenInterest"]), # 合约持仓量
+ }
+
+ previous_volume[instrument_id] = int(data["Volume"])
+ if tick["last_volume"] > 0:
+ self.on_tick(tick)
+
+ def can_time(self, hour, minute):
+ hour = str(hour)
+ minute = str(minute)
+ if len(minute) == 1:
+ minute = "0" + minute
+ return int(hour + minute)
+
+ def on_tick(self, tick):
+ # tm = self.can_time(tick["created_at"].hour, tick["created_at"].minute)
+ if tick["last_volume"] == 0:
+ return
+ quotes = tick
+ timetick = str(tick["created_at"]).replace("+08:00", "")
+ tsymbol = tick["symbol"]
+ if tsymbol not in tsymbollist.keys():
+ # 获取tick的买卖价和买卖量
+ tsymbollist[tsymbol] = tick
+ bid_p = quotes["bid_p"]
+ ask_p = quotes["ask_p"]
+ bid_v = quotes["bid_v"]
+ ask_v = quotes["ask_v"]
+ else:
+ # 获取上一个tick的买卖价和买卖量
+ rquotes = tsymbollist[tsymbol]
+ bid_p = rquotes["bid_p"]
+ ask_p = rquotes["ask_p"]
+ bid_v = rquotes["bid_v"]
+ ask_v = rquotes["ask_v"]
+ tsymbollist[tsymbol] = tick
+ tick_dt = pd.DataFrame(
+ {
+ "datetime": timetick,
+ "symbol": tick["symbol"],
+ "mainsym": tick["symbol"].rstrip("0123456789").upper(),
+ "lastprice": tick["price"],
+ "vol": tick["last_volume"],
+ "bid_p": bid_p,
+ "ask_p": ask_p,
+ "bid_v": bid_v,
+ "ask_v": ask_v,
+ },
+ index=[0],
+ )
+ sym = tick_dt["symbol"][0]
+ self.tickdata(tick_dt, sym)
+
+ def data_of(self, symbol, df):
+ global trade_dfs
+ trade_dfs[symbol] = pd.concat([trade_dfs[symbol], df], ignore_index=True)
+
+ def process(self, bidDict, askDict, symbol):
+ try:
+ # 尝试从quotedict中获取对应品种的报价数据
+ dic = quotedict[symbol]
+ bidDictResult = dic["bidDictResult"]
+ askDictResult = dic["askDictResult"]
+ except Exception:
+ # 如果获取失败,则初始化bidDictResult和askDictResult为空字典
+ bidDictResult, askDictResult = {}, {}
+
+ # 将所有买盘字典和卖盘字典的key合并,并按升序排序
+ sList = sorted(set(list(bidDict.keys()) + list(askDict.keys())))
+
+ # 遍历所有的key,将相同key的值进行累加
+ for s in sList:
+ if s in bidDict:
+ if s in bidDictResult:
+ bidDictResult[s] = int(bidDict[s]) + bidDictResult[s]
+ else:
+ bidDictResult[s] = int(bidDict[s])
+ if s not in askDictResult:
+ askDictResult[s] = 0
+ else:
+ if s in askDictResult:
+ askDictResult[s] = int(askDict[s]) + askDictResult[s]
+ else:
+ askDictResult[s] = int(askDict[s])
+ if s not in bidDictResult:
+ bidDictResult[s] = 0
+
+ # 构建包含bidDictResult和askDictResult的字典,并存入quotedict中
+ df = {"bidDictResult": bidDictResult, "askDictResult": askDictResult}
+ quotedict[symbol] = df
+ return bidDictResult, askDictResult
+
+ def tickdata(self, df, symbol):
+ tickdata = pd.DataFrame(
+ {
+ "datetime": df["datetime"],
+ "symbol": df["symbol"],
+ "lastprice": df["lastprice"],
+ "volume": df["vol"],
+ "bid_p": df["bid_p"],
+ "bid_v": df["bid_v"],
+ "ask_p": df["ask_p"],
+ "ask_v": df["ask_v"],
+ }
+ )
+ try:
+ if symbol in tickdatadict.keys():
+ rdf = tickdatadict[symbol]
+ rdftm = pd.to_datetime(rdf["bartime"][0]).strftime("%Y-%m-%d %H:%M:%S")
+ now = str(tickdata["datetime"][0])
+ if now > rdftm:
+ try:
+ oo = ofdatadict[symbol]
+ self.data_of(symbol, oo)
+ if symbol in quotedict.keys():
+ quotedict.pop(symbol)
+ if symbol in tickdatadict.keys():
+ tickdatadict.pop(symbol)
+ if symbol in ofdatadict.keys():
+ ofdatadict.pop(symbol)
+ except IOError as e:
+ print("rdftm捕获到异常", e)
+ tickdata["bartime"] = pd.to_datetime(tickdata["datetime"])
+ tickdata["open"] = tickdata["lastprice"]
+ tickdata["high"] = tickdata["lastprice"]
+ tickdata["low"] = tickdata["lastprice"]
+ tickdata["close"] = tickdata["lastprice"]
+ tickdata["starttime"] = tickdata["datetime"]
+ else:
+ tickdata["bartime"] = rdf["bartime"]
+ tickdata["open"] = rdf["open"]
+ tickdata["high"] = max(
+ tickdata["lastprice"].values, rdf["high"].values
+ )
+ tickdata["low"] = min(
+ tickdata["lastprice"].values, rdf["low"].values
+ )
+ tickdata["close"] = tickdata["lastprice"]
+ tickdata["volume"] = df["vol"] + rdf["volume"].values
+ tickdata["starttime"] = rdf["starttime"]
+ else:
+ print("新bar的第一个tick进入")
+ tickdata["bartime"] = pd.to_datetime(tickdata["datetime"])
+ tickdata["open"] = tickdata["lastprice"]
+ tickdata["high"] = tickdata["lastprice"]
+ tickdata["low"] = tickdata["lastprice"]
+ tickdata["close"] = tickdata["lastprice"]
+ tickdata["starttime"] = tickdata["datetime"]
+ except IOError as e:
+ print("捕获到异常", e)
+
+ tickdata["bartime"] = pd.to_datetime(tickdata["bartime"])
+ param = self.param_dict[self.品种]
+ bardata = (
+ tickdata.resample(
+ on="bartime", rule=param.周期, label="right", closed="right"
+ )
+ .agg(
+ {
+ "starttime": "first",
+ "symbol": "last",
+ "open": "first",
+ "high": "max",
+ "low": "min",
+ "close": "last",
+ "volume": "sum",
+ }
+ )
+ .reset_index(drop=False)
+ )
+ bardata = bardata.dropna().reset_index(drop=True)
+ bardata["bartime"] = pd.to_datetime(bardata["bartime"][0]).strftime(
+ "%Y-%m-%d %H:%M:%S"
+ )
+ tickdatadict[symbol] = bardata
+ tickdata["volume"] = df["vol"].values
+ self.orderflow_df_new(tickdata, bardata, symbol)
+
+ def orderflow_df_new(self, df_tick, df_min, symbol):
+ # startArray = pd.to_datetime(df_min["starttime"]).values
+ voluememin = df_min["volume"].values
+ highs = df_min["high"].values
+ lows = df_min["low"].values
+ opens = df_min["open"].values
+ closes = df_min["close"].values
+ # endArray = pd.to_datetime(df_min['bartime']).values
+ endArray = df_min["bartime"].values
+ # print(endArray)
+ # deltaArray = np.zeros((len(endArray),))
+ # tTickArray = pd.to_datetime(df_tick["datetime"]).values
+ bp1minickArray = df_tick["bid_p"].values
+ ap1minickArray = df_tick["ask_p"].values
+ lastTickArray = df_tick["lastprice"].values
+ volumeTickArray = df_tick["volume"].values
+ symbolarray = df_tick["symbol"].values
+ # indexFinal = 0
+ for index, tEnd in enumerate(endArray):
+ dt = endArray[index]
+ # start = startArray[index]
+ bidDict = {}
+ askDict = {}
+ bar_vol = voluememin[index]
+ bar_close = closes[index]
+ bar_open = opens[index]
+ bar_low = lows[index]
+ bar_high = highs[index]
+ bar_symbol = symbolarray[index]
+ Bp = round(bp1minickArray[0], 4)
+ Ap = round(ap1minickArray[0], 4)
+ LastPrice = round(lastTickArray[0], 4)
+ Volume = volumeTickArray[0]
+ if LastPrice >= Ap:
+ if str(LastPrice) in askDict.keys():
+ askDict[str(LastPrice)] += Volume
+ else:
+ askDict[str(LastPrice)] = Volume
+ if LastPrice <= Bp:
+ if str(LastPrice) in bidDict.keys():
+ bidDict[str(LastPrice)] += Volume
+ else:
+ bidDict[str(LastPrice)] = Volume
+ # indexFinal = indexTick
+ bidDictResult, askDictResult = self.process(bidDict, askDict, symbol)
+ bidDictResult = dict(
+ sorted(bidDictResult.items(), key=operator.itemgetter(0))
+ )
+ askDictResult = dict(
+ sorted(askDictResult.items(), key=operator.itemgetter(0))
+ )
+ prinslist = list(bidDictResult.keys())
+ asklist = list(askDictResult.values())
+ bidlist = list(bidDictResult.values())
+ delta = sum(askDictResult.values()) - sum(bidDictResult.values())
+ df = pd.DataFrame(
+ {
+ "price": pd.Series([prinslist]),
+ "Ask": pd.Series([asklist]),
+ "Bid": pd.Series([bidlist]),
+ }
+ )
+ # df=pd.DataFrame({'price':pd.Series(bidDictResult.keys()),'Ask':pd.Series(askDictResult.values()),'Bid':pd.Series(bidDictResult.values())})
+ df["symbol"] = bar_symbol
+ df["datetime"] = dt
+ df["delta"] = str(delta)
+ df["close"] = bar_close
+ df["open"] = bar_open
+ df["high"] = bar_high
+ df["low"] = bar_low
+ df["volume"] = bar_vol
+ # df['ticktime']=tTickArray[0]
+ df["dj"] = self.GetOrderFlow_dj(df)
+ ofdatadict[symbol] = df
+
+ def GetOrderFlow_dj(self, kData):
+ param = self.param_dict[self.品种]
+ Config = {
+ "Value1": param.失衡,
+ "Value2": param.堆积,
+ "Value4": True,
+ }
+ aryData = kData
+ djcout = 0
+
+ # 遍历kData中的每一行,计算djcout指标
+ for index, row in aryData.iterrows():
+ kItem = aryData.iloc[index]
+ # high = kItem["high"]
+ # low = kItem["low"]
+ # close = kItem["close"]
+ # open = kItem["open"]
+ dtime = kItem["datetime"]
+ price_s = kItem["price"]
+ Ask_s = kItem["Ask"]
+ Bid_s = kItem["Bid"]
+ delta = kItem["delta"]
+
+ price_s = price_s
+ Ask_s = Ask_s
+ Bid_s = Bid_s
+
+ gj = 0
+ xq = 0
+ gxx = 0
+ xxx = 0
+
+ # 遍历price_s中的每一个元素,计算相关指标
+ for i in np.arange(0, len(price_s), 1):
+ duiji = {
+ "price": 0,
+ "time": 0,
+ "longshort": 0,
+ }
+
+ if i == 0:
+ delta = delta
+
+ order = {
+ "Price": price_s[i],
+ "Bid": {"Value": Bid_s[i]},
+ "Ask": {"Value": Ask_s[i]},
+ }
+ # 空头堆积
+ if i >= 0 and i < len(price_s) - 1:
+ if order["Bid"]["Value"] > Ask_s[i + 1] * int(Config["Value1"]):
+ gxx += 1
+ gj += 1
+ if gj >= int(Config["Value2"]) and Config["Value4"] is True:
+ duiji["price"] = price_s[i]
+ duiji["time"] = dtime
+ duiji["longshort"] = -1
+ if float(duiji["price"]) > 0:
+ djcout += -1
+ else:
+ gj = 0
+ # 多头堆积
+ if i >= 1 and i < len(price_s) - 1:
+ if order["Ask"]["Value"] > Bid_s[i - 1] * int(Config["Value1"]):
+ xq += 1
+ xxx += 1
+ if xq >= int(Config["Value2"]) and Config["Value4"] is True:
+ duiji["price"] = price_s[i]
+ duiji["time"] = dtime
+ duiji["longshort"] = 1
+ if float(duiji["price"]) > 0:
+ djcout += 1
+ else:
+ xq = 0
+
+ # 返回计算得到的djcout值
+ return djcout
+
+ # 读取保存的数据
+ def read_to_csv(self, symbol):
+ # 文件夹路径和文件路径
+ # 使用正则表达式提取英文字母并重新赋值给symbol
+ param = self.param_dict[symbol]
+ # symbol = ''.join(re.findall('[a-zA-Z]', str(symbol)))
+ folder_path = "traderdata"
+ file_path = os.path.join(folder_path, f"{str(symbol)}_traderdata.csv")
+ # 如果文件夹不存在则创建
+ if not os.path.exists(folder_path):
+ os.makedirs(folder_path)
+
+ # 读取保留的模型数据CSV文件
+ if os.path.exists(file_path):
+ df = pd.read_csv(file_path)
+ if not df.empty and param.kgdata is True:
+ # 选择最后一行数据
+ # df = df._append(df.iloc[-1], ignore_index=True)
+ row = df.iloc[-1]
+
+ # 根据CSV文件的列名将数据赋值给相应的属性
+ param.pos = int(row["pos"])
+ param.short_trailing_stop_price = float(
+ row["short_trailing_stop_price"]
+ )
+ param.long_trailing_stop_price = float(row["long_trailing_stop_price"])
+ param.sl_long_price = float(row["sl_long_price"])
+ param.sl_shor_price = float(row["sl_shor_price"])
+ # param.out_long = int(row['out_long'])
+ # param.out_short = int(row['out_short'])
+ print("找到历史交易数据文件,已经更新持仓,止损止盈数据", df.iloc[-1])
+ param.kgdata = False
+ else:
+ pass
+ # print("没有找到历史交易数据文件", file_path)
+ # 如果没有找到CSV,则初始化变量
+
+ pass
+
+ # 保存数据
+ def save_to_csv(self, symbol):
+ param = self.param_dict[symbol]
+ # 使用正则表达式提取英文字母并重新赋值给symbol
+ # symbol = ''.join(re.findall('[a-zA-Z]', str(symbol)))
+ # 创建DataFrame
+
+ data = {
+ "datetime": [trade_dfs[symbol]["datetime"].iloc[-1]],
+ "pos": [param.pos],
+ "short_trailing_stop_price": [param.short_trailing_stop_price],
+ "long_trailing_stop_price": [param.long_trailing_stop_price],
+ "sl_long_price": [param.sl_long_price],
+ "sl_shor_price": [param.sl_shor_price],
+ # 'out_long': [param.out_long],
+ # 'out_short': [param.out_short]
+ }
+
+ df = pd.DataFrame(data)
+
+ # 将DataFrame保存到CSV文件
+ # df.to_csv(
+ # f"traderdata/{str(symbol)}_traderdata.csv",
+ # mode="a",
+ # index=False,
+ # header=False,
+ # )
+
+ traderdata_file_path = f"traderdata/{str(symbol)}_traderdata.csv"
+ if os.path.exists(traderdata_file_path):
+ # 仅保存最后一行数据
+ # csv_df = pd.read_csv(traderdata_file_path)
+ # if df["pos"].iloc[-1] != csv_df["pos"].iloc[-1]:
+ df.to_csv(traderdata_file_path, mode="a", header=False, index=False)
+ else:
+ # 创建新文件并保存整个DataFrame
+ df.to_csv(traderdata_file_path, index=False)
+ # df.to_csv(f"traderdata/{str(symbol)}_traderdata.csv", index=False)
+
+ # 每日收盘重置数据
+ def day_data_reset(self, symbol):
+ param = self.param_dict[symbol]
+ sec = "".join(re.findall("[a-zA-Z]", str(symbol)))
+ # 获取当前时间
+ current_time = datetime.now().time()
+
+ # 第一时间范围(日盘收盘)
+ clearing_time1_start = s_time(15, 5)
+ clearing_time1_end = s_time(15, 10)
+
+ # 创建一个标志变量,用于记录是否已经执行过
+ param.clearing_executed = False
+ # 检查当前时间第一个操作的时间范围内
+ if (
+ clearing_time1_start <= current_time <= clearing_time1_end
+ and not param.clearing_executed
+ ):
+ param.clearing_executed = True # 设置标志变量为已执行
+ trade_dfs[symbol].drop(
+ trade_dfs[symbol].index, inplace=True
+ ) # 清除当天的行情数据
+
+ # 检查当前时间是否在第二个操作的时间范围内(夜盘收盘)
+ elif sec in clearing_time_dict.keys():
+ clearing_time2_start = clearing_time_dict[sec]
+ clearing_time2_end = s_time(
+ clearing_time2_start.hour, clearing_time2_start.minute + 15
+ )
+ if (
+ clearing_time2_start <= current_time <= clearing_time2_end
+ and not param.clearing_executed
+ ):
+ param.clearing_executed = True # 设置标志变量为已执行
+ trade_dfs[symbol].drop(
+ trade_dfs[symbol].index, inplace=True
+ ) # 清除当天的行情数据
+ else:
+ param.clearing_executed = False
+ pass
+ return param.clearing_executed
+
+ def OnRtnTrade(self, pTrade):
+ print("||成交回报||", pTrade)
+
+ def OnRspOrderInsert(self, pInputOrder, pRspInfo, nRequestID, bIsLast):
+ print("||OnRspOrderInsert||", pInputOrder, pRspInfo, nRequestID, bIsLast)
+
+ # 订单状态通知
+ def OnRtnOrder(self, pOrder):
+ print("||订单回报||", pOrder)
+
+ def cal_sig(self, symbol_queue):
+ while True:
+ try:
+ data = symbol_queue.get(
+ block=True, timeout=5
+ ) # 如果5秒没收到新的tick行情,则抛出异常
+ instrument_id = data["InstrumentID"].decode() # 品种代码
+ size = symbol_queue.qsize()
+ if size > 1:
+ print(
+ f"当前{instrument_id}共享队列长度为{size}, 有点阻塞!!!!!"
+ )
+ self.read_to_csv(instrument_id)
+ self.day_data_reset(instrument_id)
+ param = self.param_dict[instrument_id]
+ self.品种 = instrument_id
+ self.tickcome(data)
+ trade_df = trade_dfs[instrument_id]
+ # 新K线开始,启动交易程序 and 保存行情数据
+ self.read_to_csv(instrument_id)
+ if len(trade_df) > param.cont_df:
+ # 检查文件是否存在
+ csv_file_path = f"traderdata/{instrument_id}_ofdata.csv"
+ # if os.path.exists(csv_file_path):
+ # #jerome :保存数增加'delta累计'、POC、、终极平滑值、趋势方向
+ # # 仅保存最后一行数据
+ # trade_df.tail(1).to_csv(
+ # csv_file_path, mode="a", header=False, index=False
+ # )
+ # else:
+ # # 创建新文件并保存整个DataFrame
+ # trade_df.to_csv(csv_file_path, index=False)
+ # 检查是否存在重复行
+ if os.path.exists(csv_file_path):
+ existing_df = pd.read_csv(csv_file_path, usecols=range(12))
+ # 获取要写入的新数据
+ new_data = trade_df.tail(1)
+
+ # 检查新数据是否与现有数据重复
+ is_duplicate = False
+ for _, row in existing_df.iterrows():
+ if (row['datetime'] == new_data['datetime'].iloc[0] and
+ row['price'] == new_data['price'].iloc[0] and
+ row['Ask'] == new_data['Ask'].iloc[0] and
+ row['Bid'] == new_data['Bid'].iloc[0] and
+ row['symbol'] == new_data['symbol'].iloc[0] and
+ row['delta'] == new_data['delta'].iloc[0] and
+ row['close'] == new_data['close'].iloc[0] and
+ row['open'] == new_data['open'].iloc[0] and
+ row['high'] == new_data['high'].iloc[0] and
+ row['low'] == new_data['low'].iloc[0] and
+ row['volume'] == new_data['volume'].iloc[0] and
+ row['dj'] == new_data['dj'].iloc[0]):
+ is_duplicate = True
+ break
+
+ # 检查Ask和Bid的值是否为空或全为0
+ ask_value = new_data['Ask'].iloc[0]
+ bid_value = new_data['Bid'].iloc[0]
+ is_valid_data = (
+ ask_value != [] and
+ ask_value != [0] and
+ bid_value != [] and
+ bid_value != [0]
+ )
+
+ if not is_duplicate and is_valid_data:
+ # 如果没有重复且数据有效,则写入新数据
+ new_data.to_csv(
+ csv_file_path, mode="a", header=False, index=False
+ )
+ else:
+ # 如果文件不存在,直接写入新数据
+ trade_df.to_csv(csv_file_path, index=False)
+
+ # 更新跟踪止损价格
+ if param.long_trailing_stop_price > 0 and param.pos > 0:
+
+ param.long_trailing_stop_price = (
+ trade_df["low"].iloc[-1]
+ if param.long_trailing_stop_price < trade_df["low"].iloc[-1]
+ else param.long_trailing_stop_price
+ )
+ self.save_to_csv(instrument_id)
+
+ if param.short_trailing_stop_price > 0 and param.pos < 0:
+
+ param.short_trailing_stop_price = (
+ trade_df["high"].iloc[-1]
+ if trade_df["high"].iloc[-1]
+ < param.short_trailing_stop_price
+ else param.short_trailing_stop_price
+ )
+ self.save_to_csv(instrument_id)
+
+ param.out_long = param.long_trailing_stop_price * (
+ 1 - param.trailing_stop_percent
+ )
+ param.out_short = param.short_trailing_stop_price * (
+ 1 + param.trailing_stop_percent
+ )
+ # 跟踪出场
+ if param.out_long > 0:
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "预设——多头止盈——",
+ "TR",
+ param.out_long,
+ "low",
+ trade_df["low"].iloc[-1],
+ )
+ if (
+ trade_df["low"].iloc[-1] < param.out_long
+ and param.pos > 0
+ and param.sl_long_price > 0
+ and trade_df["low"].iloc[-1] > param.sl_long_price
+ ):
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "多头止盈",
+ "TR",
+ param.out_long,
+ "low",
+ trade_df["low"].iloc[-1],
+ )
+ # 平多
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["BidPrice1"] - param.py,
+ param.Lots,
+ b"1",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["BidPrice1"] - param.py,
+ param.Lots,
+ b"1",
+ b"3",
+ )
+ param.long_trailing_stop_price = 0
+ param.out_long = 0
+ param.sl_long_price = 0
+ param.pos = 0
+ self.save_to_csv(instrument_id)
+
+ if param.out_short > 0:
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "预设——空头止盈——: ",
+ "TR",
+ param.out_short,
+ "high",
+ trade_df["high"].iloc[-1],
+ )
+ if (
+ trade_df["high"].iloc[-1] > param.out_short
+ and param.pos < 0
+ and param.sl_shor_price > 0
+ and trade_df["high"].iloc[-1] < param.sl_shor_price
+ ):
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "空头止盈: ",
+ "TR",
+ param.out_short,
+ "high",
+ trade_df["high"].iloc[-1],
+ )
+ # 平空
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["AskPrice1"] + param.py,
+ param.Lots,
+ b"0",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["AskPrice1"] + param.py,
+ param.Lots,
+ b"0",
+ b"3",
+ )
+ param.short_trailing_stop_price = 0
+ param.sl_shor_price = 0
+ self.out_shor = 0
+ param.pos = 0
+ self.save_to_csv(instrument_id)
+
+ # 固定止损
+ fixed_stop_loss_L = param.sl_long_price * (
+ 1 - param.fixed_stop_loss_percent
+ )
+ if param.pos > 0:
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "预设——多头止损",
+ "SL",
+ fixed_stop_loss_L,
+ "close",
+ trade_df["close"].iloc[-1],
+ )
+ if (
+ param.sl_long_price > 0
+ and fixed_stop_loss_L > 0
+ and param.pos > 0
+ and trade_df["close"].iloc[-1] < fixed_stop_loss_L
+ ):
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "多头止损",
+ "SL",
+ fixed_stop_loss_L,
+ "close",
+ trade_df["close"].iloc[-1],
+ )
+ # 平多
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["BidPrice1"] - param.py,
+ param.Lots,
+ b"1",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["BidPrice1"] - param.py,
+ param.Lots,
+ b"1",
+ b"3",
+ )
+ param.long_trailing_stop_price = 0
+ param.sl_long_price = 0
+ param.out_long = 0
+ param.pos = 0
+ self.save_to_csv(instrument_id)
+
+ fixed_stop_loss_S = param.sl_shor_price * (
+ 1 + param.fixed_stop_loss_percent
+ )
+ if param.pos < 0:
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "预设——空头止损",
+ "SL",
+ fixed_stop_loss_S,
+ "close",
+ trade_df["close"].iloc[-1],
+ )
+ if (
+ param.sl_shor_price > 0
+ and fixed_stop_loss_S > 0
+ and param.pos < 0
+ and trade_df["close"].iloc[-1] > fixed_stop_loss_S
+ ):
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "空头止损",
+ "SL",
+ fixed_stop_loss_S,
+ "close",
+ trade_df["close"].iloc[-1],
+ )
+ # 平空
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["AskPrice1"] + param.py,
+ param.Lots,
+ b"0",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ data["InstrumentID"],
+ data["AskPrice1"] + param.py,
+ param.Lots,
+ b"0",
+ b"3",
+ )
+ param.short_trailing_stop_price = 0
+ param.sl_shor_price = 0
+ param.out_short = 0
+ param.pos = 0
+ self.save_to_csv(instrument_id)
+
+
+ # 计算累积的delta值datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S")
+ trade_df["delta"] = trade_df["delta"].astype(float)
+ # trade_df['datetime'] = pd.to_datetime(trade_df['datetime'], format='mixed')
+ trade_df['datetime'] = pd.to_datetime(trade_df['datetime'], format='%Y-%m-%d %H:%M:%S')
+
+ # 自定义分组逻辑:前一日21:00至当日15:00为一天
+ def get_trading_day(dt):
+ # 如果时间在21:00之后,属于下一个交易日
+ if dt.hour >= 21:
+ return (dt + pd.Timedelta(days=1)).date()
+ # 如果时间在15:00之前,属于当前交易日
+ elif dt.hour < 15:
+ return dt.date()
+ # 15:00-21:00之间的数据属于当前交易日
+ else:
+ return dt.date()
+
+ trade_df['trading_day'] = trade_df['datetime'].apply(get_trading_day)
+
+ # 将日期转换为字符串
+ trade_df['trading_day'] = trade_df['trading_day'].astype(str)
+
+ # 按交易日计算delta累计
+ trade_df['delta累计'] = trade_df.groupby('trading_day')['delta'].cumsum()
+
+ trade_df = trade_df.fillna('缺值')#fillna(value=0)
+
+ def ultimate_smoother(price,period):
+ # 初始化变量(修正角度单位为弧度)
+ a1 = np.exp(-1.414 * np.pi / period)
+ b1 = 2 * a1 * np.cos(1.414 * np.pi / period) # 将180改为np.pi
+ c2 = b1
+ c3 = -a1 ** 2
+ c1 = (1 + c2 - c3) / 4
+
+ # 准备输出序列
+ us = np.zeros(len(price))
+ us_new = np.zeros(len(price))
+ trend = [None]*(len(price))
+ ma_close = np.zeros(len(price))
+
+ # 前4个点用原始价格初始化
+ for i in range(len(price)):
+ if i < 4:
+ us[i] = price.iloc[i]
+ else:
+ # 应用递归公式
+ us[i] = (1 - c1) * price.iloc[i] + (2 * c1 - c2) * price.iloc[i-1] \
+ - (c1 + c3) * price.iloc[i-2] + c2 * us[i-1] + c3 * us[i-2]
+
+ us_new = np.around(us, decimals=2)
+ ma_close = price.rolling(window=4*period).mean()#5*
+
+ # if us_new[i]>price[i] and ma_close[i]>price[i]:
+ # trend[i] = '空头趋势'
+ # elif us_new[i] ma_close.iloc[i]:
+ trend[i] = '多头趋势'
+ else:
+ trend[i] = '无趋势'
+
+
+ return us_new,trend
+
+ trade_df['终极平滑值'],trade_df['趋势方向'] = ultimate_smoother(trade_df['close'],time_period)#,df['ma_close']
+
+ trade_df['datetime'] = trade_df['datetime'].dt.strftime("%Y-%m-%d %H:%M:%S")
+
+
+ def safe_literal_eval(x):
+ """带异常处理的安全转换"""
+ try:
+ return ast.literal_eval(x)
+ except ValueError:
+ return [] # 返回空列表作为占位符
+
+ def add_poc_column(df):
+ # 安全转换列数据
+ df['price'] = df['price'].apply(safe_literal_eval)
+ df['Ask'] = df['Ask'].apply(lambda x: list(map(int, safe_literal_eval(x))))
+ df['Bid'] = df['Bid'].apply(lambda x: list(map(int, safe_literal_eval(x))))
+
+ # 定义处理函数(带数据验证)
+ def find_poc(row):
+ # 验证三个列表长度一致且非空
+ if not (len(row['price']) == len(row['Ask']) == len(row['Bid']) > 0):
+ return '缺值' # 返回空值标记异常数据
+
+ sums = [a + b for a, b in zip(row['Ask'], row['Bid'])]
+ try:
+ max_index = sums.index(max(sums))
+ return row['price'][max_index]
+ except ValueError:
+ return '缺值' # 处理空求和列表情况
+
+ # 应用处理函数
+ df['POC'] = df.apply(find_poc, axis=1)
+
+ # 可选:统计异常数据
+ error_count = df['POC'].isnull().sum()
+ if error_count > 0:
+ print(f"警告:发现 {error_count} 行异常数据(已标记为NaN)")
+
+ return df['POC']
+
+ trade_df['POC'] = add_poc_column(trade_df)
+
+ def finall_trend(delta_sum,trend):
+ f_trend = [None]*(len(delta_sum))
+ # delta_sum = delta_sum.astype(float)
+ for i in range(len(delta_sum)):
+ if (delta_sum[i] == '缺值') or (trend[i] == '缺值'):
+ f_trend[i] = '方向不明'
+ # return f_trend
+ else:
+ if delta_sum[i] > 0 and (trend[i] == '多头趋势'):
+ f_trend[i] = '强多头'
+ elif delta_sum[i] < 0 and (trend[i] == '空头趋势'):
+ f_trend[i] = '强空头'
+ else:
+ f_trend[i] = '方向不明'
+ return f_trend
+
+ trade_df['最终趋势'] = finall_trend(trade_df['delta累计'],trade_df['趋势方向'])
+
+ # table_text = f"品种:{trade_df['symbol'].iloc[-1]}, 时间:{trade_df['datetime'].iloc[-1]},close:{trade_df['close'].iloc[-1]},open:{trade_df['open'].iloc[-1]},high:{trade_df['high'].iloc[-1]},low:{trade_df['low'].iloc[-1]},delta:{trade_df['delta'].iloc[-1]}, delta累计:{trade_df['delta累计'].iloc[-1]}, dj:{trade_df['dj'].iloc[-1]},POC:{trade_df['POC'].iloc[-1]}, 终极平滑值:{trade_df['终极平滑值'].iloc[-1]}, 趋势方向:{trade_df['趋势方向'].iloc[-1]},最终趋势:{trade_df['最终趋势'].iloc[-1]}"
+ # if data["InstrumentID"]:
+ # option_buy_symbol,option_buy_price= get_otm_option(data["InstrumentID"], 'C')
+ # option_sell_symbol,option_sell_price = get_otm_option(data["InstrumentID"], 'P')
+ # print("买入平值期权为:", option_buy_symbol, ",价格为:", option_buy_price)
+ # print("卖出平值期权为:", option_sell_symbol, ",价格为:", option_sell_price)
+
+ def get_otm_option(future_symbol, trade_type):
+ def get_option_symbol(future_symbol):
+ # 创建一个字典,将期货值futuresymbol映射到对应的option symbol .
+ option_dict = {
+ "IH": "上证50股指期权",
+ "IF": "沪深300股指期权",
+ "IC": "中证500股指期权",
+ "IM": "中证1000股指期权"
+ }
+ # 解析 future_symbol 获取期货代码和到期月份
+ m = re.match(r'([A-Za-z]+)(\d+)', future_symbol)
+ if not m:
+ raise ValueError(f"future_symbol 格式不正确: {future_symbol}")
+ future_code, future_end_month = m.groups()
+ option_symbol = option_dict.get(future_code)
+ return option_symbol, future_end_month
+
+ try:
+ option_symbol, future_end_month = get_option_symbol(future_symbol)
+ option_finance_board_df = ak.option_finance_board(symbol=option_symbol, end_month=future_end_month)
+
+ len(option_finance_board_df)
+
+ half = len(option_finance_board_df) // 2
+
+ first_half_df = option_finance_board_df.iloc[:half]
+ first_half_df.columns = [f"{col}_C" for col in first_half_df.columns]
+ first = first_half_df.reset_index(drop=True)
+
+ second_half_df = option_finance_board_df.iloc[half:]
+ second_half_df.columns = [f"{col}_P" for col in second_half_df.columns]
+ second = second_half_df.reset_index(drop=True)
+
+ df = pd.concat([first, second], axis=1)
+ df['lastprice_(C-P)'] = df['lastprice_C'] - df['lastprice_P']
+
+ idx = df['lastprice_(C-P)'].abs().idxmin()
+ row = df.loc[idx, ['instrument_C', 'instrument_P', 'lastprice_C','lastprice_P','lastprice_(C-P)']]
+
+ # print(f"index={idx}, instrument_C={row['instrument_C']}, instrument_P={row['instrument_P']}, lastprice_(C-P)={row['lastprice_(C-P)']}")
+
+ # return df.loc[idx, ['instrument_C', 'instrument_P']]
+ if trade_type == 'C':
+ return row['instrument_C'], float(row['lastprice_C'])
+ elif trade_type == 'P':
+ return row['instrument_P'], float(row['lastprice_P'])
+ else:
+ raise ValueError(f"未知的 trade_type: {trade_type}")
+ except Exception as e:
+ print(f"get_otm_option error for {future_symbol}: {e}")
+ return None
+
+ def get_otm_pirce(future_symbol, trade_option_symbol):
+ def get_option_symbol(future_symbol):
+ # 创建一个字典,将期货值futuresymbol映射到对应的option symbol .
+ option_dict = {
+ "IH": "上证50股指期权",
+ "IF": "沪深300股指期权",
+ "IC": "中证500股指期权",
+ "IM": "中证1000股指期权"
+ }
+ # 解析 future_symbol 获取期货代码和到期月份
+ m = re.match(r'([A-Za-z]+)(\d+)', future_symbol)
+ if not m:
+ raise ValueError(f"future_symbol 格式不正确: {future_symbol}")
+ future_code, future_end_month = m.groups()
+ option_symbol = option_dict.get(future_code)
+ return option_symbol, future_end_month
+
+ try:
+ option_symbol, future_end_month = get_option_symbol(future_symbol)
+ option_finance_board_df = ak.option_finance_board(symbol=option_symbol, end_month=future_end_month)
+
+ mask = option_finance_board_df['instrument'] == trade_option_symbol
+ if mask.any():
+ lastprice_value = option_finance_board_df.loc[mask, 'lastprice'].iloc[0]
+ return lastprice_value
+ # print(lastprice_value)
+ else:
+ print("未找到对应的行")
+ except Exception as e:
+ print(f"get_otm_option error for {future_symbol}: {e}")
+ return None
+
+ print("trade_df['symbol'].iloc[-1]}:", trade_df['symbol'].iloc[-1])
+ print("trade_df['symbol'].iloc[-1]}的类型:", type(trade_df['symbol'].iloc[-1]))
+
+
+ # 开多、空前置条件
+ 开多条件 = (trade_df['趋势方向'].iloc[-1] == '多头趋势')
+ 开空条件 = (trade_df['趋势方向'].iloc[-1] == '空头趋势')
+
+ if len(trade_df) >= 4*time_period:
+ #开多
+ 开多1 = (trade_df['dj'].iloc[-1] >= 2) #max(0.8 * max(trade_df['dj'].iloc[-4*time_period-1:-1]), 10))
+ # print("开多1:",开多1)
+ 开多2 = (trade_df['delta'].iloc[-1] >= 10)# max(0.8 * max(trade_df['delta'].iloc[-4*time_period-1:-1]), 350))
+ 开多3 = (trade_df['delta累计'].iloc[-2] < 0 and trade_df['delta累计'].iloc[-1] > 0)
+
+ # 开空
+ 开空1 = (trade_df['dj'].iloc[-1] <= -2) #min(0.8 * min(trade_df['dj'].iloc[-4*time_period-1:-1]), -10))
+ 开空2 = (trade_df['delta'].iloc[-1] <= -10) #min(0.8 * min(trade_df['delta'].iloc[-4*time_period-1:-1]),-350))
+ # print("开空2:",开空2)
+ 开空3 = (trade_df['delta累计'].iloc[-2] > 0 and trade_df['delta累计'].iloc[-1] < 0)
+
+ # 开多组合 = (开多条件 and (开多1 or 开多2 or 开多3))
+ # 开空组合 = (开空条件 and (开空1 or 开空2 or 开空3))
+
+ # 平多组合 = (开空条件 or 开空1 or 开空2 or 开空3)
+ # 平空组合 = (开多条件 or 开多1 or 开多2 or 开多3)
+
+ 开多组合 = 开多1 and 开多2
+ 开空组合 = 开空1 and 开空2
+
+ 平多组合 = 开空1 or 开空2
+ 平空组合 = 开多1 or 开多2
+
+ option_buy_symbol = ''
+ option_buy_price = 0
+ option_sell_symbol = ''
+ option_sell_price = 0
+
+ # 平空
+ if param.pos < 0 and 平空组合:
+ # close_sell_price = get_otm_pirce(data["InstrumentID"].decode(), option_sell_symbol)
+ print(
+ "平空: ",
+ "ExchangeID: ",
+ data["ExchangeID"],
+ "InstrumentID",
+ option_sell_symbol,
+ "AskPrice1",
+ 800,#close_sell_price + param.py,
+ )
+ # 平空
+ self.insert_order(
+ data["ExchangeID"],
+ option_sell_symbol,
+ 800,#close_sell_price + param.py,
+ param.Lots,
+ b"0",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ option_sell_symbol,
+ 800,#close_sell_price + param.py,
+ param.Lots,
+ b"0",
+ b"3",
+ )
+
+ param.pos = 0
+ param.sl_shor_price = 0
+ param.short_trailing_stop_price = 0
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "反手平空:",
+ "平仓价格:",
+ data['AskPrice1'] + param.py,
+ "堆积数:",
+ trade_df["dj"].iloc[-1],
+ )
+ self.save_to_csv(instrument_id)
+
+ # 发送邮件
+ # text = f"平空交易: 交易品种为{data['InstrumentID']}, 交易时间为{trade_df['datetime'].iloc[-1]}, 反手平空的平仓价格为{data['AskPrice1']+param.py}, 交易手数位{param.Lots}"
+ text = f"C_S_T: ID:{option_sell_symbol}, datetime:{trade_df['datetime'].iloc[-1]}, C_S_T_Price:{data['AskPrice1'] + param.py}, T_Lots:{param.Lots}"
+ send_mail(text)
+
+ # 开多
+ if param.pos == 0 and 开多组合:
+ print("trade_df_last:",trade_df['symbol'].iloc[-1])
+ option_buy_symbol,option_buy_price= get_otm_option(data["InstrumentID"].decode(), 'C')
+ print("买入看涨平值期权为:", option_buy_symbol, ",价格为:", option_buy_price)
+ print(
+ "开多: ",
+ "ExchangeID: ",
+ data["ExchangeID"],
+ "InstrumentID",
+ option_buy_symbol,
+ "AskPrice1",
+ option_buy_price + param.py,
+ )
+ # 开多
+ self.insert_order(
+ data["ExchangeID"],
+ option_buy_symbol,
+ option_buy_price + param.py,
+ param.Lots,
+ b"0",
+ b"0",
+ )
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "多头开仓",
+ "开仓价格:",
+ option_buy_price + param.py,
+ "堆积数:",
+ trade_df["dj"].iloc[-1],
+ )
+ param.pos = 1
+ param.long_trailing_stop_price = data["AskPrice1"]
+ param.sl_long_price = data["AskPrice1"]
+ self.save_to_csv(instrument_id)
+
+ # 发送邮件
+ text = f"O_L_T ID:{option_buy_symbol}, datetime:{trade_df['datetime'].iloc[-1]}, O_L_T_Price:{option_buy_price + param.py}, T_Lots:{param.Lots}"
+ send_mail(text)
+
+ # 平多
+ if param.pos > 0 and 平多组合:
+ print('option_buy_symbol',option_buy_symbol)
+ # close_buy_price = get_otm_pirce(data["InstrumentID"].decode(), option_buy_symbol)
+ # print('close_buy_price:',close_buy_price)
+ print(
+ "平多: ",
+ "ExchangeID: ",
+ data["ExchangeID"],
+ "InstrumentID",
+ option_buy_symbol,
+ "BidPrice1",
+ 1,#close_buy_price - param.py,
+ )
+ # 平多
+ self.insert_order(
+ data["ExchangeID"],
+ option_buy_symbol,
+ 1,#close_buy_price - param.py,
+ param.Lots,
+ b"1",
+ b"1",
+ )
+ self.insert_order(
+ data["ExchangeID"],
+ option_buy_symbol,
+ 1,#close_buy_price - param.py,
+ param.Lots,
+ b"1",
+ b"3",
+ )
+
+ param.pos = 0
+ param.long_trailing_stop_price = 0
+ param.sl_long_price = 0
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "反手平多",
+ "平仓价格:",
+ data["BidPrice1"] - param.py,
+ "堆积数:",
+ trade_df["dj"].iloc[-1],
+ )
+ self.save_to_csv(instrument_id)
+
+ # 发送邮件
+ # text = f"平多交易: 交易品种为{data['InstrumentID']}, 交易时间为{trade_df['datetime'].iloc[-1]}, 反手平多的平仓价格{data['BidPrice1']-param.py}, 交易手数位{param.Lots}"
+ text = f"C_L_T: ID:{option_buy_symbol}, datetime:{trade_df['datetime'].iloc[-1]}, C_L_T_Price:{data['BidPrice1'] - param.py}, T_Lots:{param.Lots}"
+ send_mail(text)
+
+ # 开空
+ if param.pos == 0 and 开空组合:
+ option_sell_symbol,option_sell_price = get_otm_option(data["InstrumentID"].decode(), 'P')
+ print("买入看跌平值期权为:", option_sell_symbol, ",价格为:", option_sell_price)
+ print(
+ "开空: ",
+ "ExchangeID: ",
+ data["ExchangeID"],
+ "InstrumentID",
+ option_sell_symbol,
+ "BidPrice1",
+ data["BidPrice1"],
+ )
+ # 开空
+ self.insert_order(
+ data["ExchangeID"],
+ option_sell_symbol,
+ option_sell_price - param.py,
+ param.Lots,
+ b"1",
+ b"0",
+ )
+ print(
+ "datetime+sig: ",
+ trade_df["datetime"].iloc[-1],
+ "空头开仓",
+ "开仓价格:",
+ option_sell_price - param.py,
+ "堆积数:",
+ trade_df["dj"].iloc[-1],
+ )
+ param.pos = -1
+ param.short_trailing_stop_price = data["BidPrice1"]
+ param.sl_shor_price = data["BidPrice1"]
+ self.save_to_csv(instrument_id)
+
+ # 发送邮件
+ text = f"O_S_T: ID:{option_sell_symbol}, datetime:{trade_df['datetime'].iloc[-1]}, O_S_T_Price:{option_sell_price - param.py}, T_Lots:{param.Lots}"
+ send_mail(text)
+
+ print(trade_df)
+ symbol_label = trade_df["symbol"].iloc[-1]
+ trade_df.to_csv(f"trade_df_{symbol_label}.csv", index=False, encoding='utf-8')
+ # with open('trade_df.txt', 'w', encoding='utf-8') as file:
+ # print(trade_df, file=file)
+ print("------------------------------------------------")
+ # print(trade_df.iloc[0])
+ # print(trade_df.iloc[-1])
+ param.cont_df = len(trade_df)
+ except queue.Empty:
+ # print(f"当前合约队列为空,等待新数据插入。")
+ pass
+
+ # 将CTP推送的行情数据分发给对应线程队列去执行
+ def distribute_tick(self):
+ while True:
+ if self.status == 0:
+ data = None
+ while not self.md_queue.empty():
+ data = self.md_queue.get(block=False)
+ instrument_id = data["InstrumentID"].decode() # 品种代码
+ try:
+ self.queue_dict[instrument_id].put(
+ data, block=False
+ ) # 往对应合约队列中插入行情
+ # print(f"{instrument_id}合约数据插入。")
+ except queue.Full:
+ # 当某个线程阻塞导致对应队列容量超限时抛出异常,不会影响其他合约的信号计算
+ print(
+ f"{instrument_id}合约信号计算阻塞导致对应队列已满,请检查对应代码逻辑后重启。"
+ )
+ else:
+ time.sleep(1)
+
+ def start(self, param_dict):
+ threads = []
+ self.param_dict = param_dict
+
+ for symbol in param_dict.keys():
+ # folder_path = "traderdata"
+ # ofdata_file_path = os.path.join("traderdata", f"{str(symbol)}_ofdata.csv")
+ if os.path.exists(f"traderdata/{symbol}_ofdata.csv"):
+ columns = [
+ "price",
+ "Ask",
+ "Bid",
+ "symbol",
+ "datetime",
+ "delta",
+ "close",
+ "open",
+ "high",
+ "low",
+ "volume",
+ "dj",
+ ]
+ # import csv
+ # with open(f"traderdata/{symbol}_ofdata.csv", "r") as f:
+ # reader = csv.reader(f)
+ # for i, row in enumerate(reader, 1):
+ # if len(row) != 12:
+ # print(f"Line {i} has {len(row)} columns: {row}")
+ trade_dfs[symbol] = pd.read_csv(
+ f"traderdata/{symbol}_ofdata.csv", usecols=columns
+ )
+
+ else:
+ trade_dfs[symbol] = pd.DataFrame({})
+ self.queue_dict[symbol] = queue.Queue(
+ 20
+ ) # 为每个合约创建一个限制数为10的队列,当计算发生阻塞导致队列达到限制数时会抛出异常
+ t = threading.Thread(
+ target=self.cal_sig, args=(self.queue_dict[symbol],)
+ ) # 为每个合约单独创建一个线程计算开仓逻辑
+ threads.append(t)
+ t.start()
+ self.distribute_tick()
+ for t in threads:
+ t.join()
+
+
+def run_trader(
+ param_dict,
+ broker_id,
+ td_server,
+ investor_id,
+ password,
+ app_id,
+ auth_code,
+ md_queue=None,
+ page_dir="",
+ private_resume_type=2,
+ public_resume_type=2,
+):
+ my_trader = MyTrader(
+ broker_id,
+ td_server,
+ investor_id,
+ password,
+ app_id,
+ auth_code,
+ md_queue,
+ page_dir,
+ private_resume_type,
+ public_resume_type,
+ )
+ my_trader.start(param_dict)
+
+
+if __name__ == "__main__":
+ # 注意:运行前请先安装好algoplus,
+ # pip install AlgoPlus
+ # http://www.algo.plus/ctp/python/0103001.html
+
+ param_dict = {}
+
+ ## 交易一个品种,手动设置合约代码
+
+ # param_dict["TF2509"] = ParamObj(
+ # symbol="TF2509",
+ # Lots=1,
+ # py=5,
+ # trailing_stop_percent=0.01,
+ # fixed_stop_loss_percent=0.02,
+ # dj_X=8,
+ # delta=500,
+ # sum_delta=800,
+ # 失衡=3,
+ # 堆积=3,
+ # 周期="2min",
+ # )
+
+ ## 交易所有品种,自动设置合约代码,
+ # 交易指定品种时,symbols = ['IM','IC','ag']
+ # symbols = ['IM','IC','ag']
+ symbols = contacts_df['品种代码'].tolist()
+ # for i, symbol in enumerate(symbols, start=1):
+ # globals()[f'sb_{i}'] = get_main_contact_on_time(symbol, contacts_df)
+ # symbol = globals()[f'sb_{i}']
+ # # print("最终使用的主连代码:",symbol)
+ # param_dict[str(symbol)] = ParamObj(symbol=symbol.encode('ascii'),Lots=1,py=5,trailing_stop_percent=0.01,fixed_stop_loss_percent=0.02,dj_X=8,delta=500,sum_delta=800,失衡=3,堆积=3,周期="5min")
+ for i, symbol in enumerate(symbols, start=1):
+ if symbol in ['wr', 'RS' , 'bb', 'WH', 'fb', 'rr', 'PL']:
+ continue
+ elif symbol in ['IM',]: #symbol in ['IH', 'IF', 'IC', 'IM', 'au', 'sc']
+ globals()[f'sb_{i}'] = get_main_contact_on_time(symbol, contacts_df)
+ symbol = globals()[f'sb_{i}']
+ param_dict[str(symbol)] = ParamObj(symbol=symbol.encode('ascii'),Lots=1,py=5,trailing_stop_percent=0.02,fixed_stop_loss_percent=0.04,dj_X=8,delta=500,sum_delta=800,失衡=3,堆积=3,周期="2min")
+
+
+ ## 交易多个指定品种,自动设置合约代码,手动设置其他参数
+ # param_dict[symbol] = ParamObj(symbol=get_main_contact_on_time('IM', contacts_df),Lots=1,py=5,trailing_stop_percent=0.01,fixed_stop_loss_percent=0.02,dj_X=8,delta=500,sum_delta=800,失衡=3,堆积=3,周期="1min")
+ # param_dict[symbol] = ParamObj(symbol=get_main_contact_on_time('IC', contacts_df),Lots=1,py=5,trailing_stop_percent=0.02,fixed_stop_loss_percent=0.04,dj_X=8,delta=500,sum_delta=800,失衡=3,堆积=3,周期="1min")
+ # print(param_dict.keys())
+
+ # 用simnow模拟,不要忘记屏蔽下方实盘的future_account字典
+ # SIMULATE_SERVER = {
+ # '电信1': {'BrokerID': 9999, 'TDServer': "180.168.146.187:10201", 'MDServer': '180.168.146.187:10211', 'AppID': 'simnow_client_test', 'AuthCode': '0000000000000000'},
+ # '电信2': {'BrokerID': 9999, 'TDServer': "180.168.146.187:10202", 'MDServer': '180.168.146.187:10212', 'AppID': 'simnow_client_test', 'AuthCode': '0000000000000000'},
+ # '移动': {'BrokerID': 9999, 'TDServer': "218.202.237.33:10203", 'MDServer': '218.202.237.33:10213', 'AppID': 'simnow_client_test', 'AuthCode': '0000000000000000'},
+ # 'TEST': {'BrokerID': 9999, 'TDServer': "180.168.146.187:10130", 'MDServer': '180.168.146.187:10131', 'AppID': 'simnow_client_test', 'AuthCode': '0000000000000000'},
+ # 'N视界': {'BrokerID': 10010, 'TDServer': "210.14.72.12:4600", 'MDServer': '210.14.72.12:4602', 'AppID': '', 'AuthCode': ''},
+ # }
+ # BrokerID统一为:9999
+ # 支持上期所期权、能源中心期权、中金所期权、广期所期权、郑商所期权、大商所期权
+ # 第一组
+ # Trade Front:180.168.146.187:10201,Market Front:180.168.146.187:10211;【电信】(看穿式前置,使用监控中心生产秘钥)
+
+ # 第二组
+ # Trade Front:180.168.146.187:10202,Market Front:180.168.146.187:10212;【电信】(看穿式前置,使用监控中心生产秘钥)
+
+ # 第三组
+ # Trade Front:218.202.237.33:10203,Market Front:218.202.237.33:10213;【移动】(看穿式前置,使用监控中心生产秘钥)
+
+ # 用户注册后,默认的APPID为simnow_client_test,认证码为0000000000000000(16个0),默认开启终端认证,程序化用户可以选择不开终端认证接入。
+
+ future_account = get_simulate_account(
+ investor_id="227508", # simnow账户,注意是登录账户的ID,SIMNOW个人首页查看
+ password="Zj1234!@#%", # simnow密码
+ server_name="电信1", # 电信1、电信2、移动、TEST、N视界
+ subscribe_list=list(param_dict.keys()), # 合约列表
+ )
+ # future_account = get_simulate_account(
+ # investor_id="00033556", # simnow账户,注意是登录账户的ID,SIMNOW个人首页查看
+ # password="27138169", # simnow密码
+ # server_name="N视界", # 电信1、电信2、移动、TEST、N视界
+ # subscribe_list=list(param_dict.keys()), # 合约列表
+ # )
+ # 实盘用这个,不要忘记屏蔽上方simnow的future_account字典
+ # future_account = FutureAccount(
+ # broker_id='', # 期货公司BrokerID
+ # server_dict={'TDServer': "121.37.80.177:20002", 'MDServer': '121.37.80.177:20004'}, # TDServer为交易服务器,MDServer为行情服务器。服务器地址格式为"ip:port。"
+ # reserve_server_dict={}, # 备用服务器地址
+ # investor_id='1114', # 账户
+ # password='123456', # 密码
+ # app_id='', # 认证使用AppID
+ # auth_code='', # 认证使用授权码
+ # subscribe_list=list(param_dict.keys()), # 订阅合约列表
+ # md_flow_path='./log', # MdApi流文件存储地址,默认MD_LOCATION
+ # td_flow_path='./log', # TraderApi流文件存储地址,默认TD_LOCATION
+ # )
+
+ # 实盘用这个,不要忘记屏蔽上方simnow的future_account字典
+ # future_account = FutureAccount(
+ # broker_id='8888', # 期货公司BrokerID
+ # server_dict={'TDServer': "103.140.14.210:43205", 'MDServer': '103.140.14.210:43173'}, # TDServer为交易服务器,MDServer为行情服务器。服务器地址格式为"ip:port。"
+ # reserve_server_dict={}, # 备用服务器地址
+ # investor_id='155878', # 账户
+ # password='Zj82334475', # 密码
+ # app_id='vntech_vnpy_2.0', # 认证使用AppID
+ # auth_code='N46EKN6TJ9U7V06V', # 认证使用授权码
+ # subscribe_list=list(param_dict.keys()), # 订阅合约列表
+ # md_flow_path='./log', # MdApi流文件存储地址,默认MD_LOCATION
+ # td_flow_path='./log', # TraderApi流文件存储地址,默认TD_LOCATION
+ # )
+
+ print("开始", len(future_account.subscribe_list))
+ # 共享队列
+ share_queue = Queue(maxsize=200)
+
+ # 行情进程
+ md_process = Process(target=run_tick_engine, args=(future_account, [share_queue]))
+
+ # 交易进程
+ trader_process = Process(
+ target=run_trader,
+ args=(
+ param_dict,
+ future_account.broker_id,
+ future_account.server_dict["TDServer"],
+ future_account.investor_id,
+ future_account.password,
+ future_account.app_id,
+ future_account.auth_code,
+ share_queue, # 队列
+ future_account.td_flow_path,
+ ),
+ )
+
+ md_process.start()
+ trader_process.start()
+
+ md_process.join()
+ trader_process.join()