mirror of
https://github.com/midoks/mdserver-web.git
synced 2026-10-11 01:49:26 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da4a61b716 | ||
|
|
996c4474ec | ||
|
|
cdc05d8d98 | ||
|
|
52892ef5df | ||
|
|
ce0340689b | ||
|
|
58cdb0db95 | ||
|
|
c2c49d09e9 | ||
|
|
c9a2997cea | ||
|
|
94aba71ae4 | ||
|
|
b80c7bdc23 | ||
|
|
279231c08c | ||
|
|
ef47b839ad | ||
|
|
ffc8ae7d04 | ||
|
|
7d8269832f | ||
|
|
6ba516f66e | ||
|
|
53d4fcbd6d | ||
|
|
237ef10545 | ||
|
|
23650aa800 | ||
|
|
9998691524 | ||
|
|
bd61a953ad | ||
|
|
f53379829c | ||
|
|
04a4faba22 | ||
|
|
65d4369306 | ||
|
|
6ab650d498 | ||
|
|
2a06c108ac | ||
|
|
3bd31c55fd | ||
|
|
ea2fb6d4cd | ||
|
|
d4dccc8016 | ||
|
|
3cdc78af1f | ||
|
|
7784f0c929 | ||
|
|
a399d3d04a | ||
|
|
d261dd8fb4 | ||
|
|
273f475732 | ||
|
|
35cce62b9b | ||
|
|
7b5d2820ca | ||
|
|
46374cfb2d | ||
|
|
020bfb2abc | ||
|
|
d3ab317b34 | ||
|
|
da3dd52484 | ||
|
|
9514cd9cce | ||
|
|
654a66062a | ||
|
|
5ac5b13f9d | ||
|
|
7f39bf5c6e | ||
|
|
0e07a10dcf | ||
|
|
8cbedd00b8 | ||
|
|
d9d3d3fdb4 | ||
|
|
364c142f0a | ||
|
|
92c0177c44 | ||
|
|
6bdca8c37e | ||
|
|
fcb51e727a | ||
|
|
4523a10898 | ||
|
|
4e155c8039 | ||
|
|
8b9f861f30 | ||
|
|
3595ae6066 | ||
|
|
351d4a0ad6 | ||
|
|
c6e6771839 | ||
|
|
fb51e0fcd1 | ||
|
|
4fbf9c345d | ||
|
|
f3557e9acf | ||
|
|
cb7367264b | ||
|
|
2fb722edbf | ||
|
|
fce1608403 | ||
|
|
c6117cb599 | ||
|
|
c68e056267 | ||
|
|
4c9924580a | ||
|
|
058b3b8d62 | ||
|
|
1eb29acf7c | ||
|
|
48ac052de8 | ||
|
|
4863ff2a19 | ||
|
|
0b0fd8ea67 | ||
|
|
b8f4ed2767 | ||
|
|
732eba7d7d | ||
|
|
8b676f3c1b | ||
|
|
960ca4deca | ||
|
|
63ecec4edb | ||
|
|
c6f04aa26f | ||
|
|
8f29df66d5 | ||
|
|
2d9e62aedb | ||
|
|
eba511add2 | ||
|
|
e9e38d5224 | ||
|
|
b131ba8b93 | ||
|
|
a12046fe93 | ||
|
|
769f065bdf | ||
|
|
81f9ee5eca | ||
|
|
42fb44dd15 | ||
|
|
600bb8b516 | ||
|
|
8831d91cbe | ||
|
|
d7d734565c | ||
|
|
791e9bf156 | ||
|
|
284812b4e3 | ||
|
|
505d59bc77 | ||
|
|
02eda25aaf | ||
|
|
d19521810e | ||
|
|
f8d17724f9 | ||
|
|
6548125747 | ||
|
|
ef375619b6 | ||
|
|
fe89ae1846 | ||
|
|
4ccf142917 | ||
|
|
89fbeb85a2 | ||
|
|
62b3d15991 | ||
|
|
52a305f5cd | ||
|
|
658305ef28 | ||
|
|
19295e2832 | ||
|
|
719e8165f1 | ||
|
|
d5ba280654 | ||
|
|
00dc48b439 | ||
|
|
8cc87ba7a0 | ||
|
|
8b2f7ee188 | ||
|
|
167c551b89 | ||
|
|
b57968d106 | ||
|
|
68514b8eae | ||
|
|
88a8e35793 | ||
|
|
ec4dcb2720 | ||
|
|
f01b64edd5 | ||
|
|
ef0b2af84b | ||
|
|
90af2e0c3c | ||
|
|
344393244e | ||
|
|
96c75eb2d3 | ||
|
|
5594cbd1a8 | ||
|
|
cb87abf19a | ||
|
|
bf21399113 | ||
|
|
f0fc8a644b | ||
|
|
a668234f2a | ||
|
|
6e80450e37 | ||
|
|
5639be6713 | ||
|
|
d36da3dc59 | ||
|
|
e83c743908 | ||
|
|
3410103859 | ||
|
|
2685d9ebec | ||
|
|
c5574b3d93 | ||
|
|
832d65bac8 | ||
|
|
8394f43b3f | ||
|
|
c77ab16f3b | ||
|
|
2d10a3a69e | ||
|
|
90714961eb | ||
|
|
b2586adda0 | ||
|
|
d11dbbccb7 | ||
|
|
1390afae0e | ||
|
|
3ec2d5e757 | ||
|
|
cc0d0407af | ||
|
|
1e9e437b1c | ||
|
|
eae75e24f9 | ||
|
|
045d672d68 | ||
|
|
caeff7de55 | ||
|
|
b2785070bc | ||
|
|
545dbeecca | ||
|
|
b4fa69642c | ||
|
|
f84fcc035c | ||
|
|
f556eb9607 | ||
|
|
b8aa2ffc38 | ||
|
|
2be79ead78 | ||
|
|
c900048329 | ||
|
|
b7850eaa29 | ||
|
|
ec5a5c8d15 | ||
|
|
94c51196cf | ||
|
|
35c18c807f | ||
|
|
da3d44fc2a | ||
|
|
b9b9ab9006 | ||
|
|
5df23c79e7 | ||
|
|
ee4d49752d | ||
|
|
d26bf25359 | ||
|
|
b1edc7b2fb | ||
|
|
459fb086d0 | ||
|
|
7872391c54 | ||
|
|
c2f4171ee9 | ||
|
|
9fdfa1c22f | ||
|
|
8ad3018fe7 | ||
|
|
64883aa424 | ||
|
|
6a41f68289 | ||
|
|
ffd96b03e5 | ||
|
|
961dd5a56e | ||
|
|
f5cb8ba23d | ||
|
|
6bfe9ea2af | ||
|
|
134b9ce2f2 | ||
|
|
54582fb676 | ||
|
|
2e9864be2a | ||
|
|
73f01522ed | ||
|
|
73a67ebdd6 | ||
|
|
92c000f63e | ||
|
|
665d278b52 | ||
|
|
9393775e90 | ||
|
|
fe441c7afe | ||
|
|
c865ff433b | ||
|
|
7eb729e809 | ||
|
|
f667fbff55 | ||
|
|
7c6b5bb200 | ||
|
|
d00ee1a35a | ||
|
|
7364e19051 | ||
|
|
97263ba55d | ||
|
|
a9cccbb85f | ||
|
|
732bdd5a1c | ||
|
|
2934d8fd7d | ||
|
|
992555e80e | ||
|
|
4a1348403a | ||
|
|
6353a4dc58 | ||
|
|
9e45f493c6 | ||
|
|
66fc8c43db | ||
|
|
e4761d52bc | ||
|
|
893eccfa07 | ||
|
|
fc3dc8276a | ||
|
|
9ab5e3354d | ||
|
|
ec607798b5 | ||
|
|
b01a2d15de | ||
|
|
7e3f40cb20 | ||
|
|
7a38224198 | ||
|
|
5a4f1c3cce | ||
|
|
f5287e42ca | ||
|
|
3ec02b8ead | ||
|
|
d25e4b3093 | ||
|
|
0b81a1402e | ||
|
|
891ba787e1 | ||
|
|
85a85a83e4 | ||
|
|
449b4e6882 | ||
|
|
fba98f5ed6 | ||
|
|
fda19b6103 | ||
|
|
d6d2325506 | ||
|
|
d4354f1a65 | ||
|
|
7ebe82cf9c | ||
|
|
3029f8d263 | ||
|
|
00d3aaeb22 | ||
|
|
a8bd5cbbeb | ||
|
|
48bcf77deb | ||
|
|
a24f3b72bd | ||
|
|
e916e134dc | ||
|
|
b9b18b063c | ||
|
|
753460c594 | ||
|
|
e6f8e3e6e3 | ||
|
|
952c6da41c | ||
|
|
adb1d0b26d | ||
|
|
15efadfa9e | ||
|
|
f09cc4996b | ||
|
|
01a81156cc | ||
|
|
89ee88d734 | ||
|
|
23880fdb91 | ||
|
|
10628d1c69 | ||
|
|
897e54cb86 | ||
|
|
85da840688 | ||
|
|
10348168eb | ||
|
|
4a2570f96a | ||
|
|
65a4635b09 | ||
|
|
3a5264b783 | ||
|
|
b5866f2a90 | ||
|
|
a35913ab78 | ||
|
|
cc27602454 | ||
|
|
8920a1132c | ||
|
|
536c76b000 | ||
|
|
d054f7f9cd | ||
|
|
16bab8f0fa | ||
|
|
b7aa774eb7 | ||
|
|
cef521390a | ||
|
|
f4cda4ec36 | ||
|
|
628b65a569 | ||
|
|
142f0fc6c9 | ||
|
|
fe0f3f63a2 | ||
|
|
dd3f5fdb0c | ||
|
|
b7dd2fa05d | ||
|
|
1cf471704c | ||
|
|
54f8fdeec2 | ||
|
|
7ea45e4c0a | ||
|
|
5b93e616dc | ||
|
|
2c4176de32 | ||
|
|
114dcea023 | ||
|
|
3c6b5f3573 | ||
|
|
258b9b610f | ||
|
|
717d2b1ad0 | ||
|
|
3cdd7027d6 | ||
|
|
c5dc755cb6 | ||
|
|
89d13494c0 | ||
|
|
6fe6f55df8 | ||
|
|
2f38ecf505 | ||
|
|
c4ba4b4702 | ||
|
|
eab13531d4 | ||
|
|
62d78dbed8 | ||
|
|
f5a4f1b275 | ||
|
|
ebe761b1cf | ||
|
|
6e754f2f7b | ||
|
|
39cd3d1f4d | ||
|
|
778d595e6e | ||
|
|
5f4622fdcd | ||
|
|
9664609bbd | ||
|
|
aa92699562 | ||
|
|
edc45d5d3b | ||
|
|
ed732232f2 | ||
|
|
db04b3f98a | ||
|
|
fb7c5d9d47 | ||
|
|
7d7df74500 | ||
|
|
909ba46f66 | ||
|
|
df84ad25d6 | ||
|
|
178cfb17f3 | ||
|
|
fcef7ec3ca | ||
|
|
c1e758c9df | ||
|
|
a06c98d52f | ||
|
|
bf49153973 | ||
|
|
f40cfb7d25 | ||
|
|
dcd4527c75 | ||
|
|
225ea91f43 | ||
|
|
172ca05efa | ||
|
|
b3e2c8d9df | ||
|
|
934327052d | ||
|
|
24a94b4965 | ||
|
|
c5385541a7 | ||
|
|
5be2d5eb22 | ||
|
|
3688cf6145 | ||
|
|
1328699da0 | ||
|
|
1b0e42c79e | ||
|
|
6ca80e3d8c | ||
|
|
d992541a78 | ||
|
|
d13adc707e | ||
|
|
90d00d0d9a | ||
|
|
fbc9685b3c | ||
|
|
5507cfdb7b | ||
|
|
2c8de3e7be | ||
|
|
cbba4adb19 | ||
|
|
b1e36d8a71 | ||
|
|
af6403d752 | ||
|
|
232db27a15 | ||
|
|
e04fc75be0 | ||
|
|
5fd4c84a18 | ||
|
|
ce65fb9234 | ||
|
|
d8d8c630a5 | ||
|
|
08558b1581 | ||
|
|
21637f170f | ||
|
|
5013ffd38d | ||
|
|
f471368d21 | ||
|
|
8ddf0168fd | ||
|
|
5a4ba1297f | ||
|
|
0608c531ed | ||
|
|
5d242de4e2 | ||
|
|
b7efc0394b | ||
|
|
16681d0193 | ||
|
|
31ea0865d1 | ||
|
|
602ad3390c | ||
|
|
d8891a1767 | ||
|
|
2400a0b353 | ||
|
|
de0248d59a | ||
|
|
3eb72d23f8 | ||
|
|
c0606112b2 | ||
|
|
5decb77f1e | ||
|
|
a04b9487f1 | ||
|
|
8835cd4ab5 | ||
|
|
b17452a949 | ||
|
|
2072eee3a4 | ||
|
|
4321c0e6b3 | ||
|
|
f1f98b2624 | ||
|
|
ec37cee26c | ||
|
|
a8adaf7e5a | ||
|
|
84c763c39e | ||
|
|
33e86c4f79 | ||
|
|
c00f93a1d3 | ||
|
|
81100f21b4 | ||
|
|
b8a720919f | ||
|
|
cba37ae2fc | ||
|
|
5d15cbd4ea | ||
|
|
5f87606cf7 | ||
|
|
9b22efe3d4 | ||
|
|
675c3b27aa | ||
|
|
ddd9e70d05 | ||
|
|
30ed2049e4 | ||
|
|
b8092ed6dd | ||
|
|
c80c0962dd | ||
|
|
63c223b237 | ||
|
|
07aa49accc | ||
|
|
c595c541fb | ||
|
|
e6ec8b65ce | ||
|
|
bc8c455094 | ||
|
|
507820d25e | ||
|
|
35e08295ce | ||
|
|
9e581d8608 | ||
|
|
dcb8e69aac | ||
|
|
617b354d03 | ||
|
|
76b095b344 | ||
|
|
5ff05e8025 | ||
|
|
b973a16247 | ||
|
|
0cb5d7507f | ||
|
|
2851ebf126 | ||
|
|
2f5b1bb205 | ||
|
|
5528336698 | ||
|
|
4eda80baa8 | ||
|
|
02918d4bcd | ||
|
|
8315e3ea96 | ||
|
|
cae4a2ee13 | ||
|
|
26762eee73 | ||
|
|
2f05fb12a0 | ||
|
|
befdf2e1d3 | ||
|
|
aac7b98913 | ||
|
|
baa9b5e6bb | ||
|
|
99c919811a | ||
|
|
ac7a4e739e | ||
|
|
f8936aa7bc | ||
|
|
6933c44571 | ||
|
|
dfb2f4b3af | ||
|
|
6601aebddf | ||
|
|
4d2ca9587d | ||
|
|
694a5ceab2 | ||
|
|
e522093f6c | ||
|
|
feefa2e90a | ||
|
|
3709569cd8 | ||
|
|
052b4870bf | ||
|
|
5cb413a911 | ||
|
|
80e77dda3f | ||
|
|
7a3d554d6c | ||
|
|
ab0fb063cf | ||
|
|
caec558bea | ||
|
|
2d44b62fef | ||
|
|
f9cc72ba4a | ||
|
|
f8544d0600 | ||
|
|
c96d361b4d | ||
|
|
fa5305f332 | ||
|
|
b996bdea93 | ||
|
|
1bae6538f7 | ||
|
|
5673964de4 | ||
|
|
25623170bb | ||
|
|
2aa5739cd8 | ||
|
|
bdff56843f | ||
|
|
6e563e6d74 | ||
|
|
334a3fb6fe | ||
|
|
a1d130a674 | ||
|
|
e14f2ea247 | ||
|
|
3f478eef8e | ||
|
|
e929274b51 | ||
|
|
526c0301f6 | ||
|
|
7e7a19829d | ||
|
|
bc3854b4c4 | ||
|
|
193bf20a3e | ||
|
|
4cc5194939 | ||
|
|
46a525ae09 | ||
|
|
6d006a357e | ||
|
|
fad73b0922 | ||
|
|
d2d2d4658e | ||
|
|
84e6ce2b48 | ||
|
|
b91a9963e2 | ||
|
|
25fcf93185 | ||
|
|
26bb2005c1 | ||
|
|
ee58805ae9 | ||
|
|
1bfc02a892 | ||
|
|
0c7dc4dbee | ||
|
|
42e9859ccb | ||
|
|
bf9b6b6046 | ||
|
|
c10eac5190 | ||
|
|
d01ccf1208 | ||
|
|
703b9be83e | ||
|
|
e0fea93983 | ||
|
|
84ceef47cb | ||
|
|
255ed5944f | ||
|
|
0dc2b635ae | ||
|
|
ebd1cfe203 | ||
|
|
43b9297476 | ||
|
|
aa2f4a0167 | ||
|
|
ad31d046d4 | ||
|
|
a67ad5a7db | ||
|
|
90d4fb245a | ||
|
|
19f55fde6e | ||
|
|
227be5258e | ||
|
|
913190bfeb | ||
|
|
0b0097f367 | ||
|
|
97ac8264f2 | ||
|
|
d9cca6ccc9 | ||
|
|
28d0b1fd25 | ||
|
|
cbcfe4cb9d | ||
|
|
70c14d3369 | ||
|
|
0bcf7edab5 | ||
|
|
0f410c29f7 | ||
|
|
b451a6da25 | ||
|
|
e6c7bc1544 | ||
|
|
d1ff8f1cac | ||
|
|
d967100c14 | ||
|
|
8461c20e40 | ||
|
|
52924f06d2 | ||
|
|
fad4e7bd7d | ||
|
|
666ee1a083 | ||
|
|
8f05e47363 | ||
|
|
67d8abf7bf | ||
|
|
30842a6659 | ||
|
|
3ee500d339 | ||
|
|
c5ade905b0 | ||
|
|
8e6784bf21 | ||
|
|
4eacbca674 | ||
|
|
149fe72324 | ||
|
|
43f752afbc | ||
|
|
19efde1fda | ||
|
|
98f267057b | ||
|
|
4f98894ce4 | ||
|
|
8cb225779e | ||
|
|
57ff303c39 | ||
|
|
29cae6ae6c | ||
|
|
e0c761bdf0 | ||
|
|
6ee1a8dc52 | ||
|
|
db69532dde | ||
|
|
d4c47c16e4 | ||
|
|
a28733521b | ||
|
|
b957595944 | ||
|
|
b985d42336 | ||
|
|
9d467a13bb | ||
|
|
4629d39980 | ||
|
|
11d245dadf | ||
|
|
bb72b53f19 | ||
|
|
3bef89d625 | ||
|
|
792b1348c1 | ||
|
|
7399cd7d45 | ||
|
|
d6347f6d4a | ||
|
|
3b5585193f | ||
|
|
5efa218247 | ||
|
|
e5d312dbf9 | ||
|
|
38e1c07e0b | ||
|
|
809c9ea237 | ||
|
|
08a41f039b | ||
|
|
3dd14307d0 | ||
|
|
4122c11372 | ||
|
|
9360a9cd68 | ||
|
|
24746dab08 | ||
|
|
8879cba15d | ||
|
|
1b38c60f57 | ||
|
|
7c944b27dd | ||
|
|
523e54eeb2 | ||
|
|
7b08a3f1bc | ||
|
|
018826e1a3 | ||
|
|
a36b0b78b3 | ||
|
|
19964d39c1 | ||
|
|
31fd04456b | ||
|
|
76cf2e8fa5 | ||
|
|
f6b61c935e | ||
|
|
c736a04b4f | ||
|
|
14432fd6f9 | ||
|
|
047d84acb5 | ||
|
|
8de4e6a0ee | ||
|
|
90fbb0e9ac | ||
|
|
041f36ba00 | ||
|
|
9e6cc873cb | ||
|
|
20a6ced7b5 | ||
|
|
34d9682cc1 | ||
|
|
4ac797406a | ||
|
|
bb3cdb5d85 | ||
|
|
245923d825 | ||
|
|
41c048594e | ||
|
|
1360d2d066 | ||
|
|
6286f03432 | ||
|
|
c7a99cc61b | ||
|
|
e4aae5147c | ||
|
|
2a2e37ca97 | ||
|
|
a1489959d1 | ||
|
|
4561e37167 | ||
|
|
9fa04c9a90 | ||
|
|
aa4951b7fc | ||
|
|
53d08c261c | ||
|
|
74058243c6 | ||
|
|
62223d53be | ||
|
|
06d33758eb | ||
|
|
af85245b06 | ||
|
|
01d8ebbd40 | ||
|
|
1c1cd1f434 | ||
|
|
aaa801964c | ||
|
|
01b490dff1 | ||
|
|
1be1441352 | ||
|
|
0b47b56fb5 | ||
|
|
0a1251ad0b | ||
|
|
a506bebc94 | ||
|
|
26147acfa0 | ||
|
|
f1d0be9f61 | ||
|
|
45c1e9d28c | ||
|
|
df3db9e632 | ||
|
|
8c1e672da7 | ||
|
|
4bf5e68a9d | ||
|
|
0d4b157b1b | ||
|
|
5fd207c84c | ||
|
|
6829baba67 | ||
|
|
b9983929fc | ||
|
|
d540e7497b | ||
|
|
0e0cc248fa | ||
|
|
bcf6808e49 | ||
|
|
e2e27765ee | ||
|
|
f3b07bda65 | ||
|
|
f555f1084f | ||
|
|
212be88efe | ||
|
|
c131f0ecda | ||
|
|
e3afb4b481 | ||
|
|
ca61f70d37 | ||
|
|
90850ebfa3 | ||
|
|
771a6d6e08 | ||
|
|
55a6d17bc8 | ||
|
|
649990f2e1 | ||
|
|
48468a1c2d | ||
|
|
dd685b664d | ||
|
|
36898e3e11 | ||
|
|
737450ba6e | ||
|
|
0f63145319 | ||
|
|
6a942db113 | ||
|
|
6119adbe2d | ||
|
|
c31af5cbf4 | ||
|
|
784629f11b | ||
|
|
a581b7cdef | ||
|
|
d6918621fa | ||
|
|
dc54ec241e | ||
|
|
2f94674479 | ||
|
|
10b94514a7 | ||
|
|
f5eacd6016 | ||
|
|
7383317f0b | ||
|
|
26fa9378f5 | ||
|
|
9f37033e5c | ||
|
|
dc4ff9b694 | ||
|
|
3c9dc0a127 | ||
|
|
2afbe1c6fd | ||
|
|
309b5b9563 | ||
|
|
f64f0ebf4e | ||
|
|
dd646a155b | ||
|
|
6db38826d8 | ||
|
|
a18fd9b05c | ||
|
|
611c7b7b21 | ||
|
|
3961e95a5f | ||
|
|
d6e6a53301 | ||
|
|
e500026017 | ||
|
|
84632b6296 | ||
|
|
9b79e93967 | ||
|
|
88e7ad0411 | ||
|
|
d0e96eab91 | ||
|
|
6ac78d9eb6 | ||
|
|
5d830ae2f8 | ||
|
|
178f844420 | ||
|
|
adf3df510e | ||
|
|
f58c03c429 | ||
|
|
15f9edc5c7 | ||
|
|
07f77024ab | ||
|
|
5b8ff7e612 | ||
|
|
1182e7405e | ||
|
|
9c6d8ef8e6 | ||
|
|
eb807fb205 | ||
|
|
0602fb5569 | ||
|
|
1dd78c3ea1 | ||
|
|
3175de4576 | ||
|
|
dfd1f172bb | ||
|
|
0f6cf01fd3 | ||
|
|
3e9e7922b4 | ||
|
|
eb480c8838 | ||
|
|
a9bc4fe914 | ||
|
|
3bf1573e36 | ||
|
|
de6fd60f41 | ||
|
|
cf72ebdfd8 | ||
|
|
6e44452807 | ||
|
|
88c6abb53c | ||
|
|
27fd93f590 | ||
|
|
8b391f03c2 | ||
|
|
ea3033e22a | ||
|
|
c88dff350e | ||
|
|
73e1a312b5 | ||
|
|
28c2b6dc77 | ||
|
|
0348cc55c6 | ||
|
|
e439918e05 | ||
|
|
a5a7f405b3 | ||
|
|
4cd620fe2b | ||
|
|
b1826a60d2 | ||
|
|
50ec1c59d2 | ||
|
|
36d604ec49 | ||
|
|
7d6407afcf | ||
|
|
92358db548 | ||
|
|
161c6a69b1 | ||
|
|
215e053b6d | ||
|
|
e8a99af37f | ||
|
|
de258a2568 | ||
|
|
728db39ab7 | ||
|
|
d885870302 | ||
|
|
2c87079686 | ||
|
|
e89785c1d2 | ||
|
|
38f8eff1c5 | ||
|
|
077394ec99 | ||
|
|
7c23f85f75 | ||
|
|
20c1ceab92 | ||
|
|
e5585caa09 | ||
|
|
a11b49befe | ||
|
|
58027e4607 | ||
|
|
1285641746 | ||
|
|
843ac18d2d | ||
|
|
4383a202f2 | ||
|
|
cda412f6a9 | ||
|
|
f26b84c799 | ||
|
|
2fc3fd3ef1 | ||
|
|
57d04de8a5 | ||
|
|
3e43c22a42 | ||
|
|
ea643b58d8 | ||
|
|
d366490fe5 | ||
|
|
83093f4c85 | ||
|
|
99fcfa5223 | ||
|
|
f6786b367b | ||
|
|
eb373af6ed | ||
|
|
037e7e907b | ||
|
|
80844562e9 | ||
|
|
2a935845bf | ||
|
|
3acbad4606 | ||
|
|
1973f6a2b0 | ||
|
|
85e6a678dd | ||
|
|
798c52f23d | ||
|
|
aee405f9e2 | ||
|
|
f82ff2d482 | ||
|
|
d9d9721249 | ||
|
|
8ef84763c6 | ||
|
|
c4f6db4431 | ||
|
|
60a7e7f885 | ||
|
|
7e3c36cd27 | ||
|
|
5304abcd00 | ||
|
|
19d94893cb | ||
|
|
485424b5c9 | ||
|
|
a8af7bed62 | ||
|
|
f63e7df28d | ||
|
|
7e81da6741 | ||
|
|
0c8e69c252 | ||
|
|
da8881dd1e | ||
|
|
d10c0a2fb9 | ||
|
|
34ed4b2f25 | ||
|
|
a109b148f6 | ||
|
|
65c7eeb9c9 | ||
|
|
e45b9b4101 | ||
|
|
8c9bfd7e2d | ||
|
|
208e418bba | ||
|
|
05278bdacb | ||
|
|
5b11b35d5d | ||
|
|
9f5844c4de | ||
|
|
ae888e6d48 | ||
|
|
f5b4af6e38 | ||
|
|
84035e0cb1 | ||
|
|
6d5c3e81c8 | ||
|
|
16db84793d | ||
|
|
8b8a63c352 | ||
|
|
698cf0d263 | ||
|
|
6385d49eeb | ||
|
|
43e69909cd | ||
|
|
aeb0f866c8 | ||
|
|
3e5c005f25 | ||
|
|
bde27382b7 | ||
|
|
c17df789de | ||
|
|
ca6a84fec4 | ||
|
|
1c952ca4cd | ||
|
|
c2c7073cd9 | ||
|
|
6c84be1a82 | ||
|
|
16c90290f9 | ||
|
|
cf707df5e0 | ||
|
|
078f1b763b | ||
|
|
6060faa3ac | ||
|
|
5059157515 | ||
|
|
49924c6b2b | ||
|
|
6ff1781372 | ||
|
|
29fd4d5220 | ||
|
|
484dc13977 | ||
|
|
59fc82c788 | ||
|
|
9a4c9154f1 | ||
|
|
9949bb2493 | ||
|
|
07fc6ac8c4 | ||
|
|
6a93c3e90f | ||
|
|
7bd88335c4 | ||
|
|
9893e3bac2 | ||
|
|
946e886799 | ||
|
|
b84301debc | ||
|
|
cb5c95ce03 | ||
|
|
c0a7e1ddba | ||
|
|
220af054bb | ||
|
|
a9634f0249 | ||
|
|
fd77048d4b | ||
|
|
40cb632c80 | ||
|
|
2313af825e | ||
|
|
762efdaa9a | ||
|
|
b58cafb47a | ||
|
|
308d909e72 | ||
|
|
c88eb37b1a | ||
|
|
e3189d39f4 | ||
|
|
de351c7473 | ||
|
|
9d08b7de34 | ||
|
|
5cf0745da0 | ||
|
|
10009b65be | ||
|
|
80c1c0b8b4 | ||
|
|
9169d8899f | ||
|
|
5166e05dbe | ||
|
|
e6e9dcb48c | ||
|
|
712550cec8 | ||
|
|
f9061088fe | ||
|
|
c53390d038 | ||
|
|
6fb93b19c9 | ||
|
|
e07e8391e1 | ||
|
|
ccd8c056b1 | ||
|
|
32ee0cf14c | ||
|
|
9a6c02dd9a | ||
|
|
3dd77715d8 | ||
|
|
0f99cdb115 | ||
|
|
aecd0726e5 | ||
|
|
258b3495a1 | ||
|
|
eb52d1a79a | ||
|
|
1ad16b2b94 | ||
|
|
fc28136936 | ||
|
|
575f3c9ce4 | ||
|
|
7c9ca83b46 | ||
|
|
0d6c4010ff | ||
|
|
cb6d9762d5 | ||
|
|
a4414d2083 | ||
|
|
5bcae1b00c | ||
|
|
5c01f36fe7 | ||
|
|
84b7d1123e | ||
|
|
b819f345cf | ||
|
|
5278116f49 | ||
|
|
af36366a89 | ||
|
|
cabba7e2d2 | ||
|
|
ef6cc77dc2 | ||
|
|
4b6de5cd9f | ||
|
|
403e35c6c5 | ||
|
|
e1dbdc5886 | ||
|
|
9e0a64a51f | ||
|
|
971cc3a42a | ||
|
|
1c109ee86f | ||
|
|
b84106ae70 | ||
|
|
05881be9b9 | ||
|
|
06a7b3da19 | ||
|
|
9d77f66e47 | ||
|
|
85afff4848 | ||
|
|
8498eaa16d | ||
|
|
1e78341559 | ||
|
|
e4ed5a1d19 | ||
|
|
7b247d1e6f | ||
|
|
f98a7298c5 | ||
|
|
8e4e9ddf62 | ||
|
|
aea7fbe385 | ||
|
|
392442b7b7 | ||
|
|
cce3102beb | ||
|
|
fdf2e3854e | ||
|
|
95011544c1 | ||
|
|
fc97eb1c14 | ||
|
|
fc6ec859fe | ||
|
|
582ea54ed5 | ||
|
|
6caedb5b06 | ||
|
|
307135d495 | ||
|
|
f786be41e7 | ||
|
|
e18ae543ee | ||
|
|
ab606e0629 | ||
|
|
b6cda24231 | ||
|
|
ad4714418d |
+4
-2
@@ -176,7 +176,6 @@ plugins/op_auth
|
||||
plugins/l2tp
|
||||
plugins/openlitespeed
|
||||
plugins/tamper_proof
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/zimg
|
||||
plugins/bk_demo
|
||||
plugins/mail
|
||||
@@ -189,7 +188,10 @@ plugins/tidb
|
||||
plugins/goedge-admin
|
||||
plugins/goedge-node
|
||||
plugins/goedge-happy
|
||||
plugins/dztasks
|
||||
|
||||
/logs
|
||||
/data
|
||||
/plugins/flexcdn
|
||||
/plugins/tools
|
||||
/plugins/choose-linux-python
|
||||
*.md5
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
* SSH终端工具
|
||||
* 面板收藏功能
|
||||
* 网站子目录绑定
|
||||
* 网站备份功能
|
||||
* 插件方式管理
|
||||
|
||||
@@ -41,7 +40,7 @@ Talk is cheap, show me the code. -- linus
|
||||
|
||||
### 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
|
||||
- [虚位以待](https://bbs.midoks.icu)
|
||||
|
||||
### 主要插件介绍
|
||||
|
||||
@@ -49,8 +48,9 @@ Talk is cheap, show me the code. -- linus
|
||||
* PHP[53-84] - PHP是世界上最好的编程语言。
|
||||
* MySQL - 一种关系数据库管理系统。
|
||||
* MariaDB - 是MySQL的一个重要分支。
|
||||
* MySQL[APT/YUM] - 一种关系数据库管理系统。
|
||||
* MySQL[community] - 一种关系数据库管理系统。
|
||||
* MongoDB - 一种非关系NOSQL数据库管理系统。
|
||||
* PostgreSQL - 功能强大的开源数据库。
|
||||
* phpMyAdmin - 著名Web端MySQL管理工具。
|
||||
* Memcached - 一个高性能的分布式内存对象缓存系统。
|
||||
* Redis - 一个高性能的KV数据库。
|
||||
@@ -94,8 +94,6 @@ PHP[72-84]支持phpMyAdmin[5.2.1]
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
- [ZZZ评测](https://www.zzzvps.com/)
|
||||
|
||||
| 服务商 | LOGO | 推广地址 | 优惠码 |
|
||||
| ------------- |----------|-----------|-------|
|
||||
| digitalvirt |[](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
|
||||
@@ -187,12 +185,23 @@ curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev
|
||||
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
|
||||
```
|
||||
|
||||
### 旧版安装
|
||||
### 旧版安装/更新
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
|
||||
```
|
||||
|
||||
### 偶然问题
|
||||
- 在安装/更新时,突然出现python插件pip,psutil,可尝试如下解决方案
|
||||
```
|
||||
cd /www/server/mdserver-web rm -rf lib
|
||||
cd /www/server/mdserver-web && rm -rf lib64
|
||||
cd /www/server/mdserver-web && rm -rf bin
|
||||
cd /www/server/mdserver-web && rm -rf include
|
||||
|
||||
mw update/mw update_dev
|
||||
```
|
||||
|
||||
### 捐赠地址 USDT(TRC20)
|
||||
|
||||
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
|
||||
|
||||
@@ -82,17 +82,26 @@ mw_start_bgtask(){
|
||||
|
||||
mw_stop()
|
||||
{
|
||||
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
APP_LIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
APP_LIST=($APP_LIST)
|
||||
for p in ${APP_LIST[@]}
|
||||
do
|
||||
kill -9 $i > /dev/null 2>&1
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
PIDS=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'`
|
||||
for p in $PIDS
|
||||
TASK_LIST=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'`
|
||||
TASK_LIST=($TASK_LIST)
|
||||
for p in ${TASK_LIST[@]}
|
||||
do
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
zzpids=`ps -A -o stat,ppid,pid | grep -e '^[Zz]' | awk '{print $2}'`
|
||||
zzpids=($zzpids)
|
||||
for p in ${zzpids[@]}
|
||||
do
|
||||
kill -9 ${p} > /dev/null 2>&1
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
- 面板相关命令
|
||||
|
||||
```
|
||||
mw start | 启动面板服务器
|
||||
mw stop | 停止面板服务器
|
||||
mw restart | 重启面板服务器
|
||||
mw default | 显示登录信息
|
||||
mw db | 快捷连接MySQL
|
||||
mw redis | 快捷连接Redis
|
||||
mw valkey | 快捷连接valkey
|
||||
mw mongodb | 快捷连接MongoDB
|
||||
mw pgdb | 快捷连接PostgreSQL
|
||||
----------------------------------------
|
||||
|
||||
+3
-1
@@ -140,7 +140,9 @@ def systemTask():
|
||||
from utils.system import monitor
|
||||
try:
|
||||
while True:
|
||||
monitor.instance().run()
|
||||
monitor_status = thisdb.getOption('monitor_status',type='monitor',default='open')
|
||||
if monitor_status == 'open':
|
||||
monitor.instance().run()
|
||||
time.sleep(5)
|
||||
except Exception as ex:
|
||||
print('systemTask:',mw.getTracebackInfo())
|
||||
|
||||
+30
-6
@@ -57,7 +57,9 @@ def mwcli(mw_input=0):
|
||||
'(2) 停止面板服务',
|
||||
'(3) 启动面板服务',
|
||||
'(4) 重载面板服务',
|
||||
'(5) 修改面板端口',
|
||||
'(5) 修改面板IP',
|
||||
'(6) 修改面板端口',
|
||||
'(7) 关闭安全入口',
|
||||
'(10) 查看面板默认信息',
|
||||
'(11) 修改面板密码',
|
||||
'(12) 修改面板用户名',
|
||||
@@ -73,6 +75,7 @@ def mwcli(mw_input=0):
|
||||
'(26) 关闭二次验证',
|
||||
'(27) 查看防火墙信息',
|
||||
'(28) 自动识别防火墙端口到面板',
|
||||
'(29) 自动识别配置站点信息',
|
||||
'(100) 开启PHP52显示',
|
||||
'(101) 关闭PHP52显示',
|
||||
'(200) 切换Linux系统软件源',
|
||||
@@ -98,8 +101,9 @@ def mwcli(mw_input=0):
|
||||
mw_input = 0
|
||||
|
||||
nums = [
|
||||
1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28,
|
||||
1, 2, 3, 4, 5, 6, 7,
|
||||
10, 11, 12, 13, 14, 15,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
100, 101,
|
||||
200, 201
|
||||
]
|
||||
@@ -117,6 +121,16 @@ def mwcli(mw_input=0):
|
||||
elif mw_input == 4:
|
||||
os.system(INIT_CMD + " reload")
|
||||
elif mw_input == 5:
|
||||
in_ip = mw_input_cmd("请输入设置的面板IP:")
|
||||
in_ip = in_ip.strip()
|
||||
ip_text = panel_dir + '/data/iplist.txt'
|
||||
if not mw.isVaildIp(in_ip):
|
||||
mw.echoInfo("【"+in_ip+"】: IP不合法")
|
||||
return
|
||||
mw.writeFile(ip_text, in_ip)
|
||||
thisdb.setOption('server_ip', in_ip)
|
||||
mw.echoInfo("设置面板IP: " + in_ip)
|
||||
elif mw_input == 6:
|
||||
in_port = mw_input_cmd("请输入新的面板端口:")
|
||||
in_port_int = int(in_port.strip())
|
||||
if in_port_int < 65536 and in_port_int > 0:
|
||||
@@ -128,6 +142,9 @@ def mwcli(mw_input=0):
|
||||
else:
|
||||
mw.echoInfo("端口范围在0-65536之间")
|
||||
return
|
||||
elif mw_input == 7:
|
||||
thisdb.setOption('admin_path', '')
|
||||
mw.echoInfo("关闭安全入口成功!")
|
||||
elif mw_input == 10:
|
||||
os.system(INIT_CMD + " default")
|
||||
elif mw_input == 11:
|
||||
@@ -220,9 +237,12 @@ def mwcli(mw_input=0):
|
||||
if not run_cmd:
|
||||
mw.echoInfo("未检测到防火墙!")
|
||||
elif mw_input == 28:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().aIF()
|
||||
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
|
||||
elif mw_input == 29:
|
||||
from utils.site_reflect import parse as MwParse
|
||||
MwParse()
|
||||
mw.echoInfo("自动识别配置站点信息成功!")
|
||||
elif mw_input == 100:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
@@ -240,7 +260,8 @@ def mwcli(mw_input=0):
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52隐藏成功!")
|
||||
elif mw_input == 200:
|
||||
os.system(INIT_CMD + " mirror")
|
||||
os.system("bash <(curl -sSL https://linuxmirrors.cn/main.sh)")
|
||||
# os.system(INIT_CMD + " mirror")
|
||||
elif mw_input == 201:
|
||||
os.system('curl -Lso- bench.sh | bash')
|
||||
|
||||
@@ -283,7 +304,10 @@ def show_panel_pwd():
|
||||
|
||||
def show_panel_adminpath():
|
||||
admin_path = thisdb.getOption('admin_path')
|
||||
print('/'+admin_path)
|
||||
if admin_path == '':
|
||||
print('/login')
|
||||
else:
|
||||
print('/'+admin_path)
|
||||
|
||||
|
||||
def set_panel_username(username=None):
|
||||
|
||||
@@ -21,8 +21,8 @@ CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`dnsapi_id` TEXT,
|
||||
`email` TEXT,
|
||||
`remark` TEXT,
|
||||
`effective_date` TEXT,
|
||||
`expiration_date` TEXT,
|
||||
`effective_date` TEXT default '',
|
||||
`expiration_date` TEXT default '',
|
||||
`error` TEXT,
|
||||
`status` INTEGER default '0',
|
||||
`addtime` TEXT
|
||||
|
||||
@@ -17,8 +17,16 @@ access_key = "xxx"
|
||||
# 指定用户
|
||||
userId = 1
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
domain = sys.argv[1]
|
||||
ssl_path = sys.argv[2]
|
||||
|
||||
@@ -186,24 +186,25 @@ def initDreplace():
|
||||
|
||||
def apaOp(method):
|
||||
file = initDreplace()
|
||||
return 'ok'
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
# current_os = mw.getOs()
|
||||
# if current_os == "darwin":
|
||||
# data = mw.execShell(file + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
# if current_os.startswith("freebsd"):
|
||||
# data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
# data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
@@ -600,6 +601,8 @@ def runHookDstDomain(row):
|
||||
|
||||
hookWriteLog('开始申请【'+domain+'】SSL证书')
|
||||
cmd = "#!/bin/bash\n"
|
||||
cmd += "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:/root/.acme.sh\n"
|
||||
cmd += "export PATH\n"
|
||||
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
|
||||
@@ -274,7 +274,7 @@ function dnsapiAdd(row){
|
||||
// console.log(row);
|
||||
var option_name = '';
|
||||
var option_remark = '';
|
||||
var option_type = 'cf';
|
||||
var option_type = 'dns_cf';
|
||||
var option_val = '';
|
||||
var option_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
@@ -286,7 +286,6 @@ function dnsapiAdd(row){
|
||||
|
||||
}
|
||||
|
||||
// console.log(option_name);
|
||||
function renderDnsapiOption(name, val){
|
||||
var vlist = {};
|
||||
if (val != ''){
|
||||
@@ -303,18 +302,16 @@ function dnsapiAdd(row){
|
||||
}
|
||||
|
||||
|
||||
|
||||
var key = getDnsapiKey(name);
|
||||
var klist = key.split(':');
|
||||
// console.log(klist);
|
||||
var option_html = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var klist_val = '';
|
||||
if (klist[i] in vlist){
|
||||
klist_val = vlist[klist[i]];
|
||||
}
|
||||
|
||||
option_html += "\
|
||||
<span class='tname'>"+klist[i]+"</span>\
|
||||
option_html += "<span class='tname'>"+klist[i]+"</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='"+klist[i]+"' class='bt-input-text mr5' style='width:100%;' value='"+klist_val+"' placeholder='请输入对应值' type='text'>\
|
||||
</div>";
|
||||
@@ -379,7 +376,6 @@ function dnsapiAdd(row){
|
||||
$('select[name="type"]').html(option);
|
||||
$('select[name="type"]').change(function(){
|
||||
var name = $(this).val();
|
||||
|
||||
if (option_type == name){
|
||||
renderDnsapiOption(name, option_val);
|
||||
} else {
|
||||
@@ -667,11 +663,24 @@ function domainAdd(row){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
if (option_dnsapi_id == rdata[i]['id']){
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' selected>"+rdata[i]['name']+"</option>";
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"' selected>"+rdata[i]['name']+"</option>";
|
||||
} else {
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"'>"+rdata[i]['name']+"</option>";
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"'>"+rdata[i]['name']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
$('select[name="dnsapi_id"]').change(function(){
|
||||
var val = $('select[name="dnsapi_id"]').find("option:selected").attr('val');
|
||||
var val_arr = val.split("|");
|
||||
for (var i = 0; i < val_arr.length; i++) {
|
||||
var param = val_arr[i];
|
||||
var strictEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if(strictEmailRegex.test(param)){
|
||||
$('input[name="email"]').val(param);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
$('select[name="dnsapi_id"]').html(dnsapi_id_html);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -6,8 +6,18 @@ import os
|
||||
import time
|
||||
import json
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
@@ -69,8 +79,6 @@ def createBgTaskByName(name):
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
@@ -99,10 +107,10 @@ logs_file=$plugin_path/${rname}.log
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'urladdress': '',
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = api.add(params)
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "alist",
|
||||
"title": "Alist",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.37.4","3.38.0"],
|
||||
"versions":["3.37.4","3.38.0","3.45.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/alist",
|
||||
"path": "server/alist",
|
||||
|
||||
@@ -23,7 +23,7 @@ app_start(){
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
|
||||
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill -9
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.45.0
|
||||
|
||||
VERSION=$2
|
||||
|
||||
@@ -41,7 +41,7 @@ Install_App()
|
||||
ALIST_DIR=$serverPath/source/alist
|
||||
|
||||
if [ ! -f $ALIST_DIR/${FILE_TGZ} ];then
|
||||
wget -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
|
||||
wget --no-check-certificate -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/alist
|
||||
|
||||
@@ -20,16 +20,6 @@ if os.path.exists(web_dir):
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
_ver = sys.version_info
|
||||
is_py2 = (_ver[0] == 2)
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
DEBUG = False
|
||||
|
||||
if is_py2:
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
@@ -81,7 +71,10 @@ def status():
|
||||
def getConf():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置", [])
|
||||
rdata = {
|
||||
'use_sftp':False,
|
||||
}
|
||||
return mw.returnJson(False, "未配置", rdata)
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
return mw.returnJson(True, "OK", data)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"sort":6,
|
||||
"title":"FTP存储空间",
|
||||
"hook":["backup"],
|
||||
"tip":"soft",
|
||||
@@ -13,5 +14,5 @@
|
||||
"author":"midoks",
|
||||
"home":"https://github.com/midoks/mdserver-web",
|
||||
"date":"2022-10-23",
|
||||
"pid": "4"
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -131,6 +131,7 @@ def initConf():
|
||||
"/var/log/wtmp",
|
||||
"/var/log/secure",
|
||||
"/var/log/lastlog",
|
||||
"/var/log/syslog",
|
||||
"/var/log/cron",
|
||||
"/www/wwwlogs",
|
||||
"/www/server/rsyncd",
|
||||
@@ -156,6 +157,7 @@ def initConf():
|
||||
"/www/server/redis/data",
|
||||
"/www/server/alist/data/log",
|
||||
"/www/server/dztasks/logs",
|
||||
"/www/server/rsyncd/lsyncd.log"
|
||||
"/www/server/cron",
|
||||
]
|
||||
for i in clogcom:
|
||||
@@ -241,7 +243,7 @@ def cleanDirLog(path):
|
||||
def cleanRun():
|
||||
plugin_dir = getPluginDir()
|
||||
log_file = getServerDir()+'/clean.log'
|
||||
cmd = 'python3 '+plugin_dir+'/index.py clean > '+log_file
|
||||
cmd = 'cd '+mw.getPanelDir()+' && python3 '+plugin_dir+'/index.py clean > '+log_file
|
||||
os.system(cmd)
|
||||
return mw.returnJson(True, '执行成功!')
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"sort":2,
|
||||
"title":"日志清理",
|
||||
"tip":"soft",
|
||||
"name":"clean",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# plugins_cryptocurrency_trade
|
||||
|
||||
- https://docs.ccxt.com/en/latest/install.html#python-proxies
|
||||
|
||||
数字货币量化交易插件
|
||||
|
||||
```
|
||||
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
|
||||
|
||||
miniconda3
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
# 免费TV策略
|
||||
https://cn.tradingview.com/scripts/?script_type=strategies
|
||||
|
||||
|
||||
### tradingview pine 指标
|
||||
```
|
||||
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
|
||||
// © TenCloud
|
||||
|
||||
//@version=5
|
||||
indicator("第一个指标",shorttitle = "开心吧")
|
||||
|
||||
ema10 = ta.ema(close, 25)
|
||||
ema100 = ta.ema(close, 100)
|
||||
l1=plot(ema10, color=color.red,title = "ema10")
|
||||
l2=plot(ema100,color = color.green, title = "ema100")
|
||||
|
||||
|
||||
buy = ta.crossover(ema10,ema100)
|
||||
sell = ta.crossunder(ema10,ema100)
|
||||
|
||||
plotchar(buy, text = "buy", color=color.green)
|
||||
plotchar(sell, text="sell", color=color.red)
|
||||
|
||||
l_color = ema10>ema100 ? color.green:color.red
|
||||
|
||||
fill(l1,l2,color=color.new(l_color,70))
|
||||
|
||||
```
|
||||
|
||||
### tradingview pine 策略
|
||||
```
|
||||
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
|
||||
// © TenCloud
|
||||
|
||||
//@version=5
|
||||
strategy("第一个指标",shorttitle = "开心吧", overlay = true, initial_capital = 1000)
|
||||
|
||||
ema10 = ta.ema(close, 25)
|
||||
ema100 = ta.ema(close, 100)
|
||||
l1=plot(ema10, color=color.red,title = "ema10")
|
||||
l2=plot(ema100,color = color.green, title = "ema100")
|
||||
|
||||
|
||||
buy = ta.crossover(ema10,ema100)
|
||||
sell = ta.crossunder(ema10,ema100)
|
||||
|
||||
if buy
|
||||
strategy.entry("long1", strategy.long,1)
|
||||
|
||||
if sell
|
||||
strategy.close("long1", qty_percent = 100,comment = "平仓多单")
|
||||
// plotchar(buy, text = "buy", color=color.green)
|
||||
// plotchar(sell, text="sell", color=color.red)
|
||||
|
||||
// l_color = ema10>ema100 ? color.green:color.red
|
||||
|
||||
// fill(l1,l2,color=color.new(l_color,70))
|
||||
|
||||
|
||||
```
|
||||
@@ -0,0 +1,555 @@
|
||||
import ccxt
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import glob
|
||||
import threading
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# print(os.getcwd())
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
|
||||
|
||||
# 查看支持的交易所
|
||||
# print(ccxt.exchanges)
|
||||
|
||||
# 代理设置
|
||||
# exchange = ccxt.poloniex({
|
||||
# 'proxies': {
|
||||
# 'http': 'http://127.0.0.1:1088'
|
||||
# },
|
||||
# })
|
||||
|
||||
# exchange = ccxt.poloniex()
|
||||
exchange = ccxt.okex()
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def writeLog(log_str):
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/datasource.log'
|
||||
mw.writeFileLog(log_str, log_file, 1 * 1024 * 1024)
|
||||
return True
|
||||
|
||||
|
||||
def isSetDbConf():
|
||||
data = getConfigData()
|
||||
if 'db' in data:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def beforeDate(day=360):
|
||||
dd = time.time() - day * 86400
|
||||
d = datetime.fromtimestamp(dd)
|
||||
day = d.strftime("%Y-%m-%d")
|
||||
return day
|
||||
|
||||
|
||||
def getTextTimeShow(time):
|
||||
d = datetime.fromtimestamp(time)
|
||||
day = d.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return day
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
mysqlMsg = str(mysqlMsg)
|
||||
if "MySQLdb" in mysqlMsg:
|
||||
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
|
||||
if "2002," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "2003," in mysqlMsg:
|
||||
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
if "using password:" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库密码错误')
|
||||
if "1045" in mysqlMsg:
|
||||
return mw.returnJson(False, '连接错误!')
|
||||
if "SQL syntax" in mysqlMsg:
|
||||
return mw.returnJson(False, 'SQL语法错误!')
|
||||
if "Connection refused" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库用户不存在!')
|
||||
if "1007" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库已经存在!')
|
||||
return None
|
||||
|
||||
|
||||
def pMysqlDb():
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
data = getConfigData()
|
||||
db_data = data['db']
|
||||
db.setHost(db_data['db_host'])
|
||||
db.setPort(db_data['db_port'])
|
||||
db.setUser(db_data['db_user'])
|
||||
db.setPwd(db_data['db_pass'])
|
||||
db.setDbName(db_data['db_name'])
|
||||
return db
|
||||
|
||||
|
||||
def makeInsertSql(table, item):
|
||||
sql = "insert into " + table
|
||||
keyStr = '('
|
||||
valueStr = ' values('
|
||||
for i in item:
|
||||
# print i, item[i]
|
||||
keyStr += '`' + str(i) + '`,'
|
||||
valueStr += "'" + str(item[i]) + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
keyStr += ') '
|
||||
|
||||
valueStrLen = len(valueStr)
|
||||
valueStr = valueStr[0:valueStrLen - 1]
|
||||
valueStr += ') '
|
||||
|
||||
sql += keyStr
|
||||
sql += valueStr
|
||||
|
||||
return sql
|
||||
|
||||
|
||||
def makeReplaceSql(table, item):
|
||||
sql = "replace into " + table
|
||||
keyStr = '('
|
||||
valueStr = ' values('
|
||||
for i in item:
|
||||
# print i, item[i]
|
||||
keyStr += '`' + str(i) + '`,'
|
||||
valueStr += "'" + str(item[i]) + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
keyStr += ') '
|
||||
|
||||
valueStrLen = len(valueStr)
|
||||
valueStr = valueStr[0:valueStrLen - 1]
|
||||
valueStr += ') '
|
||||
|
||||
sql += keyStr
|
||||
sql += valueStr
|
||||
|
||||
return sql
|
||||
|
||||
|
||||
def makeUpdateSql(table, item, mid):
|
||||
sql = "update " + table + " set "
|
||||
keyStr = ''
|
||||
for i in item:
|
||||
keyStr += '`' + str(i) + '`=' + "'" + item[i] + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
sql += keyStr
|
||||
sql += " where id = '" + str(mid) + "'"
|
||||
return sql
|
||||
|
||||
|
||||
def dataToDb(table_name, data):
|
||||
pdb = pMysqlDb()
|
||||
for i in data:
|
||||
rdata = {
|
||||
'addtime': int(i[0] / 1000),
|
||||
'open': i[1],
|
||||
'high': i[2],
|
||||
'low': i[3],
|
||||
'close': i[4],
|
||||
'vol': i[5],
|
||||
}
|
||||
|
||||
# sql = """SELECT id FROM %s where addtime='%s' LIMIT 1""" % (
|
||||
# table_name, rdata['addtime'])
|
||||
# fdata = pdb.query(sql)
|
||||
# if fdata:
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", old to db ok")
|
||||
# else:
|
||||
# isql = makeReplaceSql(table_name, rdata)
|
||||
# r = pdb.execute(isql)
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
|
||||
|
||||
isql = makeReplaceSql(table_name, rdata)
|
||||
r = pdb.execute(isql)
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
|
||||
return True
|
||||
|
||||
|
||||
def makeTableName(input_type="btc", input_tf="1m"):
|
||||
table_name = "ct_%s_%s" % (input_type, input_tf)
|
||||
return table_name
|
||||
|
||||
|
||||
def isHasTable(input_type="btc", input_tf="1m"):
|
||||
pdb = pMysqlDb()
|
||||
|
||||
# input_type = 'btc'
|
||||
# input_tf = '1m'
|
||||
table_name = makeTableName(input_type, input_tf)
|
||||
mtable = pdb.query("show tables like '%s'" % (table_name,))
|
||||
if len(mtable) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def createSql(input_type="btc", input_tf="1m"):
|
||||
pdb = pMysqlDb()
|
||||
|
||||
# input_type = 'btc'
|
||||
# input_tf = '1m'
|
||||
table_name = makeTableName(input_type, input_tf)
|
||||
|
||||
mtable = pdb.query("show tables like '%s'" % (table_name,))
|
||||
if len(mtable) != 0:
|
||||
return True
|
||||
|
||||
sql_tpl = getPluginDir() + "/conf/create.sql"
|
||||
content = mw.readFile(sql_tpl)
|
||||
content = content.replace("xx1", input_type)
|
||||
content = content.replace("xx2", input_tf)
|
||||
|
||||
res = pdb.execute(content)
|
||||
# pprint(res)
|
||||
return True
|
||||
|
||||
|
||||
def getDataFetch(symbol, start_time="2023-3-1", input_tf="1m", limit=500):
|
||||
start_time = datetime.strptime(start_time, '%Y-%m-%d')
|
||||
end_time = datetime.strptime(end_time, '%Y-%m-%d')
|
||||
|
||||
start_time_stamp = int(time.mktime(start_time.timetuple())) * 1000
|
||||
data = exchange.fetch_ohlcv(symbol, timeframe=input_tf,
|
||||
since=start_time_stamp, limit=limit)
|
||||
return data
|
||||
|
||||
|
||||
def getPointData(symbol, start_time=1677713220000, input_tf="1m", limit=500):
|
||||
data = exchange.fetch_ohlcv(
|
||||
symbol, timeframe=input_tf, since=start_time, limit=limit)
|
||||
return data
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def findAndUpdateData(tag, input_tf="1m", start_time="2023-1-1", limit=300):
|
||||
pdb = pMysqlDb()
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
sql = 'SELECT addtime FROM ' + table_name + ' order by addtime desc LIMIT 1'
|
||||
qdata = pdb.query(sql)
|
||||
|
||||
start_time = datetime.strptime(start_time, '%Y-%m-%d')
|
||||
start_time = int(time.mktime(start_time.timetuple())) * 1000
|
||||
if len(qdata) != 0:
|
||||
start_time = int(qdata[0]['addtime']) * 1000
|
||||
|
||||
pre_time = toUnixTimeSecond(input_tf) * 5 * 1000
|
||||
start_time = start_time - pre_time
|
||||
# pprint(start_time)
|
||||
symbol = tag.upper() + "/USDT"
|
||||
data = getPointData(symbol, start_time, input_tf, limit)
|
||||
|
||||
# pprint(data)
|
||||
# print("------------lini===========")
|
||||
# pprint(data[1:])
|
||||
|
||||
if len(qdata) == 1:
|
||||
data = data[1:]
|
||||
|
||||
dataToDb(table_name, data)
|
||||
|
||||
now = time.strftime("%m/%d %H:%M:%S")
|
||||
data_len = len(data)
|
||||
msg_head = now + "|虚拟币:" + tag + ",tf:" + \
|
||||
input_tf + "!,总数:" + str(data_len)
|
||||
writeLog(msg_head)
|
||||
if data_len > 0:
|
||||
# print("new data time", data[data_len - 1][0])
|
||||
dt = getTextTimeShow(data[data_len - 1][0] / 1000)
|
||||
msg = now + "|最新日期:" + dt
|
||||
writeLog(msg)
|
||||
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
|
||||
sql = "SELECT addtime FROM " + table_name + \
|
||||
" order by addtime desc limit 10000,1"
|
||||
qdata = pdb.query(sql)
|
||||
|
||||
if len(qdata) > 0:
|
||||
print(qdata)
|
||||
|
||||
# input_tf_time = toUnixTimeSecond(input_tf)
|
||||
# now_t = int(time.time())
|
||||
|
||||
# del_before_t = now_t - input_tf_time * 1000
|
||||
|
||||
sql = "delete from %s where addtime<'%d' " % (
|
||||
table_name, qdata[0]['addtime'],)
|
||||
writeLog("删除冗余数据:" + str(sql))
|
||||
pdb.execute(sql)
|
||||
return True
|
||||
|
||||
|
||||
def dataToDbTpl(tag='btc', input_tf="1m", start_time="2023-1-1", limit=300):
|
||||
# tag = "btc"
|
||||
# tf = "1m"
|
||||
# start_time = "2023-1-1"
|
||||
# end_time = "2023-3-2"
|
||||
symbol = tag.lower() + '/USDT'
|
||||
if not isHasTable(tag, input_tf):
|
||||
createSql(tag, input_tf)
|
||||
data = getDataFetch(symbol, start_time, input_tf, limit)
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
|
||||
dataToDb(table_name, data)
|
||||
now = time.strftime("%m/%d %H:%M:%S")
|
||||
data_len = len(data)
|
||||
writeLog(now + "|数据获取成功!,总数:" + str(data_len))
|
||||
else:
|
||||
findAndUpdateData(tag, input_tf, start_time, limit)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def dataToDbList(input_tf="1m", start_time="2023-1-1"):
|
||||
|
||||
data = getConfigData()
|
||||
if not 'token' in data:
|
||||
writeLog("未设置同步配置,需要添加币种!")
|
||||
return
|
||||
|
||||
for t in data['token']:
|
||||
dataToDbTpl(t, input_tf, start_time)
|
||||
time.sleep(4)
|
||||
|
||||
|
||||
def dataRunToDb():
|
||||
data = getConfigData()
|
||||
if not 'db' in data:
|
||||
writeLog("数据库未设置!")
|
||||
return
|
||||
|
||||
tag = "btc"
|
||||
symbol = tag.upper() + '/USDT'
|
||||
|
||||
# pprint(exchange.fetch_ticker('XRP/USDT'))
|
||||
limit_count = 1
|
||||
start_time = "2023-1-1"
|
||||
end_time = "2023-3-2"
|
||||
tf = "1m"
|
||||
|
||||
if not isHasTable(tag, tf):
|
||||
createSql(tag, tf)
|
||||
data = getDataFetch(symbol, start_time, end_time, tf, limit_count)
|
||||
table_name = makeTableName(tag, tf)
|
||||
|
||||
dataToDb(table_name, data)
|
||||
china_datetime = str(datetime.now())
|
||||
data_len = len(data)
|
||||
writeLog(china_datetime + ":数据获取成功!,总数:" + str(data_len))
|
||||
else:
|
||||
findAndUpdateData(tag, tf, start_time, 30)
|
||||
return data
|
||||
|
||||
|
||||
def startTask():
|
||||
# 任务队列
|
||||
try:
|
||||
while True:
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
startTask()
|
||||
|
||||
|
||||
def setDaemon(t):
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
t.daemon = True
|
||||
else:
|
||||
t.setDaemon(True)
|
||||
return t
|
||||
|
||||
|
||||
def dataDay():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(2 * 365)
|
||||
dataToDbList("1d", start_time=start_time)
|
||||
time.sleep(3600)
|
||||
# time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
dataDay()
|
||||
|
||||
|
||||
def data4h():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("4h", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data4h()
|
||||
|
||||
|
||||
def data1h():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("1h", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
time.sleep(60)
|
||||
data1h()
|
||||
|
||||
|
||||
def data15m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("15m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data15m()
|
||||
|
||||
|
||||
def data5m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(180)
|
||||
dataToDbList("5m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data5m()
|
||||
|
||||
|
||||
def data1m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(30)
|
||||
dataToDbList("1m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data1m()
|
||||
|
||||
|
||||
def longRun():
|
||||
|
||||
# 日线
|
||||
d1_tt = threading.Thread(target=dataDay)
|
||||
d1_tt = setDaemon(d1_tt)
|
||||
d1_tt.start()
|
||||
|
||||
# 4h
|
||||
h4_tt = threading.Thread(target=data4h)
|
||||
h4_tt = setDaemon(h4_tt)
|
||||
h4_tt.start()
|
||||
|
||||
# 1h
|
||||
h1_tt = threading.Thread(target=data1h)
|
||||
h1_tt = setDaemon(h1_tt)
|
||||
h1_tt.start()
|
||||
|
||||
# 15m
|
||||
m15_tt = threading.Thread(target=data15m)
|
||||
m15_tt = setDaemon(m15_tt)
|
||||
m15_tt.start()
|
||||
|
||||
# 5m
|
||||
m5_tt = threading.Thread(target=data5m)
|
||||
m5_tt = setDaemon(m5_tt)
|
||||
m5_tt.start()
|
||||
|
||||
# 1m
|
||||
# h1m_tt = threading.Thread(target=data1m)
|
||||
# h1m_tt = setDaemon(h1m_tt)
|
||||
# h1m_tt.start()
|
||||
|
||||
startTask()
|
||||
|
||||
|
||||
def dataRun():
|
||||
do_num = 0
|
||||
while True:
|
||||
if do_num > 1:
|
||||
break
|
||||
do_num = do_num + 1
|
||||
dataRunToDb()
|
||||
time.sleep(6)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
data5m()()
|
||||
elif func == 'long':
|
||||
longRun()
|
||||
elif func == 'demo':
|
||||
writeLog('111')
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
curPath=`pwd`
|
||||
|
||||
# bash plugins/cryptocurrency_trade/ccxt/public_data/data.sh
|
||||
|
||||
if [ -f ${curPath}/bin/activate ];then
|
||||
source ${curPath}/bin/activate
|
||||
fi
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
|
||||
@@ -0,0 +1,19 @@
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
china_datetime = datetime.now()
|
||||
print(china_datetime)
|
||||
|
||||
|
||||
# date()
|
||||
tt = time.time() - 180 * 86400
|
||||
|
||||
# print(date("%Y-%m-%d", time.time()))
|
||||
# t = datetime.strptime(str(time.time() - 180 * 86400), "%Y-%m-%d")
|
||||
# print(t)
|
||||
|
||||
|
||||
d = datetime.fromtimestamp(tt)
|
||||
# 精确到毫秒
|
||||
str1 = d.strftime("%Y-%m-%d")
|
||||
print(str1)
|
||||
@@ -0,0 +1,352 @@
|
||||
import ccxt
|
||||
|
||||
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import glob
|
||||
import threading
|
||||
|
||||
import pandas as pd
|
||||
from decimal import Decimal
|
||||
from pprint import pprint
|
||||
from datetime import datetime
|
||||
|
||||
# print(os.getcwd())
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = ccxt.poloniex()
|
||||
|
||||
|
||||
def calc_ClosingPriceWithOutStopLossPrice(open_price, stype='buy', profit=0.5):
|
||||
# profit 百分比 %
|
||||
v = 0
|
||||
vf_len = len(str(open_price).split('.')[1])
|
||||
if stype == 'buy':
|
||||
v = open_price * (100 + profit) / 100
|
||||
else:
|
||||
v = open_price * (100 - profit) / 100
|
||||
return round(v, vf_len)
|
||||
|
||||
|
||||
def roundVal(price, compare_price):
|
||||
price = float(price)
|
||||
csplite = str(compare_price).split('.')
|
||||
clen = len(csplite)
|
||||
if clen == 2:
|
||||
vf_len = len(csplite[1])
|
||||
return round(price, vf_len)
|
||||
return price
|
||||
|
||||
|
||||
def roundValCeil(price, compare_price):
|
||||
price = float(price)
|
||||
csplite = str(compare_price).split('.')
|
||||
clen = len(csplite)
|
||||
if clen == 2:
|
||||
vf_len = len(csplite[1]) - 1
|
||||
return round(price, vf_len)
|
||||
return price
|
||||
|
||||
|
||||
def multiply(a1, a2):
|
||||
v = Decimal(str(a1)) * Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def addition(a1, a2):
|
||||
v = Decimal(str(a1)) + Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def subtract(a1, a2):
|
||||
v = Decimal(str(a1)) - Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def divided(a1, a2):
|
||||
v = Decimal(str(a1)) / Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def calc_ClosingPrice(open_price, stop_loss_price, stype='buy', profit=0.5):
|
||||
# profit 百分比 %
|
||||
v = 0
|
||||
vf_len = len(str(open_price).split('.')[1])
|
||||
if stype == 'buy':
|
||||
v = open_price * (100 + profit) / 100
|
||||
diff = open_price - stop_loss_price
|
||||
profit_price = open_price + diff * 1.5
|
||||
if profit_price > v:
|
||||
return round(profit_price, vf_len)
|
||||
else:
|
||||
v = open_price * (100 - profit) / 100
|
||||
diff = stop_loss_price - open_price
|
||||
profit_price = open_price - diff * 1.5
|
||||
if profit_price < v:
|
||||
return round(profit_price, vf_len)
|
||||
return round(v, vf_len)
|
||||
|
||||
|
||||
def toDateFromInt(time_unix, tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
|
||||
# 取格式时间
|
||||
import time
|
||||
os.environ['TZ'] = time_zone
|
||||
time_str = time.localtime(time_unix)
|
||||
time.tzset()
|
||||
|
||||
return time.strftime(tf_format, time_str)
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def getUserCfgData():
|
||||
data = getConfigData()
|
||||
if 'user' in data:
|
||||
try:
|
||||
udata = mw.deDoubleCrypt('mw', data['user'])
|
||||
udata = json.loads(udata)
|
||||
return udata
|
||||
except Exception as e:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
def initEx():
|
||||
data = getUserCfgData()
|
||||
# print(data)
|
||||
if (len(data) > 0):
|
||||
exchange = ccxt.okex({
|
||||
"apiKey": data['app_key'],
|
||||
"secret": data['secret'],
|
||||
"password": data['password'],
|
||||
})
|
||||
return exchange
|
||||
else:
|
||||
print("初始化失败,检查原因!")
|
||||
exchange = ccxt.poloniex()
|
||||
return exchange
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def notifyMsg(msg, tf='15m', tag='btc'):
|
||||
trigger_time = toUnixTimeSecond(tf)
|
||||
return mw.notifyMessage(msg, '量化交易/' + tag, trigger_time)
|
||||
|
||||
|
||||
def makeTableName(input_type="btc", input_tf="1m"):
|
||||
table_name = "ct_%s_%s" % (input_type, input_tf,)
|
||||
return table_name
|
||||
|
||||
|
||||
def writeLog(log_str):
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def writeLogEx(log_str, tag='btc'):
|
||||
tag = tag.replace('/', '_')
|
||||
# 各种币的详细API日志
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy_' + tag + '.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def writeLogErrorEx(log_str, tag='btc'):
|
||||
tag = tag.replace('/', '_')
|
||||
# 各种币的详细API日志
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy_' + tag + '.err.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def pMysqlDb():
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
data = getConfigData()
|
||||
db_data = data['db']
|
||||
|
||||
# print(db_data)
|
||||
db.setHost(db_data['db_host'])
|
||||
db.setPort(db_data['db_port'])
|
||||
db.setUser(db_data['db_user'])
|
||||
db.setPwd(db_data['db_pass'])
|
||||
db.setDbName(db_data['db_name'])
|
||||
return db
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=200):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
return df
|
||||
|
||||
|
||||
def toDataFrame(data):
|
||||
# print(data)
|
||||
data = sorted(data, key=lambda x: x["addtime"], reverse=False)
|
||||
|
||||
dfield = ['addtime', 'open', 'high', 'low', 'close']
|
||||
v = {}
|
||||
for dx in dfield:
|
||||
v[dx] = []
|
||||
|
||||
for x in data:
|
||||
for i in range(len(x)):
|
||||
field = dfield[i]
|
||||
# print(i, field)
|
||||
v[field].append(x[field])
|
||||
# pprint(data)
|
||||
frame = pd.DataFrame(v)
|
||||
# frame = frame.sort_values(by=['addtime'])
|
||||
|
||||
frame['dt'] = pd.to_datetime(frame['addtime'], unit='s')
|
||||
frame.set_index('dt', inplace=True)
|
||||
frame.index = frame.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return frame
|
||||
|
||||
|
||||
def getDataFromDb(tf="1m", tag='btc', limit=1000):
|
||||
tn = makeTableName(tag, tf)
|
||||
sql = 'select addtime,open,high,low,close from ' + \
|
||||
tn + ' order by addtime desc limit ' + str(limit)
|
||||
|
||||
pdb = pMysqlDb()
|
||||
fdata = pdb.query(sql)
|
||||
# print(fdata)
|
||||
return fdata
|
||||
|
||||
|
||||
def getDataFromDb_DF(tf="5m", tag='btc', limit=1000):
|
||||
data = getDataFromDb(tf, tag, limit)
|
||||
rdata = toDataFrame(data)
|
||||
return rdata
|
||||
|
||||
|
||||
# 消息模板类
|
||||
class MsgTpl():
|
||||
|
||||
__name = ''
|
||||
__strategy_name = ''
|
||||
__time_frame = ''
|
||||
__content = ''
|
||||
__open_time = ''
|
||||
__strategy_dt = ''
|
||||
|
||||
__stop_loss_price = ''
|
||||
__closing_price = ''
|
||||
__open_price = ''
|
||||
|
||||
__msg = ''
|
||||
|
||||
def setName(self, name):
|
||||
self.__name = name
|
||||
|
||||
def setStrategyName(self, name):
|
||||
self.__strategy_name = name
|
||||
|
||||
def setTimeFrame(self, tf):
|
||||
self.__time_frame = tf
|
||||
|
||||
def setContent(self, content):
|
||||
self.__content = content
|
||||
|
||||
def setOpenTime(self, time):
|
||||
self.__open_time = toDateFromInt(time)
|
||||
|
||||
def setStrategicDt(self, stype='buy'):
|
||||
if stype == 'buy':
|
||||
self.__strategy_dt = '做多'
|
||||
else:
|
||||
self.__strategy_dt = '做空'
|
||||
|
||||
def setStopLossPrice(self, price):
|
||||
self.__stop_loss_price = price
|
||||
|
||||
def setClosingPrice(self, price):
|
||||
self.__closing_price = price
|
||||
|
||||
def setOpenPrice(self, price):
|
||||
self.__open_price = price
|
||||
|
||||
def setMsg(self, msg):
|
||||
self.__msg = msg
|
||||
|
||||
def toText(self):
|
||||
msg = ''
|
||||
msg += '名称:' + self.__name + "\n"
|
||||
|
||||
if self.__strategy_name != '':
|
||||
msg += '策略名称:' + self.__strategy_name + "\n"
|
||||
|
||||
if self.__time_frame != '':
|
||||
msg += '时间周期:' + self.__time_frame + "\n"
|
||||
|
||||
if self.__content != '':
|
||||
msg += '策略描述:' + self.__content + "\n"
|
||||
|
||||
if self.__open_time != '':
|
||||
msg += '开盘时间:' + self.__open_time + "\n"
|
||||
|
||||
if self.__strategy_dt != '':
|
||||
msg += '开仓方向:' + self.__strategy_dt + "\n"
|
||||
|
||||
if self.__open_price != '':
|
||||
msg += '开仓价:' + str(self.__open_price) + "\n"
|
||||
|
||||
if self.__stop_loss_price != '':
|
||||
msg += '止损价:' + str(self.__stop_loss_price) + "\n"
|
||||
|
||||
if self.__closing_price != '':
|
||||
msg += '止盈价:' + str(self.__closing_price) + "\n"
|
||||
|
||||
msg += '发送时间:' + mw.getDateFromNow() + "\n"
|
||||
|
||||
if self.__msg != '':
|
||||
msg += __msg
|
||||
return msg
|
||||
@@ -0,0 +1,57 @@
|
||||
# import ccxt
|
||||
# import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
# import pandas as pd
|
||||
from pprint import pprint
|
||||
from decimal import Decimal
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/func_test.py run
|
||||
|
||||
# common.notifyMsg("任务开始")
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def multiply(a1, a2):
|
||||
v = Decimal(str(a1)) * Decimal(str(a2))
|
||||
return v
|
||||
|
||||
# print(toUnixTimeSecond("1d"))
|
||||
|
||||
# f = 19911.2
|
||||
# s = common.calc_ClosingPrice(f, 19890.2, 'buy')
|
||||
# print(s)
|
||||
|
||||
# print(sys.version_info)
|
||||
# os.environ['TZ'] = 'Europe/London'
|
||||
# time.tzset()
|
||||
# t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
|
||||
# print(t)
|
||||
|
||||
# v = multiply(26236.8, 0.003)
|
||||
# print(float(v))
|
||||
# print(v)
|
||||
|
||||
print(float('0.00255234') + float('-0.00000255234'))
|
||||
v1 = common.addition('0.00255234', '-0.00000255234')
|
||||
print(v1)
|
||||
@@ -0,0 +1,440 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py long
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = ccxt.gate({
|
||||
"apiKey": '756e1ff80526cb0ac9620c75680fc506',
|
||||
"secret": '95ac89362056bcf12ce37aabff6eb7ec78185483105f39b4a35e8dc8db8b4d3c',
|
||||
})
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
# 默认开仓数据
|
||||
# default_open_num = 10
|
||||
|
||||
default_open = {
|
||||
"BTC/USDT": 0.01,
|
||||
'ETH/USDT': 30,
|
||||
'DOT/USDT': 30,
|
||||
'CEL/USDT': 30,
|
||||
}
|
||||
|
||||
default_sell = {
|
||||
"BTC/USDT": 0.01,
|
||||
'ETH/USDT': 0.01,
|
||||
'DOT/USDT': 5,
|
||||
'CEL/USDT': 85,
|
||||
}
|
||||
|
||||
|
||||
# 做多开仓
|
||||
def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
|
||||
common.writeLogEx('------做多----------------------------------', symbol)
|
||||
|
||||
default_open_num = default_open[symbol]
|
||||
# 做多开仓 | 市价
|
||||
data = exchange.createMarketBuyOrder(
|
||||
symbol, default_open_num, {"tdMode": "cross"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 做多-止损价大于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 - 0.003))
|
||||
|
||||
# property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
|
||||
property_val = common.addition(
|
||||
order_data['info']['accFillSz'], order_data['info']['fee'])
|
||||
property_val = float(property_val)
|
||||
|
||||
# 可平仓的数量
|
||||
property_val = common.roundValCeil(
|
||||
property_val, order_data['info']['accFillSz'])
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(property_val), symbol)
|
||||
|
||||
# 止盈价
|
||||
diff = float(open_price) - float(stop_loss_price)
|
||||
closing_price_c = float(open_price) + (diff * 2)
|
||||
closing_price = float(open_price) * float((1 + profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c > closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, stop_loss_price)
|
||||
# stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价/止盈价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
# 止损条件单
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
# 止赢条件单
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止赢数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做多 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做多开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
common.writeLogEx('------做空----------------------------------', symbol)
|
||||
|
||||
# 计算借币卖币多多少,以USDT为基准
|
||||
# sell_num = float(default_open_num) / float(stop_loss_price)
|
||||
# sell_num = round(sell_num, 8)
|
||||
|
||||
sell_num = default_sell[symbol]
|
||||
# 做空开仓 | 市价
|
||||
data = exchange.createMarketSellOrder(
|
||||
symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
|
||||
|
||||
# 做空-止损价小于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 + 0.003))
|
||||
|
||||
# 止盈价
|
||||
diff = float(stop_loss_price) - float(open_price)
|
||||
closing_price_c = float(open_price) - (diff * 2)
|
||||
closing_price = float(open_price) * float((1 - profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c < closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
sl_amount = common.multiply(stop_loss_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
sl_amount = common.addition(sl_amount, 0.1)
|
||||
common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
sl_amount), stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', sl_amount, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
# 设置 -止盈价
|
||||
tp_amount = common.multiply(closing_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
tp_amount = common.addition(tp_amount, 0.1)
|
||||
common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
tp_amount), closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', tp_amount, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做空 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做空开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=230):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
df.set_index('dt', inplace=True)
|
||||
df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getMACD(df, lenght=-60):
|
||||
if len(df['close'].values) < 100:
|
||||
return False, None
|
||||
|
||||
close_p = df['close'].values
|
||||
df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
|
||||
fastperiod=12,
|
||||
slowperiod=26,
|
||||
signalperiod=9)
|
||||
return df[lenght:]
|
||||
|
||||
|
||||
def getEMA(df):
|
||||
close_p = df['close'].values
|
||||
df['ema'] = talib.EMA(np.array(close_p), timeperiod=200)
|
||||
return df
|
||||
|
||||
|
||||
def doneMacd(data, tag, timeframe):
|
||||
data = getEMA(data)
|
||||
|
||||
ma_data = getMACD(data)
|
||||
# alen = len(data)
|
||||
# print(ma_data)
|
||||
|
||||
t_data = ma_data.tail(3)
|
||||
print(t_data)
|
||||
|
||||
last_pre = t_data.iloc[0]
|
||||
last = t_data.iloc[1]
|
||||
|
||||
print(last)
|
||||
# print(last.index)
|
||||
|
||||
# print('close:', last['close'], 'ema:', last['ema'])
|
||||
|
||||
obj = common.MsgTpl()
|
||||
symbol = tag.upper() + '/USDT'
|
||||
obj.setName(symbol)
|
||||
obj.setStrategyName("MACD检查")
|
||||
obj.setTimeFrame(timeframe)
|
||||
obj.setOpenTime(last['timestamp'] / 1000)
|
||||
|
||||
now_data = t_data.iloc[2]
|
||||
# print('now_data:', now_data)
|
||||
|
||||
# msg = obj.toText()
|
||||
# print(msg)
|
||||
|
||||
if isKdj(last, last_pre) and last['close'] > last['ema']:
|
||||
# if isKdj(last, last_pre):
|
||||
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['low'], 'buy')
|
||||
# # print('closing_price:', closing_price)
|
||||
|
||||
# 做多止损点
|
||||
stop_loss_price = last_pre['low']
|
||||
buy_status, open_price, closing_price = onBuyOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
|
||||
if buy_status:
|
||||
obj.setStrategicDt('buy')
|
||||
# 做多止损点
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setContent("检查到金叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
if isDeadFork(last, last_pre) and last['close'] < last['ema']:
|
||||
# if isDeadFork(last, last_pre):
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['high'], 'sell')
|
||||
|
||||
# 做空止损点
|
||||
stop_loss_price = last_pre['high']
|
||||
sell_status, open_price, closing_price = onSellOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
if sell_status:
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setStrategicDt('sell')
|
||||
obj.setContent("检查到死叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
|
||||
def mainProcess(tag, timeframe='15m'):
|
||||
symbol = tag.upper() + '/USDT'
|
||||
data = getOnlineData(symbol, timeframe)
|
||||
|
||||
doneMacd(data, tag, timeframe)
|
||||
|
||||
|
||||
def foreachList():
|
||||
tag_list = ['btc']
|
||||
|
||||
for tag in tag_list:
|
||||
mainProcess(tag, '15m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
foreachList()
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def debug():
|
||||
while True:
|
||||
mainProcess('xrp', '1m')
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,138 @@
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py run
|
||||
|
||||
|
||||
pd.set_option('display.max_rows', None)
|
||||
|
||||
exchange = common.initEx()
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
entry_rsi = 30
|
||||
exit_rsi = 40
|
||||
|
||||
|
||||
symbol = 'XRP/USDT'
|
||||
timeframe = '15m'
|
||||
|
||||
tf_mult = exchange.parse_timeframe(timeframe) * 1000
|
||||
|
||||
|
||||
def indicators(data):
|
||||
|
||||
data['rsi'] = data.ta.rsi(length=10)
|
||||
data['ema'] = data.ta.ema(length=200)
|
||||
|
||||
# close_p = data['close'].values
|
||||
# data['rsi'] = talib.RSI(close_p, timeperiod=10)
|
||||
# data['ema'] = talib.EMA(close_p, timeperiod=200)
|
||||
return data
|
||||
|
||||
|
||||
def check_buy_sell_signals(df):
|
||||
last_row_index = len(df.index) - 1
|
||||
lastest_rsi = round(df['rsi'].iloc[-1], 2)
|
||||
lastest_price = round(df['close'].iloc[-1], 5)
|
||||
lastest_ema = round(df['ema'].iloc[-1], 5)
|
||||
lastest_ts = df['timestamp'].iloc[-1]
|
||||
|
||||
msg = "lastest_rsi:" + str(lastest_rsi) + " < entry_rsi:" + str(entry_rsi)
|
||||
msg += ",lastest_price:" + \
|
||||
str(lastest_price) + " > lastest_ema:" + str(lastest_ema)
|
||||
print(msg)
|
||||
|
||||
long_cond = (lastest_rsi < entry_rsi) and (lastest_price > lastest_ema)
|
||||
if long_cond:
|
||||
print("买入")
|
||||
order = exchange.create_market_buy_order(symbol, 1)
|
||||
|
||||
closed_orders = exchange.fetchClosedOrders(symbol, limit=2)
|
||||
if len(closed_orders) > 0:
|
||||
print("closed_orders:", closed_orders)
|
||||
most_recent_closed_order = closed_orders[-1]
|
||||
diff = lastest_ts - most_recent_closed_order['timestamp']
|
||||
last_buy_signal_cnt = int(diff / tf_mult)
|
||||
|
||||
exit_cond = (lastest_rsi > exit_rsi) and (last_buy_signal_cnt > 10)
|
||||
if exit_cond:
|
||||
print("卖出")
|
||||
order = exchange.create_market_sell_order(symbol, 1)
|
||||
return
|
||||
|
||||
|
||||
def get_hammer(df, lenght):
|
||||
# 影线要大于body的多少倍
|
||||
factor = 2
|
||||
hl_range = df['high'] - df['low']
|
||||
|
||||
body_hi = df.apply(lambda x: max(x['close'], x['open']), axis=1)
|
||||
body_lo = df.apply(lambda x: min(x['close'], x['open']), axis=1)
|
||||
body = body_hi - body_lo
|
||||
|
||||
body_avg = ta.ema(body, lenght=lenght)
|
||||
small_body = body < body_avg
|
||||
|
||||
# 上下影线站body的百分比
|
||||
shadow_percent = 10
|
||||
|
||||
# 上影线
|
||||
up_shadow = df['high'] - body_hi
|
||||
dn_shadow = body_lo - df['low']
|
||||
has_up_shadow = up_shadow > shadow_percent / 100 * body
|
||||
has_dn_shadow = dn_shadow > shadow_percent / 100 * body
|
||||
|
||||
downtrend = df['close'] < ta.ema(df['close'], 50)
|
||||
bullish_hammer = downtrend & small_body & (body > 0) & (
|
||||
dn_shadow >= factor * body) & (has_up_shadow == False)
|
||||
return bullish_hammer
|
||||
|
||||
|
||||
def runBot():
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
# format='%Y-%m-%d %H:%M:%S',
|
||||
df['dt'] = pd.to_datetime(
|
||||
df['timestamp'], unit="ms")
|
||||
|
||||
df['hammer'] = get_hammer(df, 10)
|
||||
|
||||
lastest_hammer = df.iloc[-1, -1]
|
||||
lastest_price = df.iloc[-1, 0]
|
||||
|
||||
print("lastest_price:" + str(lastest_price))
|
||||
print("lastest_hammer:" + str(lastest_hammer))
|
||||
print(df.tail())
|
||||
|
||||
if lastest_hammer:
|
||||
print("购买,做多")
|
||||
notifyMsg("购买,做多")
|
||||
|
||||
|
||||
def longRunBot():
|
||||
common.notifyMsg("任务开始")
|
||||
while True:
|
||||
runBot()
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
longRunBot()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,33 @@
|
||||
[
|
||||
{
|
||||
"id":"01",
|
||||
"name": "MACD-KDJ策略检测|1h",
|
||||
"file": "macd_kdj.py"
|
||||
},
|
||||
{
|
||||
"id":"02",
|
||||
"name": "通知测试|60s",
|
||||
"file": "notify_demo.py"
|
||||
},
|
||||
{
|
||||
"id":"03",
|
||||
"name": "okex|macd交易策略|15m",
|
||||
"file": "online_macd_trade.py"
|
||||
},
|
||||
{
|
||||
"id":"04",
|
||||
"name": "okex|动量交易策略|5m",
|
||||
"file": "momentun_trade.py"
|
||||
},
|
||||
{
|
||||
"id":"05",
|
||||
"name": "gate.io|动量交易策略|5m",
|
||||
"file": "gate_100.py"
|
||||
},
|
||||
{
|
||||
"id":"06",
|
||||
"name": "仅提醒|Vega交易策略|5m",
|
||||
"file": "vega_trade.py"
|
||||
}
|
||||
|
||||
]
|
||||
@@ -0,0 +1,121 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py long
|
||||
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
import pandas as pd
|
||||
import time
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pprint import pprint
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
pd.set_option('display.max_rows', None)
|
||||
|
||||
# import warnings
|
||||
# warnings.filterwarnings('error')
|
||||
|
||||
|
||||
def getMACD(df, lenght=-60):
|
||||
if len(df['close'].values) < 100:
|
||||
return False, None
|
||||
|
||||
close_p = df['close'].values
|
||||
df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
|
||||
fastperiod=12,
|
||||
slowperiod=26,
|
||||
signalperiod=9)
|
||||
return True, df[lenght:]
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def checkData():
|
||||
tag = 'btc'
|
||||
tf_frame = '1h'
|
||||
|
||||
data = common.getDataFromDb_DF(tf_frame, tag, 300)
|
||||
|
||||
# print(data)
|
||||
b, r = getMACD(data)
|
||||
if b:
|
||||
# rlen = len(r)
|
||||
# r = r.copy()
|
||||
# r['dt'] = pd.to_datetime(
|
||||
# r['addtime'], unit='s')
|
||||
|
||||
# r['dt'] = r['dt'].dt.tz_convert('Asia/Shanghai')
|
||||
# print(r)
|
||||
rlen = len(r)
|
||||
|
||||
t_data = r.tail(2)
|
||||
# print(r.tail(2))
|
||||
|
||||
last_pre = t_data.iloc[0]
|
||||
last = t_data.iloc[1]
|
||||
|
||||
# print(last_pre)
|
||||
# print(last)
|
||||
# print(last_pre['addtime'])
|
||||
# print(last_pre['open'])
|
||||
# print(last_pre['high'])
|
||||
# print(last_pre['low'])
|
||||
# print(last_pre['close'])
|
||||
|
||||
# print(last['addtime'])
|
||||
# print(last['open'])
|
||||
# print(last['high'])
|
||||
# print(last['low'])
|
||||
# print(last['close'])
|
||||
|
||||
now = mw.getDateFromNow()
|
||||
if isKdj(last, last_pre):
|
||||
msg = now + "|{}|{}|检查到金叉状态!".format(tag, tf_frame)
|
||||
common.notifyMsg(msg, tf_frame, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
if isDeadFork(last, last_pre):
|
||||
msg = now + "|{}|{}|检查到死叉状态!".format(tag, tf_frame)
|
||||
common.notifyMsg(msg, tf_frame, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
|
||||
def run():
|
||||
checkData()
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
checkData()
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
run()
|
||||
elif func == 'long':
|
||||
longRun()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,505 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py long
|
||||
|
||||
|
||||
# 动量策略交易
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = common.initEx()
|
||||
exchange.load_markets()
|
||||
|
||||
# 默认开仓数据
|
||||
# default_open_num = 70
|
||||
# default_sell_num = 0.003
|
||||
|
||||
default_open = {
|
||||
'BTC/USDT': 30,
|
||||
'XRP/USDT': 30,
|
||||
}
|
||||
|
||||
default_sell = {
|
||||
'BTC/USDT': 0.001,
|
||||
'XRP/USDT': 100,
|
||||
}
|
||||
|
||||
|
||||
# 做多开仓
|
||||
def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
|
||||
common.writeLogEx('------做多----------------------------------', symbol)
|
||||
|
||||
default_open_num = default_open[symbol]
|
||||
# 做多开仓 | 市价
|
||||
data = exchange.createMarketBuyOrder(
|
||||
symbol, default_open_num, {"tdMode": "cross"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 修正小数点位数
|
||||
open_price = common.roundVal(open_price, stop_loss_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
|
||||
# 做多-止损价大于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 - 0.003))
|
||||
|
||||
# property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
|
||||
|
||||
property_val = common.addition(order_data['info'][
|
||||
'accFillSz'], order_data['info']['fee'])
|
||||
property_val = float(property_val)
|
||||
# 可平仓的数量
|
||||
# property_val = common.roundValCeil(
|
||||
# property_val, order_data['info']['accFillSz'])
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(property_val), symbol)
|
||||
|
||||
# 止盈价
|
||||
diff = float(open_price) - float(stop_loss_price)
|
||||
closing_price = float(open_price) + (diff * 1.5)
|
||||
# closing_price = float(open_price) * float((1 + profit))
|
||||
# # 选择盈利多的
|
||||
# if closing_price_c > closing_price:
|
||||
# closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价/止盈价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
# 止损条件单
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
# 止赢条件单
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做多 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做多开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
common.writeLogEx('------做空----------------------------------', symbol)
|
||||
|
||||
# 计算借币卖币多多少,以USDT为基准
|
||||
# sell_num = float(default_open_num) / float(stop_loss_price)
|
||||
# sell_num = round(sell_num, 8)
|
||||
# sell_num = default_sell_num
|
||||
sell_num = default_sell[symbol]
|
||||
|
||||
# 做空开仓 | 市价
|
||||
data = exchange.createMarketSellOrder(
|
||||
symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 修正
|
||||
open_price = common.roundVal(open_price, stop_loss_price)
|
||||
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
|
||||
|
||||
# 做空-止损价小于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 + 0.003))
|
||||
|
||||
# 止盈价
|
||||
diff = float(stop_loss_price) - float(open_price)
|
||||
closing_price = float(open_price) - (diff * 1.5)
|
||||
# closing_price = float(open_price) * float((1 - profit))
|
||||
# 选择盈利多的
|
||||
# if closing_price_c < closing_price:
|
||||
# closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
sl_amount = common.multiply(stop_loss_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
sl_amount = common.addition(sl_amount, 0.1)
|
||||
common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
sl_amount), stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', float(sl_amount), stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
# 设置 -止盈价
|
||||
# tp_amount = closing_price * sell_num
|
||||
tp_amount = common.multiply(closing_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
tp_amount = common.addition(tp_amount, 0.1)
|
||||
common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
tp_amount), closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', float(tp_amount), closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做空 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做空开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=230):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
df.set_index('dt', inplace=True)
|
||||
df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getMACD(df, lenght=-60):
|
||||
if len(df['close'].values) < 100:
|
||||
return False, None
|
||||
|
||||
close_p = df['close'].values
|
||||
df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
|
||||
fastperiod=12,
|
||||
slowperiod=26,
|
||||
signalperiod=9)
|
||||
return df[lenght:]
|
||||
|
||||
|
||||
def getTarget(df):
|
||||
close = df['close'].values
|
||||
df['ema'] = talib.EMA(np.array(close), timeperiod=10)
|
||||
df['ema_200'] = talib.EMA(np.array(close), timeperiod=200)
|
||||
df['ma'] = talib.MA(close, timeperiod=10)
|
||||
df['rsi'] = talib.RSI(close, timeperiod=14)
|
||||
return df
|
||||
|
||||
|
||||
# 多头信号
|
||||
def isBuyCrondSignal(last):
|
||||
if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isBuyFristSignal(data):
|
||||
data = data.sort_values(by=['timestamp'], ascending=False)
|
||||
# print(data)
|
||||
data_len = len(data)
|
||||
signal_num = 0
|
||||
|
||||
first_data = data.iloc[1]
|
||||
# print(1, first_data['close'], first_data['ema'])
|
||||
is_buy_signal = isBuyCrondSignal(first_data)
|
||||
|
||||
for x in range(2, data_len):
|
||||
tmp = data.iloc[x]
|
||||
# print(data.iloc[x])
|
||||
# print(x, tmp['close'], tmp['ema'])
|
||||
if isBuyCrondSignal(tmp):
|
||||
signal_num = + 1
|
||||
|
||||
# print('signal_num:', signal_num)
|
||||
|
||||
if (tmp['ema'] < tmp['ma']):
|
||||
break
|
||||
|
||||
if str(tmp['ema']) == 'nan':
|
||||
break
|
||||
|
||||
print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
|
||||
if is_buy_signal and signal_num == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# 空头信号
|
||||
def isSellCrondSignal(last):
|
||||
if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isSellFristSignal(data):
|
||||
data = data.sort_values(by=['timestamp'], ascending=False)
|
||||
# print(data)
|
||||
data_len = len(data)
|
||||
signal_num = 0
|
||||
|
||||
first_data = data.iloc[1]
|
||||
# print(1, first_data['close'], first_data['ema'])
|
||||
is_sell_signal = isSellCrondSignal(first_data)
|
||||
|
||||
for x in range(2, data_len):
|
||||
tmp = data.iloc[x]
|
||||
# print(data.iloc[x])
|
||||
# print(x, tmp['close'], tmp['ema'])
|
||||
if isSellCrondSignal(tmp):
|
||||
signal_num = + 1
|
||||
|
||||
# print('signal_num:', signal_num)
|
||||
|
||||
if (tmp['ema'] < tmp['ma']):
|
||||
break
|
||||
|
||||
if str(tmp['ema']) == 'nan':
|
||||
break
|
||||
|
||||
print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
|
||||
if is_sell_signal and signal_num == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def monentunTrade(data, tag, timeframe):
|
||||
data = getTarget(data)
|
||||
# print(data)
|
||||
|
||||
key_data = data.tail(3)
|
||||
print(key_data)
|
||||
last_pre = key_data.iloc[0]
|
||||
last = key_data.iloc[1]
|
||||
|
||||
obj = common.MsgTpl()
|
||||
symbol = tag.upper() + '/USDT'
|
||||
obj.setName(symbol)
|
||||
obj.setStrategyName("动量交易策略")
|
||||
obj.setTimeFrame(timeframe)
|
||||
obj.setOpenTime(last['timestamp'] / 1000)
|
||||
|
||||
# 买入信号,并且收盘价要大于200 ema
|
||||
if isBuyFristSignal(data) and last['close'] > last['ema_200']:
|
||||
obj.setStrategicDt('buy')
|
||||
|
||||
# 做多止损点
|
||||
stop_loss_price = last['ma']
|
||||
stop_loss_price = common.roundVal(stop_loss_price, last['open'])
|
||||
buy_status, open_price, closing_price = onBuyOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
|
||||
if buy_status:
|
||||
# 做多止损点
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setContent("动量交易策略做多!")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
# 卖出信号,并且收盘价要小于200 ema
|
||||
# if isSellFristSignal(data) and last['close'] < last['ema_200']:
|
||||
# obj.setStrategicDt('sell')
|
||||
|
||||
# stop_loss_price = last['ma']
|
||||
# stop_loss_price = common.roundVal(stop_loss_price, last['open'])
|
||||
# sell_status, open_price, closing_price = onSellOrder(
|
||||
# symbol, stop_loss_price, 0.005, timeframe)
|
||||
# if sell_status:
|
||||
# obj.setStopLossPrice(str(stop_loss_price))
|
||||
# obj.setOpenPrice(str(open_price))
|
||||
# obj.setClosingPrice(str(closing_price))
|
||||
# obj.setStrategicDt('sell')
|
||||
# obj.setContent("动量交易策略作空!")
|
||||
# msg = obj.toText()
|
||||
# print(msg)
|
||||
# common.notifyMsg(msg, timeframe, tag)
|
||||
# common.writeLog(msg)
|
||||
|
||||
|
||||
def mainProcess(tag, timeframe='15m'):
|
||||
symbol = tag.upper() + '/USDT'
|
||||
data = getOnlineData(symbol, timeframe)
|
||||
|
||||
monentunTrade(data, tag, timeframe)
|
||||
|
||||
|
||||
def foreachList():
|
||||
tag_list = ['btc', 'xrp']
|
||||
for tag in tag_list:
|
||||
mainProcess(tag, '15m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
foreachList()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def debug():
|
||||
while True:
|
||||
mainProcess('btc', '5m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,48 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py long
|
||||
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
import pandas as pd
|
||||
import time
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pprint import pprint
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
def run():
|
||||
print('debug')
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
|
||||
obj = common.MsgTpl()
|
||||
obj.setName("通知测试")
|
||||
obj.setStrategyName("无")
|
||||
obj.setTimeFrame("1m")
|
||||
obj.setContent("60s通知测试")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, '1m', 'debug')
|
||||
common.writeLog(msg)
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
run()
|
||||
if func == 'long':
|
||||
longRun()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,294 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_open
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_close
|
||||
|
||||
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_open
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_cloe
|
||||
|
||||
# 获取仓位数据
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_get_trade
|
||||
|
||||
# API地址
|
||||
# https://www.gate.io/docs/developers/apiv4/zh_CN/#api
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = ccxt.gate({
|
||||
"apiKey": '756e1ff80526cb0ac9620c75680fc506',
|
||||
"secret": '95ac89362056bcf12ce37aabff6eb7ec78185483105f39b4a35e8dc8db8b4d3c',
|
||||
})
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
|
||||
def t_get_trade():
|
||||
data = exchange.fetchPositions()
|
||||
print(data)
|
||||
|
||||
|
||||
def btc_test():
|
||||
closing_price = 24599.9
|
||||
stop_loss_price = 24740.4
|
||||
|
||||
# ---------------------------
|
||||
stop_loss_args = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
print('---------止损价 执行 START ----------------------------------')
|
||||
|
||||
sl_amount = stop_loss_price * 0.001
|
||||
print(amount)
|
||||
data = exchange.create_order(
|
||||
'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
|
||||
print(data)
|
||||
print('---------止损价 执行 END ----------------------------------')
|
||||
|
||||
print('---------止盈价 执行 START ----------------------------------')
|
||||
|
||||
closing_price_args = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
cp_amount = closing_price * 0.001
|
||||
print(amount)
|
||||
data = exchange.create_order(
|
||||
'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
|
||||
print(data)
|
||||
print('---------止盈价 执行 END ----------------------------------')
|
||||
|
||||
|
||||
def testK_buy_open_sz():
|
||||
# 做多开仓
|
||||
|
||||
data = exchange.fetchTicker('BTC/USDT')
|
||||
print(data)
|
||||
# data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
|
||||
def testK_buy_open():
|
||||
# 做多开仓
|
||||
|
||||
# print(dir(exchange))
|
||||
|
||||
# exchange['options']['createMarketBuyOrderRequiresPrice'] = False
|
||||
data = exchange.fetch_ticker('BTC/USDT')
|
||||
print("now price:", data['ask'])
|
||||
|
||||
amount = 0.001
|
||||
price = data['ask']
|
||||
cost = amount * float(price)
|
||||
print('total price:', cost)
|
||||
print('amount price:', amount)
|
||||
# a_amount = round(amount / data['ask'], 5)
|
||||
# print('amount :', amount)
|
||||
# print('amount :', str(amount))
|
||||
|
||||
#
|
||||
data = exchange.createOrder(
|
||||
"BTC/USDT", type="limit", side="buy", amount=amount, price=price, params={'account': "cross_margin"})
|
||||
# data = exchange.createMarketBuyOrder('BTC/USDT', amount)
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# 数据
|
||||
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
|
||||
|
||||
# 查询委托单
|
||||
# order_id = data['info']['ordId']
|
||||
# order_id = '554437054223302656'
|
||||
# data = exchange.fetchOrder(order_id, 'BTC/USDT')
|
||||
# print(data['info'])
|
||||
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
|
||||
|
||||
# open_price = data['info']['avgPx']
|
||||
# print("开仓平均价", open_price)
|
||||
|
||||
# # 止盈价
|
||||
# closing_price = float(open_price) * float((1 + 0.005))
|
||||
# closing_price = common.roundVal(closing_price, open_price)
|
||||
# print("止盈价", closing_price)
|
||||
|
||||
# # 止损价
|
||||
# stop_loss_price = float(open_price) * float((1 - 0.01))
|
||||
# stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
# print("止损价", stop_loss_price)
|
||||
|
||||
# property_val = float(data['info']['accFillSz']) + \
|
||||
# float(data['info']['fee'])
|
||||
|
||||
# # 相同位数
|
||||
# property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
|
||||
# print("可平仓资产", property_val)
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
|
||||
# print('closed_orders', closed_orders)
|
||||
|
||||
# print(exchange.fetchBalance())
|
||||
|
||||
# 可以用,限价单
|
||||
# time.sleep(1)
|
||||
# data = exchange.createLimitSellOrder(
|
||||
# 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
# 止损价
|
||||
# 止盈价
|
||||
|
||||
# exchange_params = {
|
||||
# 'ccy': "USDT",
|
||||
# 'reduceOnly': True,
|
||||
# 'tdMode': "cross",
|
||||
# 'tpOrdPx': "-1",
|
||||
# 'tpTriggerPx': closing_price,
|
||||
# 'slOrdPx': "-1",
|
||||
# 'slTriggerPx': stop_loss_price,
|
||||
# }
|
||||
# print('---------止损价 执行----------------------------------')
|
||||
|
||||
# data = exchange.create_order(
|
||||
# 'BTC/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
# print('---------止盈价 执行----------------------------------')
|
||||
|
||||
# exchange_params = {
|
||||
# 'stopPrice': closing_price,
|
||||
# 'type': 'stopLimit',
|
||||
# }
|
||||
# data = exchange.create_order(
|
||||
# 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
|
||||
def testK_buy_close():
|
||||
# 平多
|
||||
data = exchange.create_limit_buy_order(
|
||||
'DOT/USDT', 1, 5, {"tdMode": "cross"})
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
|
||||
def testK_sell_open():
|
||||
# 做空开仓
|
||||
|
||||
# data = exchange.createMarketSellOrder(
|
||||
# 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
# 数据
|
||||
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
|
||||
|
||||
# # 查询委托单
|
||||
# order_id = data['info']['ordId']
|
||||
order_id = '554516809819832320'
|
||||
data = exchange.fetchOrder(order_id, 'DOT/USDT')
|
||||
print(data['info'])
|
||||
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
|
||||
|
||||
open_price = data['info']['avgPx']
|
||||
|
||||
print("开仓平均价", open_price)
|
||||
|
||||
# 止盈价
|
||||
closing_price = float(open_price) * float((1 - 0.005))
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
print("止盈价", closing_price)
|
||||
|
||||
# 止损价
|
||||
stop_loss_price = float(open_price) * float((1 + 0.01))
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
print("止损价", stop_loss_price)
|
||||
|
||||
property_val = float(data['info']['accFillSz'])
|
||||
# 相同位数
|
||||
print("可平仓资产", property_val)
|
||||
|
||||
# 止损价
|
||||
# 止盈价
|
||||
exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
print('---------止损价 执行----------------------------------')
|
||||
|
||||
data = exchange.create_order(
|
||||
'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
|
||||
def testK_sell_close():
|
||||
# 平多
|
||||
data = exchange.create_limit_buy_order(
|
||||
'DOT/USDT', 1, 5, {"tdMode": "cross"})
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
elif func == 'test':
|
||||
testKdan()
|
||||
elif func == 't_get_trade':
|
||||
t_get_trade()
|
||||
elif func == 't_buy_open':
|
||||
testK_buy_open()
|
||||
elif func == 't_buy_close':
|
||||
testK_buy_close()
|
||||
elif func == 't_sell_open':
|
||||
testK_sell_open()
|
||||
elif func == 't_sell_cloe':
|
||||
testK_sell_close()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,437 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py long
|
||||
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = common.initEx()
|
||||
exchange.load_markets()
|
||||
|
||||
# 默认开仓数据
|
||||
# default_open_num = 10
|
||||
|
||||
default_open = {
|
||||
'ETH/USDT': 30,
|
||||
'DOT/USDT': 30,
|
||||
'CEL/USDT': 30,
|
||||
}
|
||||
|
||||
default_sell = {
|
||||
'ETH/USDT': 0.02,
|
||||
'DOT/USDT': 5,
|
||||
'CEL/USDT': 85,
|
||||
}
|
||||
|
||||
|
||||
# 做多开仓
|
||||
def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
|
||||
common.writeLogEx('------做多----------------------------------', symbol)
|
||||
|
||||
default_open_num = default_open[symbol]
|
||||
# 做多开仓 | 市价
|
||||
data = exchange.createMarketBuyOrder(
|
||||
symbol, default_open_num, {"tdMode": "cross"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 做多-止损价大于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 - 0.003))
|
||||
|
||||
# property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
|
||||
property_val = common.addition(
|
||||
order_data['info']['accFillSz'], order_data['info']['fee'])
|
||||
property_val = float(property_val)
|
||||
|
||||
# 可平仓的数量
|
||||
property_val = common.roundValCeil(
|
||||
property_val, order_data['info']['accFillSz'])
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(property_val), symbol)
|
||||
|
||||
# 止盈价
|
||||
diff = float(open_price) - float(stop_loss_price)
|
||||
closing_price_c = float(open_price) + (diff * 2)
|
||||
closing_price = float(open_price) * float((1 + profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c > closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, stop_loss_price)
|
||||
# stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价/止盈价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
# 止损条件单
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
# 止赢条件单
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止赢数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做多 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做多开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
common.writeLogEx('------做空----------------------------------', symbol)
|
||||
|
||||
# 计算借币卖币多多少,以USDT为基准
|
||||
# sell_num = float(default_open_num) / float(stop_loss_price)
|
||||
# sell_num = round(sell_num, 8)
|
||||
|
||||
sell_num = default_sell[symbol]
|
||||
# 做空开仓 | 市价
|
||||
data = exchange.createMarketSellOrder(
|
||||
symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
|
||||
|
||||
# 做空-止损价小于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 + 0.003))
|
||||
|
||||
# 止盈价
|
||||
diff = float(stop_loss_price) - float(open_price)
|
||||
closing_price_c = float(open_price) - (diff * 2)
|
||||
closing_price = float(open_price) * float((1 - profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c < closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
sl_amount = common.multiply(stop_loss_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
sl_amount = common.addition(sl_amount, 0.1)
|
||||
common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
sl_amount), stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', sl_amount, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
# 设置 -止盈价
|
||||
tp_amount = common.multiply(closing_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
tp_amount = common.addition(tp_amount, 0.1)
|
||||
common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
tp_amount), closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', tp_amount, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做空 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做空开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=230):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
df.set_index('dt', inplace=True)
|
||||
df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getMACD(df, lenght=-60):
|
||||
if len(df['close'].values) < 100:
|
||||
return False, None
|
||||
|
||||
close_p = df['close'].values
|
||||
df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
|
||||
fastperiod=12,
|
||||
slowperiod=26,
|
||||
signalperiod=9)
|
||||
return df[lenght:]
|
||||
|
||||
|
||||
def getEMA(df):
|
||||
close_p = df['close'].values
|
||||
df['ema'] = talib.EMA(np.array(close_p), timeperiod=200)
|
||||
return df
|
||||
|
||||
|
||||
def doneMacd(data, tag, timeframe):
|
||||
data = getEMA(data)
|
||||
|
||||
ma_data = getMACD(data)
|
||||
# alen = len(data)
|
||||
# print(ma_data)
|
||||
|
||||
t_data = ma_data.tail(3)
|
||||
# print(t_data)
|
||||
|
||||
last_pre = t_data.iloc[0]
|
||||
last = t_data.iloc[1]
|
||||
|
||||
# print(last)
|
||||
# print(last.index)
|
||||
|
||||
# print('close:', last['close'], 'ema:', last['ema'])
|
||||
|
||||
obj = common.MsgTpl()
|
||||
symbol = tag.upper() + '/USDT'
|
||||
obj.setName(symbol)
|
||||
obj.setStrategyName("MACD检查")
|
||||
obj.setTimeFrame(timeframe)
|
||||
obj.setOpenTime(last['timestamp'] / 1000)
|
||||
|
||||
now_data = t_data.iloc[2]
|
||||
# print('now_data:', now_data)
|
||||
|
||||
# msg = obj.toText()
|
||||
# print(msg)
|
||||
|
||||
if isKdj(last, last_pre) and last['close'] > last['ema']:
|
||||
# if isKdj(last, last_pre):
|
||||
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['low'], 'buy')
|
||||
# # print('closing_price:', closing_price)
|
||||
|
||||
# 做多止损点
|
||||
stop_loss_price = last_pre['low']
|
||||
buy_status, open_price, closing_price = onBuyOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
|
||||
if buy_status:
|
||||
obj.setStrategicDt('buy')
|
||||
# 做多止损点
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setContent("检查到金叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
if isDeadFork(last, last_pre) and last['close'] < last['ema']:
|
||||
# if isDeadFork(last, last_pre):
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['high'], 'sell')
|
||||
|
||||
# 做空止损点
|
||||
stop_loss_price = last_pre['high']
|
||||
sell_status, open_price, closing_price = onSellOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
if sell_status:
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setStrategicDt('sell')
|
||||
obj.setContent("检查到死叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
|
||||
def mainProcess(tag, timeframe='15m'):
|
||||
symbol = tag.upper() + '/USDT'
|
||||
data = getOnlineData(symbol, timeframe)
|
||||
|
||||
doneMacd(data, tag, timeframe)
|
||||
|
||||
|
||||
def foreachList():
|
||||
tag_list = [
|
||||
'eth', 'dot'
|
||||
]
|
||||
|
||||
for tag in tag_list:
|
||||
mainProcess(tag, '15m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
foreachList()
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def debug():
|
||||
while True:
|
||||
mainProcess('xrp', '1m')
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,273 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_open
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_close
|
||||
|
||||
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_open
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_cloe
|
||||
|
||||
# 获取仓位数据
|
||||
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_get_trade
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = common.initEx()
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
|
||||
def t_get_trade():
|
||||
data = exchange.fetchPositions()
|
||||
print(data)
|
||||
|
||||
|
||||
def btc_test():
|
||||
closing_price = 24599.9
|
||||
stop_loss_price = 24740.4
|
||||
|
||||
# ---------------------------
|
||||
stop_loss_args = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
print('---------止损价 执行 START ----------------------------------')
|
||||
|
||||
sl_amount = stop_loss_price * 0.003
|
||||
print(amount)
|
||||
data = exchange.create_order(
|
||||
'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
|
||||
print(data)
|
||||
print('---------止损价 执行 END ----------------------------------')
|
||||
|
||||
print('---------止盈价 执行 START ----------------------------------')
|
||||
|
||||
closing_price_args = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
cp_amount = closing_price * 0.003
|
||||
print(amount)
|
||||
data = exchange.create_order(
|
||||
'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
|
||||
print(data)
|
||||
print('---------止盈价 执行 END ----------------------------------')
|
||||
|
||||
|
||||
def testK_buy_open_sz():
|
||||
# 做多开仓
|
||||
|
||||
data = exchange.fetchTicker('DOT/USDT')
|
||||
print(data)
|
||||
# data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
|
||||
def testK_buy_open():
|
||||
# 做多开仓
|
||||
|
||||
data = exchange.createMarketBuyOrder(
|
||||
'DOT/USDT', 1, {"tdMode": "cross"})
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# 数据
|
||||
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
|
||||
|
||||
# 查询委托单
|
||||
order_id = data['info']['ordId']
|
||||
# order_id = '554437054223302656'
|
||||
data = exchange.fetchOrder(order_id, 'DOT/USDT')
|
||||
print(data['info'])
|
||||
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
|
||||
|
||||
open_price = data['info']['avgPx']
|
||||
|
||||
print("开仓平均价", open_price)
|
||||
|
||||
# 止盈价
|
||||
closing_price = float(open_price) * float((1 + 0.005))
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
print("止盈价", closing_price)
|
||||
|
||||
# 止损价
|
||||
stop_loss_price = float(open_price) * float((1 - 0.01))
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
print("止损价", stop_loss_price)
|
||||
|
||||
property_val = float(data['info']['accFillSz']) + \
|
||||
float(data['info']['fee'])
|
||||
|
||||
# 相同位数
|
||||
property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
|
||||
print("可平仓资产", property_val)
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
|
||||
# print('closed_orders', closed_orders)
|
||||
|
||||
# print(exchange.fetchBalance())
|
||||
|
||||
# 可以用,限价单
|
||||
# time.sleep(1)
|
||||
# data = exchange.createLimitSellOrder(
|
||||
# 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
# 止损价
|
||||
# 止盈价
|
||||
|
||||
exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
print('---------止损价 执行----------------------------------')
|
||||
|
||||
data = exchange.create_order(
|
||||
'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# print('---------止盈价 执行----------------------------------')
|
||||
|
||||
# exchange_params = {
|
||||
# 'stopPrice': closing_price,
|
||||
# 'type': 'stopLimit',
|
||||
# }
|
||||
# data = exchange.create_order(
|
||||
# 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
|
||||
def testK_buy_close():
|
||||
# 平多
|
||||
data = exchange.create_limit_buy_order(
|
||||
'DOT/USDT', 1, 5, {"tdMode": "cross"})
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
|
||||
def testK_sell_open():
|
||||
# 做空开仓
|
||||
|
||||
# data = exchange.createMarketSellOrder(
|
||||
# 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
|
||||
# print(data)
|
||||
# print(type(data))
|
||||
|
||||
# 数据
|
||||
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
|
||||
|
||||
# # 查询委托单
|
||||
# order_id = data['info']['ordId']
|
||||
order_id = '554516809819832320'
|
||||
data = exchange.fetchOrder(order_id, 'DOT/USDT')
|
||||
print(data['info'])
|
||||
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
|
||||
|
||||
open_price = data['info']['avgPx']
|
||||
|
||||
print("开仓平均价", open_price)
|
||||
|
||||
# 止盈价
|
||||
closing_price = float(open_price) * float((1 - 0.005))
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
print("止盈价", closing_price)
|
||||
|
||||
# 止损价
|
||||
stop_loss_price = float(open_price) * float((1 + 0.01))
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
print("止损价", stop_loss_price)
|
||||
|
||||
property_val = float(data['info']['accFillSz'])
|
||||
# 相同位数
|
||||
print("可平仓资产", property_val)
|
||||
|
||||
# 止损价
|
||||
# 止盈价
|
||||
exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
print('---------止损价 执行----------------------------------')
|
||||
|
||||
data = exchange.create_order(
|
||||
'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
|
||||
def testK_sell_close():
|
||||
# 平多
|
||||
data = exchange.create_limit_buy_order(
|
||||
'DOT/USDT', 1, 5, {"tdMode": "cross"})
|
||||
print(data)
|
||||
print(type(data))
|
||||
|
||||
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
|
||||
# print(closed_orders)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
elif func == 'test':
|
||||
testKdan()
|
||||
elif func == 't_get_trade':
|
||||
t_get_trade()
|
||||
elif func == 't_buy_open':
|
||||
btc_test()
|
||||
elif func == 't_buy_close':
|
||||
testK_buy_close()
|
||||
elif func == 't_sell_open':
|
||||
testK_sell_open()
|
||||
elif func == 't_sell_cloe':
|
||||
testK_sell_close()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,102 @@
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py run
|
||||
|
||||
|
||||
pd.set_option('display.max_rows', None)
|
||||
|
||||
exchange = common.initEx()
|
||||
|
||||
# 查看隐形方法
|
||||
# print(dir(exchange))
|
||||
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
entry_rsi = 30
|
||||
exit_rsi = 40
|
||||
|
||||
|
||||
symbol = 'XRP/USDT'
|
||||
timeframe = '15m'
|
||||
|
||||
tf_mult = exchange.parse_timeframe(timeframe) * 1000
|
||||
|
||||
|
||||
def indicators(data):
|
||||
|
||||
data['rsi'] = data.ta.rsi(length=10)
|
||||
data['ema'] = data.ta.ema(length=200)
|
||||
|
||||
# close_p = data['close'].values
|
||||
# data['rsi'] = talib.RSI(close_p, timeperiod=10)
|
||||
# data['ema'] = talib.EMA(close_p, timeperiod=200)
|
||||
return data
|
||||
|
||||
|
||||
def check_buy_sell_signals(df):
|
||||
last_row_index = len(df.index) - 1
|
||||
lastest_rsi = round(df['rsi'].iloc[-1], 2)
|
||||
lastest_price = round(df['close'].iloc[-1], 5)
|
||||
lastest_ema = round(df['ema'].iloc[-1], 5)
|
||||
lastest_ts = df['timestamp'].iloc[-1]
|
||||
|
||||
msg = "lastest_rsi:" + str(lastest_rsi) + " < entry_rsi:" + str(entry_rsi)
|
||||
msg += ",lastest_price:" + \
|
||||
str(lastest_price) + " > lastest_ema:" + str(lastest_ema)
|
||||
print(msg)
|
||||
|
||||
long_cond = (lastest_rsi < entry_rsi) and (lastest_price > lastest_ema)
|
||||
if long_cond:
|
||||
print("买入")
|
||||
order = exchange.create_market_buy_order(symbol, 1)
|
||||
|
||||
closed_orders = exchange.fetchClosedOrders(symbol, limit=2)
|
||||
if len(closed_orders) > 0:
|
||||
print("closed_orders:", closed_orders)
|
||||
most_recent_closed_order = closed_orders[-1]
|
||||
diff = lastest_ts - most_recent_closed_order['timestamp']
|
||||
last_buy_signal_cnt = int(diff / tf_mult)
|
||||
|
||||
exit_cond = (lastest_rsi > exit_rsi) and (last_buy_signal_cnt > 10)
|
||||
if exit_cond:
|
||||
print("卖出")
|
||||
order = exchange.create_market_sell_order(symbol, 1)
|
||||
return
|
||||
|
||||
|
||||
def runBot():
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
|
||||
df = indicators(df).tail(30)
|
||||
|
||||
check_buy_sell_signals(df)
|
||||
|
||||
|
||||
def longRunBot():
|
||||
while True:
|
||||
runBot()
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
longRunBot()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,536 @@
|
||||
# cd /www/server/mdserver-web && source bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py long
|
||||
|
||||
|
||||
# 动量策略交易
|
||||
################
|
||||
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = common.initEx()
|
||||
exchange.load_markets()
|
||||
|
||||
# 默认开仓数据
|
||||
# default_open_num = 70
|
||||
# default_sell_num = 0.003
|
||||
|
||||
default_open = {
|
||||
'BTC/USDT': 70,
|
||||
'XRP/USDT': 70,
|
||||
}
|
||||
|
||||
default_sell = {
|
||||
'BTC/USDT': 0.003,
|
||||
'XRP/USDT': 185,
|
||||
}
|
||||
|
||||
|
||||
# 做多开仓
|
||||
def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
|
||||
common.writeLogEx('------做多----------------------------------', symbol)
|
||||
|
||||
default_open_num = default_open[symbol]
|
||||
# 做多开仓 | 市价
|
||||
data = exchange.createMarketBuyOrder(
|
||||
symbol, default_open_num, {"tdMode": "cross"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 修正小数点位数
|
||||
open_price = common.roundVal(open_price, stop_loss_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
|
||||
# 做多-止损价大于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 - 0.003))
|
||||
|
||||
# property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
|
||||
|
||||
property_val = common.addition(order_data['info'][
|
||||
'accFillSz'], order_data['info']['fee'])
|
||||
property_val = float(property_val)
|
||||
# 可平仓的数量
|
||||
# property_val = common.roundValCeil(
|
||||
# property_val, order_data['info']['accFillSz'])
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(property_val), symbol)
|
||||
|
||||
# 止盈价
|
||||
diff = float(open_price) - float(stop_loss_price)
|
||||
closing_price = float(open_price) + (diff * 1.5)
|
||||
# closing_price = float(open_price) * float((1 + profit))
|
||||
# # 选择盈利多的
|
||||
# if closing_price_c > closing_price:
|
||||
# closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价/止盈价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
# 止损条件单
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
# 止赢条件单
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做多 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做多开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
common.writeLogEx('------做空----------------------------------', symbol)
|
||||
|
||||
# 计算借币卖币多多少,以USDT为基准
|
||||
# sell_num = float(default_open_num) / float(stop_loss_price)
|
||||
# sell_num = round(sell_num, 8)
|
||||
# sell_num = default_sell_num
|
||||
sell_num = default_sell[symbol]
|
||||
|
||||
# 做空开仓 | 市价
|
||||
data = exchange.createMarketSellOrder(
|
||||
symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 修正
|
||||
open_price = common.roundVal(open_price, stop_loss_price)
|
||||
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
|
||||
|
||||
# 做空-止损价小于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 + 0.003))
|
||||
|
||||
# 止盈价
|
||||
diff = float(stop_loss_price) - float(open_price)
|
||||
closing_price = float(open_price) - (diff * 1.5)
|
||||
# closing_price = float(open_price) * float((1 - profit))
|
||||
# 选择盈利多的
|
||||
# if closing_price_c < closing_price:
|
||||
# closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
sl_amount = common.multiply(stop_loss_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
sl_amount = common.addition(sl_amount, 0.1)
|
||||
common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
sl_amount), stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', float(sl_amount), stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
# 设置 -止盈价
|
||||
# tp_amount = closing_price * sell_num
|
||||
tp_amount = common.multiply(closing_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
tp_amount = common.addition(tp_amount, 0.1)
|
||||
common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
tp_amount), closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', float(tp_amount), closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做空 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做空开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=500):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=500)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
df.set_index('dt', inplace=True)
|
||||
df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getTarget(df):
|
||||
close = df['close'].values
|
||||
|
||||
# vega
|
||||
df['ema144'] = talib.EMA(np.array(close), timeperiod=144)
|
||||
df['ema169'] = talib.EMA(np.array(close), timeperiod=169)
|
||||
|
||||
df['ema288'] = talib.EMA(np.array(close), timeperiod=288)
|
||||
df['ema388'] = talib.EMA(np.array(close), timeperiod=388)
|
||||
|
||||
df['rsi'] = talib.RSI(close, timeperiod=14)
|
||||
return df
|
||||
|
||||
|
||||
# 多头信号
|
||||
def isBuyCrondSignal(last):
|
||||
if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isBuyFristSignal(data):
|
||||
data = data.sort_values(by=['timestamp'], ascending=False)
|
||||
# print(data)
|
||||
data_len = len(data)
|
||||
signal_num = 0
|
||||
|
||||
first_data = data.iloc[1]
|
||||
# print(1, first_data['close'], first_data['ema'])
|
||||
is_buy_signal = isBuyCrondSignal(first_data)
|
||||
|
||||
for x in range(2, data_len):
|
||||
tmp = data.iloc[x]
|
||||
# print(data.iloc[x])
|
||||
# print(x, tmp['close'], tmp['ema'])
|
||||
if isBuyCrondSignal(tmp):
|
||||
signal_num = + 1
|
||||
|
||||
# print('signal_num:', signal_num)
|
||||
|
||||
if (tmp['ema'] < tmp['ma']):
|
||||
break
|
||||
|
||||
if str(tmp['ema']) == 'nan':
|
||||
break
|
||||
|
||||
print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
|
||||
if is_buy_signal and signal_num == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# 空头信号
|
||||
def isSellCrondSignal(last):
|
||||
if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isSellFristSignal(data):
|
||||
data = data.sort_values(by=['timestamp'], ascending=False)
|
||||
# print(data)
|
||||
data_len = len(data)
|
||||
signal_num = 0
|
||||
|
||||
first_data = data.iloc[1]
|
||||
# print(1, first_data['close'], first_data['ema'])
|
||||
is_sell_signal = isSellCrondSignal(first_data)
|
||||
|
||||
for x in range(2, data_len):
|
||||
tmp = data.iloc[x]
|
||||
# print(data.iloc[x])
|
||||
# print(x, tmp['close'], tmp['ema'])
|
||||
if isSellCrondSignal(tmp):
|
||||
signal_num = + 1
|
||||
|
||||
# print('signal_num:', signal_num)
|
||||
|
||||
if (tmp['ema'] < tmp['ma']):
|
||||
break
|
||||
|
||||
if str(tmp['ema']) == 'nan':
|
||||
break
|
||||
|
||||
print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
|
||||
if is_sell_signal and signal_num == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def isDownTrendCover(data, tag, timeframe):
|
||||
# print(tag, timeframe, "开始检测是否下跌趋势")
|
||||
# 下跌趋势回调
|
||||
key_data = data.tail(2)
|
||||
last_pre = key_data.iloc[0]
|
||||
last = key_data.iloc[1]
|
||||
|
||||
pdata = data.tail(11)
|
||||
|
||||
for x in range(10):
|
||||
t = pdata.iloc[x]
|
||||
if t['low'] > t['ema388']:
|
||||
print(tag, timeframe, "上升震荡行情过滤")
|
||||
# print(t)
|
||||
return False
|
||||
|
||||
if last['ema144'] > last['ema388']:
|
||||
return False
|
||||
|
||||
print(tag, timeframe, "检查是下跌趋势!")
|
||||
# 检查是否是下跌趋势
|
||||
if last['high'] > last['ema388']:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def isUpTrendAndCover(data, tag, timeframe):
|
||||
# print(tag, timeframe, "开始检测是否上升趋势")
|
||||
# 上升趋势回调
|
||||
key_data = data.tail(2)
|
||||
# print(key_data)
|
||||
last_pre = key_data.iloc[0]
|
||||
last = key_data.iloc[1]
|
||||
|
||||
pdata = data.tail(11)
|
||||
|
||||
for x in range(10):
|
||||
t = pdata.iloc[x]
|
||||
if t['low'] < t['ema144']:
|
||||
print(tag, timeframe, "上升震荡行情过滤")
|
||||
return False
|
||||
|
||||
if last['ema144'] < last['ema388']:
|
||||
return False
|
||||
|
||||
print(tag, timeframe, "检查是上升趋势")
|
||||
|
||||
# 检查是否是上升趋势
|
||||
if last['low'] < last['ema144']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def vegaTrade(data, tag, timeframe):
|
||||
data = getTarget(data)
|
||||
# print(data)
|
||||
|
||||
key_data = data.tail(2)
|
||||
# print(key_data)
|
||||
# last_pre = key_data.iloc[0]
|
||||
last = key_data.iloc[1]
|
||||
# print(last)
|
||||
|
||||
obj = common.MsgTpl()
|
||||
symbol = tag.upper() + '/USDT'
|
||||
obj.setName(symbol)
|
||||
obj.setStrategyName("VEGA交易策略|仅具有指导作用")
|
||||
obj.setTimeFrame(timeframe)
|
||||
obj.setOpenTime(last['addtime'])
|
||||
|
||||
if isUpTrendAndCover(data, tag, timeframe):
|
||||
obj.setStrategicDt('buy')
|
||||
obj.setContent("VEGA上升趋势!回调做多~建议!")
|
||||
|
||||
msg = obj.toText()
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
if isDownTrendCover(data, tag, timeframe):
|
||||
obj.setStrategicDt('sell')
|
||||
obj.setContent("Vega下跌趋势!回调做空~建议!")
|
||||
msg = obj.toText()
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
|
||||
def mainProcess(tag, timeframe='5m'):
|
||||
symbol = tag.upper() + '/USDT'
|
||||
data = common.getDataFromDb_DF(timeframe, tag.lower())
|
||||
vegaTrade(data, tag, timeframe)
|
||||
return True
|
||||
|
||||
|
||||
def foreachList():
|
||||
tag_list = ['btc', 'xrp', 'eth', 'ltc', 'okb']
|
||||
for tag in tag_list:
|
||||
trame_list = ['5m', '15m', '1h', '4h']
|
||||
for tf in trame_list:
|
||||
mainProcess(tag, tf)
|
||||
# time.sleep(1)
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
try:
|
||||
foreachList()
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print(mw.getTracebackInfo())
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def debug():
|
||||
while True:
|
||||
mainProcess('btc', '5m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
else:
|
||||
print('error')
|
||||
@@ -0,0 +1,128 @@
|
||||
'''
|
||||
pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
|
||||
|
||||
|
||||
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
|
||||
|
||||
|
||||
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
|
||||
'''
|
||||
|
||||
from datetime import datetime
|
||||
import akshare as ak
|
||||
import pandas as pd
|
||||
import backtrader as bt
|
||||
|
||||
|
||||
class BollStrategy(bt.Strategy): # BOLL策略程序
|
||||
params = (("nk", 13), # 求均值的天数
|
||||
('printlog', False),) # 打印log
|
||||
|
||||
def __init__(self): # 初始化
|
||||
self.data_close = self.datas[0].close # 指定价格序列
|
||||
# 初始化交易指令、买卖价格和手续费
|
||||
self.order = None
|
||||
self.buy_price = None
|
||||
self.buy_comm = None
|
||||
# Boll指标计算
|
||||
self.top = bt.indicators.BollingerBands(
|
||||
self.datas[0], period=self.params.nk).top
|
||||
self.bot = bt.indicators.BollingerBands(
|
||||
self.datas[0], period=self.params.nk).bot
|
||||
# 添加移动均线指标
|
||||
self.sma = bt.indicators.SimpleMovingAverage(
|
||||
self.datas[0], period=self.params.nk)
|
||||
|
||||
def next(self): # 买卖策略
|
||||
if self.order: # 检查是否有指令等待执行
|
||||
return
|
||||
# 检查是否持仓
|
||||
"""
|
||||
if not self.position: # 没有持仓
|
||||
if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
|
||||
self.order = self.buy(size=100) # 执行买入
|
||||
else:
|
||||
if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
|
||||
self.order = self.sell(size=100) # 执行卖出
|
||||
"""
|
||||
if not self.position: # 没有持仓
|
||||
if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
|
||||
self.log("BUY CREATE, %.2f" % self.data_close[0])
|
||||
self.order = self.buy() # 执行买入
|
||||
else:
|
||||
if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
|
||||
self.log("SELL CREATE, %.2f" % self.data_close[0])
|
||||
self.order = self.sell() # 执行卖出
|
||||
|
||||
def log(self, txt, dt=None, do_print=False): # 日志函数
|
||||
if self.params.printlog or do_print:
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print('%s, %s' % (dt.isoformat(), txt))
|
||||
|
||||
def notify_order(self, order): # 记录交易执行情况
|
||||
# 如果order为submitted/accepted,返回空
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
return
|
||||
# 指令为buy/sell,报告价格结果
|
||||
if order.status in [order.Completed]:
|
||||
if order.isbuy():
|
||||
self.log(
|
||||
f"买入:\n价格:{order.executed.price},\
|
||||
成本:{order.executed.value},\
|
||||
手续费:{order.executed.comm}"
|
||||
)
|
||||
self.buyprice = order.executed.price
|
||||
self.buycomm = order.executed.comm
|
||||
else:
|
||||
self.log(
|
||||
f"卖出:\n价格:{order.executed.price},\
|
||||
成本: {order.executed.value},\
|
||||
手续费{order.executed.comm}"
|
||||
)
|
||||
self.bar_executed = len(self)
|
||||
|
||||
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
|
||||
self.log("交易失败") # 指令取消/交易失败, 报告结果
|
||||
self.order = None
|
||||
|
||||
def notify_trade(self, trade): # 记录交易收益情况
|
||||
if not trade.isclosed:
|
||||
return
|
||||
self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
|
||||
|
||||
def stop(self): # 回测结束后输出结果
|
||||
self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
|
||||
(self.params.nk, self.broker.getvalue()), do_print=True)
|
||||
|
||||
code = "600036" # 股票代码
|
||||
start_cash = 1000000 # 初始自己为1000000
|
||||
stake = 100 # 单次交易数量为1手
|
||||
commfee = 0.0005 # 佣金为万5
|
||||
sdate = '20210101' # 回测时间段
|
||||
edate = '20220930'
|
||||
cerebro = bt.Cerebro() # 创建回测系统实例
|
||||
# 利用AKShare获取股票的前复权数据的前6列
|
||||
df_qfq = ak.stock_zh_a_hist(
|
||||
symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
|
||||
# 处理字段命名,以符合Backtrader的要求
|
||||
df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
|
||||
# 把date作为日期索引,以符合Backtrader的要求
|
||||
df_qfq.index = pd.to_datetime(df_qfq['date'])
|
||||
start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
|
||||
end_date = datetime.strptime(edate, "%Y%m%d")
|
||||
# start_date=datetime(2022,1,4)
|
||||
# end_date=datetime(2022,9,16)
|
||||
data = bt.feeds.PandasData(
|
||||
dataname=df_qfq, fromdate=start_date, todate=end_date) # 规范化数据格式
|
||||
cerebro.adddata(data) # 加载数据
|
||||
cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
|
||||
cerebro.broker.setcash(start_cash) # broker设置资金
|
||||
cerebro.broker.setcommission(commission=commfee) # broker手续费
|
||||
cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
|
||||
print("期初总资金: %.2f" % start_cash)
|
||||
cerebro.run() # 运行回测
|
||||
end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
|
||||
print("期末总资金: %.2f" % end_value)
|
||||
cerebro.plot()
|
||||
@@ -0,0 +1,154 @@
|
||||
'''
|
||||
pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
|
||||
|
||||
|
||||
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
|
||||
|
||||
|
||||
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
|
||||
'''
|
||||
|
||||
from datetime import datetime
|
||||
import akshare as ak
|
||||
import pandas as pd
|
||||
import backtrader as bt
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/ccxt/strategy")
|
||||
import common
|
||||
|
||||
|
||||
class BollStrategy(bt.Strategy): # BOLL策略程序
|
||||
params = (("nk", 13), # 求均值的天数
|
||||
('printlog', False),) # 打印log
|
||||
|
||||
def __init__(self): # 初始化
|
||||
self.data_close = self.datas[0].close # 指定价格序列
|
||||
# 初始化交易指令、买卖价格和手续费
|
||||
self.order = None
|
||||
self.buy_price = None
|
||||
self.buy_comm = None
|
||||
# Boll指标计算
|
||||
self.top = bt.indicators.BollingerBands(
|
||||
self.datas[0], period=self.params.nk).top
|
||||
self.bot = bt.indicators.BollingerBands(
|
||||
self.datas[0], period=self.params.nk).bot
|
||||
# 添加移动均线指标
|
||||
self.sma = bt.indicators.SimpleMovingAverage(
|
||||
self.datas[0], period=self.params.nk)
|
||||
|
||||
def next(self): # 买卖策略
|
||||
if self.order: # 检查是否有指令等待执行
|
||||
return
|
||||
# 检查是否持仓
|
||||
"""
|
||||
if not self.position: # 没有持仓
|
||||
if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
|
||||
self.order = self.buy(size=100) # 执行买入
|
||||
else:
|
||||
if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
|
||||
self.order = self.sell(size=100) # 执行卖出
|
||||
"""
|
||||
if not self.position: # 没有持仓
|
||||
if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
|
||||
self.log("BUY CREATE, %.2f" % self.data_close[0])
|
||||
self.order = self.buy() # 执行买入
|
||||
else:
|
||||
if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
|
||||
self.log("SELL CREATE, %.2f" % self.data_close[0])
|
||||
self.order = self.sell() # 执行卖出
|
||||
|
||||
def log(self, txt, dt=None, do_print=False): # 日志函数
|
||||
if self.params.printlog or do_print:
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print('%s, %s' % (dt.isoformat(), txt))
|
||||
|
||||
def notify_order(self, order): # 记录交易执行情况
|
||||
# 如果order为submitted/accepted,返回空
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
return
|
||||
# 指令为buy/sell,报告价格结果
|
||||
if order.status in [order.Completed]:
|
||||
if order.isbuy():
|
||||
self.log(
|
||||
f"买入:\n价格:{order.executed.price},\
|
||||
成本:{order.executed.value},\
|
||||
手续费:{order.executed.comm}"
|
||||
)
|
||||
self.buyprice = order.executed.price
|
||||
self.buycomm = order.executed.comm
|
||||
else:
|
||||
self.log(
|
||||
f"卖出:\n价格:{order.executed.price},\
|
||||
成本: {order.executed.value},\
|
||||
手续费{order.executed.comm}"
|
||||
)
|
||||
self.bar_executed = len(self)
|
||||
|
||||
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
|
||||
self.log("交易失败") # 指令取消/交易失败, 报告结果
|
||||
self.order = None
|
||||
|
||||
def notify_trade(self, trade): # 记录交易收益情况
|
||||
if not trade.isclosed:
|
||||
return
|
||||
self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
|
||||
|
||||
def stop(self): # 回测结束后输出结果
|
||||
self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
|
||||
(self.params.nk, self.broker.getvalue()), do_print=True)
|
||||
|
||||
code = "600036" # 股票代码
|
||||
start_cash = 1000000 # 初始自己为1000000
|
||||
stake = 100 # 单次交易数量为1手
|
||||
commfee = 0.0005 # 佣金为万5
|
||||
sdate = '20210101' # 回测时间段
|
||||
edate = '20220930'
|
||||
cerebro = bt.Cerebro() # 创建回测系统实例
|
||||
# 利用AKShare获取股票的前复权数据的前6列
|
||||
# df_qfq = ak.stock_zh_a_hist(
|
||||
# symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
|
||||
# 日期 开盘 收盘 最高 最低 成交量
|
||||
# 0 2021-01-04 40.46 40.40 41.01 39.25 1549523
|
||||
# 1 2021-01-05 39.99 39.41 40.03 38.67 1387177
|
||||
# 2 2021-01-06 39.33 41.38 41.43 39.23 1200646
|
||||
# 3 2021-01-07 41.52 43.13 43.20 41.49 1078322
|
||||
# 4 2021-01-08 43.52 43.83 44.25 43.04 1299595
|
||||
# .. ... ... ... ... ... ...
|
||||
# 420 2022-09-26 34.02 33.95 34.52 33.84 525491
|
||||
# 421 2022-09-27 33.89 33.90 34.05 33.44 475414
|
||||
# 422 2022-09-28 33.79 33.67 34.00 33.42 487299
|
||||
# 423 2022-09-29 33.99 33.15 34.32 33.00 617859
|
||||
# 424 2022-09-30 33.26 33.65 33.95 33.12 539717
|
||||
|
||||
tag = 'btc'
|
||||
symbol = tag.upper() + '/USDT'
|
||||
df_qfq = common.getDataFromDb_DF('15m', tag.lower())
|
||||
print(df_qfq)
|
||||
|
||||
# # 处理字段命名,以符合Backtrader的要求
|
||||
# df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
|
||||
# # 把date作为日期索引,以符合Backtrader的要求
|
||||
# df_qfq.index = pd.to_datetime(df_qfq['date'])
|
||||
# start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
|
||||
# end_date = datetime.strptime(edate, "%Y%m%d")
|
||||
start_date = datetime(2023, 4, 6)
|
||||
end_date = datetime(2023, 4, 9)
|
||||
data = bt.feeds.PandasData(dataname=df_qfq, fromdate=start_date,
|
||||
todate=end_date) # 规范化数据格式
|
||||
|
||||
cerebro.adddata(data) # 加载数据
|
||||
cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
|
||||
cerebro.broker.setcash(start_cash) # broker设置资金
|
||||
cerebro.broker.setcommission(commission=commfee) # broker手续费
|
||||
cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
|
||||
print("期初总资金: %.2f" % start_cash)
|
||||
cerebro.run() # 运行回测
|
||||
end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
|
||||
print("期末总资金: %.2f" % end_value)
|
||||
# cerebro.plot(style='candle')
|
||||
@@ -0,0 +1,61 @@
|
||||
# coding:utf-8
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import order, record, symbol
|
||||
|
||||
'''
|
||||
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
|
||||
|
||||
|
||||
cd /www/server/mdserver-web && source bin/activate && source activate catalys
|
||||
|
||||
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
|
||||
catalyst ingest-exchange -x binance -i btc_usdt -f minute
|
||||
|
||||
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
|
||||
'''
|
||||
|
||||
|
||||
def initialize(context):
|
||||
# 初始化
|
||||
context.asset = symbol('btc_usdt')
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# 循环策略
|
||||
order(context.asset, 1)
|
||||
record(btc=data.current(context.asset, 'price'))
|
||||
|
||||
|
||||
def analyze(context, perf):
|
||||
|
||||
print(perf.portfolio_value)
|
||||
|
||||
ax1 = plt.subplot(211)
|
||||
perf.portfolio_value.plot(ax=ax1)
|
||||
|
||||
ax1.set_ylabel('portfolio value')
|
||||
|
||||
ax2 = plt.subplot(212, sharex=ax1)
|
||||
perf.btc.plot(ax=ax2)
|
||||
ax2.set_ylabel('bitcoin value')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_algorithm(
|
||||
capital_base=10000,
|
||||
data_frequency='daily',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='binance',
|
||||
quote_currenty='usdt',
|
||||
start=pd.to_datetime("2018-01-01", utc=True),
|
||||
end=pd.to_datetime("2018-10-01", utc=True),
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS `ct_xx1_xx2` (
|
||||
`addtime` BIGINT(20) not NULL,
|
||||
`open` float NOT NULL,
|
||||
`high` float NOT NULL,
|
||||
`low` float NOT NULL,
|
||||
`close` float NOT NULL,
|
||||
`vol` float NOT NULL,
|
||||
UNIQUE KEY `addtime` (`addtime`)
|
||||
);
|
||||
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS `ct_order_list` (
|
||||
`id` BIGINT(20) not NULL,
|
||||
'strategy_name' varchar(50) NULL,
|
||||
`symbol` varchar(50) NOT NULL,
|
||||
`fee` float NOT NULL,
|
||||
`price` float NOT NULL,
|
||||
`closing_price` float NOT NULL comment '',
|
||||
`profit` float NOT NULL,
|
||||
`source` TEXT,
|
||||
`addtime` BIGINT(20) not NULL
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
[program:{$NAME}]
|
||||
command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 {$ABS_FILE} long"
|
||||
directory={$RUN_ROOT}
|
||||
autorestart=true
|
||||
startsecs=3
|
||||
startretries=3
|
||||
stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
|
||||
stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
|
||||
stdout_logfile_maxbytes=2MB
|
||||
stderr_logfile_maxbytes=2MB
|
||||
user=root
|
||||
priority=999
|
||||
numprocs=1
|
||||
process_name=%(program_name)s
|
||||
@@ -0,0 +1,14 @@
|
||||
[program:{$NAME}]
|
||||
command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long"
|
||||
directory={$RUN_ROOT}
|
||||
autorestart=true
|
||||
startsecs=3
|
||||
startretries=3
|
||||
stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
|
||||
stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
|
||||
stdout_logfile_maxbytes=2MB
|
||||
stderr_logfile_maxbytes=2MB
|
||||
user=root
|
||||
priority=999
|
||||
numprocs=1
|
||||
process_name=%(program_name)s
|
||||
@@ -0,0 +1,350 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace(
|
||||
'{$SERVER_APP}', service_path + '/' + getPluginName())
|
||||
return content
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
mysqlMsg = str(mysqlMsg)
|
||||
if "MySQLdb" in mysqlMsg:
|
||||
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
|
||||
if "2002," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "2003," in mysqlMsg:
|
||||
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
if "using password:" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库密码错误')
|
||||
if "1045" in mysqlMsg:
|
||||
return mw.returnJson(False, '连接错误!')
|
||||
if "SQL syntax" in mysqlMsg:
|
||||
return mw.returnJson(False, 'SQL语法错误!')
|
||||
if "Connection refused" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库用户不存在!')
|
||||
if "1007," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库已经存在!')
|
||||
return None
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def writeConf(data):
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
mw.writeFile(cfg_path, json.dumps(data))
|
||||
return True
|
||||
|
||||
|
||||
def getDbConf():
|
||||
data = getConfigData()
|
||||
if 'db' in data:
|
||||
return mw.returnJson(True, 'ok', data['db'])
|
||||
return mw.returnJson(False, 'ok', {})
|
||||
|
||||
|
||||
def getUserConf():
|
||||
data = getConfigData()
|
||||
if 'user' in data:
|
||||
return mw.returnJson(True, 'ok', data['user'])
|
||||
return mw.returnJson(False, 'ok', {})
|
||||
|
||||
|
||||
def restartSup():
|
||||
cmd = 'python3 plugins/supervisor/index.py restart'
|
||||
mw.execShell(cmd)
|
||||
|
||||
|
||||
def restartSupDst(name):
|
||||
cmd = 'python3 plugins/supervisor/index.py restart_job {"name":"' + \
|
||||
name + '","status":"stop"}'
|
||||
mw.execShell(cmd)
|
||||
|
||||
|
||||
def syncDataAddTaskUninstall():
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnJson(False, '需要安装并启动supervisor插件')
|
||||
|
||||
name = "ct_task"
|
||||
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
if os.path.exists(sup_task_dst):
|
||||
mw.execShell('rm -rf ' + sup_task_dst)
|
||||
|
||||
restartSup()
|
||||
return mw.returnJson(True, '删除同步数据任务成功!')
|
||||
|
||||
|
||||
def syncDataAddTaskInstall():
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnJson(False, '需要安装并启动supervisor插件')
|
||||
|
||||
name = "ct_task"
|
||||
sup_task_tpl = getPluginDir() + '/conf/sup_task.tpl'
|
||||
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
content = mw.readFile(sup_task_tpl)
|
||||
content = content.replace(
|
||||
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
|
||||
content = content.replace(
|
||||
'{$SUP_ROOT}', sup_path)
|
||||
content = content.replace(
|
||||
'{$NAME}', name)
|
||||
|
||||
mw.writeFile(sup_task_dst, content)
|
||||
restartSup()
|
||||
return mw.returnJson(True, '添加同步数据任务成功!')
|
||||
|
||||
|
||||
def syncDataAddTask():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['check'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
if args['check'] == "0":
|
||||
return syncDataAddTaskUninstall()
|
||||
return syncDataAddTaskInstall()
|
||||
|
||||
|
||||
def syncDataDelete():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['token'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
del_token = args['token']
|
||||
data = getConfigData()
|
||||
|
||||
if 'token' in data:
|
||||
data['token'].remove(del_token)
|
||||
writeConf(data)
|
||||
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
# callback ---------------------------------- start
|
||||
def get_datasource_logs(args):
|
||||
log_file = getServerDir() + '/logs/datasource.log'
|
||||
if not os.path.exists(log_file):
|
||||
return '暂无日志'
|
||||
data = mw.getLastLine(log_file, 10)
|
||||
return data
|
||||
|
||||
|
||||
def get_strategy_logs(args):
|
||||
log_file = getServerDir() + '/logs/strategy.log'
|
||||
if not os.path.exists(log_file):
|
||||
return '暂无日志'
|
||||
data = mw.getLastLine(log_file, 10)
|
||||
return data
|
||||
|
||||
|
||||
def save_body(args):
|
||||
|
||||
path = args['path']
|
||||
encoding = args['encoding']
|
||||
data = args['data']
|
||||
|
||||
tag = args['tag']
|
||||
|
||||
if not os.path.exists(path):
|
||||
return mw.returnData(False, '文件不存在')
|
||||
try:
|
||||
if encoding == 'ascii':
|
||||
encoding = 'utf-8'
|
||||
|
||||
data = data.encode(
|
||||
encoding, errors='ignore').decode(encoding)
|
||||
|
||||
fp = open(path, 'w+', encoding=encoding)
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
|
||||
set_strategy_restart({'id': tag})
|
||||
return mw.returnData(True, '文件保存成功')
|
||||
except Exception as ex:
|
||||
return mw.returnData(False, '文件保存错误:' + str(ex))
|
||||
|
||||
|
||||
def get_strategy_path(args):
|
||||
abs_id = args['id']
|
||||
name = "ct_strategy_" + abs_id
|
||||
|
||||
abs_file = get_strategy_absfile(abs_id)
|
||||
return mw.returnData(True, abs_file)
|
||||
|
||||
|
||||
def set_strategy_restart(args):
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnData(False, '需要安装并启动supervisor插件')
|
||||
|
||||
abs_id = args['id']
|
||||
name = "ct_strategy_" + abs_id
|
||||
|
||||
sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
|
||||
if not os.path.exists(sup_strategy_dst):
|
||||
return mw.returnData(False, '策略任务' + abs_id + '未添加!')
|
||||
|
||||
restartSupDst(name)
|
||||
return mw.returnData(True, '重启策略任务' + abs_id + '成功!')
|
||||
|
||||
|
||||
def set_strategy_status(args):
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnData(False, '需要安装并启动supervisor插件')
|
||||
|
||||
abs_id = args['id']
|
||||
name = "ct_strategy_" + abs_id
|
||||
sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
|
||||
if args['status'] == 'stop':
|
||||
if os.path.exists(sup_strategy_dst):
|
||||
os.remove(sup_strategy_dst)
|
||||
restartSup()
|
||||
return mw.returnData(True, '删除策略任务' + abs_id + '成功!')
|
||||
|
||||
abs_file = get_strategy_absfile(abs_id)
|
||||
sup_strategy_tpl = getPluginDir() + '/conf/sup_strategy.tpl'
|
||||
|
||||
content = mw.readFile(sup_strategy_tpl)
|
||||
content = content.replace(
|
||||
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
|
||||
content = content.replace(
|
||||
'{$SUP_ROOT}', sup_path)
|
||||
content = content.replace(
|
||||
'{$NAME}', name)
|
||||
content = content.replace(
|
||||
'{$ABS_FILE}', abs_file)
|
||||
|
||||
mw.writeFile(sup_strategy_dst, content)
|
||||
restartSup()
|
||||
return mw.returnData(True, '添加策略任务' + abs_id + '成功!')
|
||||
|
||||
|
||||
def get_strategy_absfile(abs_id):
|
||||
info = getPluginDir() + '/ccxt/strategy/info.json'
|
||||
info = json.loads(mw.readFile(info))
|
||||
|
||||
path = getPluginDir() + '/ccxt/strategy'
|
||||
for x in range(len(info)):
|
||||
if info[x]['id'] == abs_id:
|
||||
return path + '/' + info[x]['file']
|
||||
|
||||
return path + '/abs.py'
|
||||
|
||||
|
||||
def get_strategy_list(args):
|
||||
info = getPluginDir() + '/ccxt/strategy/info.json'
|
||||
info = json.loads(mw.readFile(info))
|
||||
|
||||
st_path = mw.getServerDir() + '/supervisor/conf.d'
|
||||
|
||||
page = 1
|
||||
page_size = 5
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
dlist_sum = len(info)
|
||||
|
||||
page_start = int((page - 1) * page_size)
|
||||
page_end = page_start + page_size
|
||||
|
||||
if page_end >= dlist_sum:
|
||||
ret_data = info[page_start:]
|
||||
else:
|
||||
ret_data = info[page_start:page_end]
|
||||
|
||||
for x in range(len(ret_data)):
|
||||
strategy_dst = st_path + '/ct_strategy_' + ret_data[x]['id'] + '.ini'
|
||||
if os.path.exists(strategy_dst):
|
||||
ret_data[x]['status'] = 'start'
|
||||
else:
|
||||
ret_data[x]['status'] = 'stop'
|
||||
|
||||
data['data'] = ret_data
|
||||
data['args'] = args
|
||||
data['list'] = mw.getPage(
|
||||
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'getStrategyList'})
|
||||
|
||||
return data
|
||||
# callback ---------------------------------- end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Executable
+26
@@ -0,0 +1,26 @@
|
||||
<style type="text/css">
|
||||
.conf_p span{width: 100px;}
|
||||
.code{padding: 20px 5px;border: 1px solid #e1e1e1;background: #fcfcfc;border-radius: 4px;line-height: 24px;}
|
||||
.code span {display: block;margin-left: 15px;margin-bottom: 0;}
|
||||
</style>
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('cryptocurrency_trade');">服务</p>
|
||||
<p onclick="dbConf();">数据库配置</p>
|
||||
<p onclick="syncDataList();">同步数据配置</p>
|
||||
<p onclick="userConf();">账户配置</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
// resetPluginWinWidth(480);
|
||||
resetPluginWinHeight(400);
|
||||
$.getScript( "/plugins/file?name=cryptocurrency_trade&f=static/js/cryptocurrency_trade.js",function(){
|
||||
pluginService('cryptocurrency_trade');
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,347 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace(
|
||||
'{$SERVER_APP}', service_path + '/' + getPluginName())
|
||||
return content
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
mysqlMsg = str(mysqlMsg)
|
||||
if "MySQLdb" in mysqlMsg:
|
||||
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
|
||||
if "2002," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "2003," in mysqlMsg:
|
||||
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
if "using password:" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库密码错误')
|
||||
if "1045" in mysqlMsg:
|
||||
return mw.returnJson(False, '连接错误!')
|
||||
if "SQL syntax" in mysqlMsg:
|
||||
return mw.returnJson(False, 'SQL语法错误!')
|
||||
if "Connection refused" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库用户不存在!')
|
||||
if "1007" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库已经存在!')
|
||||
return None
|
||||
|
||||
|
||||
def initDreplace():
|
||||
log_dir = getServerDir() + '/logs'
|
||||
if not os.path.exists(log_dir):
|
||||
d = mw.execShell('mkdir -p ' + log_dir)
|
||||
|
||||
data = getConfigData()
|
||||
data['token'] = ['btc']
|
||||
writeConf(data)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def status():
|
||||
initDreplace()
|
||||
return 'start'
|
||||
|
||||
|
||||
def start():
|
||||
syncDataAddTaskInstall()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def stop():
|
||||
syncDataAddTaskUninstall()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def op():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def writeConf(data):
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
mw.writeFile(cfg_path, json.dumps(data))
|
||||
return True
|
||||
|
||||
|
||||
def getDbConf():
|
||||
data = getConfigData()
|
||||
if 'db' in data:
|
||||
return mw.returnJson(True, 'ok', data['db'])
|
||||
return mw.returnJson(False, 'ok', {})
|
||||
|
||||
|
||||
def setDbConf():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['db_host', 'db_port',
|
||||
'db_name', 'db_user', 'db_pass'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
data = getConfigData()
|
||||
data['db'] = args
|
||||
writeConf(data)
|
||||
|
||||
db = mw.getMyORM()
|
||||
|
||||
db.setHost(args['db_host'])
|
||||
db.setPort(args['db_port'])
|
||||
db.setUser(args['db_user'])
|
||||
db.setPwd(args['db_pass'])
|
||||
testdata = db.query('select version()')
|
||||
|
||||
isError = isSqlError(testdata)
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
return mw.returnJson(True, '保存成功,并连通成功!', [])
|
||||
|
||||
|
||||
def getUserConf():
|
||||
data = getConfigData()
|
||||
if 'user' in data:
|
||||
return mw.returnJson(True, 'ok', data['user'])
|
||||
return mw.returnJson(False, 'ok', {})
|
||||
|
||||
|
||||
def getUserConf():
|
||||
data = getConfigData()
|
||||
if 'user' in data:
|
||||
try:
|
||||
udata = mw.deDoubleCrypt('mw', data['user'])
|
||||
udata = json.loads(udata)
|
||||
|
||||
return mw.returnJson(True, 'ok', udata)
|
||||
except Exception as e:
|
||||
pass
|
||||
return mw.returnJson(False, 'ok', {})
|
||||
|
||||
|
||||
def setUserConf():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['app_key', 'secret',
|
||||
'password', 'uid', 'exchange'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
data = getConfigData()
|
||||
data['user'] = mw.enDoubleCrypt('mw', json.dumps(args))
|
||||
writeConf(data)
|
||||
|
||||
return mw.returnJson(True, '保存成功!', [])
|
||||
|
||||
|
||||
def syncDataList():
|
||||
data = getConfigData()
|
||||
|
||||
name = "ct_task"
|
||||
if 'token' in data:
|
||||
|
||||
rdata = {}
|
||||
rdata['task_status'] = False
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
if os.path.exists(sup_task_dst):
|
||||
rdata['task_status'] = True
|
||||
|
||||
rdata['list'] = data['token']
|
||||
return mw.returnJson(True, 'ok', rdata)
|
||||
return mw.returnJson(False, 'ok')
|
||||
|
||||
|
||||
def syncDataAdd():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['token'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
add_token = args['token']
|
||||
data = getConfigData()
|
||||
|
||||
if 'token' in data and data['token']:
|
||||
if not add_token in data['token']:
|
||||
data['token'].append(add_token)
|
||||
else:
|
||||
data['token'] = [add_token]
|
||||
writeConf(data)
|
||||
|
||||
return mw.returnJson(True, '保存成功!')
|
||||
|
||||
|
||||
def restartSup():
|
||||
cmd = 'python3 plugins/supervisor/index.py restart'
|
||||
mw.execShell(cmd)
|
||||
|
||||
|
||||
def restartSupDst(name):
|
||||
cmd = 'python3 plugins/supervisor/index.py restart_job {"name":"' + \
|
||||
name + '","status":"stop"}'
|
||||
mw.execShell(cmd)
|
||||
|
||||
|
||||
def syncDataAddTaskUninstall():
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnJson(False, '需要安装并启动supervisor插件')
|
||||
|
||||
name = "ct_task"
|
||||
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
if os.path.exists(sup_task_dst):
|
||||
mw.execShell('rm -rf ' + sup_task_dst)
|
||||
|
||||
restartSup()
|
||||
return mw.returnJson(True, '删除同步数据任务成功!')
|
||||
|
||||
|
||||
def syncDataAddTaskInstall():
|
||||
sup_path = mw.getServerDir() + '/supervisor'
|
||||
if not os.path.exists(sup_path):
|
||||
return mw.returnJson(False, '需要安装并启动supervisor插件')
|
||||
|
||||
name = "ct_task"
|
||||
sup_task_tpl = getPluginDir() + '/conf/sup_task.tpl'
|
||||
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
|
||||
content = mw.readFile(sup_task_tpl)
|
||||
content = content.replace(
|
||||
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
|
||||
content = content.replace(
|
||||
'{$SUP_ROOT}', sup_path)
|
||||
content = content.replace(
|
||||
'{$NAME}', name)
|
||||
|
||||
mw.writeFile(sup_task_dst, content)
|
||||
restartSup()
|
||||
return mw.returnJson(True, '添加同步数据任务成功!')
|
||||
|
||||
|
||||
def syncDataAddTask():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['check'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
if args['check'] == "0":
|
||||
return syncDataAddTaskUninstall()
|
||||
return syncDataAddTaskInstall()
|
||||
|
||||
|
||||
def syncDataDelete():
|
||||
args = getArgs()
|
||||
data_args = checkArgs(args, ['token'])
|
||||
if not data_args[0]:
|
||||
return data_args[1]
|
||||
|
||||
del_token = args['token']
|
||||
data = getConfigData()
|
||||
|
||||
if 'token' in data:
|
||||
data['token'].remove(del_token)
|
||||
writeConf(data)
|
||||
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(op())
|
||||
elif func == 'reload':
|
||||
print(op())
|
||||
elif func == 'get_db_conf':
|
||||
print(getDbConf())
|
||||
elif func == 'set_db_conf':
|
||||
print(setDbConf())
|
||||
elif func == 'get_user_conf':
|
||||
print(getUserConf())
|
||||
elif func == 'set_user_conf':
|
||||
print(setUserConf())
|
||||
elif func == 'sync_data_list':
|
||||
print(syncDataList())
|
||||
elif func == 'sync_data_add':
|
||||
print(syncDataAdd())
|
||||
elif func == 'sync_data_delete':
|
||||
print(syncDataDelete())
|
||||
elif func == 'sync_data_add_task':
|
||||
print(syncDataAddTask())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"hook":[
|
||||
{
|
||||
"tag":"menu",
|
||||
"menu": {
|
||||
"title":"量化交易",
|
||||
"name":"cryptocurrency_trade",
|
||||
"path":"static/html/index.html",
|
||||
"css_path":"static/css/cryptocurrency_trade.css",
|
||||
"js_path":"static/js/cryptocurrency_trade.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag":"global_static",
|
||||
"global_static": {
|
||||
"title":"量化交易",
|
||||
"name":"cryptocurrency_trade",
|
||||
"css_path":"static/css/ico.css"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ps": "基于CCXT的数字货币量化交易插件",
|
||||
"name": "cryptocurrency_trade",
|
||||
"title": "量化交易",
|
||||
"versions": ["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/cryptocurrency_trade",
|
||||
"path":"server/cryptocurrency_trade",
|
||||
"author": "midoks",
|
||||
"date": "2022-02-25",
|
||||
"home": "https://github.com/ccxt/ccxt",
|
||||
"type": "量化交易",
|
||||
"shell": "install.sh",
|
||||
"pid": "5",
|
||||
"sort": 7
|
||||
}
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
# cd /www/server/mdserver-web/plugins/cryptocurrency_trade && bash install.sh install
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# pip3 install ccxt
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
pip3 install ccxt
|
||||
pip3 install pandas
|
||||
pip3 install pandas_ta
|
||||
pip3 install pyTelegramBotAPI
|
||||
pip3 install catalyst
|
||||
pip3 install matplotlib==3.2.2
|
||||
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/source/cryptocurrency_trade
|
||||
mkdir -p $serverPath/cryptocurrency_trade
|
||||
echo "${VERSION}" > $serverPath/cryptocurrency_trade/version.pl
|
||||
|
||||
if [ ! -f $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz ];then
|
||||
wget -O $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz https://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -d $serverPath/source/cryptocurrency_trade/ta-lib ];then
|
||||
cd $serverPath/source/cryptocurrency_trade/ta-lib && tar -xzf ta-lib-0.4.0-src.tar.gz
|
||||
cd ta-lib && ./configure --prefix=/usr && make && make install
|
||||
rm -rf $serverPath/source/cryptocurrency_trade/ta-lib
|
||||
|
||||
pip3 install ta-lib
|
||||
fi
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py start
|
||||
echo '安装完成'
|
||||
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf $serverPath/cryptocurrency_trade
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py stop
|
||||
echo "卸载完成"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
.screen-all {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.s-right .panel-default{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.s-left .panel-default{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
/* menu start */
|
||||
.menu .current .menu_plugin_cryptocurrency_trade:hover {
|
||||
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png");
|
||||
}
|
||||
|
||||
.menu .menu_plugin_cryptocurrency_trade {
|
||||
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png");
|
||||
}
|
||||
|
||||
/* menu end */
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
<div class="main-content">
|
||||
<div class="screen-all">
|
||||
<div class="content-screen safe bgw mtb15 ptb10 radius4 row" style="height: 300px;">
|
||||
<!-- start -->
|
||||
|
||||
<div class="s-left col-md-8" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">订单数据</div>
|
||||
<div class="panel-body">Panel content</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">交易图表</div>
|
||||
<div class="panel-body" style="padding: 0px;">
|
||||
<div></div>
|
||||
<div id="k_echarts" style="width: auto;height:400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">策略交易日志</div>
|
||||
<div class="panel-body" style="padding: 0px;">
|
||||
<pre id="strategy_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="s-right col-md-4" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">策略配置</div>
|
||||
<div class="panel-body divtable pd15 relative" style="padding: 2px 2px 5px;">
|
||||
<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20">序列</th>
|
||||
<th width="120">说明</th>
|
||||
<th width="10">状态</th>
|
||||
<th style="text-align: right;" width="50">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="strategy_list"></tbody>
|
||||
</table>
|
||||
<div class="dataTables_paginate paging_bootstrap pagination">
|
||||
<ul id="strategy_list_page" class="page"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">数据源日志</div>
|
||||
<div class="panel-body" style="padding: 0px;">
|
||||
<pre id="datasource_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- end -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,964 @@
|
||||
|
||||
function appPost(method,args,callback, title){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var _title = '正在获取...';
|
||||
if (typeof(title) != 'undefined'){
|
||||
_title = title;
|
||||
}
|
||||
|
||||
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function appPostN(method,args,callback, title){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var _title = '正在获取...';
|
||||
if (typeof(title) != 'undefined'){
|
||||
_title = title;
|
||||
}
|
||||
$.post('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}, function(data) {
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function appAsyncPost(method,args){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
return syncPost('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args});
|
||||
}
|
||||
|
||||
|
||||
function appPostCallbak(method, args,callback, script){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cryptocurrency_trade';
|
||||
req_data['func'] = method;
|
||||
|
||||
if (typeof(script) != 'undefined'){
|
||||
req_data['script'] = script;
|
||||
}
|
||||
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function appPostCallbakNoMsg(method, args,callback, script){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cryptocurrency_trade';
|
||||
req_data['func'] = method;
|
||||
|
||||
if (typeof(script) != 'undefined'){
|
||||
req_data['script'] = script;
|
||||
} else {
|
||||
req_data['script'] = req_data['name'];
|
||||
}
|
||||
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function dbConf(){
|
||||
appPost('get_db_conf','',function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
var db_host = '127.0.0.1';
|
||||
var db_port = '3306';
|
||||
var db_name = 'cryptocurrency_trade';
|
||||
var db_user = 'cryptocurrency_trade';
|
||||
var db_pass = 'cryptocurrency_trade';
|
||||
if(rdata['status']){
|
||||
db_data = rdata['data'];
|
||||
db_host = db_data['db_host'];
|
||||
db_port = db_data['db_port'];
|
||||
db_name = db_data['db_name'];
|
||||
db_user = db_data['db_user'];
|
||||
db_pass = db_data['db_pass'];
|
||||
}
|
||||
|
||||
var mlist = '';
|
||||
mlist += '<p><span>数据库地址</span><input style="width: 250px;" class="bt-input-text mr5" name="db_host" value="'+db_host+'" type="text"></p>'
|
||||
mlist += '<p><span>数据库端口</span><input style="width: 250px;" class="bt-input-text mr5" name="db_port" value="'+db_port+'" type="text"></p>'
|
||||
mlist += '<p><span>数据库名称</span><input style="width: 250px;" class="bt-input-text mr5" name="db_name" value="'+db_name+'" type="text"></p>'
|
||||
mlist += '<p><span>用户名</span><input style="width: 250px;" class="bt-input-text mr5" name="db_user" value="'+db_user+'" type="text"></p>'
|
||||
mlist += '<p><span>密码</span><input style="width: 250px;" class="bt-input-text mr5" name="db_pass" value="'+db_pass+'" type="text"></p>'
|
||||
|
||||
var option = '<style>.conf_p p{margin-bottom: 2px}</style>\
|
||||
<div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm" onclick="submitDbConf()">保存</button>\
|
||||
</div>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(option);
|
||||
});
|
||||
}
|
||||
|
||||
function submitDbConf(){
|
||||
|
||||
var pull_data = {};
|
||||
|
||||
pull_data['db_host'] = $('input[name="db_host"]').val();
|
||||
pull_data['db_port'] = $('input[name="db_port"]').val();
|
||||
pull_data['db_name'] = $('input[name="db_name"]').val();
|
||||
pull_data['db_user'] = $('input[name="db_user"]').val();
|
||||
pull_data['db_pass'] = $('input[name="db_pass"]').val();
|
||||
|
||||
appPost('set_db_conf',pull_data,function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function userConf(){
|
||||
appPost('get_user_conf','',function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var app_key = 'app_key';
|
||||
var secret = 'secret';
|
||||
var password = 'password';
|
||||
var uid = 'uid';
|
||||
var exchange = 'okex';
|
||||
if(rdata['status']){
|
||||
db_data = rdata['data'];
|
||||
app_key = db_data['app_key'];
|
||||
secret = db_data['secret'];
|
||||
password = db_data['password'];
|
||||
uid = db_data['uid'];
|
||||
exchange = db_data['exchange'];;
|
||||
}
|
||||
|
||||
var mlist = '';
|
||||
mlist += '<p><span>交易所</span><input style="width: 250px;" class="bt-input-text mr5" name="exchange" value="'+exchange+'" type="text"><font>必填写[okex, binance]</font></p>'
|
||||
mlist += '<p><span>apiKey</span><input style="width: 250px;" class="bt-input-text mr5" name="app_key" value="'+app_key+'" type="text"><font>必填写</font></p>'
|
||||
mlist += '<p><span>secret</span><input style="width: 250px;" class="bt-input-text mr5" name="secret" value="'+secret+'" type="text"><font>必填写</font></p>'
|
||||
mlist += '<p><span>password</span><input style="width: 250px;" class="bt-input-text mr5" name="password" value="'+password+'" type="text"><font>根据情况填写</font></p>'
|
||||
mlist += '<p><span>uid</span><input style="width: 250px;" class="bt-input-text mr5" name="uid" value="'+uid+'" type="text"><font>根据情况填写</font></p>'
|
||||
|
||||
var option = '<style>.conf_p p{margin-bottom: 2px}</style>\
|
||||
<div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm" onclick="submitUserConf()">保存</button>\
|
||||
</div>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(option);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function submitUserConf(){
|
||||
var pull_data = {};
|
||||
|
||||
pull_data['app_key'] = $('input[name="app_key"]').val();
|
||||
pull_data['secret'] = $('input[name="secret"]').val();
|
||||
pull_data['password'] = $('input[name="password"]').val();
|
||||
pull_data['uid'] = $('input[name="uid"]').val();
|
||||
pull_data['exchange'] = $('input[name="exchange"]').val();
|
||||
|
||||
appPost('set_user_conf',pull_data,function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDataList(){
|
||||
appPost('sync_data_list', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
|
||||
|
||||
var list = '';
|
||||
if (rdata['status']){
|
||||
var dlist = rdata['data']['list'];
|
||||
for(i in dlist){
|
||||
|
||||
list += '<tr>';
|
||||
list += '<td>' + dlist[i] +'</td>';
|
||||
list += '<td style="text-align:right">' +
|
||||
'<a href="javascript:;" class="btlink" onclick="syncDataDelete(\''+dlist[i]+'\')" title="删除">删除</a>' +
|
||||
'</td>';
|
||||
list += '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( list == '' ){
|
||||
list = "<tr><td colspan='2'>当前没有数据</td></tr>";
|
||||
}
|
||||
|
||||
var task_status = rdata['data']['task_status'];
|
||||
var task_status_check = '';
|
||||
if (task_status){
|
||||
task_status_check = 'checked';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<div>\
|
||||
<button onclick="syncDataAdd()" title="添加币种" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加币种</button>\
|
||||
<div class="ss-text pull-left mr50">\
|
||||
<em>是否启动</em>\
|
||||
<div class="ssh-item">\
|
||||
<input class="btswitch btswitch-ios" id="add_task" type="checkbox" '+task_status_check+'>\
|
||||
<label class="btswitch-btn" for="add_task" onclick="syncDataAddTask()"></label>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead>\
|
||||
<th>名称</th>\
|
||||
<th style="text-align:right;">操作</th></tr>\
|
||||
</thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
con += '<div class="code">\
|
||||
<span>详细如下:</span>\
|
||||
<span>*:添加同步的币种,都小写,以USDT为本币同步数据。</span>\
|
||||
<span>*:需要提前安装supervisor插件。</span>\
|
||||
</div>'
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('#databasePage').html(rdata.page);
|
||||
});
|
||||
}
|
||||
|
||||
function syncDataAddTask(){
|
||||
var at_check = $('#add_task').prop('checked');
|
||||
appPost("sync_data_add_task", {'check':at_check?'0':'1'}, function(data){
|
||||
rdata = $.parseJSON(data.data);
|
||||
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
syncDataList();
|
||||
}
|
||||
},{icon:rdata.status?1:2});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDataDelete(name){
|
||||
appPost("sync_data_delete", {"token":name}, function(data){
|
||||
rdata = $.parseJSON(data.data);
|
||||
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
syncDataList();
|
||||
}
|
||||
},{icon:rdata.status?1:2});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDataAdd() {
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加同步数据',
|
||||
closeBtn: 2,
|
||||
shift: 0,
|
||||
shadeClose: false,
|
||||
btn: ['确定', '取消'],
|
||||
content: "<div class='bt_conter bt-form pd15' style='height:auto;width:100%;'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>名称</span>\
|
||||
<div class='info-r c4'>\
|
||||
<input id='name' class='bt-input-text' type='text' name='name' placeholder='请输入名称' style='width:270px' />\
|
||||
</div>\
|
||||
</div>\
|
||||
<ul class='help-info-text c7' style='padding-left: 29px;margin-top:5px;'>\
|
||||
<li style='color:#F00'>注意:币种名称用小写!</li>\
|
||||
</ul>\
|
||||
</div>",
|
||||
yes: function(index, layero){
|
||||
|
||||
var token = $('input[name="name"]').val();
|
||||
appPost("sync_data_add", {"token":token}, function(data){
|
||||
rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
syncDataList();
|
||||
}
|
||||
},{icon:rdata.status?1:2});
|
||||
})
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function onlineEditStrategyFile(k, f, tag) {
|
||||
if(k != 0) {
|
||||
var l = $("#PathPlace input").val();
|
||||
var h = $("#textBody").val();
|
||||
var a = $("select[name=encoding]").val();
|
||||
var loadT = layer.msg("正在保存中...", {icon: 16,time: 0});
|
||||
appPostCallbakNoMsg('save_body',{'data':h,'path':f,'encoding':a,"tag":tag}, function(data){
|
||||
var rdata = data.data;
|
||||
showMsg(rdata.msg, function(){
|
||||
if (rdata.status){
|
||||
layer.close(loadT);
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2});
|
||||
});
|
||||
return
|
||||
}
|
||||
var e = layer.msg("正在读取文件,请稍候...", {icon: 16,time: 0});
|
||||
var g = f.split(".");
|
||||
var b = g[g.length - 1];
|
||||
var d;
|
||||
switch(b) {
|
||||
case "html":
|
||||
var j = {
|
||||
name: "htmlmixed",
|
||||
scriptTypes: [{
|
||||
matches: /\/x-handlebars-template|\/x-mustache/i,
|
||||
mode: null
|
||||
}, {
|
||||
matches: /(text|application)\/(x-)?vb(a|script)/i,
|
||||
mode: "vbscript"
|
||||
}]
|
||||
};
|
||||
d = j;
|
||||
break;
|
||||
case "htm":
|
||||
var j = {
|
||||
name: "htmlmixed",
|
||||
scriptTypes: [{
|
||||
matches: /\/x-handlebars-template|\/x-mustache/i,
|
||||
mode: null
|
||||
}, {
|
||||
matches: /(text|application)\/(x-)?vb(a|script)/i,
|
||||
mode: "vbscript"
|
||||
}]
|
||||
};
|
||||
d = j;
|
||||
break;
|
||||
case "js":
|
||||
d = "text/javascript";
|
||||
break;
|
||||
case "json":
|
||||
d = "application/ld+json";
|
||||
break;
|
||||
case "css":
|
||||
d = "text/css";
|
||||
break;
|
||||
case "php":
|
||||
d = "application/x-httpd-php";
|
||||
break;
|
||||
case "tpl":
|
||||
d = "application/x-httpd-php";
|
||||
break;
|
||||
case "xml":
|
||||
d = "application/xml";
|
||||
break;
|
||||
case "sql":
|
||||
d = "text/x-sql";
|
||||
break;
|
||||
case "conf":
|
||||
d = "text/x-nginx-conf";
|
||||
break;
|
||||
default:
|
||||
var j = {
|
||||
name: "htmlmixed",
|
||||
scriptTypes: [{
|
||||
matches: /\/x-handlebars-template|\/x-mustache/i,
|
||||
mode: null
|
||||
}, {
|
||||
matches: /(text|application)\/(x-)?vb(a|script)/i,
|
||||
mode: "vbscript"
|
||||
}]
|
||||
};
|
||||
d = j
|
||||
}
|
||||
$.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) {
|
||||
if(s.status === false){
|
||||
layer.msg(s.msg,{icon:5});
|
||||
return;
|
||||
}
|
||||
layer.close(e);
|
||||
var u = ["utf-8", "GBK", "GB2312", "BIG5"];
|
||||
var n = "";
|
||||
var m = "";
|
||||
var o = "";
|
||||
for(var p = 0; p < u.length; p++) {
|
||||
m = s.data.encoding == u[p] ? "selected" : "";
|
||||
n += '<option value="' + u[p] + '" ' + m + ">" + u[p] + "</option>";
|
||||
}
|
||||
var code_mirror = null;
|
||||
var r = layer.open({
|
||||
type: 1,
|
||||
shift: 5,
|
||||
closeBtn: 1,
|
||||
area: ["90%", "90%"],
|
||||
title: "在线编辑[" + f + "]",
|
||||
btn:['保存','关闭'],
|
||||
content: '<form class="bt-form pd20">\
|
||||
<div class="line">\
|
||||
<p style="color:red;margin-bottom:10px">提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\
|
||||
<select class="bt-input-text" name="encoding" style="width: 74px;position: absolute;top: 31px;right: 19px;height: 22px;z-index: 9999;border-radius: 0;">' + n + '</select>\
|
||||
</p>\
|
||||
<textarea class="mCustomScrollbar bt-input-text" id="textBody" style="width:100%;margin:0 auto;line-height: 1.8;position: relative;top: 10px;" value="" />\
|
||||
</div>\
|
||||
</form>',
|
||||
success:function(){
|
||||
$("#textBody").text(s.data.data);
|
||||
var q = $(window).height() * 0.9;
|
||||
$("#textBody").height(q - 160);
|
||||
code_mirror = CodeMirror.fromTextArea(document.getElementById("textBody"), {
|
||||
extraKeys: {
|
||||
"Ctrl-F": "findPersistent",
|
||||
"Ctrl-H": "replaceAll",
|
||||
"Ctrl-S": function() {
|
||||
$("#textBody").text(code_mirror.getValue());
|
||||
onlineEditStrategyFile(2, f,tag);
|
||||
},
|
||||
"Cmd-S":function() {
|
||||
$("#textBody").text(code_mirror.getValue());
|
||||
onlineEditStrategyFile(2, f,tag);
|
||||
},
|
||||
},
|
||||
mode: d,
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
matchtags: true,
|
||||
autoMatchParens: true
|
||||
});
|
||||
code_mirror.focus();
|
||||
code_mirror.setSize("auto", q - 150);
|
||||
$(window).resize(function(){
|
||||
var q = $(window).height() * 0.9;
|
||||
code_mirror.setSize("auto", q - 150);
|
||||
});
|
||||
},
|
||||
yes:function(){
|
||||
$("#textBody").text(code_mirror.getValue());
|
||||
onlineEditStrategyFile(1, f, tag);
|
||||
}
|
||||
});
|
||||
//////////////////
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 大屏页功能 --------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function changeDivH(){
|
||||
var l = $(window).height();
|
||||
$('.content-screen').css('height',l-80);
|
||||
$('.s-right').css('height',l-80-10);
|
||||
$('.s-left').css('height',l-80-10);
|
||||
}
|
||||
|
||||
|
||||
function dataSourceLog(){
|
||||
appPostCallbakNoMsg('get_datasource_logs',{}, function(rdata){
|
||||
$('#datasource_log').html(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function dataStrategyLog(){
|
||||
appPostCallbakNoMsg('get_strategy_logs',{}, function(rdata){
|
||||
$('#strategy_log').html(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// $('#strategy_list').find('span[data-id="01"]')
|
||||
function setStrategyStatus(id,status){
|
||||
appPostCallbakNoMsg('set_strategy_status',{"id":id,"status":status}, function(data){
|
||||
var rdata = data.data;
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
if (status == 'start'){
|
||||
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-pause').addClass('glyphicon-play').css('color','#20a53a');
|
||||
} else{
|
||||
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-play').addClass('glyphicon-pause').css('color','red');
|
||||
}
|
||||
}
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function setStrategyRestart(id){
|
||||
appPostCallbakNoMsg('set_strategy_restart',{"id":id}, function(data){
|
||||
console.log(data);
|
||||
var rdata = data.data;
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
}
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
}
|
||||
|
||||
function setStrategyEdit(id){
|
||||
appPostCallbakNoMsg('get_strategy_path',{"id":id}, function(data){
|
||||
onlineEditStrategyFile(0,data.data.msg,id);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getStrategyList(p=1){
|
||||
appPostCallbakNoMsg('get_strategy_list',{'page':p}, function(rdata){
|
||||
// console.log(rdata);
|
||||
|
||||
ldata = rdata.data.data;
|
||||
var tBody = '';
|
||||
|
||||
for (var i = 0; i < ldata.length; i++) {
|
||||
tBody += '<tr data-id="'+ldata[i]['id']+'">'
|
||||
tBody += '<td>'+ldata[i]['id']+'</td>';
|
||||
tBody += '<td>'+ldata[i]['name']+'</td>';
|
||||
|
||||
if (ldata[i]['status'] == 'start'){
|
||||
tBody += '<td><span style="color:#20a53a;cursor: pointer;" class="strategy_status glyphicon glyphicon-play"></span></td>';
|
||||
} else{
|
||||
tBody += '<td><span style="color:red;cursor: pointer;" class="strategy_status glyphicon glyphicon-pause"></span></td>';
|
||||
}
|
||||
|
||||
tBody += "<td style='text-align: right;'><a class='btlink restart'>重启</a> | <a class='btlink edit'>编辑</a></td>";
|
||||
tBody +='<tr>';
|
||||
}
|
||||
|
||||
// console.log(tBody);
|
||||
$('#strategy_list').html(tBody);
|
||||
$('#strategy_list_page').html(rdata.data.list);
|
||||
|
||||
|
||||
$('#strategy_list .strategy_status').click(function(){
|
||||
var id = $(this).parent().parent().data('id');
|
||||
var status = 'stop';
|
||||
if ($(this).hasClass('glyphicon-pause')){
|
||||
status = 'start';
|
||||
}
|
||||
setStrategyStatus(id,status);
|
||||
});
|
||||
|
||||
$('#strategy_list .restart').click(function(){
|
||||
var id = $(this).parent().parent().data('id');
|
||||
setStrategyRestart(id);
|
||||
});
|
||||
|
||||
$('#strategy_list .edit').click(function(){
|
||||
var id = $(this).parent().parent().data('id');
|
||||
setStrategyEdit(id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function calcKLineChats(){
|
||||
|
||||
var chartDom = document.getElementById('k_echarts');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
const upColor = '#ec0000';
|
||||
const upBorderColor = '#8A0000';
|
||||
const downColor = '#00da3c';
|
||||
const downBorderColor = '#008F28';
|
||||
// Each item: open,close,lowest,highest
|
||||
const data0 = splitData([
|
||||
['2013/1/24', 2320.26, 2320.26, 2287.3, 2362.94],
|
||||
['2013/1/25', 2300, 2291.3, 2288.26, 2308.38],
|
||||
['2013/1/28', 2295.35, 2346.5, 2295.35, 2346.92],
|
||||
['2013/1/29', 2347.22, 2358.98, 2337.35, 2363.8],
|
||||
['2013/1/30', 2360.75, 2382.48, 2347.89, 2383.76],
|
||||
['2013/1/31', 2383.43, 2385.42, 2371.23, 2391.82],
|
||||
['2013/2/1', 2377.41, 2419.02, 2369.57, 2421.15],
|
||||
['2013/2/4', 2425.92, 2428.15, 2417.58, 2440.38],
|
||||
['2013/2/5', 2411, 2433.13, 2403.3, 2437.42],
|
||||
['2013/2/6', 2432.68, 2434.48, 2427.7, 2441.73],
|
||||
['2013/2/7', 2430.69, 2418.53, 2394.22, 2433.89],
|
||||
['2013/2/8', 2416.62, 2432.4, 2414.4, 2443.03],
|
||||
['2013/2/18', 2441.91, 2421.56, 2415.43, 2444.8],
|
||||
['2013/2/19', 2420.26, 2382.91, 2373.53, 2427.07],
|
||||
['2013/2/20', 2383.49, 2397.18, 2370.61, 2397.94],
|
||||
['2013/2/21', 2378.82, 2325.95, 2309.17, 2378.82],
|
||||
['2013/2/22', 2322.94, 2314.16, 2308.76, 2330.88],
|
||||
['2013/2/25', 2320.62, 2325.82, 2315.01, 2338.78],
|
||||
['2013/2/26', 2313.74, 2293.34, 2289.89, 2340.71],
|
||||
['2013/2/27', 2297.77, 2313.22, 2292.03, 2324.63],
|
||||
['2013/2/28', 2322.32, 2365.59, 2308.92, 2366.16],
|
||||
['2013/3/1', 2364.54, 2359.51, 2330.86, 2369.65],
|
||||
['2013/3/4', 2332.08, 2273.4, 2259.25, 2333.54],
|
||||
['2013/3/5', 2274.81, 2326.31, 2270.1, 2328.14],
|
||||
['2013/3/6', 2333.61, 2347.18, 2321.6, 2351.44],
|
||||
['2013/3/7', 2340.44, 2324.29, 2304.27, 2352.02],
|
||||
['2013/3/8', 2326.42, 2318.61, 2314.59, 2333.67],
|
||||
['2013/3/11', 2314.68, 2310.59, 2296.58, 2320.96],
|
||||
['2013/3/12', 2309.16, 2286.6, 2264.83, 2333.29],
|
||||
['2013/3/13', 2282.17, 2263.97, 2253.25, 2286.33],
|
||||
['2013/3/14', 2255.77, 2270.28, 2253.31, 2276.22],
|
||||
['2013/3/15', 2269.31, 2278.4, 2250, 2312.08],
|
||||
['2013/3/18', 2267.29, 2240.02, 2239.21, 2276.05],
|
||||
['2013/3/19', 2244.26, 2257.43, 2232.02, 2261.31],
|
||||
['2013/3/20', 2257.74, 2317.37, 2257.42, 2317.86],
|
||||
['2013/3/21', 2318.21, 2324.24, 2311.6, 2330.81],
|
||||
['2013/3/22', 2321.4, 2328.28, 2314.97, 2332],
|
||||
['2013/3/25', 2334.74, 2326.72, 2319.91, 2344.89],
|
||||
['2013/3/26', 2318.58, 2297.67, 2281.12, 2319.99],
|
||||
['2013/3/27', 2299.38, 2301.26, 2289, 2323.48],
|
||||
['2013/3/28', 2273.55, 2236.3, 2232.91, 2273.55],
|
||||
['2013/3/29', 2238.49, 2236.62, 2228.81, 2246.87],
|
||||
['2013/4/1', 2229.46, 2234.4, 2227.31, 2243.95],
|
||||
['2013/4/2', 2234.9, 2227.74, 2220.44, 2253.42],
|
||||
['2013/4/3', 2232.69, 2225.29, 2217.25, 2241.34],
|
||||
['2013/4/8', 2196.24, 2211.59, 2180.67, 2212.59],
|
||||
['2013/4/9', 2215.47, 2225.77, 2215.47, 2234.73],
|
||||
['2013/4/10', 2224.93, 2226.13, 2212.56, 2233.04],
|
||||
['2013/4/11', 2236.98, 2219.55, 2217.26, 2242.48],
|
||||
['2013/4/12', 2218.09, 2206.78, 2204.44, 2226.26],
|
||||
['2013/4/15', 2199.91, 2181.94, 2177.39, 2204.99],
|
||||
['2013/4/16', 2169.63, 2194.85, 2165.78, 2196.43],
|
||||
['2013/4/17', 2195.03, 2193.8, 2178.47, 2197.51],
|
||||
['2013/4/18', 2181.82, 2197.6, 2175.44, 2206.03],
|
||||
['2013/4/19', 2201.12, 2244.64, 2200.58, 2250.11],
|
||||
['2013/4/22', 2236.4, 2242.17, 2232.26, 2245.12],
|
||||
['2013/4/23', 2242.62, 2184.54, 2182.81, 2242.62],
|
||||
['2013/4/24', 2187.35, 2218.32, 2184.11, 2226.12],
|
||||
['2013/4/25', 2213.19, 2199.31, 2191.85, 2224.63],
|
||||
['2013/4/26', 2203.89, 2177.91, 2173.86, 2210.58],
|
||||
['2013/5/2', 2170.78, 2174.12, 2161.14, 2179.65],
|
||||
['2013/5/3', 2179.05, 2205.5, 2179.05, 2222.81],
|
||||
['2013/5/6', 2212.5, 2231.17, 2212.5, 2236.07],
|
||||
['2013/5/7', 2227.86, 2235.57, 2219.44, 2240.26],
|
||||
['2013/5/8', 2242.39, 2246.3, 2235.42, 2255.21],
|
||||
['2013/5/9', 2246.96, 2232.97, 2221.38, 2247.86],
|
||||
['2013/5/10', 2228.82, 2246.83, 2225.81, 2247.67],
|
||||
['2013/5/13', 2247.68, 2241.92, 2231.36, 2250.85],
|
||||
['2013/5/14', 2238.9, 2217.01, 2205.87, 2239.93],
|
||||
['2013/5/15', 2217.09, 2224.8, 2213.58, 2225.19],
|
||||
['2013/5/16', 2221.34, 2251.81, 2210.77, 2252.87],
|
||||
['2013/5/17', 2249.81, 2282.87, 2248.41, 2288.09],
|
||||
['2013/5/20', 2286.33, 2299.99, 2281.9, 2309.39],
|
||||
['2013/5/21', 2297.11, 2305.11, 2290.12, 2305.3],
|
||||
['2013/5/22', 2303.75, 2302.4, 2292.43, 2314.18],
|
||||
['2013/5/23', 2293.81, 2275.67, 2274.1, 2304.95],
|
||||
['2013/5/24', 2281.45, 2288.53, 2270.25, 2292.59],
|
||||
['2013/5/27', 2286.66, 2293.08, 2283.94, 2301.7],
|
||||
['2013/5/28', 2293.4, 2321.32, 2281.47, 2322.1],
|
||||
['2013/5/29', 2323.54, 2324.02, 2321.17, 2334.33],
|
||||
['2013/5/30', 2316.25, 2317.75, 2310.49, 2325.72],
|
||||
['2013/5/31', 2320.74, 2300.59, 2299.37, 2325.53],
|
||||
['2013/6/3', 2300.21, 2299.25, 2294.11, 2313.43],
|
||||
['2013/6/4', 2297.1, 2272.42, 2264.76, 2297.1],
|
||||
['2013/6/5', 2270.71, 2270.93, 2260.87, 2276.86],
|
||||
['2013/6/6', 2264.43, 2242.11, 2240.07, 2266.69],
|
||||
['2013/6/7', 2242.26, 2210.9, 2205.07, 2250.63],
|
||||
['2013/6/13', 2190.1, 2148.35, 2126.22, 2190.1]
|
||||
]);
|
||||
function splitData(rawData) {
|
||||
const categoryData = [];
|
||||
const values = [];
|
||||
for (var i = 0; i < rawData.length; i++) {
|
||||
categoryData.push(rawData[i].splice(0, 1)[0]);
|
||||
values.push(rawData[i]);
|
||||
}
|
||||
return {
|
||||
categoryData: categoryData,
|
||||
values: values
|
||||
};
|
||||
}
|
||||
function calculateMA(dayCount) {
|
||||
var result = [];
|
||||
for (var i = 0, len = data0.values.length; i < len; i++) {
|
||||
if (i < dayCount) {
|
||||
result.push('-');
|
||||
continue;
|
||||
}
|
||||
var sum = 0;
|
||||
for (var j = 0; j < dayCount; j++) {
|
||||
sum += +data0.values[i - j][1];
|
||||
}
|
||||
result.push(sum / dayCount);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
option = {
|
||||
title: {
|
||||
text: '上证指数',
|
||||
left: 0
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
|
||||
},
|
||||
grid: {
|
||||
left: '10%',
|
||||
right: '10%',
|
||||
bottom: '15%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data0.categoryData,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
splitLine: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
},
|
||||
yAxis: {
|
||||
scale: true,
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 50,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
show: true,
|
||||
type: 'slider',
|
||||
top: '90%',
|
||||
start: 50,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '日K',
|
||||
type: 'candlestick',
|
||||
data: data0.values,
|
||||
itemStyle: {
|
||||
color: upColor,
|
||||
color0: downColor,
|
||||
borderColor: upBorderColor,
|
||||
borderColor0: downBorderColor
|
||||
},
|
||||
markPoint: {
|
||||
label: {
|
||||
formatter: function (param) {
|
||||
return param != null ? Math.round(param.value) + '' : '';
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: 'Mark',
|
||||
coord: ['2013/5/31', 2300],
|
||||
value: 2300,
|
||||
itemStyle: {
|
||||
color: 'rgb(41,60,85)'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'highest value',
|
||||
type: 'max',
|
||||
valueDim: 'highest'
|
||||
},
|
||||
{
|
||||
name: 'lowest value',
|
||||
type: 'min',
|
||||
valueDim: 'lowest'
|
||||
},
|
||||
{
|
||||
name: 'average value on close',
|
||||
type: 'average',
|
||||
valueDim: 'close'
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
formatter: function (param) {
|
||||
return param.name + '<br>' + (param.data.coord || '');
|
||||
}
|
||||
}
|
||||
},
|
||||
markLine: {
|
||||
symbol: ['none', 'none'],
|
||||
data: [
|
||||
[
|
||||
{
|
||||
name: 'from lowest to highest',
|
||||
type: 'min',
|
||||
valueDim: 'lowest',
|
||||
symbol: 'circle',
|
||||
symbolSize: 10,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'max',
|
||||
valueDim: 'highest',
|
||||
symbol: 'circle',
|
||||
symbolSize: 10,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
{
|
||||
name: 'min line on close',
|
||||
type: 'min',
|
||||
valueDim: 'close'
|
||||
},
|
||||
{
|
||||
name: 'max line on close',
|
||||
type: 'max',
|
||||
valueDim: 'close'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA5',
|
||||
type: 'line',
|
||||
data: calculateMA(5),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA10',
|
||||
type: 'line',
|
||||
data: calculateMA(10),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA20',
|
||||
type: 'line',
|
||||
data: calculateMA(20),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA30',
|
||||
type: 'line',
|
||||
data: calculateMA(30),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
var tag = $.getUrlParam('tag');
|
||||
if(tag == 'cryptocurrency_trade'){
|
||||
changeDivH();
|
||||
|
||||
// 获取数据源更新日志
|
||||
dataSourceLog();
|
||||
setInterval(function(){
|
||||
dataSourceLog();
|
||||
},3000);
|
||||
|
||||
// 获取策略更新日志
|
||||
dataStrategyLog();
|
||||
setInterval(function(){
|
||||
dataStrategyLog();
|
||||
},5000);
|
||||
|
||||
getStrategyList(1);
|
||||
|
||||
calcKLineChats();
|
||||
}
|
||||
});
|
||||
|
||||
$(window).resize(function(){
|
||||
changeDivH();
|
||||
});
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ def singleton(cls):
|
||||
@singleton
|
||||
class nosqlMySQL():
|
||||
|
||||
__sid = None
|
||||
|
||||
__DB_PASS = None
|
||||
__DB_USER = None
|
||||
__DB_PORT = 3306
|
||||
@@ -37,9 +39,19 @@ class nosqlMySQL():
|
||||
pass
|
||||
|
||||
def setSid(self, sid):
|
||||
self.__sid = sid
|
||||
self.__config = self.get_options(sid=sid)
|
||||
|
||||
def conn(self):
|
||||
|
||||
if self.__sid is None:
|
||||
return False
|
||||
|
||||
if self.__sid is not None:
|
||||
mycnf_path = "{}/{}/etc/my.cnf".format(mw.getServerDir(),self.__sid)
|
||||
if not os.path.exists(mycnf_path):
|
||||
return False
|
||||
|
||||
self.__DB_PORT = int(self.__config['port'])
|
||||
self.__DB_USER = self.__config['username']
|
||||
self.__DB_PASS = self.__config['password']
|
||||
@@ -72,8 +84,10 @@ class nosqlMySQL():
|
||||
result['host'] = '127.0.0.1'
|
||||
result['username'] = 'root'
|
||||
|
||||
if sid in ['mysql', 'mysql-apt', 'mysql-yum']:
|
||||
if sid in ['mysql', 'mysql-apt', 'mysql-yum', 'mysql-community']:
|
||||
my_cnf_path = "{}/{}/etc/my.cnf".format(mw.getServerDir(),sid)
|
||||
if not os.path.exists(my_cnf_path):
|
||||
return False
|
||||
|
||||
mydb_content = mw.readFile(my_cnf_path)
|
||||
if not mydb_content: return False
|
||||
@@ -118,6 +132,10 @@ class nosqlMySQL():
|
||||
local_mysql_yum = "{}/mysql-yum/etc/my.cnf".format(mw.getServerDir())
|
||||
if os.path.exists(local_mysql_yum):
|
||||
data.append({'name':'本地服务器[yum]', 'val':'mysql-yum'})
|
||||
|
||||
local_mysql_yum = "{}/mysql-community/etc/my.cnf".format(mw.getServerDir())
|
||||
if os.path.exists(local_mysql_yum):
|
||||
data.append({'name':'本地服务器[Tar]', 'val':'mysql-community'})
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
@singleton
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"sort": 1000,
|
||||
"ps": "Docker是一个开源的应用容器引擎",
|
||||
"name": "docker",
|
||||
"title": "Docker",
|
||||
|
||||
@@ -51,6 +51,9 @@ Install_App()
|
||||
fi
|
||||
# FlameGraph end
|
||||
|
||||
# apt install linux-tools-$(uname -r) linux-tools-generic -y
|
||||
# apt install linux-tools-$(uname -r) linux-tools-generic -y --fix-missing
|
||||
|
||||
|
||||
|
||||
shell_file=${curPath}/versions/${OSNAME}.sh
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 mw-plugin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,8 @@
|
||||
# dztasks
|
||||
任务调度
|
||||
|
||||
|
||||
### 安装
|
||||
```
|
||||
rm -rf /www/server/mdserver-web/plugins/dztasks && cd /www/server/mdserver-web/plugins && rm -rf dztasks && git clone https://github.com/mw-plugin/dztasks && cd dztasks && rm -rf .git && cd /www/server/mdserver-web/plugins/dztasks && bash install.sh install 1.1
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
app_name = dztasks
|
||||
run_mode = prod
|
||||
|
||||
[web]
|
||||
http_port = 11011
|
||||
|
||||
[session]
|
||||
provider = file
|
||||
|
||||
[admin]
|
||||
user = {$ADMIN_NAME}
|
||||
pass = {$ADMIN_PASS}
|
||||
|
||||
[plugins]
|
||||
path = {$SERVER_APP}/plugins
|
||||
|
||||
[security]
|
||||
install_lock = true
|
||||
secret_key = VQOXEKGNIAIDXQI
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Executable
+32
@@ -0,0 +1,32 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('dztasks');">服务</p>
|
||||
<p onclick="pluginInitD('dztasks');">自启动</p>
|
||||
<p onclick="dzCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('dztasks',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('dztasks','','run_log');">运行日志</p>
|
||||
<p onclick="zdReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinHeight(600);
|
||||
$.getScript( "/plugins/file?name=dztasks&f=js/dztasks.js", function(){
|
||||
pluginService('dztasks', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
Executable
+318
@@ -0,0 +1,318 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'dztasks'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/custom/conf/app.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/config/dztasks.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
def configTpl():
|
||||
# path = getPluginDir() + '/tpl'
|
||||
# pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pidfile\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def status():
|
||||
cmd = "ps aux|grep dztasks|grep -v grep|grep -v python|grep -v mdserver-web|awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/dztasks')
|
||||
content = content.replace('{$ADMIN_NAME}', mw.getRandomString(6))
|
||||
content = content.replace('{$ADMIN_PASS}', mw.getRandomString(10))
|
||||
return content
|
||||
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# log
|
||||
dataLog = getServerDir() + '/data'
|
||||
if not os.path.exists(dataLog):
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
app_dir = getServerDir() + '/custom/conf'
|
||||
if not os.path.exists(app_dir):
|
||||
mw.execShell('mkdir -p ' + app_dir)
|
||||
|
||||
# config replace
|
||||
dst_conf = getServerDir() + '/custom/conf/app.conf'
|
||||
dst_conf_init = getServerDir() + '/init.pl'
|
||||
if not os.path.exists(dst_conf_init):
|
||||
content = mw.readFile(getConfTpl())
|
||||
# print(content)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(dst_conf, content)
|
||||
mw.writeFile(dst_conf_init, 'ok')
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
content = mw.readFile(systemServiceTpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(systemService, content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def dzOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return dzOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return dzOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = dzOp('restart')
|
||||
|
||||
log_file = runLog()
|
||||
mw.execShell("echo '' > " + log_file)
|
||||
return status
|
||||
|
||||
def reload():
|
||||
return dzOp('reload')
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/logs.pl'
|
||||
|
||||
|
||||
def getDzPort():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'port\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def homePage():
|
||||
http_port = getDzPort()
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+":"+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "任务调度管理",
|
||||
"name": "dztasks",
|
||||
"title": "dztasks",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.1"],
|
||||
"tip": "soft",
|
||||
"checks": "server/dztasks",
|
||||
"path": "server/dztasks",
|
||||
"display": 1,
|
||||
"author": "dztasks",
|
||||
"date": "2024-10-06",
|
||||
"home": "",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=dztasks server
|
||||
After=network.service
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/dztasks
|
||||
ExecStart={$SERVER_PATH}/dztasks/dztasks web
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
PermissionsStartOnly=true
|
||||
LimitNOFILE=5000
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
RestartPreventExitStatus=1
|
||||
PrivateTmp=false
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: dztasks Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: dztasks
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts dztasks
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple dztasks init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
RED='\033[31m'
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
BLUE='\033[34m'
|
||||
PLAIN='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
SUCCESS='[\033[32mOK\033[0m]'
|
||||
COMPLETE='[\033[32mDONE\033[0m]'
|
||||
WARN='[\033[33mWARN\033[0m]'
|
||||
ERROR='[\033[31mERROR\033[0m]'
|
||||
WORKING='[\033[34m*\033[0m]'
|
||||
|
||||
app_start(){
|
||||
echo "Starting dztasks server..."
|
||||
cd {$SERVER_PATH}/dztasks
|
||||
./dztasks web >> {$SERVER_PATH}/dztasks/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopp start"
|
||||
pids=`ps -ef| grep dztasks | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'`
|
||||
arr=($pids)
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
echo "dztasks stopp end"
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/dztasks && bash install.sh install 1.0
|
||||
# cd /wwww/server/mdserver-web/plugins/dztasks && bash install.sh install 1.0
|
||||
VERSION=$2
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
DZ_ARCH_NAME=amd64
|
||||
if [ "$sysArch" == "arm64" ];then
|
||||
DZ_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" == "x86_64" ]; then
|
||||
DZ_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" == "aarch64" ]; then
|
||||
DZ_ARCH_NAME=aarch64
|
||||
fi
|
||||
|
||||
DZ_NAME=linux
|
||||
if [ "$sysName" == "Darwin" ];then
|
||||
DZ_NAME=darwin
|
||||
fi
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/dztasks
|
||||
mkdir -p $serverPath/dztasks
|
||||
|
||||
FILE_TGZ=dztasks_v${VERSION}_${DZ_NAME}_${DZ_ARCH_NAME}.tar.gz
|
||||
DZ_DIR=$serverPath/source/dztasks
|
||||
|
||||
echo $FILE_TGZ
|
||||
# https://github.com/midoks/dztasks/releases/download/1.0/dztasks_v1.0_darwin_amd64.tar.gz
|
||||
if [ ! -f $DZ_DIR/${FILE_TGZ} ];then
|
||||
wget --no-check-certificate -O $DZ_DIR/${FILE_TGZ} https://github.com/midoks/dztasks/releases/download/${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
cd $DZ_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/dztasks
|
||||
echo "${VERSION}" > $serverPath/dztasks/version.pl
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/dztasks/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/dztasks/index.py initd_install
|
||||
|
||||
echo '安装dztasks成功!'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/dztasks.service ];then
|
||||
systemctl stop dztasks
|
||||
systemctl disable dztasks
|
||||
rm -rf /usr/lib/systemd/system/dztasks.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/dztasks.service ];then
|
||||
systemctl stop dztasks
|
||||
systemctl disable dztasks
|
||||
rm -rf /lib/systemd/system/dztasks.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/dztasks/initd/dztasks ];then
|
||||
$serverPath/dztasks/initd/dztasks stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/dztasks ];then
|
||||
rm -rf $serverPath/dztasks
|
||||
fi
|
||||
|
||||
echo "卸载dztasks成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
function zdPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'dztasks';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function zdPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'dztasks';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function commonHomePage(){
|
||||
zdPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function dzCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function zdReadme(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>自己修改配置</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"sort": 9,
|
||||
"ps": "防止恶意IP地址暴力破解服务、站点,禁止导致多个身份验证错误的主机",
|
||||
"name": "fail2ban",
|
||||
"title": "fail2ban",
|
||||
|
||||
@@ -8,7 +8,6 @@ import re
|
||||
import json
|
||||
import io
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
@@ -16,6 +15,12 @@ if os.path.exists(web_dir):
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
gd_dir = mw.getServerDir() +'/gdrive/lib'
|
||||
cmd = 'ls '+gd_dir+' | grep python | cut -d \\ -f 1 | awk \'END {print}\''
|
||||
info = mw.execShell(cmd)
|
||||
p = gd_dir +'/'+ info[0].strip() + "/site-packages"
|
||||
sys.path.append(p)
|
||||
|
||||
# -----------------------------
|
||||
import google.oauth2.credentials
|
||||
import google_auth_oauthlib.flow
|
||||
|
||||
@@ -12,7 +12,6 @@ import json
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
@@ -21,16 +20,6 @@ if os.path.exists(web_dir):
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
_ver = sys.version_info
|
||||
is_py2 = (_ver[0] == 2)
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
DEBUG = False
|
||||
|
||||
if is_py2:
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"hook":["backup"],
|
||||
"id":998,
|
||||
"sort":3,
|
||||
"title":"谷歌云网盘",
|
||||
"tip":"lib",
|
||||
"name":"gdrive",
|
||||
|
||||
@@ -2,8 +2,15 @@
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
|
||||
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
|
||||
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
|
||||
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
|
||||
|
||||
# cd /www/server/mdserver-web/plugins/gdrive && /bin/bash install.sh install 2.0
|
||||
|
||||
P_VER=`python3 -V | awk '{print $2}'`
|
||||
echo "python:$P_VER"
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
@@ -14,9 +21,9 @@ serverPath=$(dirname "$rootPath")
|
||||
PATH=$PATH:${rootPath}/bin
|
||||
export PATH
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
# if [ -f ${rootPath}/bin/activate ];then
|
||||
# source ${rootPath}/bin/activate
|
||||
# fi
|
||||
|
||||
VERSION=$2
|
||||
|
||||
@@ -25,9 +32,24 @@ Install_App()
|
||||
tmp_ping=`ping -c 1 google.com 2>&1`
|
||||
echo $tmp_ping
|
||||
if [ $? -eq 0 ];then
|
||||
tmp=`python -V 2>&1|awk '{print $2}'`
|
||||
pVersion=${tmp:0:3}
|
||||
GDDIR=$serverPath/gdrive
|
||||
mkdir -p $GDDIR
|
||||
|
||||
if [ ! -f ${GDDIR}/bin/activate ];then
|
||||
if version_ge "$P_VER" "3.11.0" ;then
|
||||
echo "python3 > 3.11"
|
||||
cd ${GDDIR} && python3 -m venv ${GDDIR}
|
||||
else
|
||||
echo "python3 < 3.10"
|
||||
cd ${GDDIR} && python3 -m venv .
|
||||
fi
|
||||
cd ${GDDIR} && source ${GDDIR}/bin/activate
|
||||
else
|
||||
cd ${GDDIR} && source ${GDDIR}/bin/activate
|
||||
fi
|
||||
|
||||
tmp=`python3 -V 2>&1|awk '{print $2}'`
|
||||
pVersion=${tmp:0:3}
|
||||
|
||||
which pip
|
||||
if [ "$?" -eq "0" ];then
|
||||
@@ -59,7 +81,7 @@ Install_App()
|
||||
fi
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/gdrive
|
||||
|
||||
echo "${VERSION}" > $serverPath/gdrive/version.pl
|
||||
echo '安装完成'
|
||||
else
|
||||
|
||||
@@ -189,7 +189,7 @@ function gdList(file_id){
|
||||
if(rdata.status === false){
|
||||
showMsg(rdata.msg,function(){
|
||||
authApi();
|
||||
},{icon:2},2000);
|
||||
},{icon:2});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ unset GIT_DIR
|
||||
|
||||
|
||||
|
||||
cd $GIT_PROJECT_DIR && git pull
|
||||
# cd $GIT_PROJECT_DIR && git pull
|
||||
cd $GIT_PROJECT_DIR && sudo git pull
|
||||
|
||||
# func 2
|
||||
# cd $GIT_PROJECT_DIR && env -i git pull origin master
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<p class="bgw" onclick="pluginService('gorse');">服务</p>
|
||||
<p onclick="pluginInitD('gorse');">自启动</p>
|
||||
<p onclick="pluginConfigTpl('gorse',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="gorseCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginLogs('gorse','','run_log');">运行日志</p>
|
||||
<p onclick="gorseReadme();">相关说明</p>
|
||||
|
||||
|
||||
+36
-1
@@ -125,7 +125,7 @@ def initRedisConf():
|
||||
requirepass = ""
|
||||
conf = mw.getServerDir() + '/redis/redis.conf'
|
||||
content = mw.readFile(conf)
|
||||
rep = r"^(requirepass" + r"\)\s*([.0-9A-Za-z_& ~]+)"
|
||||
rep = r"^(requirepass)\s*([.0-9A-Za-z_& ~]+)"
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if tmp:
|
||||
requirepass = tmp.groups()[1]
|
||||
@@ -304,6 +304,34 @@ def initdUinstall():
|
||||
def runLog():
|
||||
return getServerDir() + '/logs.pl'
|
||||
|
||||
def getGorseInfo():
|
||||
conf_file = getConf()
|
||||
content = mw.readFile(conf_file)
|
||||
|
||||
rdata = {}
|
||||
|
||||
rep = r'dashboard_user_name\s*=\s*"(.*)"'
|
||||
tmp = re.search(rep, content)
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if tmp:
|
||||
rdata['dashboard_user_name'] = tmp.groups()[0]
|
||||
|
||||
|
||||
rep = r'dashboard_password\s*=\s*"(.*)"'
|
||||
tmp = re.search(rep, content)
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if tmp:
|
||||
rdata['dashboard_password'] = tmp.groups()[0]
|
||||
|
||||
rep = r'http_port\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if tmp:
|
||||
rdata['http_port'] = tmp.groups()[0]
|
||||
|
||||
rdata['ip'] = mw.getHostAddr()
|
||||
return mw.returnJson(True,'ok', rdata)
|
||||
|
||||
def installPreInspection():
|
||||
redis_path = mw.getServerDir() + "/redis"
|
||||
if not os.path.exists(redis_path):
|
||||
@@ -312,6 +340,11 @@ def installPreInspection():
|
||||
mongodb_path = mw.getServerDir() + "/mongodb"
|
||||
if not os.path.exists(mongodb_path):
|
||||
return "默认需要安装MongoDB"
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
glibc_ver = mw.getGlibcVersion()
|
||||
if float(glibc_ver) < 2.32:
|
||||
return '当前libc{}过低,需要大于2.31'.format(glibc_ver)
|
||||
return 'ok'
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -342,5 +375,7 @@ if __name__ == "__main__":
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'info':
|
||||
print(getGorseInfo())
|
||||
else:
|
||||
print('error')
|
||||
|
||||
@@ -54,8 +54,38 @@ function gorsePostCallbak(method, version, args,callback){
|
||||
}
|
||||
|
||||
|
||||
function gorseReadme(){
|
||||
function gorseCommonFunc(){
|
||||
gorsePost('info', '', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var info = rdata['data'];
|
||||
var con = '<p class="conf_p">\
|
||||
<span>用户名</span>\
|
||||
<input class="bt-input-text mr5" type="text" value="' + info.dashboard_user_name + '">\
|
||||
</p>';
|
||||
|
||||
con += '<p class="conf_p">\
|
||||
<span>密码</span>\
|
||||
<input class="bt-input-text mr5" type="text" value="' + info.dashboard_password +'">\
|
||||
</p>';
|
||||
con += '<p class="conf_p">\
|
||||
<span>端口</span>\
|
||||
<input class="bt-input-text mr5" type="text" value="' + info.http_port +'">\
|
||||
</p>';
|
||||
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button id="open_url" class="btn btn-default btn-sm">打开Gorse</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('#open_url').click(function(){
|
||||
var url = 'http://' + info.ip + ':' + info.http_port;
|
||||
window.open(url);
|
||||
copyText(url);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function gorseReadme(){
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>参考官方</li>';
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"password": "{CRYPT-SHA512}$6$WheNHdlVwDoL4s.x$DrZ10TpIGQa5wd0jbvtm8eaTleJCf1nec3ihOaNwMdPUKVFCphXwtnTSmFFXjhGa45RlrSEWhDVyjLCMiV\/.c. V2hlTkhkbFZ3RG9MNHMueA==",
|
||||
"default": "lam",
|
||||
"sessionTimeout": "30",
|
||||
"hideLoginErrorDetails": "false",
|
||||
"logLevel": "4",
|
||||
"logDestination": "SYSLOG",
|
||||
"allowedHosts": "",
|
||||
"passwordMinLength": "10",
|
||||
"passwordMinUpper": "0",
|
||||
"passwordMinLower": "0",
|
||||
"passwordMinNumeric": "0",
|
||||
"passwordMinClasses": "0",
|
||||
"passwordMinSymbol": "0",
|
||||
"checkedRulesCount": "-1",
|
||||
"passwordMustNotContainUser": "false",
|
||||
"passwordMustNotContain3Chars": "false",
|
||||
"externalPwdCheckUrl": "",
|
||||
"errorReporting": "default",
|
||||
"allowedHostsSelfService": "",
|
||||
"license": "",
|
||||
"licenseEmailFrom": "",
|
||||
"licenseEmailTo": "",
|
||||
"licenseWarningType": "all",
|
||||
"licenseEmailDateSent": "",
|
||||
"mailServer": "",
|
||||
"mailUser": "",
|
||||
"mailPassword": "",
|
||||
"mailEncryption": "TLS",
|
||||
"mailAttribute": "mail",
|
||||
"mailBackupAttribute": "passwordselfresetbackupmail",
|
||||
"configDatabaseType": "files",
|
||||
"configDatabaseServer": "",
|
||||
"configDatabasePort": "",
|
||||
"configDatabaseName": "",
|
||||
"configDatabaseUser": "",
|
||||
"configDatabasePassword": "",
|
||||
"moduleSettings": "eyJyZXF1ZXN0QWNjZXNzIjp7Imhpc3RvcnlSZXRlbnRpb25QZXJpb2QiOiIzNjUwIiwiZXhwaXJhdGlvblBlcmlvZCI6IjMwIn19"
|
||||
}
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
server
|
||||
{
|
||||
listen 888;
|
||||
server_name 127.0.0.1;
|
||||
index index.html index.htm index.php;
|
||||
root {$SERVER_PATH}/lam;
|
||||
|
||||
#error_page 404 /404.html;
|
||||
include {$PHP_CONF_PATH}/enable-php-{$PHP_VER}.conf;
|
||||
|
||||
#AUTH_START
|
||||
auth_basic "Authorization";
|
||||
auth_basic_user_file {$SERVER_PATH}/lam/pma.pass;
|
||||
#AUTH_END
|
||||
|
||||
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||
{
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
location ~ .*\.(js|css)?$
|
||||
{
|
||||
expires 12h;
|
||||
}
|
||||
|
||||
location ~ /.*\.(log|pass|json|pl)$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
|
||||
location ~ /\.
|
||||
{
|
||||
deny all;
|
||||
}
|
||||
|
||||
access_log {$SERVER_PATH}/lam/access.log;
|
||||
error_log {$SERVER_PATH}/lam/error.log;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Executable
+24
@@ -0,0 +1,24 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('lam');">服务</p>
|
||||
<p onclick="pluginConfig('lam');">重写模版</p>
|
||||
<p onclick="homePage()">主页</p>
|
||||
<p onclick="phpVer(56)">PHP版本</p>
|
||||
<p onclick="safeConf();">安全设置</p>
|
||||
<p onclick="pluginLogs('lam','','access_log');">访问日志</p>
|
||||
<p onclick="pluginLogs('lam','','error_log');">错误日志</p>
|
||||
<p onclick="pluginConfig('lam','','config_inc');" title="config.inc.php">配置</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinHeight(500);
|
||||
$.getScript( "/plugins/file?name=lam&f=js/lam.js", function(){
|
||||
pluginService('lam');
|
||||
});
|
||||
</script>
|
||||
Executable
+476
@@ -0,0 +1,476 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
from utils.site import sites as MwSites
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'lam'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
return mw.getServerDir() + '/web_conf/nginx/vhost/lam.conf'
|
||||
|
||||
|
||||
def getConfInc():
|
||||
return getServerDir() + "/" + getCfg()['path'] + '/config/config.cfg'
|
||||
|
||||
|
||||
def getPort():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'listen\s*(.*);'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
|
||||
def getHomePage():
|
||||
try:
|
||||
port = getPort()
|
||||
ip = '127.0.0.1'
|
||||
if not mw.isAppleSystem():
|
||||
ip = mw.getLocalIp()
|
||||
|
||||
cfg = getCfg()
|
||||
auth = cfg['username']+':'+cfg['password']
|
||||
rand_path = cfg['path']
|
||||
url = 'http://' + auth + '@' + ip + ':' + port + '/' + rand_path + '/templates/login.php'
|
||||
return mw.returnJson(True, 'OK', url)
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '插件未启动!')
|
||||
|
||||
|
||||
def getPhpVer(expect=83):
|
||||
php_vers = MwSites.instance().getPhpVersion()
|
||||
v = php_vers['data']
|
||||
is_find = False
|
||||
for i in range(len(v)):
|
||||
t = str(v[i]['version'])
|
||||
if (t == expect):
|
||||
is_find = True
|
||||
return str(t)
|
||||
expect_str = str(expect)
|
||||
new_ex = expect_str[0:1]+"."+expect_str[1:2]
|
||||
if t.find(new_ex) > -1:
|
||||
is_find = True
|
||||
return str(t)
|
||||
if not is_find:
|
||||
if len(v) > 1:
|
||||
return v[1]['version']
|
||||
return v[0]['version']
|
||||
return str(expect)
|
||||
|
||||
|
||||
def getCachePhpVer():
|
||||
cacheFile = getServerDir() + '/php.pl'
|
||||
v = ''
|
||||
if os.path.exists(cacheFile):
|
||||
v = mw.readFile(cacheFile)
|
||||
else:
|
||||
v = getPhpVer()
|
||||
mw.writeFile(cacheFile, v)
|
||||
return v
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
php_ver = getCachePhpVer()
|
||||
tmp = mw.execShell('cat /dev/urandom | head -n 32 | md5sum | head -c 16')
|
||||
blowfish_secret = tmp[0].strip()
|
||||
# print php_ver
|
||||
php_conf_dir = mw.getServerDir() + '/web_conf/php/conf'
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$PHP_CONF_PATH}', php_conf_dir)
|
||||
content = content.replace('{$PHP_VER}', php_ver)
|
||||
content = content.replace('{$BLOWFISH_SECRET}', blowfish_secret)
|
||||
|
||||
cfg = getCfg()
|
||||
content = content.replace('{$PMA_PATH}', cfg['path'])
|
||||
|
||||
port = cfg["port"]
|
||||
rep = r'listen\s*(.*);'
|
||||
content = re.sub(rep, "listen " + port + ';', content)
|
||||
return content
|
||||
|
||||
|
||||
def initCfg():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
data = {}
|
||||
data['port'] = '987'
|
||||
data['path'] = ''
|
||||
data['username'] = 'admin'
|
||||
data['password'] = 'admin'
|
||||
mw.writeFile(cfg, json.dumps(data))
|
||||
|
||||
|
||||
def setCfg(key, val):
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
data[key] = val
|
||||
mw.writeFile(cfg, json.dumps(data))
|
||||
|
||||
|
||||
def getCfg():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
return data
|
||||
|
||||
|
||||
def returnCfg():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
data = mw.readFile(cfg)
|
||||
return data
|
||||
|
||||
|
||||
def status():
|
||||
conf = getConf()
|
||||
conf_inc = getServerDir() + "/" + getCfg()["path"] + '/config/config.cfg'
|
||||
# 两个文件都在,才算启动成功
|
||||
if os.path.exists(conf) and os.path.exists(conf_inc):
|
||||
return 'start'
|
||||
return 'stop'
|
||||
|
||||
|
||||
def __release_port(port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().addAcceptPort(port, 'LAM默认端口', 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
|
||||
def __delete_port(port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
|
||||
def openPort():
|
||||
conf = getCfg()
|
||||
port = conf['port']
|
||||
for i in [port]:
|
||||
__release_port(i)
|
||||
return True
|
||||
|
||||
|
||||
def delPort():
|
||||
conf = getCfg()
|
||||
port = conf['port']
|
||||
for i in [port]:
|
||||
__delete_port(i)
|
||||
return True
|
||||
|
||||
|
||||
def start():
|
||||
initCfg()
|
||||
openPort()
|
||||
|
||||
pma_dir = getServerDir() + "/lam"
|
||||
if os.path.exists(pma_dir):
|
||||
rand_str = mw.getRandomString(6)
|
||||
rand_str = rand_str.lower()
|
||||
pma_dir_dst = pma_dir + "_" + rand_str
|
||||
mw.execShell("mv " + pma_dir + " " + pma_dir_dst)
|
||||
mw.execShell("chown -R www:www " + pma_dir_dst)
|
||||
mw.execShell("chmod -R 777 " + pma_dir_dst+'/sess')
|
||||
mw.execShell("chmod -R 777 " + pma_dir_dst+'/tmp')
|
||||
setCfg('path', 'lam_' + rand_str)
|
||||
|
||||
file_tpl = getPluginDir() + '/conf/lam.conf'
|
||||
file_run = getConf()
|
||||
if not os.path.exists(file_run):
|
||||
centent = mw.readFile(file_tpl)
|
||||
centent = contentReplace(centent)
|
||||
mw.writeFile(file_run, centent)
|
||||
|
||||
pma_path = getServerDir() + '/pma.pass'
|
||||
if not os.path.exists(pma_path):
|
||||
username = mw.getRandomString(8)
|
||||
password = mw.getRandomString(10)
|
||||
pass_cmd = username + ':' + mw.hasPwd(password)
|
||||
setCfg('username', username)
|
||||
setCfg('password', password)
|
||||
mw.writeFile(pma_path, pass_cmd)
|
||||
|
||||
tmp = getServerDir() + "/" + getCfg()["path"] + '/tmp'
|
||||
if not os.path.exists(tmp):
|
||||
os.mkdir(tmp)
|
||||
mw.execShell("chown -R www:www " + tmp)
|
||||
|
||||
conf_run = getServerDir() + "/" + getCfg()["path"] + '/config/config.cfg'
|
||||
if not os.path.exists(conf_run):
|
||||
conf_tpl = getPluginDir() + '/conf/config.cfg'
|
||||
centent = mw.readFile(conf_tpl)
|
||||
centent = contentReplace(centent)
|
||||
mw.writeFile(conf_run, centent)
|
||||
|
||||
log_a = accessLog()
|
||||
log_e = errorLog()
|
||||
|
||||
for i in [log_a, log_e]:
|
||||
if os.path.exists(i):
|
||||
cmd = "echo '' > " + i
|
||||
mw.execShell(cmd)
|
||||
|
||||
mw.restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def stop():
|
||||
conf = getConf()
|
||||
if os.path.exists(conf):
|
||||
os.remove(conf)
|
||||
delPort()
|
||||
mw.restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def restart():
|
||||
return start()
|
||||
|
||||
|
||||
def reload():
|
||||
file_tpl = getPluginDir() + '/conf/lam.conf'
|
||||
file_run = getConf()
|
||||
if os.path.exists(file_run):
|
||||
centent = mw.readFile(file_tpl)
|
||||
centent = contentReplace(centent)
|
||||
mw.writeFile(file_run, centent)
|
||||
return start()
|
||||
|
||||
|
||||
def setPhpVer():
|
||||
args = getArgs()
|
||||
|
||||
if not 'phpver' in args:
|
||||
return 'phpver missing'
|
||||
|
||||
cacheFile = getServerDir() + '/php.pl'
|
||||
mw.writeFile(cacheFile, args['phpver'])
|
||||
|
||||
file_tpl = getPluginDir() + '/conf/lam.conf'
|
||||
file_run = getConf()
|
||||
|
||||
content = mw.readFile(file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(file_run, content)
|
||||
|
||||
mw.restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def getSetPhpVer():
|
||||
cacheFile = getServerDir() + '/php.pl'
|
||||
if os.path.exists(cacheFile):
|
||||
return mw.readFile(cacheFile).strip()
|
||||
return ''
|
||||
|
||||
|
||||
def getPmaOption():
|
||||
data = getCfg()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
|
||||
def getPmaPort():
|
||||
try:
|
||||
port = getPort()
|
||||
return mw.returnJson(True, 'OK', port)
|
||||
except Exception as e:
|
||||
# print(e)
|
||||
return mw.returnJson(False, '插件未启动!')
|
||||
|
||||
|
||||
def setPmaPort():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['port'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
port = args['port']
|
||||
if port == '80':
|
||||
return mw.returnJson(False, '80端不能使用!')
|
||||
|
||||
file = getConf()
|
||||
if not os.path.exists(file):
|
||||
return mw.returnJson(False, '插件未启动!')
|
||||
content = mw.readFile(file)
|
||||
rep = r'listen\s*(.*);'
|
||||
content = re.sub(rep, "listen " + port + ';', content)
|
||||
mw.writeFile(file, content)
|
||||
|
||||
setCfg("port", port)
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
|
||||
def setPmaUsername():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['username'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
username = args['username']
|
||||
setCfg('username', username)
|
||||
|
||||
cfg = getCfg()
|
||||
pma_path = getServerDir() + '/pma.pass'
|
||||
username = mw.getRandomString(10)
|
||||
pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
|
||||
mw.writeFile(pma_path, pass_cmd)
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
|
||||
def setPmaPassword():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['password'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
password = args['password']
|
||||
setCfg('password', password)
|
||||
|
||||
cfg = getCfg()
|
||||
pma_path = getServerDir() + '/pma.pass'
|
||||
username = mw.getRandomString(10)
|
||||
pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
|
||||
mw.writeFile(pma_path, pass_cmd)
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
|
||||
def setPmaPath():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = args['path']
|
||||
|
||||
if len(path) < 5:
|
||||
return mw.returnJson(False, '不能小于5位!')
|
||||
|
||||
old_path = getServerDir() + "/" + getCfg()['path']
|
||||
new_path = getServerDir() + "/" + path
|
||||
|
||||
mw.execShell("mv " + old_path + " " + new_path)
|
||||
setCfg('path', path)
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
|
||||
def accessLog():
|
||||
return getServerDir() + '/access.log'
|
||||
|
||||
|
||||
def errorLog():
|
||||
return getServerDir() + '/error.log'
|
||||
|
||||
|
||||
def installVersion():
|
||||
return mw.readFile(getServerDir() + '/version.pl')
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'version':
|
||||
print(installVersion())
|
||||
elif func == 'get_cfg':
|
||||
print(returnCfg())
|
||||
elif func == 'config_inc':
|
||||
print(getConfInc())
|
||||
elif func == 'get_home_page':
|
||||
print(getHomePage())
|
||||
elif func == 'set_php_ver':
|
||||
print(setPhpVer())
|
||||
elif func == 'get_set_php_ver':
|
||||
print(getSetPhpVer())
|
||||
elif func == 'get_pma_port':
|
||||
print(getPmaPort())
|
||||
elif func == 'set_pma_port':
|
||||
print(setPmaPort())
|
||||
elif func == 'get_pma_option':
|
||||
print(getPmaOption())
|
||||
elif func == 'set_pma_username':
|
||||
print(setPmaUsername())
|
||||
elif func == 'set_pma_password':
|
||||
print(setPmaPassword())
|
||||
elif func == 'set_pma_path':
|
||||
print(setPmaPath())
|
||||
elif func == 'access_log':
|
||||
print(accessLog())
|
||||
elif func == 'error_log':
|
||||
print(errorLog())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"title":"LAM",
|
||||
"tip":"soft",
|
||||
"name":"lam",
|
||||
"type":"运行环境",
|
||||
"ps":"LDAP管理工具(LAM)",
|
||||
"versions":["9.0"],
|
||||
"shell":"install.sh",
|
||||
"checks":"server/lam",
|
||||
"path": "server/lam",
|
||||
"author":"gruberroland",
|
||||
"home":"https://github.com/LDAPAccountManager",
|
||||
"date":"2025-1-28",
|
||||
"pid": "2"
|
||||
}
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /www/server/mdserver-web/plugins/lam && bash install.sh install 9.0
|
||||
# cd /www/server/mdserver-web && python3 plugins/lam/index.py start
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
if [ "$sys_os" == "Darwin" ];then
|
||||
BAK='_bak'
|
||||
else
|
||||
BAK=''
|
||||
fi
|
||||
|
||||
sysName=`uname`
|
||||
echo "use system: ${sysName}"
|
||||
|
||||
if [ "${sysName}" == "Darwin" ]; then
|
||||
OSNAME='macos'
|
||||
elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
|
||||
OSNAME='centos'
|
||||
elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
|
||||
OSNAME='fedora'
|
||||
elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
|
||||
OSNAME='debian'
|
||||
elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
|
||||
OSNAME='ubuntu'
|
||||
elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
|
||||
OSNAME='raspbian'
|
||||
else
|
||||
OSNAME='unknow'
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
if [ -d $serverPath/lam ];then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p ${serverPath}/lam
|
||||
mkdir -p ${serverPath}/source/lam
|
||||
echo "${1}" > ${serverPath}/lam/version.pl
|
||||
|
||||
VER=$1
|
||||
|
||||
# https://github.com/LDAPAccountManager/lam/releases/download/9.0/ldap-account-manager-9.0.tar.bz2
|
||||
FDIR=ldap-account-manager-${VER}
|
||||
FILE=ldap-account-manager-${VER}.tar.bz2
|
||||
DOWNLOAD=https://github.com/LDAPAccountManager/lam/releases/download/9.0/${FILE}
|
||||
|
||||
|
||||
if [ ! -f $serverPath/source/phpmyadmin/$FILE ];then
|
||||
wget --no-check-certificate -O $serverPath/source/lam/$FILE $DOWNLOAD
|
||||
fi
|
||||
|
||||
if [ ! -d $serverPath/source/lam/$FDIR ];then
|
||||
cd $serverPath/source/lam && tar jxvf $FILE
|
||||
fi
|
||||
|
||||
cp -r $serverPath/source/lam/$FDIR $serverPath/lam/
|
||||
cd $serverPath/lam/ && mv $FDIR lam
|
||||
# rm -rf $serverPath/source/lam/$FDIR
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/lam/index.py start
|
||||
echo '安装完成'
|
||||
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/lam/index.py stop
|
||||
|
||||
rm -rf ${serverPath}/lam
|
||||
echo '卸载完成'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App $2
|
||||
else
|
||||
Uninstall_App $2
|
||||
fi
|
||||
Executable
+164
@@ -0,0 +1,164 @@
|
||||
function str2Obj(str){
|
||||
var data = {};
|
||||
kv = str.split('&');
|
||||
for(i in kv){
|
||||
v = kv[i].split('=');
|
||||
data[v[0]] = v[1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function pmaPost(method,args,callback){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(str2Obj(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'lam', func:method, args:_args}, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function pmaAsyncPost(method,args){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(str2Obj(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
return syncPost('/plugins/run', {name:'lam', func:method, args:_args});
|
||||
}
|
||||
|
||||
function homePage(){
|
||||
pmaPost('get_home_page', '', function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata.status){
|
||||
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var con = '<button class="btn btn-default btn-sm" onclick="window.open(\'' + rdata.data + '\')">主页</button>';
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
//phpmyadmin切换php版本
|
||||
function phpVer(version) {
|
||||
|
||||
var _version = pmaAsyncPost('get_set_php_ver','')
|
||||
if (_version['data'] != ''){
|
||||
version = _version['data'];
|
||||
}
|
||||
|
||||
$.post('/site/get_php_version', function(data) {
|
||||
var rdata = data['data'];
|
||||
// console.log(rdata);
|
||||
var body = "<div class='ver line'><span class='tname'>PHP版本</span><select id='phpver' class='bt-input-text mr20' name='phpVersion' style='width:110px'>";
|
||||
var optionSelect = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
optionSelect = rdata[i].version == version ? 'selected' : '';
|
||||
body += "<option value='" + rdata[i].version + "' " + optionSelect + ">" + rdata[i].name + "</option>"
|
||||
}
|
||||
body += '</select><button class="btn btn-success btn-sm" onclick="phpVerChange(\'phpversion\',\'get\')">保存</button></div>';
|
||||
$(".soft-man-con").html(body);
|
||||
},'json');
|
||||
}
|
||||
|
||||
function phpVerChange(type, msg) {
|
||||
var phpver = $("#phpver").val();
|
||||
pmaPost('set_php_ver', 'phpver='+phpver, function(data){
|
||||
if ( data.data == 'ok' ){
|
||||
layer.msg('设置成功!',{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
} else {
|
||||
layer.msg('设置失败!',{icon:2,time:2000,shade: [0.3, '#000']});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//phpmyadmin安全设置
|
||||
function safeConf() {
|
||||
pmaPost('get_pma_option', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if (!rdata.status){
|
||||
layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
var cfg = rdata.data;
|
||||
var con = '<div class="ver line">\
|
||||
<span class="tname">访问端口</span>\
|
||||
<input style="width:110px" class="bt-input-text phpmyadmindk mr20" name="Name" id="pmport" value="' + cfg['port'] + '" placeholder="phpmyadmin访问端口" maxlength="5" type="number">\
|
||||
<button class="btn btn-success btn-sm" onclick="setPamPort()">保存</button>\
|
||||
</div>\
|
||||
<div class="ver line">\
|
||||
<span class="tname">用户名</span>\
|
||||
<input style="width:110px" class="bt-input-text mr20" name="username" id="pmport" value="' + cfg['username'] + '" placeholder="认证用户名" type="text">\
|
||||
<button class="btn btn-success btn-sm" onclick="setPmaUsername()">保存</button>\
|
||||
</div>\
|
||||
<div class="ver line">\
|
||||
<span class="tname">密码</span>\
|
||||
<input style="width:110px" class="bt-input-text mr20" name="password" id="pmport" value="' + cfg['password'] + '" placeholder="密码" type="text">\
|
||||
<button class="btn btn-success btn-sm" onclick="setPmaPassword()">保存</button>\
|
||||
</div>\
|
||||
<hr/>\
|
||||
<div class="ver line">\
|
||||
<span class="tname">路径名</span>\
|
||||
<input style="width:180px" class="bt-input-text mr20" name="path" id="pmport" value="' + cfg['path'] + '" placeholder="" type="text">\
|
||||
<button class="btn btn-success btn-sm" onclick="setPmaPath()">保存</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
function setPmaUsername(){
|
||||
var username = $("input[name=username]").val();
|
||||
pmaPost('set_pma_username',{'username':username}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function setPmaPassword(){
|
||||
var password = $("input[name=password]").val();
|
||||
pmaPost('set_pma_password',{'password':password}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function setPmaPath(){
|
||||
var path = $("input[name=path]").val();
|
||||
pmaPost('set_pma_path',{'path':path}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
//修改phpmyadmin端口
|
||||
function setPamPort() {
|
||||
var pmport = $("#pmport").val();
|
||||
if (pmport < 80 || pmport > 65535) {
|
||||
layer.msg('端口范围不合法!', { icon: 2 });
|
||||
return;
|
||||
}
|
||||
var data = 'port=' + pmport;
|
||||
|
||||
pmaPost('set_pma_port',data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# LDAP Defaults
|
||||
#
|
||||
|
||||
# See ldap.conf(5) for details
|
||||
# This file should be world readable but not world writable.
|
||||
|
||||
BASE dc=mw,dc=com
|
||||
URI ldap://{$IP} ldap://{$IP}:666
|
||||
|
||||
#SIZELIMIT 12
|
||||
#TIMELIMIT 15
|
||||
#DEREF never
|
||||
|
||||
# TLS certificates (needed for GnuTLS)
|
||||
TLS_CACERT /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Executable
+30
@@ -0,0 +1,30 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('ldap');">服务</p>
|
||||
<p onclick="pluginInitD('ldap');">自启动</p>
|
||||
<p onclick="pluginConfigTpl('ldap',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('ldap','','run_log');">运行日志</p>
|
||||
<p onclick="ladpReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=ldap&f=js/ldap.js", function(){
|
||||
pluginService('ldap', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
Executable
+354
@@ -0,0 +1,354 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'ldap'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
# path = getServerDir() + "/redis.conf"
|
||||
path = "/etc/ldap/ldap.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/config/ldap.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
def configTpl():
|
||||
path = "/etc/ldap/schema"
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pidfile\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def status():
|
||||
pid_file = "/var/run/slapd/slapd.pid"
|
||||
if not os.path.exists(pid_file):
|
||||
return 'stop'
|
||||
|
||||
# data = mw.execShell(
|
||||
# "ps aux|grep redis |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
|
||||
|
||||
# if data[0] == '':
|
||||
# return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
conf = getConf()
|
||||
conf_tpl = getConfTpl()
|
||||
if not os.path.exists(conf):
|
||||
content = mw.readFile(conf_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(conf, content)
|
||||
return True
|
||||
|
||||
|
||||
def ladpOp(method):
|
||||
initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
return 'ok'
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service slapd ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' slapd')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return ladpOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return ladpOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = ladpOp('restart')
|
||||
|
||||
log_file = runLog()
|
||||
mw.execShell("echo '' > " + log_file)
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return ladpOp('reload')
|
||||
|
||||
|
||||
def getPort():
|
||||
conf = getConf()
|
||||
content = mw.readFile(conf)
|
||||
|
||||
rep = r"^(port)\s*([.0-9A-Za-z_& ~]+)"
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if tmp:
|
||||
return tmp.groups()[1]
|
||||
return '6379'
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status slapd | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc slapd_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable slapd')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc slapd_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable slapd')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/data/redis.log'
|
||||
|
||||
|
||||
def getRedisConfInfo():
|
||||
conf = getConf()
|
||||
|
||||
gets = [
|
||||
{'name': 'bind', 'type': 2, 'ps': '绑定IP(修改绑定IP可能会存在安全隐患)','must_show':1},
|
||||
{'name': 'port', 'type': 2, 'ps': '绑定端口','must_show':1},
|
||||
{'name': 'timeout', 'type': 2, 'ps': '空闲链接超时时间,0表示不断开','must_show':1},
|
||||
{'name': 'maxclients', 'type': 2, 'ps': '最大连接数','must_show':1},
|
||||
{'name': 'databases', 'type': 2, 'ps': '数据库数量','must_show':1},
|
||||
{'name': 'requirepass', 'type': 2, 'ps': 'redis密码,留空代表没有设置密码','must_show':1},
|
||||
{'name': 'maxmemory', 'type': 2, 'ps': 'MB,最大使用内存,0表示不限制','must_show':1},
|
||||
{'name': 'slaveof', 'type': 2, 'ps': '同步主库地址','must_show':0},
|
||||
{'name': 'masterauth', 'type': 2, 'ps': '同步主库密码', 'must_show':0}
|
||||
]
|
||||
content = mw.readFile(conf)
|
||||
|
||||
result = []
|
||||
for g in gets:
|
||||
rep = r"^(" + g['name'] + r'\)\s*([.0-9A-Za-z_& ~]+)'
|
||||
tmp = re.search(rep, content, re.M)
|
||||
if not tmp:
|
||||
if g['must_show'] == 0:
|
||||
continue
|
||||
|
||||
g['value'] = ''
|
||||
result.append(g)
|
||||
continue
|
||||
g['value'] = tmp.groups()[1]
|
||||
if g['name'] == 'maxmemory':
|
||||
g['value'] = g['value'].strip("mb")
|
||||
result.append(g)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def getRedisConf():
|
||||
data = getRedisConfInfo()
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def submitRedisConf():
|
||||
gets = ['bind', 'port', 'timeout', 'maxclients',
|
||||
'databases', 'requirepass', 'maxmemory','slaveof','masterauth']
|
||||
args = getArgs()
|
||||
conf = getConf()
|
||||
content = mw.readFile(conf)
|
||||
for g in gets:
|
||||
if g in args:
|
||||
rep = g + r'\s*([.0-9A-Za-z_& ~]+)'
|
||||
val = g + ' ' + args[g]
|
||||
|
||||
if g == 'maxmemory':
|
||||
val = g + ' ' + args[g] + "mb"
|
||||
|
||||
if g == 'requirepass' and args[g] == '':
|
||||
content = re.sub('requirepass', '#requirepass', content)
|
||||
if g == 'requirepass' and args[g] != '':
|
||||
content = re.sub('#requirepass', 'requirepass', content)
|
||||
content = re.sub(rep, val, content)
|
||||
|
||||
if g != 'requirepass':
|
||||
content = re.sub(rep, val, content)
|
||||
mw.writeFile(conf, content)
|
||||
reload()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
def installPreInspection():
|
||||
slapd_path = '/etc/ldap/slapd.d'
|
||||
if not os.path.exists(slapd_path):
|
||||
return "需要手动执行:apt install -y slapd ldap-utils"
|
||||
return 'ok'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'get_redis_conf':
|
||||
print(getRedisConf())
|
||||
elif func == 'submit_redis_conf':
|
||||
print(submitRedisConf())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sort":4,
|
||||
"ps": "LDAP轻量目录服务",
|
||||
"name": "ldap",
|
||||
"title": "LDAP",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"tip": "soft",
|
||||
"install_pre_inspection":true,
|
||||
"checks": "server/ldap",
|
||||
"path": "server/ldap",
|
||||
"display": 1,
|
||||
"author": "ladp",
|
||||
"date": "2025-01-28",
|
||||
"home": "",
|
||||
"type": 0,
|
||||
"pid": "4"
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
sysArch=`arch`
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# https://juejin.cn/post/7309323953683480588
|
||||
|
||||
# dpkg-reconfigure slapd
|
||||
|
||||
# 该命令将在本地服务器上查找并返回在 “dc=bytedance,dc=local” 这个起点(和其下的所有子目录)下,所有 cn 属性有值的条目的详细信息
|
||||
# ldapsearch -x -H ldap://localhost -b "dc=bytedance,dc=local" "(cn=*)"
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
apt install -y slapd ldap-utils
|
||||
|
||||
mkdir -p $serverPath/ldap
|
||||
echo "${VERSION}" > $serverPath/ldap/version.pl
|
||||
echo "${VERSION}安装完成"
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
apt remove -y slapd ldap-utils
|
||||
rm -rf $serverPath/ldap/version.pl
|
||||
rm -rf $serverPath/ldap
|
||||
echo "卸载ldap成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
sysArch=`arch`
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# https://juejin.cn/post/7309323953683480588
|
||||
|
||||
# dpkg-reconfigure slapd
|
||||
# slappasswd 修改密码
|
||||
|
||||
# 该命令将在本地服务器上查找并返回在 “dc=bytedance,dc=local” 这个起点(和其下的所有子目录)下,所有 cn 属性有值的条目的详细信息
|
||||
# ldapsearch -x -H ldap://localhost -b "dc=bytedance,dc=local" "(cn=*)"
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
apt install -y slapd ldap-utils
|
||||
|
||||
mkdir -p $serverPath/ldap
|
||||
echo "${VERSION}" > $serverPath/ldap/version.pl
|
||||
echo "${VERSION}安装完成"
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf $serverPath/ldap/version.pl
|
||||
echo "卸载ldap成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
function ldapPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'ldap';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function ldapPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'ldap';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
//配置修改
|
||||
function getLadpConfig(version) {
|
||||
ladpPost('get_redis_conf', version,'',function(data){
|
||||
// console.log(data);
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '70'
|
||||
if (rdata[i].name == 'error_reporting') w = '250';
|
||||
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
|
||||
switch (rdata[i].type) {
|
||||
case 0:
|
||||
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;"><option value="1" ' + selected_1 + '>开启</option><option value="0" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
case 1:
|
||||
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;"><option value="On" ' + selected_1 + '>开启</option><option value="Off" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
}
|
||||
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
|
||||
}
|
||||
var con = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right"><button class="btn btn-success btn-sm mr5" onclick="getRedisConfig(\'' + version + '\')">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitConf(\'' + version + '\')">保存</button></div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
//提交配置
|
||||
function submitConf(version) {
|
||||
var data = {
|
||||
version: version,
|
||||
bind: $("input[name='bind']").val(),
|
||||
'port': $("input[name='port']").val(),
|
||||
'timeout': $("input[name='timeout']").val(),
|
||||
maxclients: $("input[name='maxclients']").val(),
|
||||
databases: $("input[name='databases']").val(),
|
||||
requirepass: $("input[name='requirepass']").val(),
|
||||
maxmemory: $("input[name='maxmemory']").val(),
|
||||
};
|
||||
|
||||
redisPost('submit_ladp_conf', version, data, function(ret_data){
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function ladpReadme(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>集群创建1</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@ def binLog():
|
||||
return mw.returnJson(False, '0')
|
||||
con = con.replace('#log-bin=mysql-bin', 'log-bin=mysql-bin')
|
||||
con = con.replace('#binlog_format=mixed', 'binlog_format=mixed')
|
||||
mw.writeFile(conf, con)
|
||||
mw.execShell('sync')
|
||||
restart()
|
||||
else:
|
||||
@@ -294,11 +295,12 @@ def binLog():
|
||||
return mw.returnJson(True, dsize)
|
||||
con = con.replace('log-bin=mysql-bin', '#log-bin=mysql-bin')
|
||||
con = con.replace('binlog_format=mixed', '#binlog_format=mixed')
|
||||
mw.writeFile(conf, con)
|
||||
mw.execShell('sync')
|
||||
restart()
|
||||
mw.execShell('rm -f ' + path + '/mysql-bin.*')
|
||||
|
||||
mw.writeFile(conf, con)
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
def binLogList():
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"sort":2,
|
||||
"hook":["database"],
|
||||
"title":"MariaDB",
|
||||
"tip":"soft",
|
||||
@@ -8,7 +9,7 @@
|
||||
"uninstall_pre_inspection":true,
|
||||
"checks": "server/mariadb",
|
||||
"path": "server/mariadb",
|
||||
"versions":["10.6","10.7","10.8","10.9","10.11","11.0","11.1","11.2","11.3","11.4","11.5","11.6"],
|
||||
"versions":["10.6","10.7","10.8","10.9","10.11","11.0","11.1","11.2","11.3","11.4","11.5","11.6","11.7","11.8"],
|
||||
"shell":"install.sh",
|
||||
"checks":"server/mariadb",
|
||||
"path":"server/mariadb",
|
||||
|
||||
@@ -12,15 +12,19 @@ if sys.platform != 'darwin':
|
||||
os.chdir('/www/server/mdserver-web')
|
||||
|
||||
|
||||
chdir = os.getcwd()
|
||||
sys.path.append(chdir + '/class/core')
|
||||
# chdir = os.getcwd()
|
||||
# sys.path.append(chdir + '/class/core')
|
||||
|
||||
# reload(sys)
|
||||
# sys.setdefaultencoding('utf-8')
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import mw
|
||||
import db
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
|
||||
class backupTools:
|
||||
|
||||
@@ -13,7 +13,7 @@ sysName=`uname`
|
||||
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.11.10
|
||||
MY_VER=10.11.11
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ sysName=`uname`
|
||||
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.6.20
|
||||
MY_VER=10.6.21
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ sysName=`uname`
|
||||
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=11.4.4
|
||||
MY_VER=11.4.5
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
#https://dev.mysql.com/downloads/mysql/5.5.html#downloads
|
||||
#https://dev.mysql.com/downloads/file/?id=480541
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
sysName=`uname`
|
||||
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=11.7.2
|
||||
|
||||
Install_app()
|
||||
{
|
||||
mkdir -p ${mariadbDir}
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
if [ "$sysName" != "Darwin" ];then
|
||||
mkdir -p /var/log/mariadb
|
||||
touch /var/log/mariadb/mariadb.log
|
||||
fi
|
||||
|
||||
# ----- cpu start ------
|
||||
if [ -z "${cpuCore}" ]; then
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ -f /proc/cpuinfo ];then
|
||||
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
|
||||
fi
|
||||
|
||||
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
|
||||
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
|
||||
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
|
||||
cpuCore="${MEM_INFO}"
|
||||
fi
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ "$cpuCore" -gt "2" ];then
|
||||
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
# ----- cpu end ------
|
||||
|
||||
# if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
# wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
# fi
|
||||
|
||||
# https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz
|
||||
if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
|
||||
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
INSTALL_CMD=cmake
|
||||
# check cmake version
|
||||
CMAKE_VERSION=`cmake -version | grep version | awk '{print $3}' | awk -F '.' '{print $1}'`
|
||||
if [ "$CMAKE_VERSION" -eq "2" ];then
|
||||
mkdir -p /var/log/mariadb
|
||||
touch /var/log/mariadb/mariadb.log
|
||||
INSTALL_CMD=cmake3
|
||||
fi
|
||||
|
||||
if [ ! -d $serverPath/mariadb ];then
|
||||
cd ${mariadbDir}/mariadb-${MY_VER} && ${INSTALL_CMD} \
|
||||
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
|
||||
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \
|
||||
-DMYSQL_USER=mysql \
|
||||
-DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \
|
||||
-DWITH_MYISAM_STORAGE_ENGINE=1 \
|
||||
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
|
||||
-DWITH_MEMORY_STORAGE_ENGINE=1 \
|
||||
-DENABLED_LOCAL_INFILE=1 \
|
||||
-DWITH_PARTITION_STORAGE_ENGINE=1 \
|
||||
-DEXTRA_CHARSETS=all \
|
||||
-DDEFAULT_CHARSET=utf8mb4 \
|
||||
-DDEFAULT_COLLATION=utf8mb4_general_ci \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/g++
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mariadb ];then
|
||||
echo '11.7' > $serverPath/mariadb/version.pl
|
||||
echo '安装完成'
|
||||
else
|
||||
echo '安装失败'
|
||||
echo 'install fail'>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d ${mariadbDir}/mariadb-${MY_VER} ];then
|
||||
rm -rf ${mariadbDir}/mariadb-${MY_VER}
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
rm -rf $serverPath/mariadb
|
||||
echo '卸载完成'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user