openfund-core 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

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.
openfund/core/__init__.py CHANGED
@@ -0,0 +1,14 @@
1
+ # import time
2
+ # from apscheduler.schedulers.blocking import BlockingScheduler
3
+
4
+
5
+ # def taskDetail(taskName: str):
6
+ # currTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
7
+ # print(f"{taskName}-->", "currTime:", currTime)
8
+
9
+
10
+ # if __name__ == "__main__":
11
+ # apSchedule = BlockingScheduler()
12
+ # apSchedule.add_job(func=taskDetail, trigger="interval", seconds=5, args=["task-A"])
13
+
14
+ # apSchedule.start()
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from openfund.core.base_tool import Tool as BaseTool
6
+ from openfund.core.pyopenfund import Openfund
7
+
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class BinanceUMFuturesTool(BaseTool):
13
+ def __init__(self, openfund: Openfund | None = None) -> None:
14
+ super().__init__(openfund, "binance")
15
+
16
+ def time(self):
17
+ return self.umclient.time()
18
+
19
+ def ping(self):
20
+ return self.umclient.ping()
21
+
22
+ def klines(self, symbol: str, interval: str = "1m", **kwargs):
23
+ return self.umclient.klines(symbol=symbol, interval=interval, **kwargs)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from poetry.utils.authenticator import Authenticator
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ from openfund.core.base_tool import Tool as BaseTool
9
+ from openfund.core.pyopenfund import Openfund
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class BinanceTools(BaseTool):
16
+ def __init__(self, openfund: Openfund | None = None) -> None:
17
+ super().__init__(openfund, "binance")
18
+
19
+ def get_time(self):
20
+ return self.client.time()
21
+
22
+ def get_account(self):
23
+ return self.client.account()
24
+
25
+ def get_klines(self, symbol: str, interval: str, **kwargs):
26
+ return self.client.klines(symbol=symbol, interval=interval, **kwargs)
@@ -10,22 +10,54 @@ ORDER_STATUS_PENDING_CANCEL = "PENDING_CANCEL"
10
10
  ORDER_STATUS_REJECTED = "REJECTED"
11
11
  ORDER_STATUS_EXPIRED = "EXPIRED"
12
12
 
13
- KLINE_INTERVAL_1SECOND = "1s"
14
- KLINE_INTERVAL_1MINUTE = "1m"
15
- KLINE_INTERVAL_3MINUTE = "3m"
16
- KLINE_INTERVAL_5MINUTE = "5m"
17
- KLINE_INTERVAL_15MINUTE = "15m"
18
- KLINE_INTERVAL_30MINUTE = "30m"
19
- KLINE_INTERVAL_1HOUR = "1h"
20
- KLINE_INTERVAL_2HOUR = "2h"
21
- KLINE_INTERVAL_4HOUR = "4h"
22
- KLINE_INTERVAL_6HOUR = "6h"
23
- KLINE_INTERVAL_8HOUR = "8h"
24
- KLINE_INTERVAL_12HOUR = "12h"
25
- KLINE_INTERVAL_1DAY = "1d"
26
- KLINE_INTERVAL_3DAY = "3d"
27
- KLINE_INTERVAL_1WEEK = "1w"
28
- KLINE_INTERVAL_1MONTH = "1M"
13
+
14
+ class KlineInterval(Enum):
15
+ KLINE_INTERVAL_1SECOND = ("1s", 1, "s")
16
+ KLINE_INTERVAL_1MINUTE = ("1m", 1, "m")
17
+ KLINE_INTERVAL_3MINUTE = ("3m", 3, "m")
18
+ KLINE_INTERVAL_5MINUTE = ("5m", 5, "m")
19
+ KLINE_INTERVAL_15MINUTE = ("15m", 15, "m")
20
+ KLINE_INTERVAL_30MINUTE = ("30m", 30, "m")
21
+ KLINE_INTERVAL_1HOUR = ("1h", 1, "h")
22
+ KLINE_INTERVAL_2HOUR = ("2h", 2, "h")
23
+ KLINE_INTERVAL_4HOUR = ("4h", 4, "h")
24
+ KLINE_INTERVAL_6HOUR = ("6h", 6, "h")
25
+ KLINE_INTERVAL_8HOUR = ("8h", 8, "h")
26
+ KLINE_INTERVAL_12HOUR = ("12h", 12, "h")
27
+ KLINE_INTERVAL_1DAY = ("1d", 1, "d")
28
+ KLINE_INTERVAL_3DAY = ("3d", 3, "d")
29
+ KLINE_INTERVAL_1WEEK = ("1w", 1, "d")
30
+ KLINE_INTERVAL_1MONTH = ("1M", 1, "M")
31
+
32
+ def __init__(self, value: str, unit: int, unitType: str):
33
+ self._value_ = value
34
+ self.unit = unit
35
+ self.unitType = unitType
36
+
37
+ @classmethod
38
+ def getByUnit(cls, unit: int, unitType: str) -> str:
39
+ for interval in KlineInterval:
40
+ if interval.unit == unit and interval.unitType == unitType:
41
+ return interval.value
42
+ return None
43
+
44
+
45
+ # KLINE_INTERVAL_1SECOND = "1s"
46
+ # KLINE_INTERVAL_1MINUTE = "1m"
47
+ # KLINE_INTERVAL_3MINUTE = "3m"
48
+ # KLINE_INTERVAL_5MINUTE = "5m"
49
+ # KLINE_INTERVAL_15MINUTE = "15m"
50
+ # KLINE_INTERVAL_30MINUTE = "30m"
51
+ # KLINE_INTERVAL_1HOUR = "1h"
52
+ # KLINE_INTERVAL_2HOUR = "2h"
53
+ # KLINE_INTERVAL_4HOUR = "4h"
54
+ # KLINE_INTERVAL_6HOUR = "6h"
55
+ # KLINE_INTERVAL_8HOUR = "8h"
56
+ # KLINE_INTERVAL_12HOUR = "12h"
57
+ # KLINE_INTERVAL_1DAY = "1d"
58
+ # KLINE_INTERVAL_3DAY = "3d"
59
+ # KLINE_INTERVAL_1WEEK = "1w"
60
+ # KLINE_INTERVAL_1MONTH = "1M"
29
61
 
