├── babydaviddagi.png ├── test.py ├── template.env ├── requirements.txt ├── backtest.py ├── telegramBot.py ├── README.md └── babydaviddagi.py /babydaviddagi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daviddme/BabyDaviddAGI/HEAD/babydaviddagi.png -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from babydaviddagi import BabyDaviddAGI 2 | 3 | bda = BabyDaviddAGI( 4 | openai_api_key="", 5 | pinecone_api_key="", 6 | pinecone_environment="", 7 | pinecone_table_name="" 8 | ) 9 | idea = bda.brainstorm_agent("RSI-MACD Crossover with MACD Above 20") 10 | print(idea) 11 | print("="*20) 12 | code = bda.coder_agent(idea) 13 | print(code) 14 | print("="*20) 15 | print(bda.backtester_agent(code)) 16 | print("="*20) 17 | print(bda.optimizer_agent()) 18 | 19 | # with open("./backtest.py", "r") as f: 20 | # bda.code = f.read() 21 | # print(bda.optimizer_agent()) -------------------------------------------------------------------------------- /template.env: -------------------------------------------------------------------------------- 1 | # cp template.env. .env 2 | # Edit your .env file with your own values 3 | # Don't commit your .env file to git/push to GitHub! 4 | # Don't modify/delete .env.example unless adding extensions to the project 5 | # which require new variable to be added to the .env file 6 | 7 | # API CONFIG 8 | OPENAI_API_KEY=sk- 9 | OPENAI_API_MODEL=gpt-3.5-turbo # alternatively, gpt-4, text-davinci-003, etc 10 | PINECONE_API_KEY= 11 | PINECONE_ENVIRONMENT=us-east1-gcp 12 | 13 | # TABLE CONFIG 14 | TABLE_NAME=strategy 15 | 16 | # PROJECT CONFIG 17 | OBJECTIVE=Code a RSI cross over strategy 18 | # For backwards compatibility 19 | # FIRST_TASK can be used instead of INITIAL_TASK 20 | INITIAL_TASK=Develop a task list and a clear outline of the project 21 | 22 | # Extensions 23 | # List additional extensions to load (except .env.example!) 24 | DOTENV_EXTENSIONS= 25 | # Set to true to enable command line args support 26 | ENABLE_COMMAND_LINE_ARGS=false 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.4 2 | aiosignal==1.3.1 3 | anyio==3.6.2 4 | async-timeout==4.0.2 5 | attrs==22.2.0 6 | Backtesting==0.3.3 7 | bokeh==3.1.0 8 | certifi==2022.12.7 9 | charset-normalizer==3.1.0 10 | colorama==0.4.6 11 | contourpy==1.0.7 12 | dnspython==2.3.0 13 | frozenlist==1.3.3 14 | h11==0.14.0 15 | httpcore==0.16.3 16 | httpx==0.23.3 17 | idna==3.4 18 | Jinja2==3.1.2 19 | loguru==0.7.0 20 | MarkupSafe==2.1.2 21 | multidict==6.0.4 22 | numpy==1.24.2 23 | openai==0.27.4 24 | packaging==23.1 25 | pandas==2.0.0 26 | Pillow==9.5.0 27 | pinecone-client==2.2.1 28 | python-dateutil==2.8.2 29 | python-dotenv==1.0.0 30 | python-telegram-bot==20.2 31 | pytz==2023.3 32 | PyYAML==6.0 33 | requests==2.28.2 34 | rfc3986==1.5.0 35 | six==1.16.0 36 | sniffio==1.3.0 37 | TA-Lib==0.4.26 38 | tornado==6.2 39 | tqdm==4.65.0 40 | typing_extensions==4.5.0 41 | tzdata==2023.3 42 | urllib3==1.26.15 43 | win32-setctime==1.1.0 44 | xyzservices==2023.2.0 45 | yarl==1.8.2 46 | -------------------------------------------------------------------------------- /backtest.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | warnings.filterwarnings("ignore") 3 | import talib 4 | from backtesting import Strategy, Backtest 5 | from backtesting.test import GOOG 6 | 7 | class SmaCross(Strategy): 8 | # Define indicator variables 9 | n1 = 12 10 | n2 = 26 11 | n3 = 9 12 | 13 | def init(self): 14 | # Define indicator variables using talib 15 | self.macd, self.macd_signal, self.macd_hist = self.I(talib.MACD, self.data.Close, self.n1, self.n2, self.n3) 16 | self.rsi = self.I(talib.RSI, self.data.Close, timeperiod=14) 17 | 18 | def next(self): 19 | # Check buy and sell conditions 20 | if self.macd[-1] > 20 and self.macd[-1] > self.macd_signal[-1] and self.macd[-2] < self.macd_signal[-2] and self.rsi[-1] > 50: 21 | self.buy() 22 | 23 | elif self.position and (self.macd[-1] < self.macd_signal[-1] or self.macd[-2] > self.macd_signal[-2] or self.rsi[-1] < 50): 24 | self.sell() 25 | 26 | bt = Backtest(GOOG, SmaCross, cash=10_000, commission=.002) 27 | stats = bt.run() 28 | 29 | stats = bt.optimize(n1=range(6, 24, 5), n2=range(13, 52, 5), n3=range(4, 18, 3)) 30 | # Print final statistics 31 | print("Final Equity: ", stats["Equity Final [$]"]) 32 | print("Peak Equity ", stats["Equity Peak [$]"]) 33 | print("Return Rate: ", stats["Return [%]"]) 34 | print("Buy and hold return rate: ", stats["Buy & Hold Return [%]"]) 35 | print("Sharpe Ratio: ", stats["Sharpe Ratio"]) 36 | print("Max Drawdown Rate: ", stats["Max. Drawdown [%]"]) 37 | print("Trade Count", stats["# Trades"]) -------------------------------------------------------------------------------- /telegramBot.py: -------------------------------------------------------------------------------- 1 | from babydaviddagi import BabyDaviddAGI 2 | import logging 3 | from telegram import Update 4 | from telegram.ext import Application, CommandHandler, ContextTypes 5 | from dotenv import dotenv_values 6 | 7 | config = dotenv_values("./.env") 8 | 9 | # Enable logging 10 | logging.basicConfig( 11 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO 12 | ) 13 | 14 | 15 | async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: 16 | """Sends explanation on how to use the bot.""" 17 | await update.message.reply_text("Hi! Use /run ") 18 | 19 | 20 | async def run(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: 21 | """Starts Bot""" 22 | bda = bda = BabyDaviddAGI( 23 | openai_api_key = config["OPENAI_API_KEY"], 24 | pinecone_api_key = config["PINECONE_API_KEY"], 25 | pinecone_environment = config["PINECONE_ENVIRONMENT"], 26 | pinecone_table_name = config["PINECONE_TABLENAME"] 27 | ) 28 | bda.start(" ".join(context.args)) 29 | await update.message.reply_text(bda.idea) 30 | await update.message.reply_text(bda.code) 31 | await update.message.reply_text(bda.optimizer_result) 32 | await update.message.reply_text(bda.analyst_result) 33 | 34 | 35 | async def babydaviddagi(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: 36 | """Start BabyDaviddAGI with detail information""" 37 | bda = bda = BabyDaviddAGI( 38 | openai_api_key = config["OPENAI_API_KEY"], 39 | pinecone_api_key = config["PINECONE_API_KEY"], 40 | pinecone_environment = config["PINECONE_ENVIRONMENT"], 41 | pinecone_table_name = config["PINECONE_TABLENAME"] 42 | ) 43 | bda.brainstorm_agent(user_specs=" ".join(context.args)) 44 | await update.message.reply_text(bda.idea) 45 | 46 | bda.coder_agent() 47 | await update.message.reply_text(bda.code) 48 | 49 | bda.backtester_agent() 50 | await update.message.reply_text(bda.bactest_result) 51 | 52 | bda.optimizer_agent() 53 | await update.message.reply_text(bda.optimizer_result) 54 | 55 | await update.message.reply_text(bda.analyst_agent()) 56 | 57 | 58 | def main() -> None: 59 | """Run bot.""" 60 | # Create the Application and pass it your bot's token. 61 | application = ( 62 | Application.builder() 63 | .token(config["TELEGRAM_BOT_TOKEN"]) 64 | .build() 65 | ) 66 | 67 | # on different commands - answer in Telegram 68 | application.add_handler(CommandHandler(["start", "help"], start)) 69 | application.add_handler(CommandHandler("babydaviddagi", babydaviddagi)) 70 | application.add_handler(CommandHandler("run", run)) 71 | 72 | # Run the bot until the user presses Ctrl-C 73 | application.run_polling() 74 | 75 | 76 | if __name__ == "__main__": 77 | main() 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

