Skip to content

Commit 5601aa9

Browse files
committed
Update readme and add class inspection
1 parent de6b3bb commit 5601aa9

File tree

5 files changed

+132
-18
lines changed

5 files changed

+132
-18
lines changed

README.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,38 @@ The Investing Algorithm Framework is a Python framework that enables swift and e
4545
- [ ] **AWS Lambda support (Planned)**: Stateless running for cloud function deployments in AWS.
4646
- [ ] **Azure App services support (Planned)**: deployments in Azure app services with Web UI.
4747

48+
## Quickstart
49+
50+
1. First install the framework using `pip`. The Investing Algorithm Framework is hosted on [PyPi](https://pypi.org/project/Blankly/).
51+
52+
```bash
53+
$ pip install investing-algorithm-framework
54+
```
55+
56+
2. Next, just run:
57+
58+
```bash
59+
$ investing-algorithm-framewor init
60+
```
61+
62+
or if you want the web version:
63+
64+
```bash
65+
$ investing-algorithm-framework init --web
66+
```
67+
> You can always change the app to the web version by changing the `app.py` file.
68+
69+
The command will create the file `app.py` and an example script called `strategy.py`.
70+
71+
From there, you start building your trading bot in the `strategy.py`.
72+
73+
More information can be found on our [docs](https://docs.blankly.finance)
74+
75+
> Make sure you leave the `app.py` file as is, as it is the entry point for the framework.
76+
> You can change the `bot.py` file to your liking and add other files to the working directory.
77+
> The framework will automatically pick up the files in the working directory.
78+
```
79+
4880
## Example implementation
4981
5082
The following algorithm connects to binance and buys BTC every 2 hours.
@@ -262,14 +294,6 @@ app.add_portfolio_configuration(
262294
We are continuously working on improving the performance of the framework. If
263295
you have any suggestions, please let us know.
264296
265-
## How to install
266-
267-
You can download the framework with pypi.
268-
269-
```bash
270-
pip install investing-algorithm-framework
271-
```
272-
273297
## Installation for local development
274298
275299
The framework is built with poetry. To install the framework for local development, you can run the following commands:

examples/backtest_example/run_backtest.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
from datetime import datetime, timedelta
55

66

7-
from investing_algorithm_framework import CCXTOHLCVMarketDataSource, \
8-
CCXTTickerMarketDataSource, PortfolioConfiguration, \
9-
create_app, pretty_print_backtest, BacktestDateRange, TimeUnit, \
10-
TradingStrategy, OrderSide, DEFAULT_LOGGING_CONFIG, Context
7+
from investing_algorithm_framework import (
8+
CCXTOHLCVMarketDataSource, CCXTTickerMarketDataSource, PortfolioConfiguration, create_app, pretty_print_backtest, BacktestDateRange, TimeUnit, TradingStrategy, OrderSide, DEFAULT_LOGGING_CONFIG, Context
9+
)
1110

1211
import tulipy as ti
1312

investing_algorithm_framework/app/algorithm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class is responsible for managing the strategies and executing
2222
them in the correct order.
2323
2424
Args:
25-
name (str): The name of the algorithm
25+
name (str): (Optional) The name of the algorithm
2626
description (str): The description of the algorithm
2727
context (dict): The context of the algorithm, for backtest
2828
references

investing_algorithm_framework/app/app.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(self, state_handler=None, name=None):
5555
self._on_after_initialize_hooks = []
5656
self._state_handler = state_handler
5757
self._name = name
58-
self._algorithm = Algorithm()
58+
self._algorithm = Algorithm(name=self.name)
5959

6060
@property
6161
def algorithm(self) -> Algorithm:
@@ -630,6 +630,8 @@ def run_backtest(
630630
output_directory=None,
631631
algorithm: Algorithm = None
632632
) -> BacktestReport:
633+
print(self.name)
634+
print(self.algorithm.name)
633635
"""
634636
Run a backtest for an algorithm. This method should be called when
635637
running a backtest.
@@ -696,8 +698,13 @@ def run_backtest(
696698
config[RESOURCE_DIRECTORY], "backtest_reports"
697699
)
698700

699-
backtest_service.write_report_to_json(
700-
report=report, output_directory=output_directory
701+
# backtest_service.write_report_to_json(
702+
# report=report, output_directory=output_directory
703+
# )
704+
backtest_service.save_report(
705+
report=report,
706+
algorithm=self.algorithm,
707+
output_directory=output_directory
701708
)
702709
return report
703710

@@ -815,9 +822,14 @@ def run_backtests(
815822
self.config[RESOURCE_DIRECTORY], "backtest_reports"
816823
)
817824

818-
backtest_service.write_report_to_json(
819-
report=report, output_directory=output_directory
825+
backtest_service.save_report(
826+
report=report,
827+
algorithm=algorithm,
828+
output_directory=output_directory
820829
)
830+
# backtest_service.write_report_to_json(
831+
# report=report, output_directory=output_directory
832+
# )
821833
reports.append(report)
822834

823835
return reports

investing_algorithm_framework/services/backtesting/backtest_service.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime, timedelta
22
import re
33
import os
4+
import inspect
45
import json
56
import pandas as pd
67
from dateutil import parser
@@ -683,6 +684,84 @@ def _is_backtest_report(self, path: str) -> bool:
683684

684685
return False
685686

687+
def save_report(
688+
self,
689+
report: BacktestReport,
690+
algorithm,
691+
output_directory: str
692+
) -> None:
693+
output_directory = os.path.join(output_directory, algorithm.name)
694+
collected_imports = set()
695+
class_definitions = []
696+
self.write_report_to_json(report, output_directory)
697+
698+
collected_imports = []
699+
class_definitions = []
700+
701+
for strategy in algorithm.strategies:
702+
cls = strategy.__class__
703+
file_path = inspect.getfile(cls)
704+
705+
if os.path.exists(file_path):
706+
with open(file_path, "r") as f:
707+
lines = f.readlines()
708+
709+
class_started = False
710+
class_code = []
711+
current_import = []
712+
713+
for line in lines:
714+
stripped_line = line.strip()
715+
716+
# Start collecting an import line
717+
if stripped_line.startswith(("import ", "from ")):
718+
current_import.append(line.rstrip())
719+
720+
# Handle single-line import directly
721+
if not stripped_line.endswith(("\\", "(")):
722+
collected_imports.append(" ".join(current_import))
723+
current_import = []
724+
725+
# Continue collecting multi-line imports
726+
elif current_import:
727+
current_import.append(line.rstrip())
728+
729+
# Stop when the multi-line import finishes
730+
if not stripped_line.endswith(("\\", ",")):
731+
collected_imports.append(" ".join(current_import))
732+
current_import = []
733+
734+
# Catch any unfinished import (just in case)
735+
if current_import:
736+
collected_imports.append(" ".join(current_import))
737+
738+
# Capture class definitions and functions
739+
if stripped_line.startswith("class ") or stripped_line.startswith("def "):
740+
class_started = True
741+
742+
if class_started:
743+
class_code.append(line)
744+
745+
if class_code:
746+
class_definitions.append("".join(class_code))
747+
748+
filename = os.path.join(
749+
output_directory,
750+
f"algorithm.py"
751+
)
752+
753+
# Save everything to a single file
754+
with open(filename, "w") as f:
755+
# Write unique imports at the top
756+
for imp in collected_imports:
757+
f.write(imp)
758+
759+
f.write("\n\n")
760+
761+
# Write class and function definitions
762+
for class_def in class_definitions:
763+
f.write(class_def + "\n\n")
764+
686765
def write_report_to_json(
687766
self, report: BacktestReport, output_directory: str
688767
) -> None:

0 commit comments

Comments
 (0)