30
62
  SIDE_BUY = "BUY"
31
63
  SIDE_SELL = "SELL"
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import csv
5
+
6
+ from abc import abstractmethod
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING
9
+
10
+ from apscheduler.job import Job
11
+
12
+ from openfund.core.pyopenfund import Openfund
13
+ from openfund.core.factory import Factory
14
+
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class Collector:
20
+ def __init__(self, openfund: Openfund | None = None) -> None:
21
+ self._openfund: Openfund = openfund
22
+ if self._openfund is None:
23
+ self._openfund = Factory.create_openfund()
24
+ self._job: Job = None
25
+
26
+ @property
27
+ def openfund(self) -> Openfund:
28
+ return self._openfund
29
+
30
+ @abstractmethod
31
+ def collect(self) -> None:
32
+ raise NotImplementedError()
33
+
34
+ @abstractmethod
35
+ def start(self) -> int:
36
+ raise NotImplementedError()
37
+ # from apscheduler.schedulers.background import BlockingScheduler
38
+
39
+ # apSchedule = BlockingScheduler()
40
+ # self.openfund.scheduler.add_job(
41
+ # func=self.collect, trigger="interval", minutes=5,seconds=5 id="um_futures_collector"
42
+ # )
43
+
44
+ def stop(self) -> int:
45
+ if self._job is not None:
46
+ self._job.remove()
47
+ logger.debug(f"{self._job.name} is stop .")
48
+ return 0
49
+
50
+ def pause(self) -> int:
51
+ if self._job is not None:
52
+ self._job.pause()
53
+ logger.debug(f"{self._job.name} is pause .")
54
+ return 0
55
+
56
+ def resume(self) -> int:
57
+ if self._job is not None:
58
+ self._job.resume()
59
+ logger.debug(f"{self._job.name} is resume .")
60
+ return 0
61
+
62
+ def _write_to_csv(self, file: Path, listData: list) -> None:
63
+
64
+ # 如果路径不存在,创建路径
65
+ file.parent.mkdir(parents=True, exist_ok=True)
66
+
67
+ with open(file, "a", newline="") as file:
68
+ writer = csv.writer(file)
69
+ # 时间戳倒序,插入文件尾部
70
+ writer.writerows(sorted(listData, key=lambda x: x[0], reverse=True))
71
+
72
+ logger.debug("2、{}条写入{}文件...".format(len(listData), file))
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from pathlib import Path
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ from poetry.utils.authenticator import Authenticator
10
+ from binance.spot import Spot as Client
11
+ from binance.um_futures import UMFutures as UMClient
12
+
13
+ from openfund.core.pyopenfund import Openfund
14
+ from openfund.core.factory import Factory
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class Tool:
21
+ def __init__(
22
+ self, openfund: Openfund | None = None, toolname: str = "binance"
23
+ ) -> None:
24
+ self._openfund: Openfund = openfund
25
+ if self._openfund is None:
26
+ self._openfund = Factory.create_openfund()
27
+
28
+ self._toolname = toolname
29
+ self._password_manager = Authenticator(
30
+ self._openfund._poetry.config
31
+ )._password_manager
32
+
33
+ self._client = None
34
+ self._umclient = None
35
+
36
+ @property
37
+ def api_key(self) -> str:
38
+ return self._password_manager.get_http_auth(self._toolname).get("username")
39
+
40
+ @property
41
+ def apk_secret(self) -> str:
42
+ return self._password_manager.get_http_auth(self._toolname).get("password")
43
+
44
+ @property
45
+ def openfund(self) -> Openfund:
46
+ return self._openfund
47
+
48
+ @property
49
+ def client(self) -> Client:
50
+ if self._client is None:
51
+ self._client = Client(self.api_key, self.apk_secret)
52
+ return self._client
53
+
54
+ @property
55
+ def umclient(self) -> UMClient:
56
+ if self._umclient is None:
57
+ self._umclient = UMClient(self.api_key, self.apk_secret)
58
+ return self._umclient
openfund/core/factory.py CHANGED
@@ -4,19 +4,25 @@ import logging
4
4
 
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING
7
+
7
8
  from poetry.factory import Factory as BaseFactory
8
9
  from openfund.core.pyopenfund import Openfund
9
10
 
11
+
10
12
  if TYPE_CHECKING:
11
13
  from poetry.poetry import Poetry
12
14
 
13
15
 
14
16
  logger = logging.getLogger(__name__)
17
+ _APP_NAME = "pyopenfund"
15
18
 
16
19
 
17
20
  class Factory(BaseFactory):
21
+ _openfund: Openfund = None
22
+
18
23
  def __init__(self) -> None:
19
24
  super().__init__()
25
+ self._init_log()
20
26
 
