refactor/code-style-standardization #1

Open
strategy155 wants to merge 6 commits from refactor/code-style-standardization into dev
18 changed files with 612 additions and 193 deletions
Showing only changes of commit bbba7bfe89 - Show all commits

151
CLAUDE.md Normal file
View File

@ -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/<script_name>.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`

0
docs/trading-flow.md Normal file
View File

View File

@ -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

View File

@ -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
Review

bad practice

bad practice
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

View File

@ -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)
Review

Horrible naming.

Horrible naming.
#print(ans)
#ans = np.asarray(ser.ewm(span=40,adjust=False).mean().to_list())
#print(ans)

View File

@ -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

View File

@ -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)
Review

All the operations like this should be decopupled, the variabels inside should be named properly, the keys in the dicts should be explicit and moved to constants. Along all places.

All the operations like this should be decopupled, the variabels inside should be named properly, the keys in the dicts should be explicit and moved to constants. Along all places.
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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
Review

Quite a bad naming here.

Quite a bad naming here.
ans={'y':y,'x':x}

View File

@ -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']

View File

@ -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())

View File

@ -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())

View File

@ -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"
Review

not normal, should be put to constant, or even better this system should be revound.

not normal, should be put to constant, or even better this system should be revound.
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:
Review

Probably you also need to comment on the calsss tacttrbute,s per rgoogle codestyle no?

Probably you also need to comment on the calsss tacttrbute,s per rgoogle codestyle no?
"""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 {}
Review

whait is this.

whait is this.
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
Review

all about it is horrible.

all about it is horrible.
class ind_BB(CoreIndicator):
Review

NAMING!

NAMING!
"""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
)
Review

some problems I see here as welll.

some problems I see here as welll.
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
Review

it is better, but please use normal intermediate variables operations, and the constants as keys. OR maybe to pydantic dataclasses we go?

it is better, but please use normal intermediate variables operations, and the constants as keys. OR maybe to pydantic dataclasses we go?
return ans

View File

@ -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

View File

@ -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<indAnsDict['ind_BB']['mSTD'][-1]:
ans='up'
else:
ans='none'
return ans
class signalsAgrigator:
def create_indicators_instance(self, req: dict) -> IndicatorsAggregator:
"""Create indicators aggregator from configuration.
Args:
req: Request dictionary containing indicators configuration.
Returns:
IndicatorsAggregator instance.
"""
return IndicatorsAggregator(req['indicators'])
Review

is it a goodpattersn thoug'h, maybe we can formalise the dispatching scheme better?

is it a goodpattersn thoug'h, maybe we can formalise the dispatching scheme better?
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
Review

maybe more enums for things like this one?

maybe more enums for things like this one?
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])
Review

bloody hell

bloody hell
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
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