Compare commits
No commits in common. "ba2a6cd2ebb2b84fa02dd35314fcde8171604141" and "e97f76222cb7b0090407d02a216321fbbfbc3769" have entirely different histories.
ba2a6cd2eb
...
e97f76222c
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -15,6 +15,9 @@
|
|||||||
],
|
],
|
||||||
"python.envFile": "${workspaceFolder}/.env",
|
"python.envFile": "${workspaceFolder}/.env",
|
||||||
"python.testing.debugPort": 3000,
|
"python.testing.debugPort": 3000,
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"python.linting.pylintEnabled": false,
|
||||||
|
"python.linting.mypyEnabled": true,
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.py": "python"
|
"*.py": "python"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
from typing import Dict, Any, List, Optional
|
||||||
from typing import Callable, Dict, Any, List, Optional
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -64,6 +63,7 @@ class RESTSender(NamedObject):
|
|||||||
f"Failed to send status={excpt.response.status_code} {excpt.response.text}" # type: ignore
|
f"Failed to send status={excpt.response.status_code} {excpt.response.text}" # type: ignore
|
||||||
) from excpt
|
) from excpt
|
||||||
|
|
||||||
|
|
||||||
class MdSummary(HistMdBar):
|
class MdSummary(HistMdBar):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -105,7 +105,6 @@ class MdSummary(HistMdBar):
|
|||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
MdSummaryCallbackT = Callable[[List[MdSummary]], None]
|
|
||||||
|
|
||||||
class MdSummaryCollector(NamedObject):
|
class MdSummaryCollector(NamedObject):
|
||||||
sender_: RESTSender
|
sender_: RESTSender
|
||||||
@ -115,7 +114,6 @@ class MdSummaryCollector(NamedObject):
|
|||||||
history_depth_sec_: int
|
history_depth_sec_: int
|
||||||
|
|
||||||
history_: List[MdSummary]
|
history_: List[MdSummary]
|
||||||
callbacks_: List[MdSummaryCallbackT]
|
|
||||||
timer_: Optional[Timer]
|
timer_: Optional[Timer]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -132,13 +130,9 @@ class MdSummaryCollector(NamedObject):
|
|||||||
self.interval_sec_ = interval_sec
|
self.interval_sec_ = interval_sec
|
||||||
self.history_depth_sec_ = history_depth_sec
|
self.history_depth_sec_ = history_depth_sec
|
||||||
|
|
||||||
self.history_ = []
|
self.history_depth_sec_ = []
|
||||||
self.callbacks_ = []
|
|
||||||
self.timer_ = None
|
self.timer_ = None
|
||||||
|
|
||||||
def add_callback(self, cb: MdSummaryCallbackT) -> None:
|
|
||||||
self.callbacks_.append(cb)
|
|
||||||
|
|
||||||
def rqst_data(self) -> Dict[str, Any]:
|
def rqst_data(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"exch_acct": self.exch_acct_,
|
"exch_acct": self.exch_acct_,
|
||||||
@ -151,32 +145,22 @@ class MdSummaryCollector(NamedObject):
|
|||||||
response: requests.Response = self.sender_.send_post(
|
response: requests.Response = self.sender_.send_post(
|
||||||
endpoint="md_summary", post_body=self.rqst_data()
|
endpoint="md_summary", post_body=self.rqst_data()
|
||||||
)
|
)
|
||||||
if response.status_code not in (200, 201):
|
|
||||||
Log.error(f"{self.fname()}: Received error: {response.status_code} - {response.text}")
|
|
||||||
return []
|
|
||||||
return MdSummary.from_REST_response(response=response)
|
return MdSummary.from_REST_response(response=response)
|
||||||
|
|
||||||
def get_last(self) -> Optional[MdSummary]:
|
def get_last(self) -> Optional[MdSummary]:
|
||||||
rqst_data = self.rqst_data()
|
rqst_data = self.rqst_data()
|
||||||
rqst_data["history_depth_sec"] = self.interval_sec_ * 2
|
rqst_data["history_depth_sec"] = self.interval_sec_
|
||||||
response: requests.Response = self.sender_.send_post(
|
response: requests.Response = self.sender_.send_post(
|
||||||
endpoint="md_summary", post_body=rqst_data
|
endpoint="md_summary", post_body=rqst_data
|
||||||
)
|
)
|
||||||
if response.status_code not in (200, 201):
|
|
||||||
Log.error(f"{self.fname()}: Received error: {response.status_code} - {response.text}")
|
|
||||||
return None
|
|
||||||
res = MdSummary.from_REST_response(response=response)
|
res = MdSummary.from_REST_response(response=response)
|
||||||
return None if len(res) == 0 else res[-1]
|
return None if len(res) == 0 else res[-1]
|
||||||
|
|
||||||
def is_empty(self) -> bool:
|
|
||||||
return len(self.history_) == 0
|
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
if self.timer_:
|
if self.timer_:
|
||||||
Log.error(f"{self.fname()}: Timer is already started")
|
Log.error(f"{self.fname()}: Timer is already started")
|
||||||
return
|
return
|
||||||
self.history_ = self.get_history()
|
self.history_ = self.get_history()
|
||||||
self.run_callbacks()
|
|
||||||
self.timer_ = Timer(
|
self.timer_ = Timer(
|
||||||
start_in_sec=self.interval_sec_,
|
start_in_sec=self.interval_sec_,
|
||||||
is_periodic=True,
|
is_periodic=True,
|
||||||
@ -185,19 +169,15 @@ class MdSummaryCollector(NamedObject):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def _load_new(self) -> None:
|
async def _load_new(self) -> None:
|
||||||
|
|
||||||
last: Optional[MdSummary] = self.get_last()
|
last: Optional[MdSummary] = self.get_last()
|
||||||
if not last:
|
if not last:
|
||||||
Log.warning(f"{self.fname()}: did not get last update")
|
# URGENT logging
|
||||||
return
|
return
|
||||||
if not self.is_empty() and last.ts_ns_ <= self.history_[-1].ts_ns_:
|
if last.ts_ns_ <= self.history_[-1].ts_ns_:
|
||||||
Log.info(f"{self.fname()}: Received {last}. Already Have: {self.history_[-1]}")
|
# URGENT logging
|
||||||
return
|
return
|
||||||
self.history_.append(last)
|
self.history_.append(last)
|
||||||
self.run_callbacks()
|
# URGENT implement notification
|
||||||
|
|
||||||
def run_callbacks(self) -> None:
|
|
||||||
[cb(self.history_) for cb in self.callbacks_]
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
if self.timer_:
|
if self.timer_:
|
||||||
@ -217,7 +197,6 @@ class CvttRESTClient(NamedObject):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
config = Config(json_src={"cvtt_base_url": "http://cvtt-tester-01.cvtt.vpn:23456"})
|
config = Config(json_src={"cvtt_base_url": "http://cvtt-tester-01.cvtt.vpn:23456"})
|
||||||
# config = Config(json_src={"cvtt_base_url": "http://dev-server-02.cvtt.vpn:23456"})
|
|
||||||
|
|
||||||
cvtt_client = CvttRESTClient(config)
|
cvtt_client = CvttRESTClient(config)
|
||||||
|
|
||||||
@ -229,16 +208,6 @@ if __name__ == "__main__":
|
|||||||
history_depth_sec=24 * 3600,
|
history_depth_sec=24 * 3600,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _calback(history: List[MdSummary]) -> None:
|
hist = mdsc.get_history()
|
||||||
Log.info(f"MdSummary Hist Length is {len(history)}. Last summary: {history[-1] if len(history) > 0 else '[]'}")
|
last = mdsc.get_last()
|
||||||
|
|
||||||
mdsc.add_callback(_calback)
|
|
||||||
|
|
||||||
async def __run() -> None:
|
|
||||||
Log.info("Starting...")
|
|
||||||
await mdsc.start()
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
|
|
||||||
asyncio.run(__run())
|
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -12,11 +12,11 @@ from cvttpy_tools.settings.cvtt_types import JsonDictT
|
|||||||
# ---
|
# ---
|
||||||
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
||||||
# ---
|
# ---
|
||||||
from pairs_trading.lib.pt_strategy.live.ti_sender import TradingInstructionsSender
|
from pt_strategy.live.ti_sender import TradingInstructionsSender
|
||||||
from pairs_trading.lib.pt_strategy.model_data_policy import ModelDataPolicy
|
from pt_strategy.model_data_policy import ModelDataPolicy
|
||||||
from pairs_trading.lib.pt_strategy.pt_market_data import RealTimeMarketData
|
from pt_strategy.pt_market_data import RealTimeMarketData
|
||||||
from pairs_trading.lib.pt_strategy.pt_model import Prediction
|
from pt_strategy.pt_model import Prediction
|
||||||
from pairs_trading.lib.pt_strategy.trading_pair import PairState, TradingPair
|
from pt_strategy.trading_pair import PairState, TradingPair
|
||||||
|
|
||||||
"""
|
"""
|
||||||
--config=pair.cfg
|
--config=pair.cfg
|
||||||
@ -104,9 +104,9 @@ class PtLiveStrategy(NamedObject):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def _send_trading_instructions(
|
async def _send_trading_instructions(
|
||||||
self, trading_instructions: List[TradingInstruction]
|
self, trading_instructions: pd.DataFrame
|
||||||
) -> None:
|
) -> None:
|
||||||
pass # URGENT implement _send_trading_instructions
|
pass
|
||||||
|
|
||||||
def _create_trading_instructions(
|
def _create_trading_instructions(
|
||||||
self, prediction: Prediction, last_row: pd.Series
|
self, prediction: Prediction, last_row: pd.Series
|
||||||
@ -186,11 +186,6 @@ class PtLiveStrategy(NamedObject):
|
|||||||
}
|
}
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def _create_close_trade_instructions(
|
|
||||||
self, pair: TradingPair, row: pd.Series #, prediction: Prediction
|
|
||||||
) -> List[TradingInstruction]:
|
|
||||||
return [] # URGENT implement _create_close_trade_instructions
|
|
||||||
|
|
||||||
def _handle_outstanding_positions(self) -> Optional[pd.DataFrame]:
|
def _handle_outstanding_positions(self) -> Optional[pd.DataFrame]:
|
||||||
trades = None
|
trades = None
|
||||||
pair = self.trading_pair_
|
pair = self.trading_pair_
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
```python
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
# from cvtt_client.mkt_data import (CvttPricerWebSockClient,
|
from cvtt_client.mkt_data import (CvttPricerWebSockClient,
|
||||||
# CvttPricesSubscription, MessageTypeT,
|
CvttPricesSubscription, MessageTypeT,
|
||||||
# SubscriptionIdT)
|
SubscriptionIdT)
|
||||||
from cvttpy_tools.app import App
|
from cvttpy_tools.app import App
|
||||||
from cvttpy_tools.base import NamedObject
|
from cvttpy_tools.base import NamedObject
|
||||||
from cvttpy_tools.config import Config
|
from cvttpy_tools.config import Config
|
||||||
from cvttpy_tools.logger import Log
|
from cvttpy_tools.logger import Log
|
||||||
from cvttpy_tools.settings.cvtt_types import JsonDictT
|
from cvttpy_tools.settings.cvtt_types import JsonDictT
|
||||||
from pairs_trading.lib.pt_strategy.live.live_strategy import PtLiveStrategy
|
from pt_strategy.live.live_strategy import PtLiveStrategy
|
||||||
from pairs_trading.lib.pt_strategy.trading_pair import TradingPair
|
from pt_strategy.trading_pair import TradingPair
|
||||||
|
|
||||||
"""
|
"""
|
||||||
--config=pair.cfg
|
--config=pair.cfg
|
||||||
@ -84,4 +83,3 @@ class PtMktDataClient(NamedObject):
|
|||||||
await self._subscribe()
|
await self._subscribe()
|
||||||
|
|
||||||
await self.pricer_client_.run()
|
await self.pricer_client_.run()
|
||||||
```
|
|
||||||
66
pyproject.toml
Normal file
66
pyproject.toml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=45", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "pairs-trading"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Pairs Trading Backtesting Framework"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
target-version = ['py38']
|
||||||
|
include = '\.pyi?$'
|
||||||
|
extend-exclude = '''
|
||||||
|
/(
|
||||||
|
# directories
|
||||||
|
\.eggs
|
||||||
|
| \.git
|
||||||
|
| \.hg
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| \.venv
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tool.flake8]
|
||||||
|
max-line-length = 88
|
||||||
|
extend-ignore = ["E203", "W503"]
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
"__pycache__",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
".venv",
|
||||||
|
".mypy_cache",
|
||||||
|
".tox"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.8"
|
||||||
|
warn_return_any = true
|
||||||
|
warn_unused_configs = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
check_untyped_defs = true
|
||||||
|
disallow_untyped_decorators = true
|
||||||
|
no_implicit_optional = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
warn_no_return = true
|
||||||
|
warn_unreachable = true
|
||||||
|
strict_equality = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = [
|
||||||
|
"numpy.*",
|
||||||
|
"pandas.*",
|
||||||
|
"matplotlib.*",
|
||||||
|
"seaborn.*",
|
||||||
|
"scipy.*",
|
||||||
|
"sklearn.*"
|
||||||
|
]
|
||||||
|
ignore_missing_imports = true
|
||||||
25
pyrightconfig.json
Normal file
25
pyrightconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules",
|
||||||
|
"**/__pycache__",
|
||||||
|
"**/.*",
|
||||||
|
"results",
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"ignore": [],
|
||||||
|
"defineConstant": {},
|
||||||
|
"typeCheckingMode": "basic",
|
||||||
|
"useLibraryCodeForTypes": true,
|
||||||
|
"autoImportCompletions": true,
|
||||||
|
"autoSearchPaths": true,
|
||||||
|
"extraPaths": [
|
||||||
|
"lib",
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"stubPath": "./typings",
|
||||||
|
"venvPath": ".",
|
||||||
|
"venv": "python3.12-venv"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user