21
27
  def create_poetry(
22
28
  self,
@@ -26,12 +32,66 @@ class Factory(BaseFactory):
26
32
  poetry = super().create_poetry(cwd=cwd, with_groups=with_groups)
27
33
  return poetry
28
34
 
35
+ @classmethod
29
36
  def create_openfund(
30
- self,
37
+ cls,
31
38
  cwd: Path | None = None,
32
39
  with_groups: bool = True,
33
40
  ) -> Openfund:
41
+ if cls._openfund is not None:
42
+ return cls._openfund
43
+
44
+ if cwd is None:
45
+ cwd = Path.cwd()
46
+
47
+ poetry = Factory().create_poetry(cwd=cwd, with_groups=with_groups)
48
+ cls._openfund = Openfund(poetry)
49
+ cls._openfund.scheduler.start()
50
+ return cls._openfund
51
+
52
+ def _init_log(self):
53
+ from openfund.core.pyopenfund import user_log_path
54
+
55
+ log_file = user_log_path(
56
+ _APP_NAME, appauthor=False, ensure_exists=True
57
+ ).resolve()
58
+ log_file = (
59
+ user_log_path(_APP_NAME, appauthor=False, ensure_exists=True)
60
+ .joinpath("openfund-core.log")
61
+ .resolve()
62
+ )
63
+ fileHandler = FileHandler(log_file)
64
+ fileHandler.setFormatter(FileFormatter())
65
+ console_handler = logging.StreamHandler()
66
+ console_handler.setFormatter(FileFormatter())
67
+ logging.basicConfig(
68
+ level=logging.DEBUG,
69
+ handlers=[console_handler, fileHandler],
70
+ )
71
+
72
+
73
+ from logging.handlers import TimedRotatingFileHandler
74
+
75
+
76
+ class FileHandler(TimedRotatingFileHandler):
77
+ def __init__(
78
+ self,
79
+ filename,
80
+ when="midnight",
81
+ interval=1,
82
+ backupCount=7,
83
+ encoding=None,
84
+ delay=False,
85
+ utc=False,
86
+ ) -> None:
87
+ super().__init__(filename, when, interval, backupCount, encoding, delay, utc)
88
+
89
+
90
+ class FileFormatter(logging.Formatter):
91
+
92
+ _format = "%(asctime)s - %(process)d | %(threadName)s | %(module)s.%(funcName)s:%(lineno)d - %(levelname)s -%(message)s"
34
93
 
35
- poetry = self.create_poetry(cwd=cwd, with_groups=with_groups)
94
+ _datefmt = "%Y-%m-%d-%H:%M:%S" # 时间
36
95
 
37
- return Openfund(poetry)
96
+ def __init__(self, fmt=_format, datefmt=_datefmt, style="%") -> None:
97
+ super().__init__(fmt, datefmt, style)
@@ -1,20 +1,31 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
4
+ import os
5
+ import sys
6
+
3
7
  from typing import TYPE_CHECKING
4
- from typing import Any
5
8
 
6
- from poetry.core.pyproject.toml import PyProjectTOML
9
+ from pathlib import Path
7
10
 
11
+ from platformdirs import user_cache_path
12
+ from platformdirs import user_config_path
13
+ from platformdirs import user_data_path
14
+ from platformdirs import user_log_path
15
+ from apscheduler.schedulers.background import BackgroundScheduler
8
16
 
9
17
  if TYPE_CHECKING:
10
- from poetry.core.packages.project_package import ProjectPackage
11
18
  from poetry.poetry import Poetry
12
19
  from openfund.core.factory import Factory
13
20
 
21
+ logger = logging.getLogger(__name__)
22
+ _APP_NAME = "pyopenfund"
23
+
14
24
 
15
25
  class Openfund:
16
26
  def __init__(self, poetry: Openfund) -> None:
17
27
  self._poetry: Poetry = poetry
28
+ self._schedule: BackgroundScheduler = None
18
29
 
19
30
  @property
20
31
  def poetry(self) -> Poetry:
@@ -30,3 +41,45 @@ class Openfund:
30
41
  )
31
42
 
32
43
  return self._poetry
