progress. Initial untested version
This commit is contained in:
parent
b196863a34
commit
bd6cf1d4d0
@ -1,8 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from typing import Any, Dict, List, Optional
|
||||||
from typing import Any, Dict, List, Optional, cast
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
@ -10,15 +8,14 @@ import pandas as pd
|
|||||||
from cvttpy_tools.base import NamedObject
|
from cvttpy_tools.base import NamedObject
|
||||||
from cvttpy_tools.app import App
|
from cvttpy_tools.app import App
|
||||||
from cvttpy_tools.config import Config
|
from cvttpy_tools.config import Config
|
||||||
from cvttpy_tools.settings.cvtt_types import BookIdT, IntervalSecT
|
from cvttpy_tools.settings.cvtt_types import IntervalSecT
|
||||||
from cvttpy_tools.timeutils import SecPerHour, current_nanoseconds
|
from cvttpy_tools.timeutils import SecPerHour, current_nanoseconds, NanoPerSec
|
||||||
from cvttpy_tools.logger import Log
|
from cvttpy_tools.logger import Log
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
||||||
from cvttpy_trading.trading.mkt_data.md_summary import MdTradesAggregate
|
from cvttpy_trading.trading.mkt_data.md_summary import MdTradesAggregate
|
||||||
from cvttpy_trading.trading.trading_instructions import TradingInstructions
|
from cvttpy_trading.trading.trading_instructions import TradingInstructions
|
||||||
from cvttpy_trading.trading.accounting.cvtt_book import CvttBook
|
|
||||||
from cvttpy_trading.trading.trading_instructions import TargetPositionSignal
|
from cvttpy_trading.trading.trading_instructions import TargetPositionSignal
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
@ -29,23 +26,6 @@ from pairs_trading.apps.pairs_trader import PairsTrader
|
|||||||
from pairs_trading.lib.pt_strategy.pt_market_data import LiveMarketData
|
from pairs_trading.lib.pt_strategy.pt_market_data import LiveMarketData
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
--config=pair.cfg
|
|
||||||
--pair=PAIR-BTC-USDT:COINBASE_AT,PAIR-ETH-USDT:COINBASE_AT
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# class TradingInstructionType(Enum):
|
|
||||||
# TARGET_POSITION = "TARGET_POSITION"
|
|
||||||
|
|
||||||
|
|
||||||
# @dataclass
|
|
||||||
# class TradingInstruction(NamedObject):
|
|
||||||
# type_: TradingInstructionType
|
|
||||||
# exch_instr_: ExchangeInstrument
|
|
||||||
# specifics_: Dict[str, Any]
|
|
||||||
|
|
||||||
|
|
||||||
class PtLiveStrategy(NamedObject):
|
class PtLiveStrategy(NamedObject):
|
||||||
config_: Config
|
config_: Config
|
||||||
instruments_: List[ExchangeInstrument]
|
instruments_: List[ExchangeInstrument]
|
||||||
@ -59,12 +39,9 @@ class PtLiveStrategy(NamedObject):
|
|||||||
model_data_policy_: ModelDataPolicy
|
model_data_policy_: ModelDataPolicy
|
||||||
pairs_trader_: PairsTrader
|
pairs_trader_: PairsTrader
|
||||||
|
|
||||||
# ti_sender_: TradingInstructionsSender
|
|
||||||
|
|
||||||
# for presentation: history of prediction values and trading signals
|
# for presentation: history of prediction values and trading signals
|
||||||
predictions_df_: pd.DataFrame
|
predictions_df_: pd.DataFrame
|
||||||
trading_signals_df_: pd.DataFrame
|
trading_signals_df_: pd.DataFrame
|
||||||
# book_: CvttBook
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -155,7 +132,15 @@ class PtLiveStrategy(NamedObject):
|
|||||||
await self._send_trading_instructions(trading_instructions)
|
await self._send_trading_instructions(trading_instructions)
|
||||||
|
|
||||||
def _is_md_actual(self, hist_aggr: List[MdTradesAggregate]) -> bool:
|
def _is_md_actual(self, hist_aggr: List[MdTradesAggregate]) -> bool:
|
||||||
return False # URGENT _is_md_actual
|
LAG_THRESHOLD = 5 * NanoPerSec
|
||||||
|
|
||||||
|
if len(hist_aggr) == 0:
|
||||||
|
Log.warning(f"{self.fname()} list of aggregates IS EMPTY")
|
||||||
|
return False
|
||||||
|
# MAYBE check market data length
|
||||||
|
if current_nanoseconds() - hist_aggr[-1].time_ns_ > LAG_THRESHOLD:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _create_md_df(self, hist_aggr: List[MdTradesAggregate]) -> pd.DataFrame:
|
def _create_md_df(self, hist_aggr: List[MdTradesAggregate]) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
@ -259,8 +244,8 @@ class PtLiveStrategy(NamedObject):
|
|||||||
|
|
||||||
return trd_instructions
|
return trd_instructions
|
||||||
|
|
||||||
def _strength(self, scaled_disequilibrium) -> float:
|
def _strength(self, scaled_disequilibrium: float) -> float:
|
||||||
# URGENT PtLiveStrategy._strength()
|
# TODO PtLiveStrategy._strength()
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
def _create_open_trade_instructions(
|
def _create_open_trade_instructions(
|
||||||
|
|||||||
@ -4,11 +4,13 @@ from datetime import date, datetime
|
|||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
# ---
|
||||||
|
from cvttpy_tools.config import Config
|
||||||
|
# ---
|
||||||
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
||||||
|
# ---
|
||||||
from pairs_trading.lib.pt_strategy.trading_pair import TradingPair
|
from pairs_trading.lib.pt_strategy.trading_pair import TradingPair
|
||||||
|
|
||||||
|
|
||||||
# Recommended replacement adapters and converters for Python 3.12+
|
# Recommended replacement adapters and converters for Python 3.12+
|
||||||
# From: https://docs.python.org/3/library/sqlite3.html#sqlite3-adapter-converter-recipes
|
# From: https://docs.python.org/3/library/sqlite3.html#sqlite3-adapter-converter-recipes
|
||||||
def adapt_date_iso(val: date) -> str:
|
def adapt_date_iso(val: date) -> str:
|
||||||
@ -20,12 +22,10 @@ def adapt_datetime_iso(val: datetime) -> str:
|
|||||||
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
|
"""Adapt datetime.datetime to timezone-naive ISO 8601 date."""
|
||||||
return val.isoformat()
|
return val.isoformat()
|
||||||
|
|
||||||
|
|
||||||
def convert_date(val: bytes) -> date:
|
def convert_date(val: bytes) -> date:
|
||||||
"""Convert ISO 8601 date to datetime.date object."""
|
"""Convert ISO 8601 date to datetime.date object."""
|
||||||
return datetime.fromisoformat(val.decode()).date()
|
return datetime.fromisoformat(val.decode()).date()
|
||||||
|
|
||||||
|
|
||||||
def convert_datetime(val: bytes) -> datetime:
|
def convert_datetime(val: bytes) -> datetime:
|
||||||
"""Convert ISO 8601 datetime to datetime.datetime object."""
|
"""Convert ISO 8601 datetime to datetime.datetime object."""
|
||||||
return datetime.fromisoformat(val.decode())
|
return datetime.fromisoformat(val.decode())
|
||||||
@ -120,7 +120,7 @@ def create_result_database(db_path: str) -> None:
|
|||||||
def store_config_in_database(
|
def store_config_in_database(
|
||||||
db_path: str,
|
db_path: str,
|
||||||
config_file_path: str,
|
config_file_path: str,
|
||||||
config: Dict,
|
config: Config,
|
||||||
datafiles: List[Tuple[str, str]],
|
datafiles: List[Tuple[str, str]],
|
||||||
instruments: List[ExchangeInstrument],
|
instruments: List[ExchangeInstrument],
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -137,7 +137,7 @@ def store_config_in_database(
|
|||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# Convert config to JSON string
|
# Convert config to JSON string
|
||||||
config_json = json.dumps(config, indent=2, default=str)
|
config_json = json.dumps(config.data(), indent=2, default=str)
|
||||||
|
|
||||||
# Convert lists to comma-separated strings for storage
|
# Convert lists to comma-separated strings for storage
|
||||||
datafiles_str = ", ".join([f"{datafile}" for _, datafile in datafiles])
|
datafiles_str = ", ".join([f"{datafile}" for _, datafile in datafiles])
|
||||||
@ -206,9 +206,9 @@ class PairResearchResult:
|
|||||||
trades_: Dict[DayT, pd.DataFrame]
|
trades_: Dict[DayT, pd.DataFrame]
|
||||||
outstanding_positions_: Dict[DayT, List[OutstandingPositionT]]
|
outstanding_positions_: Dict[DayT, List[OutstandingPositionT]]
|
||||||
symbol_roundtrip_trades_: Dict[str, List[Dict[str, Any]]]
|
symbol_roundtrip_trades_: Dict[str, List[Dict[str, Any]]]
|
||||||
|
config_: Config
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
self.config_ = config
|
self.config_ = config
|
||||||
self.trades_ = {}
|
self.trades_ = {}
|
||||||
self.outstanding_positions_ = {}
|
self.outstanding_positions_ = {}
|
||||||
@ -220,13 +220,6 @@ class PairResearchResult:
|
|||||||
self.trades_[day] = trades
|
self.trades_[day] = trades
|
||||||
self.outstanding_positions_[day] = outstanding_positions
|
self.outstanding_positions_[day] = outstanding_positions
|
||||||
|
|
||||||
# def all_trades(self) -> List[TradeT]:
|
|
||||||
# """Get all trades across all days as a flat list."""
|
|
||||||
# all_trades_list: List[TradeT] = []
|
|
||||||
# for day_trades in self.trades_.values():
|
|
||||||
# all_trades_list.extend(day_trades.to_dict(orient="records"))
|
|
||||||
# return all_trades_list
|
|
||||||
|
|
||||||
def outstanding_positions(self) -> List[OutstandingPositionT]:
|
def outstanding_positions(self) -> List[OutstandingPositionT]:
|
||||||
"""Get all outstanding positions across all days as a flat list."""
|
"""Get all outstanding positions across all days as a flat list."""
|
||||||
res: List[Dict[str, Any]] = []
|
res: List[Dict[str, Any]] = []
|
||||||
|
|||||||
@ -27,22 +27,6 @@ class PairState(Enum):
|
|||||||
CLOSE_STOP_PROFIT = 6
|
CLOSE_STOP_PROFIT = 6
|
||||||
|
|
||||||
|
|
||||||
# def get_symbol(instrument: Dict[str, str]) -> str:
|
|
||||||
# if "symbol" in instrument:
|
|
||||||
# return instrument["symbol"]
|
|
||||||
# elif "instrument_id" in instrument:
|
|
||||||
# instrument_id = instrument["instrument_id"]
|
|
||||||
# instrument_pfx = instrument_id[: instrument_id.find("-") + 1]
|
|
||||||
# symbol = instrument_id[len(instrument_pfx) :]
|
|
||||||
# instrument["symbol"] = symbol
|
|
||||||
# instrument["instrument_id_pfx"] = instrument_pfx
|
|
||||||
# return symbol
|
|
||||||
# else:
|
|
||||||
# raise ValueError(
|
|
||||||
# f"Invalid instrument: {instrument}, missing symbol or instrument_id"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
class TradingPair(NamedObject, ABC):
|
class TradingPair(NamedObject, ABC):
|
||||||
config_: Config
|
config_: Config
|
||||||
model_: Any # "PairsTradingModel"
|
model_: Any # "PairsTradingModel"
|
||||||
@ -67,13 +51,12 @@ class TradingPair(NamedObject, ABC):
|
|||||||
self.instruments_[0].user_data_["symbol"] = instruments[0].instrument_id().split("-", 1)[1]
|
self.instruments_[0].user_data_["symbol"] = instruments[0].instrument_id().split("-", 1)[1]
|
||||||
self.instruments_[1].user_data_["symbol"] = instruments[1].instrument_id().split("-", 1)[1]
|
self.instruments_[1].user_data_["symbol"] = instruments[1].instrument_id().split("-", 1)[1]
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def run(self, market_data: pd.DataFrame, data_params: DataWindowParams) -> Prediction: # type: ignore[assignment]
|
||||||
return (
|
self.market_data_ = market_data[
|
||||||
f"{self.__class__.__name__}:"
|
data_params.training_start_index_ : data_params.training_start_index_
|
||||||
f" symbol_a={self.symbol_a()},"
|
+ data_params.training_size_
|
||||||
f" symbol_b={self.symbol_b()},"
|
]
|
||||||
f" model={self.model_.__class__.__name__}"
|
return self.model_.predict(pair=self)
|
||||||
)
|
|
||||||
|
|
||||||
def colnames(self) -> List[str]:
|
def colnames(self) -> List[str]:
|
||||||
return [
|
return [
|
||||||
@ -91,9 +74,15 @@ class TradingPair(NamedObject, ABC):
|
|||||||
|
|
||||||
def get_instrument_b(self) -> ExchangeInstrument:
|
def get_instrument_b(self) -> ExchangeInstrument:
|
||||||
return self.instruments_[1]
|
return self.instruments_[1]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"{self.__class__.__name__}:"
|
||||||
|
f" symbol_a={self.symbol_a()},"
|
||||||
|
f" symbol_b={self.symbol_b()},"
|
||||||
|
f" model={self.model_.__class__.__name__}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ResearchTradingPair(TradingPair):
|
class ResearchTradingPair(TradingPair):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -109,8 +98,6 @@ class ResearchTradingPair(TradingPair):
|
|||||||
"state": PairState.INITIAL,
|
"state": PairState.INITIAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
# URGENT set exchange instruments for the pair
|
|
||||||
|
|
||||||
def is_closed(self) -> bool:
|
def is_closed(self) -> bool:
|
||||||
return self.user_data_["state"] in [
|
return self.user_data_["state"] in [
|
||||||
PairState.CLOSE,
|
PairState.CLOSE,
|
||||||
@ -228,13 +215,6 @@ class ResearchTradingPair(TradingPair):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, market_data: pd.DataFrame, data_params: DataWindowParams) -> Prediction: # type: ignore[assignment]
|
|
||||||
self.market_data_ = market_data[
|
|
||||||
data_params.training_start_index_ : data_params.training_start_index_
|
|
||||||
+ data_params.training_size_
|
|
||||||
]
|
|
||||||
return self.model_.predict(pair=self)
|
|
||||||
|
|
||||||
class LiveTradingPair(TradingPair):
|
class LiveTradingPair(TradingPair):
|
||||||
|
|
||||||
def __init__(self, config: Config, instruments: List[ExchangeInstrument]):
|
def __init__(self, config: Config, instruments: List[ExchangeInstrument]):
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import os
|
|||||||
import glob
|
import glob
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
# ---
|
# ---
|
||||||
from cvttpy_tools.config import CvttAppConfig
|
from cvttpy_tools.config import Config
|
||||||
# ---
|
# ---
|
||||||
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
from cvttpy_trading.trading.instrument import ExchangeInstrument
|
||||||
|
|
||||||
@ -10,13 +10,13 @@ DayT = str
|
|||||||
DataFileNameT = str
|
DataFileNameT = str
|
||||||
|
|
||||||
def resolve_datafiles(
|
def resolve_datafiles(
|
||||||
config: Dict, date_pattern: str, instruments: List[ExchangeInstrument]
|
config: Config, date_pattern: str, instruments: List[ExchangeInstrument]
|
||||||
) -> List[Tuple[DayT, DataFileNameT]]:
|
) -> List[Tuple[DayT, DataFileNameT]]:
|
||||||
resolved_files: List[Tuple[DayT, DataFileNameT]] = []
|
resolved_files: List[Tuple[DayT, DataFileNameT]] = []
|
||||||
for exch_inst in instruments:
|
for exch_inst in instruments:
|
||||||
pattern = date_pattern
|
pattern = date_pattern
|
||||||
inst_type = exch_inst.user_data_.get("instrument_type", "?instrument_type?")
|
inst_type = exch_inst.user_data_.get("instrument_type", "?instrument_type?")
|
||||||
data_dir = config["market_data_loading"][inst_type]["data_directory"]
|
data_dir = config.get_value(f"market_data_loading/{inst_type}/data_directory")
|
||||||
if "*" in pattern or "?" in pattern:
|
if "*" in pattern or "?" in pattern:
|
||||||
# Handle wildcards
|
# Handle wildcards
|
||||||
if not os.path.isabs(pattern):
|
if not os.path.isabs(pattern):
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class Runner(NamedObject):
|
|||||||
# Resolve data files (CLI takes priority over config)
|
# Resolve data files (CLI takes priority over config)
|
||||||
instruments: List[ExchangeInstrument] = self._get_instruments()
|
instruments: List[ExchangeInstrument] = self._get_instruments()
|
||||||
datafiles = resolve_datafiles(
|
datafiles = resolve_datafiles(
|
||||||
config=CvttAppConfig.instance().to_dict(),
|
config=CvttAppConfig.instance(),
|
||||||
date_pattern=App.instance().get_argument("date_pattern"),
|
date_pattern=App.instance().get_argument("date_pattern"),
|
||||||
instruments=instruments,
|
instruments=instruments,
|
||||||
)
|
)
|
||||||
@ -77,7 +77,7 @@ class Runner(NamedObject):
|
|||||||
is_config_stored = False
|
is_config_stored = False
|
||||||
# Process each data file
|
# Process each data file
|
||||||
|
|
||||||
results = PairResearchResult(config=CvttAppConfig.instance().to_dict())
|
results = PairResearchResult(config=CvttAppConfig.instance())
|
||||||
for day in sorted(days):
|
for day in sorted(days):
|
||||||
md_datafiles = [datafile for md_day, datafile in datafiles if md_day == day]
|
md_datafiles = [datafile for md_day, datafile in datafiles if md_day == day]
|
||||||
if not all([os.path.exists(datafile) for datafile in md_datafiles]):
|
if not all([os.path.exists(datafile) for datafile in md_datafiles]):
|
||||||
@ -89,7 +89,7 @@ class Runner(NamedObject):
|
|||||||
store_config_in_database(
|
store_config_in_database(
|
||||||
db_path=App.instance().get_argument("result_db"),
|
db_path=App.instance().get_argument("result_db"),
|
||||||
config_file_path=App.instance().get_argument("config"),
|
config_file_path=App.instance().get_argument("config"),
|
||||||
config=CvttAppConfig.instance().to_dict(),
|
config=CvttAppConfig.instance(),
|
||||||
datafiles=datafiles,
|
datafiles=datafiles,
|
||||||
instruments=instruments,
|
instruments=instruments,
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user