⭐ BabyDaviddAGI ⭐

2 | 3 | A huge thanks @uysalibov how is a coding genius!! 4 | [AI Trading Bots](https://daviddtech.com) 5 | [Free TradingView indicators](https://daviddtech.com/indicators) 6 |

7 | 8 |

9 | 10 | Inspired from [@babyagi](https://github.com/yoheinakajima/babyagi). 11 | 12 | 13 | - The BabyDaviddAGI class is a Python implementation of an Artificial General Intelligence (AGI) system that utilizes OpenAI's GPT model and Pinecone for generating profitable trading strategies. It can brainstorm ideas, generate Python code, perform backtesting, optimize strategies, and analyze results. 14 | 15 | ## Agents 16 | * **Brainstorm Agent**: Generates brainstorm idea based on user agent 17 | * **Coder Agent**: Generates Python code with using braistorm idea 18 | * **Backtest Agent**: Backtest generated python code using `backtesting.py` 19 | * **Optimize Agent**: Find better settings for generated strategy 20 | * **Analyst Agent**: Analyse all results and give 1-10 point for generated strategy 21 | 22 | ### Installation 23 | To use BabyDaviddAGI, you need to install dependencies: 24 | ```sh 25 | pip install -r requirements.txt 26 | ``` 27 | 28 | ### Telegram Bot Usage 29 | - Add your credenticals to `.env` file 30 | - Run the bot ```python telegramBot.py``` 31 | 32 | ### Example 33 | 34 | #### Basic Example 35 | ```py 36 | bda = bda = BabyDaviddAGI( 37 | openai_api_key = "open_ai_key", 38 | pinecone_api_key = "pinecone_key", 39 | pinecone_environment = "asia-southeast1-gcp", 40 | pinecone_table_name = "babydaviddagi", 41 | ) 42 | bda.start("RSI-MACD Crossover with MACD Above 20") 43 | print(bda.idea) 44 | print(bda.code) 45 | print(bda.backtest_result) 46 | print(bda.optimizer_result) 47 | print(bda.analyst_result) 48 | ``` 49 | 50 | - Difference of `start` function is it returns back to `brainstorm_agent` if analyst rate smaller than 5. 51 | 52 | #### Initial Task 53 | ```py 54 | >>> bda = BabyDaviddAGI( 55 | openai_api_key = "open_ai_key", 56 | pinecone_api_key = "pinecone_key", 57 | pinecone_environment = "asia-southeast1-gcp", 58 | pinecone_table_name = "babydaviddagi", 59 | ) 60 | 61 | ``` 62 | 63 | #### Brainstorm Idea 64 | ```py 65 | >>> bda.brainstorm_agent("RSI-MACD Crossover with MACD Above 20") 66 | >>> bda.idea 67 | """One potential trading strategy based on the RSI-MACD Crossover with MACD above 20 could involve identifying stocks with a history of strong momentum and a tendency to trend upward. 68 | When the MACD line crosses above the signal line and the RSI is above 50, a long position could be taken. 69 | Conversely, when the MACD line crosses below the signal line and the RSI is below 50, a short position could be taken. 70 | This strategy could be further optimized by using stop-loss orders to limit potential losses and taking profits at predetermined levels. 71 | Additionally, monitoring news and market trends could provide valuable insights into potential shifts in stock prices, allowing for quick adjustments to the trading strategy.""" 72 | ``` 73 | 74 | #### Generated Code 75 | ```py 76 | >>> bda.coder_agent() 77 | >>> bda.code 78 | import warnings 79 | warnings.filterwarnings("ignore") 80 | import talib 81 | from backtesting import Strategy, Backtest 82 | from backtesting.test import GOOG 83 | 84 | class RsiMacdCrossover(Strategy): 85 | # Define indicator variables here which you will give parameter in self.I function. 86 | n1 = 12 87 | n2 = 26 88 | n3 = 9 89 | n4 = 50 90 | 91 | def init(self): 92 | # Define every indicator here as a class variable in init function. 93 | # Don't forget every indicator have to be in self.I function. 94 | self.macd, self.macd_signal, self.macd_hist = self.I(talib.MACD, self.data.Close, self.n1, self.n2, self.n3) 95 | self.rsi = self.I(talib.RSI, self.data.Close, self.n4) 96 | 97 | def next(self): 98 | # Check buy and sell conditions 99 | # When the MACD line crosses above the signal line and the RSI is above 50, a long position could be taken. 100 | if self.macd > self.macd_signal and self.rsi > 50: 101 | self.buy() 102 | # When the MACD line crosses below the signal line and the RSI is below 50, a short position could be taken. 103 | elif self.macd < self.macd_signal and self.rsi < 50: 104 | self.sell() 105 | 106 | bt = Backtest(GOOG, RsiMacdCrossover, cash=10_000, commission=.002) 107 | stats = bt.run() 108 | 109 | # Print the backtest results 110 | print("Final Equity: ", stats["Equity Final [$]"]) 111 | print("Peak Equity ", stats["Equity Peak [$]"]) 112 | print("Return Rate: ", stats["Return [%]"]) 113 | print("Buy and hold return rate: ", stats["Buy & Hold Return [%]"]) 114 | print("Sharpe Ratio: ", stats["Sharpe Ratio"]) 115 | print("Max Drawdown Rate: ", stats["Max. Drawdown [%]"]) 116 | print("Trade Count", stats["# Trades"]) 117 | ``` 118 | 119 | #### Backtest Result 120 | ```py 121 | >>> bda.backtester_agent() 122 | >>> bda.backtest_result 123 | "Final Equity: 39931.122 124 | Peak Equity 40383.622 125 | Return Rate: 299.31122 126 | Buy and hold return rate: 703.4582419772772 127 | Sharpe Ratio: 0.4401023836593147 128 | Max Drawdown Rate: -65.2224463139885 129 | Trade Count 1" 130 | ``` 131 | 132 | #### Optimizer Code 133 | ```py 134 | >>> bda.optimizer_agent() 135 | # Add the following code into the backtest code 136 | stats = bt.optimize(n1=range(6, 24, 5), n2=range(13, 52, 5), n3=range(4, 18, 5), n4=range(25, 100, 7)) 137 | 138 | ``` 139 | 140 | #### Optimizer Result 141 | ```py 142 | >>> bda.optimizer_agent() 143 | >>> bda.optimizer_result 144 | "Final Equity: 41182.3528 145 | Peak Equity 41297.388399999996 146 | Return Rate: 311.823528 147 | Buy and hold return rate: 703.4582419772772 148 | Sharpe Ratio: 0.4605294109801069 149 | Max Drawdown Rate: -65.04024963221835 150 | Trade Count 4 151 | " 152 | ``` 153 | 154 | #### Analyst Result 155 | ```js 156 | >>> print(bda.analyst_agent()) 157 | { 158 | 'rate': 8, 159 | 'summary': `The backtest results on original settings show a decent return rate and a positive Sharpe ratio, indicating that the strategy has potential. 160 | However, the max drawdown rate is concerning and suggests that the strategy may be risky. 161 | The backtest results on optimized settings show a slightly better return rate and Sharpe ratio, but the increase in trade count raises concerns about potential overfitting. 162 | Further analysis and testing are needed to determine the effectiveness of this strategy on crypto markets.` 163 | } 164 | 165 | ``` 166 | 167 | 168 | Made with love by [AI Trading Bots](https://daviddtech.com) & [@DaviddTech](https://www.youtube.com/@DaviddTech). 169 | 170 | 171 | -------------------------------------------------------------------------------- /babydaviddagi.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Union, List 2 | import logging, time, subprocess, sys, re, random, json, uuid 3 | import openai 4 | import pinecone 5 | 6 | 7 | 8 | 9 | class BabyDaviddAGI: 10 | def __init__( 11 | self, 12 | openai_api_key: str, 13 | pinecone_api_key: str, 14 | openai_api_model: str = "gpt-3.5-turbo", 15 | pinecone_environment: str = "asia-southeast1-gcp", 16 | pinecone_table_name: str = "babydaviddagi", 17 | objective: str = "profitable trading strategy", 18 | ) -> None: 19 | """ 20 | Initialize the BabyDaviddAGI instance with the provided API keys, model name, environment, table name, and objective. 21 | 22 | Args: 23 | openai_api_key (str): The API key for OpenAI. 24 | pinecone_api_key (str): The API key for Pinecone. 25 | openai_api_model (str, optional): The model name for OpenAI API. Default is "gpt-3.5-turbo". 26 | pinecone_environment (str, optional): The environment for Pinecone. Default is "asia-southeast1-gcp". 27 | pinecone_table_name (str, optional): The table name for Pinecone index. Default is "babydaviddagi". 28 | objective (str, optional): The objective of the trading strategy. Default is "profitable trading strategy". 29 | 30 | Returns: 31 | None 32 | """ 33 | logging.basicConfig( 34 | format="%(asctime)s : \u001b[33m%(levelname)s\u001b[0m : \033[91m\033[1m%(message)s\u001b[0m", 35 | datefmt="%m/%d/%Y %I:%M:%S", 36 | ) 37 | self._openai_api_key = openai_api_key 38 | self._pinecone_api_key = pinecone_api_key 39 | self._openai_api_model = openai_api_model 40 | self._pinecone_environment = pinecone_environment 41 | self._pinecone_table_name = pinecone_table_name 42 | 43 | openai.api_key = self._openai_api_key 44 | 45 | pinecone.init( 46 | api_key=self._pinecone_api_key, environment=self._pinecone_environment 47 | ) 48 | if self._pinecone_table_name not in pinecone.list_indexes(): 49 | pinecone.create_index( 50 | self._pinecone_table_name, 51 | dimension=1536, 52 | metric="cosine", 53 | pod_type="p1", 54 | ) 55 | self.pine_index = pinecone.Index(self._pinecone_table_name) 56 | self.OBJECTIVE = objective 57 | 58 | self.idea = "" 59 | self.code = "" 60 | self.bactest_result = "" 61 | self.optimizer_result = "" 62 | self.analyst_result: Dict[str, str] = {} 63 | 64 | if "gpt-4" in self._openai_api_model.lower(): 65 | logging.warn("Be Carefully! You're using GPT-4. Monitor your costs") 66 | 67 | def start(self, user_prompt:str) -> None: 68 | """ 69 | Start the BabyDaviddAGI pipeline with the provided user prompt. 70 | 71 | Args: 72 | user_prompt (str): The user prompt to initiate the pipeline. 73 | 74 | Returns: 75 | None 76 | """ 77 | while True: 78 | self.brainstorm_agent(user_specs=user_prompt) 79 | self.coder_agent() 80 | self.backtester_agent() 81 | self.optimizer_agent() 82 | if self.analyst_agent()["rate"] >= 5: 83 | break 84 | 85 | def brainstorm_agent(self, user_specs: str) -> str: 86 | """ 87 | Generate brainstorming ideas for a given user specification using OpenAI's GPT model. 88 | 89 | Args: 90 | user_specs (str): User specification for brainstorming ideas. 91 | 92 | Returns: 93 | str: Generated brainstorming ideas. 94 | 95 | """ 96 | last_datas = self.context_agent(query=user_specs, n=5) 97 | 98 | prompt = f""" 99 | You are a task creation AI responsible for brainstorming idea for {self.OBJECTIVE} based on {user_specs}, 100 | The last generated ideas: {data['idea'] for data in last_datas}. 101 | Results of that ideas: {data['result'] for data in last_datas}. 102 | Don't overlap with last ideas. Idea dont exceed 150 word""" 103 | self.idea = self.__openai_call(prompt) 104 | return self.idea 105 | 106 | def coder_agent(self) -> str: 107 | """ 108 | Generate Python code using brainstorm idea and OpenAI's GPT model. 109 | 110 | Returns: 111 | str: Generated Python Code. 112 | 113 | """ 114 | prompt = f""" 115 | You are advance level Python developer who performs one task based on the following objective: {self.OBJECTIVE}. 116 | Your task: {self.idea}. 117 | I created template python code, i will provide to you. Create indicator variables using talib. 118 | Here is the my code. Edit this code. I wrote comments to change. Define indicators like i provide and edit if elif conditions but dont touch other codes. 119 | Also don't use complicated conditions and don't create a new class. 120 | Return just python code. 121 | # you can add new import from backtesting but you can't delete any import from here 122 | import talib 123 | from backtesting import Strategy, Backtest 124 | from backtesting.test import GOOG 125 | 126 | class SmaCross(Strategy): 127 | # Create indicator variables here which you will give parameter in self.I function. 128 | n1 = 30 129 | n2 = 50 130 | def init(self): 131 | # edit here: define every indicator here as a class variable. Don't forget every indicator have to be in self.I function. 132 | # also dont give name and dont create indicator variable in init function all variables have to be defined before this function 133 | self.sma1 = self.I(talib.SMA, self.data.Close, self.n1) 134 | self.sma2 = self.I(talib.RSI, self.data.Close, self.n2) 135 | 136 | def next(self): 137 | # edit if conditon: check buy conditions example provided here 138 | # buy the asset 139 | if self.sma1 > self.sma2: 140 | self.buy() 141 | 142 | # edit elif condition: check sell conditions example provided here 143 | # sell the asset 144 | elif (self.sma1 < n2): 145 | self.sell() 146 | 147 | bt = Backtest(GOOG, SmaCross, cash=10_000, commission=.002) 148 | stats = bt.run() 149 | 150 | # dont change or delete after here 151 | print("Final Equity: ", stats["Equity Final [$]"]) 152 | print("Peak Equity ", stats["Equity Peak [$]"]) 153 | print("Return Rate: ", stats["Return [%]"]) 154 | print("Buy and hold return rate: ", stats["Buy & Hold Return [%]"]) 155 | print("Sharpe Ratio: ", stats["Sharpe Ratio"]) 156 | print("Max Drawdown Rate: ", stats["Max. Drawdown [%]"]) 157 | print("Trade Count", stats["# Trades"]) 158 | """ 159 | self.code = self.__openai_call(prompt, temperature=0.7, max_tokens=2000) 160 | return self.code 161 | 162 | def backtester_agent(self) -> str: 163 | """ 164 | Perform backtesting of a trading strategy using the code generated by the coder agent. 165 | 166 | Returns: 167 | str: Results of the backtesting. 168 | 169 | """ 170 | code = 'import warnings\nwarnings.filterwarnings("ignore")\n' + self.code 171 | self.code = code 172 | with open("./backtest.py", "w") as fp: 173 | fp.write(code) 174 | 175 | try: 176 | output = ( 177 | subprocess.run( 178 | [sys.executable, "./backtest.py"], capture_output=True, check=True 179 | ) 180 | .stdout.decode("utf-8") 181 | .replace("\n", " ") 182 | .replace("\r", "") 183 | ) 184 | except subprocess.CalledProcessError as e: 185 | output = e.stderr 186 | self.bactest_result = str(output) 187 | return self.bactest_result 188 | 189 | def optimizer_agent(self): 190 | """ 191 | Perform optimization of backtested results using the parameters generated by the optimizer generator. 192 | 193 | Returns: 194 | str: Results of the optimization. 195 | 196 | """ 197 | params = self.__optimizer_generator() 198 | func_params = ", ".join([f"{item[0]}={item[1]}" for item in params.items()]) 199 | 200 | func = f"stats = bt.optimize({func_params})" 201 | 202 | contents = self.code.splitlines() 203 | 204 | contents.insert(len(contents) - 8, func) 205 | 206 | with open("./backtest.py", "w") as f: 207 | contents = "\n".join(contents) 208 | f.write(contents) 209 | 210 | try: 211 | output = ( 212 | subprocess.run( 213 | [sys.executable, "./backtest.py"], capture_output=True, check=True 214 | ) 215 | .stdout.decode("utf-8") 216 | .replace("\n", " ") 217 | .replace("\r", "") 218 | ) 219 | except subprocess.CalledProcessError as e: 220 | output = e.stderr 221 | self.optimizer_result = str(output) 222 | return self.optimizer_result 223 | 224 | def analyst_agent(self): 225 | """ 226 | Act like a data analyst, evaluate the backtest results for a trading strategy, and provide a grade from 1 to 10, 227 | where 1 indicates a bad result and 10 indicates a good result. Also, analyze for potential overfitting of the strategy. 228 | Returns the analyst's evaluation as a JSON object. 229 | 230 | Returns: 231 | dict: Analyst's evaluation in the form of a JSON object with keys 'rate' and 'summary'. 232 | """ 233 | prompt = f""" 234 | Act like a data analyst, you are working for a large quant trading firm. 235 | I have created the "{self.idea}" strategy. 236 | I want you to look at the results and grade them from 1 to 10, 1 being bad and 10 being good. 237 | My objective is to trade this on crypto markets. 238 | Please analyst for potential overfitting. 239 | Backtest results on original settings : "{self.bactest_result}" 240 | Backtest results on optimised results : "{self.optimizer_result}". 241 | I want result as just json. For example {{ 242 | "rate": # your point, 243 | "summary": # explain why you give that point 244 | }} 245 | """ 246 | analyst_point = json.loads(self.__openai_call(prompt)) 247 | 248 | vector = self.__get_embedding(self.idea) 249 | self.pine_index.upsert( 250 | vectors=[ 251 | ( 252 | uuid.uuid4().hex, 253 | vector, 254 | {"idea": self.idea, "result": analyst_point["summary"]} 255 | ) 256 | ], 257 | namespace=self.OBJECTIVE 258 | ) 259 | self.analyst_result = analyst_point 260 | return analyst_point 261 | 262 | def context_agent(self, query: str, n: int): 263 | """ 264 | Retrieve context from the Pine database based on a given query and number of responses. 265 | 266 | Args: 267 | query (str): Query to search for in the database. 268 | n (int): Number of responses to retrieve. 269 | 270 | Returns: 271 | list: List of tuples containing the idea and result metadata from the Pine database. 272 | """ 273 | embedded_vectors = self.__get_embedding(query) 274 | query_responses = self.pine_index.query( 275 | embedded_vectors, top_k=n, include_metadata=True, namespace=self.OBJECTIVE 276 | ) 277 | sorted_responses = sorted( 278 | query_responses.matches, key=lambda x: x.score, reverse=True 279 | ) 280 | return [ 281 | (str(response.metadata["idea"]), str(response.metadata["result"])) 282 | for response in sorted_responses 283 | ] 284 | 285 | def __openai_call( 286 | self, 287 | prompt: str, 288 | temperature: Optional[float] = 0.5, 289 | max_tokens: Optional[int] = 200, 290 | ) -> str: 291 | """ 292 | Call the OpenAI API to generate text based on the given prompt using the configured model. 293 | 294 | Args: 295 | prompt (str): The prompt to generate text from. 296 | temperature (Optional[float]): The temperature parameter for controlling the randomness of the generated text. 297 | Defaults to 0.5. 298 | max_tokens (Optional[int]): The maximum number of tokens in the generated text. Defaults to 200. 299 | 300 | Returns: 301 | str: The generated text from the OpenAI API response. 302 | """ 303 | try: 304 | messages = [{"role": "system", "content": prompt}] 305 | response = openai.ChatCompletion.create( 306 | model=self._openai_api_model, 307 | messages=messages, 308 | temperature=temperature, 309 | max_tokens=max_tokens, 310 | n=1, 311 | stop=None, 312 | ) 313 | return response.choices[0].message.content.strip() 314 | except openai.error.RateLimitError: 315 | logging.warn( 316 | "OpenAI API RateLimit has been exceeded. Waiting 10 second and trying again." 317 | ) 318 | time.sleep(10) 319 | return self.__openai_call(prompt, temperature, max_tokens) 320 | except Exception as e: 321 | logging.critical(f"Unknown error while OpenAPI Call: {e}") 322 | return "" 323 | 324 | def __get_embedding(self, text: str, model: str = "text-embedding-ada-002") -> List[float]: 325 | """ 326 | Get the embedding vector for the given text using the specified OpenAI embedding model. 327 | 328 | Args: 329 | text (str): The input text to get the embedding vector for. 330 | model (str): The name of the OpenAI embedding model to use. Defaults to "text-embedding-ada-002". 331 | 332 | Returns: 333 | List[float]: The embedding vector for the given text. 334 | """ 335 | text = text.replace("\n", " ") 336 | return openai.Embedding.create(input=[text], model=model)["data"][0][ 337 | "embedding" 338 | ] 339 | 340 | def __optimizer_generator(self) -> Dict: 341 | """ 342 | Generate optimizer data from the code string. 343 | 344 | Returns: 345 | Dict: A dictionary containing optimizer data extracted from the code string. 346 | """ 347 | datas = {} 348 | for line in self.code.splitlines(): 349 | find = re.search(r".* = [0-9]+", line) 350 | if find: 351 | find_var = find.group(0).strip().split(" = ") 352 | datas[find_var[0]] = range( 353 | int(int(find_var[1]) / 2), 354 | int(find_var[1]) * 2, 355 | random.choice([3, 5, 7]), 356 | ) 357 | 358 | if datas == {}: 359 | count = 0 360 | temp_data = [] 361 | temp = self.code.splitlines() 362 | for num, line in enumerate(self.code.splitlines()): 363 | find = re.search(r"(self.I)(.*)", line) 364 | if find: 365 | find_var = find.group(2).split(",")[2:] 366 | data = {} 367 | for var in find_var: 368 | var = var.replace("(", "").replace(")", "") 369 | if "=" in var: 370 | data[f"n{count}"] = var.split("=")[1].strip() 371 | else: 372 | data[f"n{count}"] = var.strip() 373 | 374 | count += 1 375 | temp_data.append(data) 376 | temp[ 377 | num 378 | ] = f'{temp[num].split("=", 1)[0]} = self.I{find.group(2).split(",")[0]}, {find.group(2).split(",")[1]}, {", ".join([f"self.{x[0]}" for x in data.items()])})' 379 | 380 | for num, line in enumerate(self.code.splitlines()): 381 | if "class" in line: 382 | for variables in temp_data: 383 | for var in variables.items(): 384 | temp.insert(num + 1, f" {var[0]} = {var[1]}") 385 | 386 | self.code = "\n".join(temp) 387 | return self.__optimizer_generator() 388 | 389 | return datas 390 | --------------------------------------------------------------------------------