44
+
45
+ @property
46
+ def dataDir(self) -> Path:
47
+ # openfund_home = os.getenv("OPENFUND_HOME")
48
+ # if openfund_home:
49
+ # return Path(openfund_home).expanduser()
50
+ return Path(
51
+ os.getenv("OPENFUND_DATA_DIR")
52
+ or user_data_path(_APP_NAME, appauthor=False, roaming=True)
53
+ ).joinpath("data")
54
+
55
+ @property
56
+ def cacheDir(self) -> Path:
57
+ return Path(
58
+ os.getenv("OPENFUND_CACHE_DIR")
59
+ or user_cache_path(_APP_NAME, appauthor=False)
60
+ )
61
+
62
+ @property
63
+ def configDir(self) -> Path:
64
+
65
+ return Path(
66
+ os.getenv("OPENFUND_CONFIG_DIR")
67
+ or user_config_path(_APP_NAME, appauthor=False, roaming=True)
68
+ ).joinpath("config")
69
+
70
+ @property
71
+ def logDir(self) -> Path:
72
+
73
+ return Path(
74
+ os.getenv("OPENFUND_LOG_DIR")
75
+ or user_log_path(_APP_NAME, appauthor=False, roaming=True)
76
+ )
77
+
78
+ @property
79
+ def scheduler(self) -> BackgroundScheduler:
80
+ if self._schedule is None:
81
+ self._schedule = BackgroundScheduler(
82
+ timezone="MST",
83
+ )
84
+
85
+ return self._schedule
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ import logging
5
+ import time
6
+ from pathlib import Path
7
+
8
+ from openfund.core.api_tools.enums import KlineInterval
9
+ from openfund.core.base_tool import Tool as BaseTool
10
+ from openfund.core.api_tools.binance_futures_tools import BinanceUMFuturesTool
11
+ from openfund.core.base_collector import Collector as BaseCollector
12
+
13
+ from openfund.core.utils.time_tools import TimeTools
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class KLinesCollector(BaseCollector):
19
+ def __init__(
20
+ self,
21
+ hisSwitch: int = 0,
22
+ hisDateTime: int = 0,
23
+ pool: list = None,
24
+ interval: int = 5,
25
+ client: BaseTool = None,
26
+ ) -> None:
27
+ super().__init__()
28
+ self._pool = pool
29
+ if self._pool is None:
30
+ self._pool = ["BTCUSDT", "ETHUSDT"]
31
+ logger.debug("+++++++++++++++ KLinesCollector init +++++++++++++ ")
32
+
33
+ self._interval = interval
34
+ self._hisSwitch = hisSwitch
35
+ self._hisDateTime = hisDateTime
36
+ self._dataDir = self.openfund.dataDir
37
+ self._client = client
38
+ if self._client is None:
39
+ self._client = BinanceUMFuturesTool().umclient
40
+ # self._job = None
41
+
42
+ def collect(self) -> None:
43
+
44
+ for symbol in self._pool:
45
+ logger.debug("{} symbol 开始 ++++++++++++++++++++++++++++ ".format(symbol))
46
+ latestRecords = 1 # 采集最近一次的记录数量
47
+ records = 0 # 累计记录数
48
+ queryCount = 0 # 执行次数
49
+ nextEndTime = 0
50
+ params = {"limit": 1000}
51
+ while latestRecords != 0: # 循环读取,直到记录为空
52
+ queryCount += 1
53
+ if nextEndTime != 0:
54
+ params = {"limit": 1000, "endTime": nextEndTime}
55
+
56
+ logger.debug("1、{}第{}次开始执行...".format(symbol, queryCount))
57
+ listData = []
58
+ try:
59
+ listData = self._client.klines(
60
+ symbol,
61
+ KlineInterval.getByUnit(self._interval, "m"),
62
+ **params,
63
+ )
64
+ except Exception as e:
65
+ # print("Error:", e)
66
+ logger.error(e)
67
+ time.sleep(10)
68
+ continue
69
+
70
+ latestRecords = len(listData)
71
+ data_file = Path(
72
+ self._dataDir.joinpath("klines")
73
+ .joinpath(symbol)
74
+ .joinpath(
75
+ "klines_{}.csv".format(
76
+ KlineInterval.getByUnit(self._interval, "m")
77
+ )
78
+ )
79
+ )
80
+ self._write_to_csv(data_file, listData)
81
+
82
+ if latestRecords > 0:
83
+ nextEndTime = (
84
+ # -1 不和close时间戳相同,避免重新拉取重复数据
85
+ listData[0][0]
86
+ - 1
87
+ )
88
+
89
+ logger.debug(
90
+ "3、下次结束时间 %s %s"
91
+ % (TimeTools.format_timestamp(nextEndTime), nextEndTime)
92
+ )
93
+
94
+ if self._hisSwitch == 0 or nextEndTime <= self._hisDateTime:
95
+ break
96
+ else:
97
+ logger.debug("4、结束...")
98
+
99
+ # time.sleep(0.1)
100
+ records = latestRecords + records
101
+ logger.info("5、{} 抓取数据 {} 条记录...".format(symbol, records))
102
+ logger.debug("{} symbol --------------------------------- ".format(symbol))
103
+
104
+ # def taskDetail(taskName: str):
105
+ # currTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
106
+ # logger.debug(f"{taskName}-->", "currTime:", currTime)
107
+
108
+ def start(self) -> int:
109
+
110
+ # self.collect()
111
+ self._job = self.openfund.scheduler.add_job(
112
+ func=self.collect,
113
+ trigger="interval",
114
+ # minutes=self._interval,
115
+ seconds=self._interval,
116
+ id="um_futures_collector",
117
+ )
118
+
119
+ logger.debug("调度任务已启动,每%s分钟执行一次。", self._interval)
120
+
121
+ return 0
122
+
123
+
124
+ # if __name__ == "__main__":
125
+ # from openfund.core.services.um_futures_collector import KLinesCollector
126
+
127
+ # collector = KLinesCollector()
128
+
129
+ # collector.start()
130
+ # logger.debug(f"main collector.start() ===== ")
131
+ # i = 0
132
+ # while i < 10:
133
+
134
+ # logger.debug("main i=%s ===== ", i)
135
+ # if i == 2:
136
+ # collector.pause()
137
+ # if i == 4:
138
+ # collector.resume()
139
+ # if i == 8:
140
+ # collector.stop()
141
+ # time.sleep(5)
142
+ # i += 1
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python
2
+
3
+ import time
4
+ from datetime import date, datetime, timedelta
5
+
6
+
7
+ class TimeTools:
8
+
9
+ @staticmethod
10
+ def print_timestamp(timestamp):
11
+ print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp / 1000)))
12
+
13
+ @staticmethod
14
+ def format_timestamp(timestamp):
15
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp / 1000))
16
+
17
+ @staticmethod
18
+ def format_date(timestamp):
19
+ return time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000))
20
+
21
+ @staticmethod
22
+ def format_date_to(to_days):
23
+ now = date.today()
24
+ to = now + timedelta(days=to_days)
25
+ return to
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openfund-core
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary:
5
5
  Author: yang99love
6
6
  Author-email: yang99love@hotmail.com
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: apscheduler (>=3.10.4,<4.0.0)
15
16
  Requires-Dist: binance-connector (>=3.9.0,<4.0.0)
16
17
  Requires-Dist: binance-futures-connector (>=4.1.0,<5.0.0)
17
18
  Requires-Dist: httpretty (>=1.1.4,<2.0.0)
