emtl 0.1.2.dev2__tar.gz → 0.1.4.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.bumpversion.cfg +1 -1
  2. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.cookiecutterrc +1 -1
  3. emtl-0.1.4.dev1/.github/workflows/document.yml +38 -0
  4. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.github/workflows/github-actions.yml +46 -9
  5. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.readthedocs.yml +1 -1
  6. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/CHANGELOG.rst +5 -0
  7. {emtl-0.1.2.dev2/src/emtl.egg-info → emtl-0.1.4.dev1}/PKG-INFO +10 -6
  8. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/README.rst +3 -3
  9. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/conf.py +3 -1
  10. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/setup.py +7 -7
  11. emtl-0.1.4.dev1/src/emtl/__init__.py +11 -0
  12. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl/_version.py +2 -2
  13. emtl-0.1.4.dev1/src/emtl/const.py +19 -0
  14. emtl-0.1.4.dev1/src/emtl/core.py +139 -0
  15. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl/tests/test_cli.py +1 -1
  16. emtl-0.1.4.dev1/src/emtl/tests/test_core.py +27 -0
  17. emtl-0.1.4.dev1/src/emtl/utils.py +51 -0
  18. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1/src/emtl.egg-info}/PKG-INFO +10 -6
  19. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl.egg-info/SOURCES.txt +4 -0
  20. emtl-0.1.4.dev1/src/emtl.egg-info/requires.txt +4 -0
  21. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/tox.ini +1 -2
  22. emtl-0.1.2.dev2/src/emtl/__init__.py +0 -9
  23. emtl-0.1.2.dev2/src/emtl/core.py +0 -8
  24. emtl-0.1.2.dev2/src/emtl/tests/test_core.py +0 -14
  25. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.coveragerc +0 -0
  26. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.editorconfig +0 -0
  27. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.gitignore +0 -0
  28. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/.pre-commit-config.yaml +0 -0
  29. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/AUTHORS.rst +0 -0
  30. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/CONTRIBUTING.rst +0 -0
  31. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/LICENSE +0 -0
  32. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/ci/bootstrap.py +0 -0
  33. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/ci/requirements.txt +0 -0
  34. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/ci/templates/.github/workflows/github-actions.yml +0 -0
  35. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/authors.rst +0 -0
  36. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/changelog.rst +0 -0
  37. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/contributing.rst +0 -0
  38. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/index.rst +0 -0
  39. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/installation.rst +0 -0
  40. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/readme.rst +0 -0
  41. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/reference/emtl.rst +0 -0
  42. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/reference/index.rst +0 -0
  43. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/requirements.txt +0 -0
  44. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/spelling_wordlist.txt +0 -0
  45. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/docs/usage.rst +0 -0
  46. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/pyproject.toml +0 -0
  47. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/pytest.ini +0 -0
  48. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/setup.cfg +0 -0
  49. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl/__main__.py +0 -0
  50. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl/cli.py +0 -0
  51. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl/tests/__init__.py +0 -0
  52. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl.egg-info/dependency_links.txt +0 -0
  53. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl.egg-info/entry_points.txt +0 -0
  54. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl.egg-info/not-zip-safe +0 -0
  55. {emtl-0.1.2.dev2 → emtl-0.1.4.dev1}/src/emtl.egg-info/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.1.1
2
+ current_version = 0.1.3
3
3
  commit = True
4
4
  tag = True
5
5
 
@@ -54,7 +54,7 @@ default_context:
54
54
  sphinx_theme: "furo"
55
55
  test_matrix_separate_coverage: "yes"
56
56
  tests_inside_package: "yes"
57
- version: "0.1.0"
57
+ version: "0.1.2"
58
58
  version_manager: "bump2version"
59
59
  website: "riiy.gihub.io/emtl"
60
60
  year_from: "2024"
@@ -0,0 +1,38 @@
1
+ name: Documentation
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Publish documentation
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: 3.11
18
+ architecture: x64
19
+ - name: install dependencies
20
+ run: |
21
+ python -mpip install --progress-bar=off -r ci/requirements.txt
22
+ python -mpip install Sphinx==7.2.6
23
+ virtualenv --version
24
+ pip --version
25
+ tox --version
26
+ pip list --format=freeze
27
+ - name: Build
28
+ env:
29
+ TOXPYTHON: 'python311'
30
+ run: >
31
+ tox -e docs -v
32
+ - name: Publish
33
+ uses: peaceiris/actions-gh-pages@v3
34
+ env:
35
+ github_token: ${{ secrets.GITHUB_TOKEN }}
36
+ with:
37
+ github_token: ${{ secrets.GITHUB_TOKEN }}
38
+ publish_dir: ./dist/docs
@@ -1,5 +1,12 @@
1
1
  name: build
