progress. Initial untested version

This commit is contained in:
Oleg Sheynin 2026-01-11 18:17:05 +00:00
parent b196863a34
commit bd6cf1d4d0
6 changed files with 46 additions and 88 deletions

View File

@ -1,8 +1,6 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, cast
from enum import Enum
from typing import Any, Dict, List, Optional
import pandas as pd
@ -10,15 +8,14 @@ import pandas as pd
from cvttpy_tools.base import NamedObject
from cvttpy_tools.app import App
from cvttpy_tools.config import Config
from cvttpy_tools.settings.cvtt_types import BookIdT, IntervalSecT
from cvttpy_tools.timeutils import SecPerHour, current_nanoseconds
from cvttpy_tools.settings.cvtt_types import IntervalSecT
from cvttpy_tools.timeutils import SecPerHour, current_nanoseconds, NanoPerSec
from cvttpy_tools.logger import Log
# ---
from cvttpy_trading.trading.instrument import ExchangeInstrument
from cvttpy_trading.trading.mkt_data.md_summary import MdTradesAggregate
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
# ---
@ -29,23 +26,6 @@ from pairs_trading.apps.pairs_trader import PairsTrader
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):
config_: Config
instruments_: List[ExchangeInstrument]
@ -59,12 +39,9 @@ class PtLiveStrategy(NamedObject):
model_data_policy_: ModelDataPolicy
pairs_trader_: PairsTrader
# ti_sender_: TradingInstructionsSender
# for presentation: history of prediction values and trading signals
predictions_df_: pd.DataFrame
trading_signals_df_: pd.DataFrame
# book_: CvttBook
def __init__(
self,
@ -155,7 +132,15 @@ class PtLiveStrategy(NamedObject):
await self._send_trading_instructions(trading_instructions)
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:
"""
@ -259,8 +244,8 @@ class PtLiveStrategy(NamedObject):
return trd_instructions
def _strength(self, scaled_disequilibrium) -> float:
# URGENT PtLiveStrategy._strength()
def _strength(self, scaled_disequilibrium: float) -> float:
# TODO PtLiveStrategy._strength()
return 1.0
def _create_open_trade_instructions(

View File

@ -4,11 +4,13 @@ from datetime import date, datetime
from typing import Any, Dict, List, Optional, Tuple
import pandas as pd
# ---
from cvttpy_tools.config import Config
# ---
from cvttpy_trading.trading.instrument import ExchangeInstrument
# ---
from pairs_trading.lib.pt_strategy.trading_pair import TradingPair
# Recommended replacement adapters and converters for Python 3.12+
# From: https://docs.python.org/3/library/sqlite3.html#sqlite3-adapter-converter-recipes
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."""
return val.isoformat()
def convert_date(val: bytes) -> date:
"""Convert ISO 8601 date to datetime.date object."""
return datetime.fromisoformat(val.decode()).date()
def convert_datetime(val: bytes) -> datetime:
"""Convert ISO 8601 datetime to datetime.datetime object."""
return datetime.fromisoformat(val.decode())
@ -120,7 +120,7 @@ def create_result_database(db_path: str) -> None:
def store_config_in_database(
db_path: str,
config_file_path: str,
config: Dict,
config: Config,
datafiles: List[Tuple[str, str]],
instruments: List[ExchangeInstrument],
) -> None:
@ -137,7 +137,7 @@ def store_config_in_database(
cursor = conn.cursor()
# 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
datafiles_str = ", ".join([f"{datafile}" for _, datafile in datafiles])
@ -206,9 +206,9 @@ class PairResearchResult:
trades_: Dict[DayT, pd.DataFrame]
outstanding_positions_: Dict[DayT, List[OutstandingPositionT]]
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.trades_ = {}
self.outstanding_positions_ = {}
@ -220,13 +220,6 @@ class PairResearchResult:
self.trades_[day] = trades
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]:
"""Get all outstanding positions across all days as a flat list."""
res: List[Dict[str, Any]] = []

View File

@ -27,22 +27,6 @@ class PairState(Enum):
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):
config_: Config
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_[1].user_data_["symbol"] = instruments[1].instrument_id().split("-", 1)[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__}"
)
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)
def colnames(self) -> List[str]:
return [
@ -91,9 +74,15 @@ class TradingPair(NamedObject, ABC):
def get_instrument_b(self) -> ExchangeInstrument:
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):
def __init__(
@ -109,8 +98,6 @@ class ResearchTradingPair(TradingPair):
"state": PairState.INITIAL,
}
# URGENT set exchange instruments for the pair
def is_closed(self) -> bool:
return self.user_data_["state"] in [
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):
def __init__(self, config: Config, instruments: List[ExchangeInstrument]):

View File

@ -2,7 +2,7 @@ import os
import glob
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
@ -10,13 +10,13 @@ DayT = str
DataFileNameT = str
def resolve_datafiles(
config: Dict, date_pattern: str, instruments: List[ExchangeInstrument]
config: Config, date_pattern: str, instruments: List[ExchangeInstrument]
) -> List[Tuple[DayT, DataFileNameT]]:
resolved_files: List[Tuple[DayT, DataFileNameT]] = []
for exch_inst in instruments:
pattern = date_pattern
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:
# Handle wildcards
if not os.path.isabs(pattern):

View File

@ -58,7 +58,7 @@ class Runner(NamedObject):
# Resolve data files (CLI takes priority over config)
instruments: List[ExchangeInstrument] = self._get_instruments()
datafiles = resolve_datafiles(
config=CvttAppConfig.instance().to_dict(),
config=CvttAppConfig.instance(),
date_pattern=App.instance().get_argument("date_pattern"),
instruments=instruments,
)
@ -77,7 +77,7 @@ class Runner(NamedObject):
is_config_stored = False
# Process each data file
results = PairResearchResult(config=CvttAppConfig.instance().to_dict())
results = PairResearchResult(config=CvttAppConfig.instance())
for day in sorted(days):
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]):
@ -89,7 +89,7 @@ class Runner(NamedObject):
store_config_in_database(
db_path=App.instance().get_argument("result_db"),
config_file_path=App.instance().get_argument("config"),
config=CvttAppConfig.instance().to_dict(),
config=CvttAppConfig.instance(),
datafiles=datafiles,
instruments=instruments,
)

File diff suppressed because one or more lines are too long