@@ -0,0 +1,30 @@
1
+ openfund/core/__init__.py,sha256=EHcPx-8t19Rp43en5LclC-v-VuRK3CFRrh5rCA8h0w0,422
2
+ openfund/core/api_tools/__init__.py,sha256=paV2IWM0QkD0ddq0UWdbsDeQTW3OBAPjMf51V5yMRgI,458
3
+ openfund/core/api_tools/binance_futures_tools.py,sha256=T2WLpVmtnK9FwWlaoXrzmejJoYheQjmx51gc7_tolO8,604
4
+ openfund/core/api_tools/binance_tools.py,sha256=B5Dwblu04VSY8ppVO1a_a7NumqLEsiZ6FKIh03WD1AI,688
5
+ openfund/core/api_tools/enums.py,sha256=HXtX8Qz62q7SHsn2URxKWIoeZvVimsLrhMzN_-uBDgQ,10050
6
+ openfund/core/base_collector.py,sha256=x-ELr-lR34Q2zJO2cD2h5tZFaTl93z46jwOLvBKkckc,2049
7
+ openfund/core/base_tool.py,sha256=wq5b4-Rz_SITPp09UfOwBXXaMB82nJgdamHkGndYKwM,1553
8
+ openfund/core/factory.py,sha256=Skjy6Br8k45H7_D2S5wKW0uqzY_S6Fmkddr1ErgnYiI,2557
9
+ openfund/core/openfund_old/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ openfund/core/openfund_old/continuous_klines.py,sha256=KaGW4bo88RVEg0e_Cvpg8nvQD8qkGt4k_yLvtvzFCeU,4954
11
+ openfund/core/openfund_old/depth.py,sha256=L6yejSRpd37fmAk7Qowaxw1ELPwDGxKE0JCtlkJ00YE,2682
12
+ openfund/core/openfund_old/historical_trades.py,sha256=JhvhyDp4CVG1gRhlKBaa01DWGWRHnMO3AV_PORTkkIw,4243
13
+ openfund/core/openfund_old/index_info.py,sha256=idJCW5vvVZpMjmlXpu24emRC8MH2dcEUZq2YnaDB1q4,1766
14
+ openfund/core/openfund_old/index_price_kline.py,sha256=rAqG3HGZT4SzSPAN_Hk3GLSt-oh_-Fc4h4n9W4iQKKE,3952
15
+ openfund/core/openfund_old/klines.py,sha256=O_NtJD53HTJL3CTXkV5TrhAe2WBbcVhKA-3sCLaCJY8,3136
16
+ openfund/core/openfund_old/klines_qrr.py,sha256=OK2SQuUpAb6PCOP8H-60kQE23nM9dB91pDlduH_AowE,3230
17
+ openfund/core/openfund_old/mark_price.py,sha256=wjLx4Vw_S6Ee1-yIwsOKkiX1R6iaebQAuteQibJAhR8,3535
18
+ openfund/core/openfund_old/mark_price_klines.py,sha256=FfQW3oh-XM2eR2dV5HAgJYu66ZPdWMVPVgbqe0-kc-c,4017
19
+ openfund/core/openfund_old/ticker_24hr_price_change.py,sha256=SDv68uPN1OGTN_Dukl3AK7awE1nZuxxho-1PckhZptk,2607
20
+ openfund/core/pyopenfund.py,sha256=SfcDCFs_YgrJfg00AbNtFZuGkOncvSEUXj8ghfCz4Ww,2144
21
+ openfund/core/services/um_futures_collector.py,sha256=StyI1Ml-GQmUJHYPcx6klJxs9KUPgFg6tAkJiuhd-rI,4717
22
+ openfund/core/sycu_exam/__init__.py,sha256=uLZK-YxXqXfAtTTekgfnfqRIdVedM9SBHnmGD3j5sYM,20
23
+ openfund/core/sycu_exam/exam.py,sha256=x7E3r6DgTMnlUz9q6fVysCPSabp-pQUqhGKw0xRpFrU,395
24
+ openfund/core/sycu_exam/random_grade_cplus.py,sha256=8FIUnF4ys-at2tAtg0yYXru_mmxhs20ywG5DY9TiHWo,9487
25
+ openfund/core/sycu_exam/random_grade_web.py,sha256=A2x9-jFx6L_AKAmWGrIrKcc9httJHTA5i0Uc8nGiPTE,8572
26
+ openfund/core/utils/time_tools.py,sha256=_orqX684epJyh5_xV94JbpQPprIpiuiS__scLeiXr6I,646
27
+ openfund_core-0.0.3.dist-info/LICENSE,sha256=xazLvYVG6Uw0rtJK_miaYXYn0Y7tWmxIJ35I21fCOFE,11356
28
+ openfund_core-0.0.3.dist-info/METADATA,sha256=LPqgMyAeGmboAKS0OCdLYNwVPx4LPzQB8w2LMApk-AE,3205
29
+ openfund_core-0.0.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
30
+ openfund_core-0.0.3.dist-info/RECORD,,
@@ -1,39 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import logging
4
- from poetry.utils.authenticator import Authenticator
5
-
6
- from typing import TYPE_CHECKING
7
- from binance.spot import Spot as Client
8
- from openfund.core.pyopenfund import Openfund
9
-
10
-
11
- tools = "binance"
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- class BinanceTools:
16
- def __init__(self, openfund: Openfund) -> None:
17
- logger.debug("######## BinanceTools init ########")
18
- self._openfund: Openfund = openfund
19
- self._password_manager = Authenticator(
20
- self._openfund._poetry.config
21
- )._password_manager
22
-
23
- @property
24
- def api_key(self) -> str:
25
- return self._password_manager.get_http_auth(tools).get("username")
26
-
27
- @property
28
- def apk_secret(self) -> str:
29
- return self._password_manager.get_http_auth(tools).get("password")
30
-
31
- def get_time():
32
- client = Client()
33
- return client.time()
34
-
35
- def get_account(self):
36
- logger.debug("######## BinanceTools.get_account() ########")
37
- logger.debug("######## BinanceTools api_key=%s ########", self.api_key)
38
- client = Client(self.api_key, self.apk_secret)
39
- return client.account()
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env python
2
- import csv
3
- import logging
4
- import time
5
-
6
- from datetime import datetime
7
-
8
- from binance.um_futures import UMFutures
9
- from binance.um_futures import UMFutures
10
- from binance.error import ClientError
11
-
12
- from libs.time_tools import format_timestamp
13
- from libs.file_tools import create_path
14
- from libs.prepare_env import get_api_key, get_path
15
- from libs import enums
16
-
17
- # 数据路径、日志路径
18
- data_path, log_path = get_path()
19
- # client
20
- um_futures_client = UMFutures()
21
- # 合同类型
22
- contractTypes = [type.value for type in enums.ContractType]
23
- # 币种类型
24
- # POOL = enums.CUR_SYMBOL_POOL
25
- POOL = [
26
- "BTCUSDT_240329",
27
- "BLURUSDT",
28
- "DYDXUSDT",
29
- "ETHBTC",
30
- "ETHBUSD",
31
- "ETHUSDT",
32
- "ETHUSDT_231229",
33
- "ETHUSDT_240329",
34
- "GMTBUSD",
35
- "GMTUSDT",
36
- "LTCBUSD",
37
- "LTCUSDT",
38
- "MATICBUSD",
39
- "MATICUSDT",
40
- "SOLBUSD",
41
- "SOLUSDT",
42
- ]
43
- interval = enums.KLINE_INTERVAL_5MINUTE
44
- limit = 1500
45
- errorLimit = 100
46
- sleepSec = 10
47
-
48
- # 历史截止数据开关
49
- hisSwitch = 0
50
- # hisSwitch 打开的情况下,抓取数据截止时间
51
- hisDateTime = 1653974399999
52
-
53
- for symbol in POOL:
54
- fileName = "{}/continuous_klines_{}_{}.log".format(
55
- log_path, symbol, format_timestamp(datetime.now().timestamp() * 1000)
56
- )
57
- # print(fileName)
58
- logging.basicConfig(
59
- format="%(asctime)s %(name)s:%(levelname)s:%(message)s",
60
- datefmt="%Y-%m-%d %H:%M:%S",
61
- level=logging.INFO,
62
- filename=fileName,
63
- )
64
- logging.info("{} symbol 开始 ++++++++++++++++++++++++++++ ".format(symbol))
65
- total = 0
66
- for contractType in contractTypes:
67
- latestRecords = 1
68
- records = 0 # 累计记录数
69
- queryTimes = 0 # 执行次数
70
- nextEndTime = 0
71
- errorCount = 0
72
- params = {"limit": limit}
73
- while latestRecords != 0: # 循环读取,直到记录为空
74
- queryTimes = queryTimes + 1
75
- if nextEndTime != 0:
76
- params = {"limit": limit, "endTime": nextEndTime}
77
-
78
- logging.info(
79
- "1、{}-{} 第{}次开始执行...".format(symbol, contractType, queryTimes)
80
- )
81
- listData = []
82
- try:
83
- listData = um_futures_client.continuous_klines(
84
- symbol, contractType, interval, **params
85
- )
86
- except KeyError as err:
87
- print("KeyError:", err)
88
- logging.error(err)
89
- break
90
- except ClientError as error:
91
- logging.error(
92
- "Found error. status: {}, error code: {}, error message: {}".format(
93
- error.status_code, error.error_code, error.error_message
94
- )
95
- )
96
- if error.error_code == -4144 and error.status_code == 400:
97
- break
98
- except Exception as e:
99
- print("Error:", e)
100
- logging.error(e)
101
- errorCount = errorCount + 1
102
- if errorCount > errorLimit:
103
- break
104
- else:
105
- time.sleep(sleepSec)
106
- continue
107
-
108
- latestRecords = len(listData)
109
- records = latestRecords + records
110
- logging.info("2、{}条写入文件...".format(latestRecords))
111
-
112
- path = "{}/continuous_klines/{}/{}/".format(data_path, symbol, contractType)
113
- create_path(path) # 不存在路径进行呢创建
114
-
115
- with open(
116
- "{}continuous_klines_{}_{}_{}.csv".format(
117
- path, symbol, contractType, interval
118
- ),
119
- "a",
120
- newline="",
121
- ) as file:
122
- writer = csv.writer(file)
123
- # 时间戳倒序,插入文件尾部
124
- writer.writerows(sorted(listData, key=lambda x: x[0], reverse=True))
125
-
126
- # 拿到最后一条记录后,获取close时间戳,变为下一次截止时间戳
127
- if latestRecords > 0:
128
- nextEndTime = (
129
- listData[0][0] - 1
130
- ) # -1 不和close时间戳相同,避免重新拉取重复数据
131
- errorCount = 0
132
- logging.info(
133
- "3、下次结束时间 {} {}".format(
134
- format_timestamp(nextEndTime), nextEndTime
135
- )
136
- )
137
-
138
- if nextEndTime <= hisDateTime and hisSwitch == 1:
139
- break
140
- else:
141
- logging.info("4、结束...")
142
-
143
- total = total + records
144
- logging.info("5、{} 抓取数据 {} 条记录...".format(symbol, records))
145
-
146
- logging.info("6、{} 抓取数据 {} 条记录...".format(symbol, total))
147
- logging.info("{} symbol --------------------------------- ".format(symbol))
@@ -1,6 +0,0 @@
1
- from .email_tools import mail
2
- from .time_tools import *
3
- from enums import *
4
- from .log_tools import Logger
5
-
6
- from .prepare_env import *
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env python
2
- import ssl
3
- import os
4
- import sys
5
- import logging
6
-
7
- import smtplib
8
-
9
- from email.mime.text import MIMEText
10
-
11
- from email.header import Header
12
- from email.message import EmailMessage
13
- import time
14
-
15
- # app = "email_tools"
16
- # logging.basicConfig(
17
- # format="%(asctime)s %(name)s:%(levelname)s:%(message)s",
18
- # datefmt="%Y-%m-%d %H:%M:%S",
19
- # level=logging.DEBUG,
20
- # )
21
-
22
- curPath = os.path.abspath(os.path.dirname(__file__))
23
- sys.path.append(curPath)
24
-
25
- from prepare_env import get_mail
26
-
27
- # logging.info("---- Read config.ini -----")
28
- (email_address, email_password, email_receiver) = get_mail()
29
- # logging.info(
30
- # "---- Read config.ini Finish!----{0}||{1}".format(email_address, email_receiver)
31
- # )
32
-
33
-
34
- def mail(topic, content):
35
- EMAIL_ADDRESS = email_address
36
- EMAIL_PASSWORD = email_password
37
- context = ssl.create_default_context()
38
- sender = EMAIL_ADDRESS
39
- receiver = email_receiver.split(",")
40
-
41
- subject = topic
42
- body = content
43
- msg = EmailMessage()
44
- msg["subject"] = subject
45
- msg["From"] = sender
46
- msg["To"] = receiver
47
- msg.set_content(body)
48
-
49
- with smtplib.SMTP_SSL("smtp.qq.com", 465, context=context) as smtp:
50
- smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
51
- smtp.send_message(msg)
52
- print("---- send_message ----\n{0}".format(msg))
53
-
54
-
55
- if __name__ == "__main__":
56
- mail("QRR", "BTCUSDT [2023-12-04 21:44:59 ~ 2023-12-04 21:39:59],QRR=9.4 > 5")
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import os
4
-
5
-
6
- def create_path(path):
7
- isExists = os.path.exists(path)
8
- if not isExists:
9
- os.makedirs(path)
10
-
11
-
12
-
13
-
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env python
2
- import sys
3
- import os
4
-
5
- from loguru import logger
6
- from datetime import datetime
7
-
8
- # log_path = "F:\PythonProject\Logs\\"
9
- from prepare_env import get_path
10
-
11
-
12
- class Logger:
13
- def __init__(self, log_name):
14
- data_path, log_path = get_path()
15
- self.logger = logger # 初始化一个logger
16
- self.logger.remove() # 清空所有设置
17
- # 添加控制台输出的格式,sys.stdout为输出到屏幕
18
- self.logger.add(
19
- sys.stdout,
20
- format="<green>{time:YYYYMMDD HH:mm:ss}</green> | " # 颜色>时间
21
- "{process.name} | " # 进程名
22
- "{thread.name} | " # 进程名
23
- "<cyan>{module}</cyan>.<cyan>{function}</cyan>" # 模块名.方法名
24
- ":<cyan>{line}</cyan> | " # 行号
25
- "<level>{level}</level>: " # 等级
26
- "<level>{message}</level>", # 日志内容
27
- )
28
- # 输出到文件
29
- rq = datetime.now().strftime("%Y%m%d")
30
- if not log_path.endswith("/"):
31
- log_path = log_path + "/"
32
- file_name = log_path + log_name + "_" + rq + ".log" # 文件名称
33
- self.logger.add(
34
- file_name,
35
- level="INFO",
36
- format="{time:YYYYMMDD HH:mm:ss} - " # 时间
37
- "{process.name} | " # 进程名
38
- "{thread.name} | " # 进程名
39
- "{module}.{function}:{line} - {level} -{message}", # 模块名.方法名:行号
40
- rotation="50 MB",
41
- compression="tar.gz",
42
- )
43
-
44
- def get_log(self):
45
- return self.logger
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import os
4
- import pathlib
5
- from configparser import ConfigParser
6
-
7
- config = ConfigParser()
8
- config_file_path = os.path.join(
9
- pathlib.Path(__file__).parent.resolve(), "..", "config.ini"
10
- )
11
- config.read(config_file_path)
12
-
13
-
14
- def get_api_key():
15
-
16
- return config["keys"]["api_key"], config["keys"]["api_secret"]
17
-
18
-
19
- def get_path():
20
- return config["path"]["data_path"], config["path"]["log_path"]
21
-
22
-
23
- def get_mail():
24
- return (
25
- config["mail"]["email_address"],
26
- config["mail"]["email_password"],
27
- config["mail"]["email_receiver"],
28
- )
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env python
2
- import logging
3
- import time
4
-
5
- from binance.cm_futures import CMFutures
6
- from binance.lib.utils import config_logging
7
-
8
- config_logging(logging, logging.DEBUG)
9
-
10
- cm_futures_client = CMFutures(show_limit_usage=True)
11
-
12
- logging.info(cm_futures_client.time())
13
-
14
-
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import time
4
- from datetime import date, datetime, timedelta
5
-
6
-
7
- def print_timestamp(timestamp):
8
- print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp / 1000)))
9
-
10
-
11
- def format_timestamp(timestamp):
12
- return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp / 1000))
13
-
14
-
15
- def format_date(timestamp):
16
- return time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000))
17
-
18
-
19
- def format_date_to(to_days):
20
- now = date.today()
21
- to = now + timedelta(days=to_days)
22
- return to
@@ -1 +0,0 @@
1
- from .sample import *
@@ -1,20 +0,0 @@
1
- # sample.py
2
-
3
- # create a variable in the module
4
- sample_variable = "This is a string variable in the sample.py module"
5
-
6
- # A function in the module
7
- def say_hello(name):
8
- return f"Hello, {name} welcome to this simple module."
9
-
10
- # This is another function in the module
11
- def add(a, b):
12
- return f"{a} + {b} is = {a+b}"
13
-
14
-
15
-
16
-
17
-
18
- # print(sample_variable)
19
- # print(say_hello("小明"))
20
- # print(add(2, 3))
@@ -1,35 +0,0 @@
1
- openfund/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- openfund/core/binance_tools/__init__.py,sha256=paV2IWM0QkD0ddq0UWdbsDeQTW3OBAPjMf51V5yMRgI,458
3
- openfund/core/binance_tools/binance_tools.py,sha256=sZqO8G3c_icGcQ2MRC9IgKXOXVlenCrXndG2fzi5Km4,1162
4
- openfund/core/binance_tools/continuous_klines.py,sha256=bUkqG5j8bJ2SCav4uKv1jDjGJ1Sk8P8VeAXiSjGpD_c,4816
5
- openfund/core/factory.py,sha256=0YXOvouwNeFLwpalD7B6t6X8vvjqOnhNakchEuK2Uog,837
6
- openfund/core/libs/__init__.py,sha256=QrVRHJhxr4dGr565neJweoOSKFHucVEo6-RMEvYLkNo,134
7
- openfund/core/libs/email_tools.py,sha256=KiGaJIb0nCE0G-jka5SFtO8Mtng6nrhin2Y44-1ddxA,1411
8
- openfund/core/libs/enums.py,sha256=WaPk56JowNe8g2wFhdSkrwLrt3Ic6HI8dDEldTKqddA,8902
9
- openfund/core/libs/file_tools.py,sha256=oHVtqRe7ag54JmyB6EI9SSfcCoZuDjytsO7DiwuQ468,145
10
- openfund/core/libs/log_tools.py,sha256=YWuGZYTuc4qgr0uL6ej299ox8FpNEg_uaEOBrBeQ0HI,1570
11
- openfund/core/libs/prepare_env.py,sha256=_R0SSCy1BaE71QTUe1gRiFqNTsCpQ2vgEujBS3jtEHo,576
12
- openfund/core/libs/time.py,sha256=HmqFzkJKSJ_H6irLfZV4oTvo0NHyRO-CeU8DMyJr_yg,272
13
- openfund/core/libs/time_tools.py,sha256=t2LWuv41P7lfJDys9wDkp3y_LZK24DiflTvnK-eJsnM,519
14
- openfund/core/openfund_old/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- openfund/core/openfund_old/continuous_klines.py,sha256=KaGW4bo88RVEg0e_Cvpg8nvQD8qkGt4k_yLvtvzFCeU,4954
16
- openfund/core/openfund_old/depth.py,sha256=L6yejSRpd37fmAk7Qowaxw1ELPwDGxKE0JCtlkJ00YE,2682
17
- openfund/core/openfund_old/historical_trades.py,sha256=JhvhyDp4CVG1gRhlKBaa01DWGWRHnMO3AV_PORTkkIw,4243
18
- openfund/core/openfund_old/index_info.py,sha256=idJCW5vvVZpMjmlXpu24emRC8MH2dcEUZq2YnaDB1q4,1766
19
- openfund/core/openfund_old/index_price_kline.py,sha256=rAqG3HGZT4SzSPAN_Hk3GLSt-oh_-Fc4h4n9W4iQKKE,3952
20
- openfund/core/openfund_old/klines.py,sha256=O_NtJD53HTJL3CTXkV5TrhAe2WBbcVhKA-3sCLaCJY8,3136
21
- openfund/core/openfund_old/klines_qrr.py,sha256=OK2SQuUpAb6PCOP8H-60kQE23nM9dB91pDlduH_AowE,3230
22
- openfund/core/openfund_old/mark_price.py,sha256=wjLx4Vw_S6Ee1-yIwsOKkiX1R6iaebQAuteQibJAhR8,3535
23
- openfund/core/openfund_old/mark_price_klines.py,sha256=FfQW3oh-XM2eR2dV5HAgJYu66ZPdWMVPVgbqe0-kc-c,4017
24
- openfund/core/openfund_old/ticker_24hr_price_change.py,sha256=SDv68uPN1OGTN_Dukl3AK7awE1nZuxxho-1PckhZptk,2607
25
- openfund/core/pyopenfund.py,sha256=SoIHAOMx3pAgG80mT-Ku0A1MB7RNoolC-9VcdwL_kZQ,726
26
- openfund/core/sample/__init__.py,sha256=9V4H_36LT47CTXTn7lOmAtboIGLyVu5WaV7xj_E-04k,22
27
- openfund/core/sample/sample.py,sha256=UstbqxMwiM_UMiQM0SZLxPqi2Vyb96SL8Xe-QxXksjs,393
28
- openfund/core/sycu_exam/__init__.py,sha256=uLZK-YxXqXfAtTTekgfnfqRIdVedM9SBHnmGD3j5sYM,20
29
- openfund/core/sycu_exam/exam.py,sha256=x7E3r6DgTMnlUz9q6fVysCPSabp-pQUqhGKw0xRpFrU,395
30
- openfund/core/sycu_exam/random_grade_cplus.py,sha256=8FIUnF4ys-at2tAtg0yYXru_mmxhs20ywG5DY9TiHWo,9487
31
- openfund/core/sycu_exam/random_grade_web.py,sha256=A2x9-jFx6L_AKAmWGrIrKcc9httJHTA5i0Uc8nGiPTE,8572
32
- openfund_core-0.0.2.dist-info/LICENSE,sha256=xazLvYVG6Uw0rtJK_miaYXYn0Y7tWmxIJ35I21fCOFE,11356
33
- openfund_core-0.0.2.dist-info/METADATA,sha256=cQxMF7v_YqW0eVeZWcre49kqjyW7Otxz5rDAffLD5ok,3160
34
- openfund_core-0.0.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
35
- openfund_core-0.0.2.dist-info/RECORD,,
File without changes