2
- on: [push, pull_request, workflow_dispatch]
2
+ on:
3
+ push:
4
+ branches:
5
+ - master
6
+ pull_request:
7
+ branches:
8
+ - master
9
+ workflow_dispatch:
3
10
  jobs:
4
11
  test:
5
12
  name: ${{ matrix.name }}
@@ -25,23 +32,47 @@ jobs:
25
32
  python_arch: 'x64'
26
33
  tox_env: 'py310-cover'
27
34
  os: 'ubuntu-latest'
35
+ - name: 'py310-nocov (ubuntu)'
36
+ python: '3.10'
37
+ toxpython: 'python3.10'
38
+ python_arch: 'x64'
39
+ tox_env: 'py310-nocov'
40
+ os: 'ubuntu-latest'
41
+ - name: 'py310-cover (macos)'
42
+ python: '3.10'
43
+ toxpython: 'python3.10'
44
+ python_arch: 'x64'
45
+ tox_env: 'py310-cover'
46
+ os: 'macos-latest'
47
+ - name: 'py310-nocov (macos)'
48
+ python: '3.10'
49
+ toxpython: 'python3.10'
50
+ python_arch: 'x64'
51
+ tox_env: 'py310-nocov'
52
+ os: 'macos-latest'
28
53
  - name: 'py311-cover (ubuntu)'
29
54
  python: '3.11'
30
55
  toxpython: 'python3.11'
31
56
  python_arch: 'x64'
32
57
  tox_env: 'py311-cover'
33
58
  os: 'ubuntu-latest'
34
- - name: 'py312-cover (ubuntu)'
35
- python: '3.12'
36
- toxpython: 'python3.12'
59
+ - name: 'py311-nocov (ubuntu)'
60
+ python: '3.11'
61
+ toxpython: 'python3.11'
37
62
  python_arch: 'x64'
38
- tox_env: 'py312-cover'
63
+ tox_env: 'py311-nocov'
39
64
  os: 'ubuntu-latest'
40
- - name: 'py312-cover (macos)'
41
- python: '3.12'
42
- toxpython: 'python3.12'
65
+ - name: 'py311-cover (macos)'
66
+ python: '3.11'
67
+ toxpython: 'python3.11'
68
+ python_arch: 'x64'
69
+ tox_env: 'py311-cover'
70
+ os: 'macos-latest'
71
+ - name: 'py311-nocov (macos)'
72
+ python: '3.11'
73
+ toxpython: 'python3.11'
43
74
  python_arch: 'x64'
44
- tox_env: 'py312-cover'
75
+ tox_env: 'py311-nocov'
45
76
  os: 'macos-latest'
46
77
  steps:
47
78
  - uses: actions/checkout@v4
@@ -61,6 +92,8 @@ jobs:
61
92
  - name: test
62
93
  env:
63
94
  TOXPYTHON: '${{ matrix.toxpython }}'
95
+ EM_USERNAME: ${{ secrets.EM_USERNAME }}
96
+ EM_PASSWORD: ${{ secrets.EM_PASSWORD }}
64
97
  run: >
65
98
  tox -e ${{ matrix.tox_env }} -v
66
99
  finish:
@@ -86,6 +119,8 @@ jobs:
86
119
  env:
87
120
  TOXPYTHON: 'python311'
88
121
  PYTEST_ADDOPTS: '--cov-append'
122
+ EM_USERNAME: ${{ secrets.EM_USERNAME }}
123
+ EM_PASSWORD: ${{ secrets.EM_PASSWORD }}
89
124
  run: >
90
125
  PYTEST_ADDOPTS=--cov-append tox -e py310-cover -v
91
126
  - uses: codecov/codecov-action@v4
@@ -95,3 +130,5 @@ jobs:
95
130
  verbose: true
96
131
  env:
