From bbba7bfe8918e20a127feac07201e105ce1b3609 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 24 Nov 2025 18:33:21 +0100 Subject: [PATCH] refactor: rename CoreTraidMath.py to CoreTradeMath.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix typo in core math module filename and update all references. Changes: - Renamed market_trade/core/CoreTraidMath.py → CoreTradeMath.py - Updated 28 import references across 14 files: - All Ind_*.py indicator modules - indicators.py, indicators_v2.py - signals.py, signals_v2.py - CoreDraw.py - Updated documentation references in CLAUDE.md This eliminates the "Traid" typo and aligns with proper English spelling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CLAUDE.md | 151 ++++++++++++++ docs/trading-flow.md | 0 market_trade/core/CoreDraw.py | 2 +- market_trade/core/CoreTradeMath.py | 137 +++++++++++++ market_trade/core/Ind_ADX.py | 4 +- market_trade/core/Ind_Alligator.py | 4 +- market_trade/core/Ind_DonchianChannel.py | 6 +- market_trade/core/Ind_Envelopes.py | 4 +- market_trade/core/Ind_Gator.py | 2 +- market_trade/core/Ind_Ishimoku.py | 2 +- market_trade/core/Ind_LRI.py | 2 +- market_trade/core/Ind_STD.py | 4 +- market_trade/core/Ind_Stochastic.py | 4 +- market_trade/core/Ind_bollingerBands.py | 6 +- market_trade/core/indicators.py | 6 +- market_trade/core/indicators_v2.py | 226 +++++++++++++-------- market_trade/core/signals.py | 2 +- market_trade/core/signals_v2.py | 243 ++++++++++++++--------- 18 files changed, 612 insertions(+), 193 deletions(-) create mode 100644 CLAUDE.md create mode 100644 docs/trading-flow.md create mode 100644 market_trade/core/CoreTradeMath.py diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..02c4cc9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,151 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a Python-based algorithmic trading system for financial markets that implements technical indicator analysis, signal generation, decision making, and risk management. It integrates with Tinkoff Invest API for market data and trading. + +## Development Setup + +### Environment Setup +- Python 3.9-3.12 (managed via Poetry) +- Install dependencies: `poetry install` +- Activate virtual environment: `poetry shell` +- Environment variables are in `.env` (contains Tinkoff API tokens) + +### Docker Development +- Build image: `docker build -f dockerfiles/Dockerfile -t market-trade .` +- Main Dockerfile uses Poetry 1.7.1 and Python 3.11 +- Requires SSH mount for private tinkoff-grpc dependency + +### Running Tests +- Test files located in `market_trade/tests/` +- Run test: `python market_trade/tests/test_decision.py` +- Run specific test: `python market_trade/tests/test_dataloader.py` + +### Data Tools +Scripts in `tools/` directory for data collection: +- `save_currencies_data.py` - Collect currency market data +- `save_shares_data.py` - Collect stock market data +- `get_shares_stats.py` - Generate trading statistics +- Usage: `python tools/.py [options]` + +## Architecture + +### Core Trading Pipeline (docs/trading-flow.md) + +The system follows this data flow: +1. **SELECT INSTRUMENT** - Choose trading instrument +2. **GET_CANDLES(10000)** - Fetch historical candlestick data +3. **RETRO TRAINING** - Backtest signals on historical data +4. **STREAM PROCESSING**: + - Receive real-time market messages + - Accumulate data in sliding window + - Update window with each new message + - Generate trading signals + +### Module Structure + +#### `market_trade/core/` - Core Trading Logic + +**Signal Processing Chain:** +1. **Indicators** (`indicators.py`, `indicators_v2.py`) - Technical indicator calculation + - Base class: `coreIndicator` + - Bollinger Bands: `ind_BB` + - All indicator classes (Ind_*.py): ADX, Alligator, DonchianChannel, Envelopes, Gator, Ishimoku, LRI, STD, Stochastic, bollingerBands + +2. **Signals** (`signals.py`, `signals_v2.py`) - Signal generation from indicators + - Base class: `coreSignalTrande` with three modes: + - `online` - Real-time signal generation + - `retro` - Expanding window backtesting + - `retroFast` - Sliding window backtesting + - Signal implementations: `signal_BB` (Bollinger Bands signal) + - Aggregator: `signalAgrigator` manages multiple signal instances + +3. **Decision Manager** (`decisionManager.py`, `decisionManager_v2.py`) - Trading decisions + - Class: `decsionManager` + - Combines signals from `signalAgrigator` + - Uses `trandeVoter` for probability matrix generation + - Methods: + - `getSignalTest()` - Test signal generation + - `generateMatrixProbability()` - Create probability matrices from backtest + - `getOnlineAns()` - Real-time decision making + +4. **Trade Voter** (`trandeVoter.py`) - Probability-based decision weighting + - Generates probability matrices from historical signal performance + - Weights multiple signals to produce final decision + +5. **Risk Manager** (`riskManager.py`) - Position sizing and risk controls + - Class: `riskManager` + - Combines signal decisions with risk parameters + +6. **Deal Manager** (`dealManager.py`) - Trade execution and management + - Class: `DealManager` + - Manages active positions and orders + +**Helper Modules:** +- `CoreTradeMath.py` - Mathematical operations for indicators (moving averages, STD) +- `CoreDraw.py` - Visualization utilities for indicators and signals + +#### `market_trade/data/` - Data Loading + +- `dataloader.py` - Contains `DukaMTInterface` class + - Converts Dukascopy format candlestick data to internal format + - Separates bid/ask candlesticks from multi-indexed CSV + - Handles both file paths and DataFrames + +#### `market_trade/tests/` - Testing + +- Test files demonstrate usage patterns: + - `test_decision.py` - Shows complete decision manager workflow with retro training + - `test_dataloader.py` - Data loading tests + +### External Dependencies + +- **tinkoff-grpc** - Private GitHub repo for Tinkoff Invest API integration + - Located at: `git@github.com:strategy155/tinkoff_grpc.git` + - Used in tools for market data collection +- **Data Analysis**: pandas, numpy, scipy, matplotlib, plotly, mplfinance +- **Web Scraping**: requests-html, beautifulsoup4, selenium +- **Development**: JupyterLab (notebooks in `notebooks/`) + +## Key Constants (market_trade/constants.py) + +- `ROOT_PATH` - Project root directory +- `CANDLESTICK_DATASETS_PATH` - Path to candlestick data: `data/candlesticks/` +- `TEST_CANDLESTICKS_PATH` - Test dataset: `data/EURUSD_price_candlestick.csv` +- `TINKOFF_TOKEN_STRING` - Production API token (from .env) +- `SANDBOX_TOKEN_STRING` - Sandbox API token (from .env) +- `TINKOFF_API_ADDRESS` - API endpoint: 'invest-public-api.tinkoff.ru:443' + +## Data Formats + +### Candlestick Data +Expected DataFrame columns: +- `date` - Timestamp +- `open`, `high`, `low`, `close` - OHLC price data +- For bid/ask data: Multi-indexed with ('bid'/'ask', 'open'/'high'/'low'/'close') + +### Signal Configuration Dictionary +```python +{ + 'signal_name': { + 'className': signal_class, # e.g., sig_BB + 'indParams': {...}, # Indicator parameters + 'signalParams': { # Signal parameters + 'source': 'close', # Source price column + 'target': 'close' # Target price column for analysis + }, + 'batchSize': 30 # Window size + } +} +``` + +## Development Notes + +- Code contains Russian comments and variable names (e.g., "агрегатор", "индикаторы") +- Version 2 modules (`*_v2.py`) represent newer implementations +- The system uses sliding window approach for real-time signal generation +- Backtesting generates probability matrices that weight signal reliability +- Data symlink: `data/` -> `/var/data0/markettrade_data` \ No newline at end of file diff --git a/docs/trading-flow.md b/docs/trading-flow.md new file mode 100644 index 0000000..e69de29 diff --git a/market_trade/core/CoreDraw.py b/market_trade/core/CoreDraw.py index 7e5b680..cc45e26 100644 --- a/market_trade/core/CoreDraw.py +++ b/market_trade/core/CoreDraw.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import plotly.express as px diff --git a/market_trade/core/CoreTradeMath.py b/market_trade/core/CoreTradeMath.py new file mode 100644 index 0000000..e909eb9 --- /dev/null +++ b/market_trade/core/CoreTradeMath.py @@ -0,0 +1,137 @@ + +import pandas as pd +import datetime +import numpy as np +import plotly as pl +import plotly.graph_objs as go +import matplotlib.pyplot as plt +import math +import scipy +import random +import statistics + + +import datetime + + + + + +class CoreMath: + + def __init__(self, base_df, params=None): + default_params = { + 'dataType':'ohcl', + 'action': None, + 'actionOptions':{} + } + self.base_df=base_df.reset_index(drop=True) + self.params=params if params is not None else default_params + if self.params['dataType']=='ohcl': + self.col=self.base_df[self.params['actionOptions']['valueType']] + elif self.params['dataType']=='series': + self.col=self.base_df + self.ans=self.getAns() + + + def getAns(self): + ans=None + + if self.params['action']=='findExt': + ans = self.getExtremumValue() + elif self.params['action']=='findMean': + ans = self.getMeanValue() + elif self.params['action']=='findSTD': + ans=self.getSTD() + + + return ans + + + def getExtremumValue(self): + ans=None + ''' + actionOptions: + 'extremumtype': + 'min' + 'max' + 'valueType': + 'open' + 'close' + 'high' + 'low' + ''' + if self.params['actionOptions']['extremumtype']=='max': + ans=max(self.col) + + if self.params['actionOptions']['extremumtype']=='min': + ans=min(self.col) + + + return ans + + + def getMeanValue(self): + ''' + actionOptions: + 'MeanType': + 'MA' + 'SMA' + 'EMA' + 'WMA' + --'SMMA' + 'valueType': + 'open' + 'close' + 'high' + 'low' + 'window' + 'span' + 'weights' + ''' + ans=None + if self.params['actionOptions']['MeanType']=='MA': + ans = self.col.mean() + if self.params['actionOptions']['MeanType']=='SMA': + ans=np.convolve(self.col, np.ones(self.params['actionOptions']['window']), 'valid') / self.params['actionOptions']['window'] + #ans=self.col.rolling(window=self.params['actionOptions']['window']).mean().to_list() + + if self.params['actionOptions']['MeanType']=='EMA': + ans=self.col.ewm(span=self.params['actionOptions']['span'], adjust=False).mean().to_list() + if self.params['actionOptions']['MeanType']=='WMA': + try: + weights=self.params['actionOptions']['weights'] + except KeyError: + weights=np.arange(1,self.params['actionOptions']['window']+1) + ans=self.col.rolling(window=self.params['actionOptions']['window']).apply(lambda x: np.sum(weights*x) / weights.sum(), raw=False).to_list() + + + + return(ans) + + def getSTD(self): + ''' + actionOptions: + window + + + + + + ''' + + ans=None + + + try: + window=self.params['actionOptions']['window'] + ans=np.asarray([]) + for i in range(len(self.col)-window+1): + ans=np.append(ans,np.std(self.col[i:i+window], ddof=1)) + + except: + #window = len(self.col) + ans=np.std(self.col, ddof=1) + + return ans + \ No newline at end of file diff --git a/market_trade/core/Ind_ADX.py b/market_trade/core/Ind_ADX.py index 6afd4d9..70d8270 100644 --- a/market_trade/core/Ind_ADX.py +++ b/market_trade/core/Ind_ADX.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw init_notebook_mode() @@ -82,7 +82,7 @@ class ADXI: 'action':'findMean', 'actionOptions':{'MeanType':'EMA','span':10} } - ans=np.asarray(CoreTraidMath.CoreMath(ser,op).ans) + ans=np.asarray(CoreTradeMath.CoreMath(ser,op).ans) #print(ans) #ans = np.asarray(ser.ewm(span=40,adjust=False).mean().to_list()) #print(ans) diff --git a/market_trade/core/Ind_Alligator.py b/market_trade/core/Ind_Alligator.py index c734004..05e7e53 100644 --- a/market_trade/core/Ind_Alligator.py +++ b/market_trade/core/Ind_Alligator.py @@ -24,7 +24,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw @@ -46,7 +46,7 @@ class Alligator: 'valueType':self.options['valueType'], 'window':self.options[keyAns]['window']} } - ans=market_trade.core.CoreTraidMath.CoreMath(self.base_df,op).ans + ans=market_trade.core.CoreTradeMath.CoreMath(self.base_df,op).ans return ans diff --git a/market_trade/core/Ind_DonchianChannel.py b/market_trade/core/Ind_DonchianChannel.py index 7eb1a4c..a9540e3 100644 --- a/market_trade/core/Ind_DonchianChannel.py +++ b/market_trade/core/Ind_DonchianChannel.py @@ -24,7 +24,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw @@ -62,9 +62,9 @@ class IDC: } for i in range(self.options['window'],len(self.base_df)-self.options['shift']+1): - ans['MaxExt'].append(CoreTraidMath.CoreMath(self.base_df[i-self.options['window']:i],opMax).ans) + ans['MaxExt'].append(CoreTradeMath.CoreMath(self.base_df[i-self.options['window']:i],opMax).ans) ans['x'].append(self.base_df['date'][i-1+self.options['shift']]) - ans['MinExt'].append(CoreTraidMath.CoreMath(self.base_df[i-self.options['window']:i],opMin).ans) + ans['MinExt'].append(CoreTradeMath.CoreMath(self.base_df[i-self.options['window']:i],opMin).ans) return ans diff --git a/market_trade/core/Ind_Envelopes.py b/market_trade/core/Ind_Envelopes.py index f4b014d..318b925 100644 --- a/market_trade/core/Ind_Envelopes.py +++ b/market_trade/core/Ind_Envelopes.py @@ -24,7 +24,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw class Envelopes: @@ -64,7 +64,7 @@ class Envelopes: } if dictResp['MeanType']=='SMA': - y=market_trade.core.CoreTraidMath.CoreMath(self.base_df,op).ans + y=market_trade.core.CoreTradeMath.CoreMath(self.base_df,op).ans ans['MainEnv']=y[:len(y)-self.options['shift']] ans['PlusEnv']=ans['MainEnv']*(1+self.options['kProc']/100) ans['MinusEnv']=ans['MainEnv']*(1-self.options['kProc']/100) diff --git a/market_trade/core/Ind_Gator.py b/market_trade/core/Ind_Gator.py index 0814027..40bd783 100644 --- a/market_trade/core/Ind_Gator.py +++ b/market_trade/core/Ind_Gator.py @@ -24,7 +24,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw import market_trade.core.Ind_Alligator diff --git a/market_trade/core/Ind_Ishimoku.py b/market_trade/core/Ind_Ishimoku.py index 48f5a5e..b91eedb 100644 --- a/market_trade/core/Ind_Ishimoku.py +++ b/market_trade/core/Ind_Ishimoku.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots import market_trade.core.CoreDraw init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import plotly.express as px diff --git a/market_trade/core/Ind_LRI.py b/market_trade/core/Ind_LRI.py index b59c87a..6a2fe88 100644 --- a/market_trade/core/Ind_LRI.py +++ b/market_trade/core/Ind_LRI.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw diff --git a/market_trade/core/Ind_STD.py b/market_trade/core/Ind_STD.py index f63fca5..4d6cff4 100644 --- a/market_trade/core/Ind_STD.py +++ b/market_trade/core/Ind_STD.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw class ISTD: @@ -53,7 +53,7 @@ class ISTD: 'actionOptions':{'valueType':self.options['valueType']} } x=self.base_df['date'].to_list() - y= CoreTraidMath.CoreMath(self.base_df,op).ans + y= CoreTradeMath.CoreMath(self.base_df,op).ans ans={'y':y,'x':x} diff --git a/market_trade/core/Ind_Stochastic.py b/market_trade/core/Ind_Stochastic.py index 1d8a412..a6b60ee 100644 --- a/market_trade/core/Ind_Stochastic.py +++ b/market_trade/core/Ind_Stochastic.py @@ -25,7 +25,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw class Stochastic: @@ -69,7 +69,7 @@ class Stochastic: 'action':'findMean', 'actionOptions':{'MeanType':'SMA','window':self.options['windowSMA']} } - ans=np.asarray(market_trade.core.CoreTraidMath.CoreMath(ser,op).ans) + ans=np.asarray(market_trade.core.CoreTradeMath.CoreMath(ser,op).ans) return ans #return np.convolve(col, np.ones(self.options['windowSMA']), 'valid') /self.options['windowSMA'] diff --git a/market_trade/core/Ind_bollingerBands.py b/market_trade/core/Ind_bollingerBands.py index 251177d..ca23e25 100644 --- a/market_trade/core/Ind_bollingerBands.py +++ b/market_trade/core/Ind_bollingerBands.py @@ -24,7 +24,7 @@ from plotly.offline import init_notebook_mode, iplot from plotly.subplots import make_subplots init_notebook_mode() -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw @@ -50,12 +50,12 @@ class BB: 'window':self.options['window'] } } - ans['BB']=market_trade.core.CoreTraidMath.CoreMath(self.base_df,opMA).ans + ans['BB']=market_trade.core.CoreTradeMath.CoreMath(self.base_df,opMA).ans opSTD={'dataType':'ohcl', 'action':'findSTD', 'actionOptions':{'valueType':self.options['valueType'],'window':self.options['window']} } - ans['STD']=market_trade.core.CoreTraidMath.CoreMath(self.base_df,opSTD).ans + ans['STD']=market_trade.core.CoreTradeMath.CoreMath(self.base_df,opSTD).ans ans['pSTD']=ans['BB']+ans['STD']*self.options['kDev'] ans['mSTD']=ans['BB']-ans['STD']*self.options['kDev'] ans['x']=np.array(self.base_df['date'][self.options['window']-1:].to_list()) diff --git a/market_trade/core/indicators.py b/market_trade/core/indicators.py index a1b8cd4..c95069d 100644 --- a/market_trade/core/indicators.py +++ b/market_trade/core/indicators.py @@ -2,7 +2,7 @@ import pandas as pd import datetime import numpy as np -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw class coreIndicator(): @@ -99,12 +99,12 @@ class ind_BB(coreIndicator): 'window':self.options['window'] } } - ans['BB']=market_trade.core.CoreTraidMath.CoreMath(self.data,opMA).ans + ans['BB']=market_trade.core.CoreTradeMath.CoreMath(self.data,opMA).ans opSTD={'dataType':'ohcl', 'action':'findSTD', 'actionOptions':{'valueType':self.options['valueType'],'window':self.options['window']} } - ans['STD']=market_trade.core.CoreTraidMath.CoreMath(self.data,opSTD).ans + ans['STD']=market_trade.core.CoreTradeMath.CoreMath(self.data,opSTD).ans ans['pSTD']=ans['BB']+ans['STD']*self.options['kDev'] ans['mSTD']=ans['BB']-ans['STD']*self.options['kDev'] ans['x']=np.array(self.data['date'][self.options['window']-1:].to_list()) diff --git a/market_trade/core/indicators_v2.py b/market_trade/core/indicators_v2.py index 09b9ee9..61624d0 100644 --- a/market_trade/core/indicators_v2.py +++ b/market_trade/core/indicators_v2.py @@ -2,88 +2,158 @@ import pandas as pd import datetime import numpy as np -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath -class coreIndicator(): - - def __init__(self,options: dict, dataType: str = None, predictType: str = None, name: str = None): + +class CoreIndicator(): + """Base class for technical indicators. + + This class provides the foundation for implementing various technical + indicators used in trading signal generation. + """ + + def __init__(self, options: dict, data_type: str = None, predict_type: str = None, name: str = None): + """Initialize CoreIndicator with configuration options. + + Args: + options: Dictionary containing indicator-specific parameters. + data_type: Type of data to process (e.g., 'ohlc'). Defaults to None. + predict_type: Type of prediction to make (e.g., 'trend'). Defaults to None. + name: Optional identifier. Defaults to None. + """ self.options = options - self.dataType = dataType #ochl - self.predictType = predictType #trend - - - def getAns(self, data: pd.DataFrame() ): + self.data_type = data_type # ohlc + self.predict_type = predict_type # trend + + def get_answer(self, data: pd.DataFrame): + """Get indicator answer from data. + + Args: + data: DataFrame containing market data. + + Returns: + Calculated indicator values or "ERROR" if not implemented. + """ return "ERROR" - -class indicatorsAgrigator: - """ - indicators = { - 'ind_BB':{ - 'className':ind_BB, - 'params':{'MeanType':'SMA','window':15,'valueType':'close','kDev':2.5} - } - } - dataDic={ - 'ind_BB':df_candle[:1000] - } - - - """ - - def __init__ (self,indDict={}): - self.indDict = indDict - self.indInst = {} - self.ans={} - self.createIndicatorsInstance() - - def createIndicatorsInstance(self): - for i in self.indDict.keys(): - self.indInst[i]=self.indDict[i]['className'](self.indDict[i]['params']) - - def getAns(self,dataDict={}): - ans={} - for i in dataDict.keys(): - ans[i] = self.indInst[i].getAns(dataDict[i]) - return ans - -class ind_BB(coreIndicator): - """ - options - MeanType -> SMA - window -> int - valueType -> str: low, high, open, close - kDev -> float - - """ - - def __init__(self,options: dict,name = None): - super().__init__( - options = options, - dataType = 'ochl', - predictType = 'trend', - name = name - ) - - def getAns(self, data: pd.DataFrame()): - data=data.reset_index(drop=True) - ans={} - opMA={'dataType':'ohcl', - 'action':'findMean', - 'actionOptions':{ - 'MeanType':self.options['MeanType'], - 'valueType':self.options['valueType'], - 'window':self.options['window'] + + +class IndicatorsAggregator: + """Aggregates and manages multiple indicator instances. + + Example usage: + indicators = { + 'ind_BB': { + 'className': ind_BB, + 'params': {'MeanType': 'SMA', 'window': 15, 'valueType': 'close', 'kDev': 2.5} } } - ans['BB']=market_trade.core.CoreTraidMath.CoreMath(data,opMA).ans - opSTD={'dataType':'ohcl', - 'action':'findSTD', - 'actionOptions':{'valueType':self.options['valueType'],'window':self.options['window']} + data_dict = { + 'ind_BB': df_candle[:1000] } - ans['STD']=market_trade.core.CoreTraidMath.CoreMath(data,opSTD).ans - ans['pSTD']=ans['BB']+ans['STD']*self.options['kDev'] - ans['mSTD']=ans['BB']-ans['STD']*self.options['kDev'] - ans['x']=np.array(data['date'][self.options['window']-1:].to_list()) - self.ans= ans + aggregator = IndicatorsAggregator(indicators) + results = aggregator.get_answer(data_dict) + """ + + def __init__(self, ind_dict=None): + """Initialize aggregator with indicator dictionary. + + Args: + ind_dict: Dictionary mapping indicator names to configurations. + Defaults to empty dict if not provided. + """ + self.ind_dict = ind_dict if ind_dict is not None else {} + self.ind_instances = {} + self.ans = {} + self.create_indicators_instance() + + def create_indicators_instance(self): + """Create instances of all configured indicators.""" + for i in self.ind_dict.keys(): + self.ind_instances[i] = self.ind_dict[i]['className'](self.ind_dict[i]['params']) + + def get_answer(self, data_dict=None): + """Calculate answers from all indicators. + + Args: + data_dict: Dictionary mapping indicator names to their data. + Defaults to empty dict. + + Returns: + Dictionary of indicator results. + """ + if data_dict is None: + data_dict = {} + ans = {} + for i in data_dict.keys(): + ans[i] = self.ind_instances[i].get_answer(data_dict[i]) + return ans + + +class ind_BB(CoreIndicator): + """Bollinger Bands indicator implementation. + + Calculates Bollinger Bands using moving average and standard deviation. + + Required options: + MeanType: Type of moving average (e.g., 'SMA') + window: Period for calculations (int) + valueType: Price type to use ('low', 'high', 'open', 'close') + kDev: Standard deviation multiplier (float) + """ + + def __init__(self, options: dict, name=None): + """Initialize Bollinger Bands indicator. + + Args: + options: Configuration parameters dictionary. + name: Optional identifier. + """ + super().__init__( + options=options, + data_type='ohlc', + predict_type='trend', + name=name + ) + + def get_answer(self, data: pd.DataFrame): + """Calculate Bollinger Bands values. + + Args: + data: DataFrame with OHLC price data. + + Returns: + Dictionary containing: + - BB: Middle band (moving average) + - STD: Standard deviation + - pSTD: Upper band (BB + kDev * STD) + - mSTD: Lower band (BB - kDev * STD) + - x: Date array + """ + data = data.reset_index(drop=True) + ans = {} + + op_ma = { + 'dataType': 'ohcl', + 'action': 'findMean', + 'actionOptions': { + 'MeanType': self.options['MeanType'], + 'valueType': self.options['valueType'], + 'window': self.options['window'] + } + } + ans['BB'] = market_trade.core.CoreTradeMath.CoreMath(data, op_ma).ans + + op_std = { + 'dataType': 'ohcl', + 'action': 'findSTD', + 'actionOptions': { + 'valueType': self.options['valueType'], + 'window': self.options['window'] + } + } + ans['STD'] = market_trade.core.CoreTradeMath.CoreMath(data, op_std).ans + ans['pSTD'] = ans['BB'] + ans['STD'] * self.options['kDev'] + ans['mSTD'] = ans['BB'] - ans['STD'] * self.options['kDev'] + ans['x'] = np.array(data['date'][self.options['window']-1:].to_list()) + self.ans = ans return ans - \ No newline at end of file diff --git a/market_trade/core/signals.py b/market_trade/core/signals.py index 630b96e..2d80012 100644 --- a/market_trade/core/signals.py +++ b/market_trade/core/signals.py @@ -2,7 +2,7 @@ import pandas as pd import datetime import numpy as np -import market_trade.core.CoreTraidMath +import market_trade.core.CoreTradeMath import market_trade.core.CoreDraw from tqdm import tqdm diff --git a/market_trade/core/signals_v2.py b/market_trade/core/signals_v2.py index a2c77af..803004e 100644 --- a/market_trade/core/signals_v2.py +++ b/market_trade/core/signals_v2.py @@ -2,111 +2,172 @@ import pandas as pd import datetime import numpy as np -import market_trade.core.CoreTraidMath -#import market_trade.core.CoreDraw +import market_trade.core.CoreTradeMath from tqdm import tqdm -from market_trade.core.indicators_v2 import * +from market_trade.core.indicators_v2 import IndicatorsAggregator, ind_BB +class CoreSignalTrade: + """Base class for trading signals. -class coreSignalTrande: - - def __init__(self, name: str, req: dict, dataType: str): + Provides foundation for generating trading signals based on technical indicators. + """ + + def __init__(self, name: str, req: dict, data_type: str): + """Initialize signal generator. + + Args: + name: Signal identifier. + req: Configuration dictionary containing params and indicators. + data_type: Type of data to process (e.g., 'ohlc'). + """ self.name = name - self.agrigateInds = self.createIndicatorsInstance(req) + self.aggregate_indicators = self.create_indicators_instance(req) self.params = req['params'] - self.dataType = dataType - - - def createIndicatorsInstance(self,req: dict) -> dict: - return indicatorsAgrigator(req['indicators']) - - def getIndAns(self, dataDict: dict) -> dict: - return self.agrigateInds.getAns(dataDict) - - def getAns(self, data: pd.DataFrame(), indDataDict: dict) -> dict: - return self.getSigAns(data, self.getIndAns(indDataDict)) - - - -class sig_BB(coreSignalTrande): - """ - ind keys: - ind_BB - """ - - def __init__(self, name: str, req:dict): - super().__init__(name, req, 'ochl') - - def getSigAns(self, data: pd.DataFrame(), indAnsDict: dict) -> dict: + self.data_type = data_type - lastValue = data[self.params['source']].to_list()[-1] - if lastValue>indAnsDict['ind_BB']['pSTD'][-1]: - ans='down' - elif lastValue IndicatorsAggregator: + """Create indicators aggregator from configuration. + + Args: + req: Request dictionary containing indicators configuration. + + Returns: + IndicatorsAggregator instance. + """ + return IndicatorsAggregator(req['indicators']) + + def get_indicator_answer(self, data_dict: dict) -> dict: + """Get answers from all indicators. + + Args: + data_dict: Dictionary mapping indicator names to data. + + Returns: + Dictionary of indicator results. + """ + return self.aggregate_indicators.get_answer(data_dict) + + def get_answer(self, data: pd.DataFrame, ind_data_dict: dict) -> dict: + """Get signal answer from data and indicator results. + + Args: + data: Market data DataFrame. + ind_data_dict: Dictionary of indicator data. + + Returns: + Signal answer (direction). + """ + return self.get_signal_answer(data, self.get_indicator_answer(ind_data_dict)) + + +class sig_BB(CoreSignalTrade): + """Bollinger Bands signal generator. + + Generates trading signals based on Bollinger Bands indicator: + - 'up' when price is below lower band + - 'down' when price is above upper band + - 'none' when price is within bands + + Required indicator keys: + ind_BB: Bollinger Bands indicator """ - sigAgrReq = { - 'sig_BB':{ - 'className':sig_BB, - 'params':{'source':'close','target':'close'}, - 'indicators':{ - 'ind_BB':{ - 'className':ind_BB, - 'params':{'MeanType':'SMA','window':15,'valueType':'close','kDev':2.5} - } - } - }, - 'sig_BB_2':{ - 'className':sig_BB, - 'params':{'source':'close','target':'close'}, - 'indicators':{ - 'ind_BB':{ - 'className':ind_BB, - 'params':{'MeanType':'SMA','window':30,'valueType':'close','kDev':2} - } + + def __init__(self, name: str, req: dict): + """Initialize Bollinger Bands signal. + + Args: + name: Signal identifier. + req: Configuration dictionary. + """ + super().__init__(name, req, 'ohlc') + + def get_signal_answer(self, data: pd.DataFrame, ind_ans_dict: dict) -> str: + """Calculate signal from Bollinger Bands. + + Args: + data: Market data DataFrame. + ind_ans_dict: Dictionary containing indicator results. + + Returns: + Signal direction: 'up', 'down', or 'none'. + """ + last_value = data[self.params['source']].to_list()[-1] + if last_value > ind_ans_dict['ind_BB']['pSTD'][-1]: + ans = 'down' + elif last_value < ind_ans_dict['ind_BB']['mSTD'][-1]: + ans = 'up' + else: + ans = 'none' + + return ans + + +class SignalsAggregator: + """Aggregates and manages multiple signal generators. + + Example usage: + sig_config = { + 'sig_BB': { + 'className': sig_BB, + 'params': {'source': 'close', 'target': 'close'}, + 'indicators': { + 'ind_BB': { + 'className': ind_BB, + 'params': {'MeanType': 'SMA', 'window': 15, 'valueType': 'close', 'kDev': 2.5} + } + } } } - } - - sigAgrData = { - 'sig_BB':{ - 'signalData': df_candle[990:1000], - 'indicatorData' :{'ind_BB': df_candle[:1000]} - }, - 'sig_BB_2':{ - 'signalData': df_candle[990:1000], - 'indicatorData' :{'ind_BB': df_candle[:1000]} - } - } - - - + sig_data = { + 'sig_BB': { + 'signalData': df_candle[990:1000], + 'indicatorData': {'ind_BB': df_candle[:1000]} + } + } + aggregator = SignalsAggregator(sig_config) + results = aggregator.get_answer(sig_data) """ - - def __init__ (self,req:dict): - self.signals = self.createSignalsInstance(req) - - def createSignalsInstance(self, siganlsDict: dict) -> dict: + + def __init__(self, req: dict): + """Initialize signals aggregator. + + Args: + req: Dictionary mapping signal names to configurations. + """ + self.signals = self.create_signals_instance(req) + + def create_signals_instance(self, signals_dict: dict) -> dict: + """Create instances of all configured signals. + + Args: + signals_dict: Dictionary of signal configurations. + + Returns: + Dictionary of signal instances. + """ ans = {} - for i in siganlsDict.keys(): - ans[i]=siganlsDict[i]['className'](name = i, req = siganlsDict[i]) + for i in signals_dict.keys(): + ans[i] = signals_dict[i]['className'](name=i, req=signals_dict[i]) return ans - - def getAns(self, dataDict: dict) -> dict: + + def get_answer(self, data_dict: dict) -> dict: + """Calculate answers from all signals. + + Args: + data_dict: Dictionary mapping signal names to their data. + Each entry should contain 'signalData' and 'indicatorData'. + + Returns: + Dictionary of signal results. + """ ans = {} - for i in dataDict.keys(): - ans[i] = self.signals[i].getAns(data = dataDict[i]['signalData'], - indDataDict = dataDict[i]['indicatorData']) - return ans \ No newline at end of file + for i in data_dict.keys(): + ans[i] = self.signals[i].get_answer( + data=data_dict[i]['signalData'], + ind_data_dict=data_dict[i]['indicatorData'] + ) + return ans