97
132
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
133
+ EM_USERNAME: ${{ secrets.EM_USERNAME }}
134
+ EM_PASSWORD: ${{ secrets.EM_PASSWORD }}
@@ -6,7 +6,7 @@ formats: all
6
6
  build:
7
7
  os: ubuntu-22.04
8
8
  tools:
9
- python: "3"
9
+ python: "3.10"
10
10
  python:
11
11
  install:
12
12
  - requirements: docs/requirements.txt
@@ -2,6 +2,11 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ 0.1.2 (2024-04-18)
6
+ ------------------
7
+
8
+ * add login && add asset position
9
+
5
10
  0.1.1 (2024-04-16)
6
11
  ------------------
7
12
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emtl
3
- Version: 0.1.2.dev2
4
- Summary: 东方财富交易系统
3
+ Version: 0.1.4.dev1
4
+ Summary: 东方财富自动交易系统
5
5
  Home-page: https://github.com/riiy/emtl
6
6
  Author: Eastmoney Trade Library
7
7
  Author-email: riiyzhou@gmail.com
@@ -9,7 +9,7 @@ License: MIT
9
9
  Project-URL: Documentation, https://emtl.readthedocs.io/
10
10
  Project-URL: Changelog, https://emtl.readthedocs.io/en/latest/changelog.html
11
11
  Project-URL: Issue Tracker, https://github.com/riiy/emtl/issues
12
- Keywords: trade,东方财富,股票,交易
12
+ Keywords: autotrade,trade,东方财富,股票,交易,交易机器人
13
13
  Platform: UNKNOWN
14
14
  Classifier: Development Status :: 2 - Pre-Alpha
15
15
  Classifier: Intended Audience :: Developers
@@ -21,10 +21,9 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Programming Language :: Python :: 3.10
23
23
  Classifier: Programming Language :: Python :: 3.11
24
- Classifier: Programming Language :: Python :: 3.12
25
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
25
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
26
+ Requires-Python: >=3.10
28
27
  License-File: LICENSE
29
28
  License-File: AUTHORS.rst
30
29
 
@@ -34,7 +33,7 @@ Overview
34
33
 
35
34
 
36
35
 
37
- 东方财富交易系统
36
+ 东方财富自动交易系统
38
37
 
39
38
  * Free software: MIT license
40
39
 
@@ -85,6 +84,11 @@ Note, to combine the coverage data from all the tox environments run:
85
84
  Changelog
86
85
  =========
87
86
 
87
+ 0.1.2 (2024-04-18)
88
+ ------------------
89
+
90
+ * add login && add asset position
91
+
88
92
  0.1.1 (2024-04-16)
89
93
  ------------------
90
94
 
@@ -41,15 +41,15 @@ Overview
41
41
  :alt: Supported implementations
42
42
  :target: https://pypi.org/project/emtl
43
43
 
44
- .. |commits-since| image:: https://img.shields.io/github/commits-since/riiy/emtl/v0.1.1.svg
44
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/riiy/emtl/v0.1.3.svg
45
45
  :alt: Commits since latest release
46
- :target: https://github.com/riiy/emtl/compare/v0.1.1...master
46
+ :target: https://github.com/riiy/emtl/compare/v0.1.3...master
47
47
 
48
48
 
49
49
 
50
50
  .. end-badges
51
51
 
52
- 东方财富交易系统
52
+ 东方财富自动交易系统
53
53
 
54
54
  * Free software: MIT license
55
55
 
@@ -1,3 +1,5 @@
1
+ needs_sphinx = "7.2"
2
+ sphinx = "7.2"
1
3
  extensions = [
2
4
  "sphinx.ext.autodoc",
3
5
  "sphinx.ext.autosummary",
@@ -23,7 +25,7 @@ except Exception:
23
25
  import traceback
24
26
 
25
27
  traceback.print_exc()
26
- version = release = "0.1.1"
28
+ version = release = "0.1.3"
27
29
 
28
30
  pygments_style = "trac"
29
31
  templates_path = ["."]
@@ -16,10 +16,10 @@ setup(
16
16
  use_scm_version={
17
17
  "local_scheme": "dirty-tag",
18
18
  "write_to": "src/emtl/_version.py",
19
- "fallback_version": "0.1.1",
19
+ "fallback_version": "0.1.3",
20
20
  },
21
21
  license="MIT",
22
- description="东方财富交易系统",
22
+ description="东方财富自动交易系统",
23
23
  long_description="{}\n{}".format(
24
24
  re.compile("^.. start-badges.*^.. end-badges", re.M | re.S).sub("", read("README.rst")),
25
25
  re.sub(":[a-z]+:`~?(.*?)`", r"``\1``", read("CHANGELOG.rst")),
@@ -47,7 +47,7 @@ setup(
47
47
  # "Programming Language :: Python :: 3.9",
48
48
  "Programming Language :: Python :: 3.10",
49
49
  "Programming Language :: Python :: 3.11",
50
- "Programming Language :: Python :: 3.12",
50
+ # "Programming Language :: Python :: 3.12",
51
51
  "Programming Language :: Python :: Implementation :: CPython",
52
52
  # "Programming Language :: Python :: Implementation :: PyPy",
53
53
  # uncomment if you test on these interpreters:
@@ -62,15 +62,15 @@ setup(
62
62
  "Issue Tracker": "https://github.com/riiy/emtl/issues",
63
63
  },
64
64
  keywords=[
65
+ "autotrade",
65
66
  "trade",
66
67
  "东方财富",
67
68
  "股票",
68
69
  "交易",
70
+ "交易机器人",
69
71
  ],
70
- python_requires=">=3.8",
71
- install_requires=[
72
- # eg: "aspectlib==1.1.1", "six>=1.7",
73
- ],
72
+ python_requires=">=3.10",
73
+ install_requires=["rsa==4.9", "requests==2.31.0", "ddddocr==1.4.11", "cryptography==42.0.5"],
74
74
  extras_require={
75
75
  # eg:
76
76
  # "rst": ["docutils>=0.11"],
@@ -0,0 +1,11 @@
1
+ __version__ = "0.1.3"
2
+
3
+ from .core import emt
4
+ from .core import login
5
+ from .core import query_asset_and_position
6
+
7
+ __all__ = [
8
+ "emt",
9
+ "login",
10
+ "query_asset_and_position",
11
+ ]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1.2.dev2'
16
- __version_tuple__ = version_tuple = (0, 1, 2, 'dev2')
15
+ __version__ = version = '0.1.4.dev1'
16
+ __version_tuple__ = version_tuple = (0, 1, 4, 'dev1')
@@ -0,0 +1,19 @@
1
+ _urls: dict = {
2
+ "yzm": "https://jywg.18.cn/Login/YZM?randNum=",
3
+ "login": "https://jywg.18.cn/Login/Authentication?validatekey=",
4
+ "query_asset_and_pos": "https://jywg.18.cn/Com/queryAssetAndPositionV1?validatekey=",
5
+ "query_orders": "https://jywg.18.cn/Search/GetOrdersData?validatekey=",
6
+ "query_trades": "https://jywg.18.cn/Search/GetDealData?validatekey=",
7
+ "query_his_orders": "https://jywg.18.cn/Search/GetHisOrdersData?validatekey=",
8
+ "query_his_trades": "https://jywg.18.cn/Search/GetHisDealData?validatekey=",
9
+ "query_funds_flow": "https://jywg.18.cn/Search/GetFundsFlow?validatekey=",
10
+ "query_positions": "https://jywg.18.cn/Search/GetStockList?validatekey=",
11
+ "insert_order": "https://jywg.18.cn/Trade/SubmitTradeV2?validatekey=",
12
+ "cancel_order": "https://jywg.18.cn/Trade/RevokeOrders?validatekey=",
13
+ }
14
+ _base_headers: dict = {
15
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) "
16
+ "Chrome/114.0.0.0 Safari/537.36",
17
+ "Origin": "https://jywg.18.cn",
18
+ "Host": "jywg.18.cn",
19
+ }
@@ -0,0 +1,139 @@
1
+ import re
2
+ from random import SystemRandom
3
+ from typing import Any
4
+ from typing import Optional
5
+
6
+ from ddddocr import DdddOcr
7
+ from requests import Response
8
+ from requests import Session
9
+ from requests import get
10
+
11
+ from .const import _base_headers
12
+ from .const import _urls
13
+ from .utils import Response as Resp
14
+ from .utils import emt_trade_encrypt
15
+ from .utils import get_logger
16
+ from .utils import response_deserialize
17
+
18
+ logger = get_logger(__name__)
19
+ ocr = DdddOcr(show_ad=False)
20
+ session = Session()
21
+ _em_validate_key = ""
22
+
23
+
24
+ def emt(user, password):
25
+ return user, password
26
+
27
+
28
+ def _check_resp(resp: Response):
29
+ if resp.status_code != 200:
30
+ logger.error(f"request {resp.url} fail, code={resp.status_code}, response={resp.text}")
31
+ return False
32
+ return True
33
+
34
+
35
+ def _query_something(tag: str, count: int = 100, data: Optional[dict] = None) -> Optional[Response]:
36
+ """通用查询函数
37
+
38
+ :param tag: 请求类型
39
+ :param count: 查询数量,可选
40
+ :param data: 请求提交数据,可选
41
+ :return:
42
+ """
43
+ assert _em_validate_key, "em_validatekey is empty"
44
+ assert tag in _urls, f"{tag} not in url list"
45
+ url = _urls[tag] + _em_validate_key
46
+ if data is None:
47
+ if count <= 0:
48
+ count = 100
49
+ elif count > 1000:
50
+ count = 1000
51
+ data = {
52
+ "qqhs": count,
53
+ "dwc": "",
54
+ }
55
+ headers = _base_headers.copy()
56
+ headers["X-Requested-With"] = "XMLHttpRequest"
57
+ logger.debug(f"(tag={tag}), (data={data}), (url={url})")
58
+ resp = session.post(url, headers=headers, data=data)
59
+ if not _check_resp(resp):
60
+ return None
61
+ logger.debug(resp.text)
62
+ return resp
63
+
64
+
65
+ def _get_captcha_code() -> Optional[tuple[float, Any]]:
66
+ """get random number and captcha code."""
67
+ cryptogen = SystemRandom()
68
+ random_num = cryptogen.random()
69
+ resp = get(f"{_urls['yzm']}{random_num}", headers=_base_headers, timeout=60)
70
+ if not _check_resp(resp):
71
+ return None
72
+ code = ocr.classification(resp.content)
73
+ logger.debug(f"random_num={random_num}, code={code}")
74
+ if code:
75
+ try:
76
+ return random_num, int(code)
77
+ except Exception as e:
78
+ logger.error(f"get_captcha_code found exception: {e}, ocr result={code}")
79
+ return _get_captcha_code()
80
+
81
+
82
+ def _get_em_validate_key():
83
+ """获取 em_validatekey"""
84
+ url = "https://jywg.18.cn/Trade/Buy"
85
+ resp = session.get(url, headers=_base_headers)
86
+ if not _check_resp(resp):
87
+ return None
88
+ match_result = re.findall(r'id="em_validatekey" type="hidden" value="(.*?)"', resp.text)
89
+ if match_result:
90
+ _em_validatekey = match_result[0].strip()
91
+ global _em_validate_key
92
+ _em_validate_key = _em_validatekey
93
+ return _em_validatekey
94
+
95
+
96
+ def login(username: str, password: str, duration: int = 30) -> Optional[str]:
97
+ """Login.
98
+ :param username: 用户名
99
+ :param password: 密码(明文)
100
+ :param duration: 在线时长(分钟)
101
+ :return:
102
+ """
103
+ if (ret := _get_captcha_code()) is None:
104
+ return ""
105
+ random_num, code = ret
106
+ logger.error(code)
107
+ headers = _base_headers.copy()
108
+ headers["X-Requested-With"] = "XMLHttpRequest"
109
+ headers["Referer"] = "https://jywg.18.cn/Login?el=1&clear=&returl=%2fTrade%2fBuy"
110
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
111
+ url = _urls["login"]
112
+ data = {
113
+ "userId": username.strip(),
114
+ "password": emt_trade_encrypt(password.strip()),
115
+ "randNumber": random_num,
116
+ "identifyCode": code,
117
+ "duration": duration,
118
+ "authCode": "",
119
+ "type": "Z",
120
+ "secInfo": "",
121
+ }
122
+ resp = session.post(url, headers=headers, data=data)
123
+ if not _check_resp(resp):
124
+ return None
125
+ data = resp.json()
126
+ try:
127
+ resp = response_deserialize(data)
128
+ if resp and resp.status == 0 and resp.message.strip() == "":
129
+ logger.info(f"login success for {resp.data[0]['khmc']}({username})")
130
+ return _get_em_validate_key()
131
+ except KeyError as e:
132
+ logger.error(f"param data found exception:[{e}], [data={data}]")
133
+
134
+
135
+ def query_asset_and_position() -> Optional[Resp]:
136
+ """Get asset and position."""
137
+ resp = _query_something("query_asset_and_pos")
138
+ if resp:
139
+ return response_deserialize(resp.json())
@@ -1,5 +1,5 @@
1
1
  import subprocess
2
2
 
3
3
 
4
- def test_main():
4
+ def test_cli():
5
5
  assert subprocess.check_output(["emt", "-u=foo", "-p=bar"], text=True) == "('foo', 'bar')\n"
@@ -0,0 +1,27 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from .. import emt
5
+ from .. import login
6
+ from .. import query_asset_and_position
7
+ from ..utils import Response
8
+
9
+
10
+ def test_emt():
11
+ assert emt("user", "pass") == ("user", "pass")
12
+
13
+
14
+ def test_emt_empty():
15
+ assert emt("", "") == ("", "")
16
+
17
+
18
+ def test_login():
19
+ validate_key = login(os.getenv("EM_USERNAME", ""), os.getenv("EM_PASSWORD", ""))
20
+ if validate_key:
21
+ assert len(validate_key) == len("b91d8012-b70b-4265-bb5d-f79442531017")
22
+
23
+
24
+ def test_query_asset_position():
25
+ resp: Optional[Response] = query_asset_and_position()
26
+ if resp:
27
+ assert resp.error_code == 0
@@ -0,0 +1,51 @@
1
+ import base64
2
+ import logging
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+ from typing import Optional
6
+
7
+ from cryptography.hazmat.primitives import serialization
8
+ from cryptography.hazmat.primitives.asymmetric import padding
9
+ from cryptography.hazmat.primitives.asymmetric import rsa
10
+
11
+ rsa_public_key = """
12
+ -----BEGIN PUBLIC KEY-----
13
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHdsyxT66pDG4p73yope7jxA92
14
+ c0AT4qIJ/xtbBcHkFPK77upnsfDTJiVEuQDH+MiMeb+XhCLNKZGp0yaUU6GlxZdp
15
+ +nLW8b7Kmijr3iepaDhcbVTsYBWchaWUXauj9Lrhz58/6AE/NF0aMolxIGpsi+ST
16
+ 2hSHPu3GSXMdhPCkWQIDAQAB
17
+ -----END PUBLIC KEY-----
18
+ """
19
+
20
+
21
+ @dataclass
22
+ class Response:
23
+ message: str
24
+ status: int
25
+ error_code: str
26
+ data: Any
27
+
28
+ def is_ok(self) -> bool:
29
+ return not self.message and self.status == 0
30
+
31
+
32
+ def get_logger(name):
33
+ """Logger."""
34
+ formater = "%(asctime)s %(name)-20s %(funcName)s %(lineno)d: %(levelname)-8s: %(message)s"
35
+ logging.basicConfig(format=formater, force=True, level=logging.INFO)
36
+ logger = logging.getLogger(name)
37
+ return logger
38
+
39
+
40
+ def response_deserialize(data: dict) -> Optional[Response]:
41
+ if data is None:
42
+ return None
43
+ return Response(
44
+ data["Message"] if "Message" in data else "", data["Status"], data["Errcode"] if "Errcode" in data else "", data["Data"]
45
+ )
46
+
47
+
48
+ def emt_trade_encrypt(content: str) -> str:
49
+ _pub_key: rsa.RSAPublicKey = serialization.load_pem_public_key(rsa_public_key.encode("utf-8")) # type:ignore
50
+ encrypt_text = _pub_key.encrypt(content.encode(), padding.PKCS1v15())
51
+ return base64.b64encode(encrypt_text).decode("utf-8")
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emtl
3
- Version: 0.1.2.dev2
4
- Summary: 东方财富交易系统
3
+ Version: 0.1.4.dev1
4
+ Summary: 东方财富自动交易系统
5
5
  Home-page: https://github.com/riiy/emtl
6
6
  Author: Eastmoney Trade Library
7
7
  Author-email: riiyzhou@gmail.com
@@ -9,7 +9,7 @@ License: MIT
9
9
  Project-URL: Documentation, https://emtl.readthedocs.io/
10
10
  Project-URL: Changelog, https://emtl.readthedocs.io/en/latest/changelog.html
11
11
  Project-URL: Issue Tracker, https://github.com/riiy/emtl/issues
12
- Keywords: trade,东方财富,股票,交易
12
+ Keywords: autotrade,trade,东方财富,股票,交易,交易机器人
13
13
  Platform: UNKNOWN
14
14
  Classifier: Development Status :: 2 - Pre-Alpha
15
15
  Classifier: Intended Audience :: Developers
@@ -21,10 +21,9 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Programming Language :: Python :: 3.10
23
23
  Classifier: Programming Language :: Python :: 3.11
24
- Classifier: Programming Language :: Python :: 3.12
25
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
25
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
26
+ Requires-Python: >=3.10
28
27
  License-File: LICENSE
29
28
  License-File: AUTHORS.rst
30
29
 
@@ -34,7 +33,7 @@ Overview
34
33
 
35
34
 
36
35
 
37
- 东方财富交易系统
36
+ 东方财富自动交易系统
38
37
 
39
38
  * Free software: MIT license
40
39
 
@@ -85,6 +84,11 @@ Note, to combine the coverage data from all the tox environments run:
85
84
  Changelog
86
85
  =========
87
86
 
87
+ 0.1.2 (2024-04-18)
88
+ ------------------
89
+
90
+ * add login && add asset position
91
+
88
92
  0.1.1 (2024-04-16)
89
93
  ------------------
90
94
 
@@ -14,6 +14,7 @@ pyproject.toml
14
14
  pytest.ini
15
15
  setup.py
16
16
  tox.ini
17
+ .github/workflows/document.yml
17
18
  .github/workflows/github-actions.yml
18
19
  ci/bootstrap.py
19
20
  ci/requirements.txt
@@ -34,12 +35,15 @@ src/emtl/__init__.py
34
35
  src/emtl/__main__.py
35
36
  src/emtl/_version.py
36
37
  src/emtl/cli.py
38
+ src/emtl/const.py
37
39
  src/emtl/core.py
40
+ src/emtl/utils.py
38
41
  src/emtl.egg-info/PKG-INFO
39
42
  src/emtl.egg-info/SOURCES.txt
40
43
  src/emtl.egg-info/dependency_links.txt
41
44
  src/emtl.egg-info/entry_points.txt
42
45
  src/emtl.egg-info/not-zip-safe
46
+ src/emtl.egg-info/requires.txt
43
47
  src/emtl.egg-info/top_level.txt
44
48
  src/emtl/tests/__init__.py
45
49
  src/emtl/tests/test_cli.py
@@ -0,0 +1,4 @@
1
+ cryptography==42.0.5
2
+ ddddocr==1.4.11
3
+ requests==2.31.0
4
+ rsa==4.9
@@ -14,7 +14,7 @@ envlist =
14
14
  clean,
15
15
  check,
16
16
  docs,
17
- {py310,py311,py312}-{cover,nocov},
17
+ {py310,py311}-{cover,nocov},
18
18
  report
19
19
  ignore_basepython_conflict = true
20
20
 
@@ -22,7 +22,6 @@ ignore_basepython_conflict = true
22
22
  basepython =
23
23
  py310: {env:TOXPYTHON:python3.10}
24
24
  py311: {env:TOXPYTHON:python3.11}
25
- py312: {env:TOXPYTHON:python3.12}
26
25
  {bootstrap,clean,check,report,docs,codecov}: {env:TOXPYTHON:python3}
27
26
  setenv =
28
27
  PYTHONPATH={toxinidir}/tests
@@ -1,9 +0,0 @@
1
- __version__ = "0.1.1"
2
-
3
- from .core import emt
4
- from .core import login
5
-
6
- __all__ = [
7
- "emt",
8
- "login",
9
- ]
@@ -1,8 +0,0 @@
1
- def emt(user, password):
2
- return user, password
3
-
4
-
5
- def login(user: str, password: str) -> str:
6
- """Login."""
7
- valide_key = user + password
8
- return valide_key
@@ -1,14 +0,0 @@
1
- from .. import emt
2
- from .. import login
3
-
4
-
5
- def test_emt():
6
- assert emt("user", "pass") == ("user", "pass")
7
-
8
-
9
- def test_emt_empty():
10
- assert emt("", "") == ("", "")
11
-
12
-
13
- def test_login():
14
- assert login("user", "pass") == "userpass"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes