USE THIS SOFTWARE AT YOUR OWN RISK. THE AUTHOR ASSUMES NO LIABILITY FOR YOUR TRADING OUTCOMES.
It is important to note that this project is under active development and was developed as an experiment. Currently, only yahooFinance API is supported but contributions are welcome!
On default, backtest run on a 1d
candles. The other possible intervals are limited to the data API you are using. Noted that, this Application on default runs on yahoo finance
.
in yfinance
'1m','2m','5m','15m','30m','60m','90m', '1h','1d','5d','1wk','1mo','3mo'
class ExampleModel(BaseModel):
def __init__(self):
super().__init__()
# ...
self.universe_management.setIntervals(['5m'])
# ...
class ExampleModel(BaseModel):
def __init__(self):
super().__init__()
# ...
self.universe_management.setIntervals(['5m', '1d'])
# ...
If you have already a list of equities you would be backtesting, you can simply put the list of Tickers inside the model as below.
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.equities = ['NVDA', 'SPY', 'TSLA']
# ...
If your model select the tickers from requirements or from a other resources, you implement your custom equity selection model as below. I showed a model reading csv
file in below.
class ReadCSVEquitySelectionModel():
def __init__(): # can used for pre-prompting info
# ... do something
def prepare_equities(self): # must be this method name
df = pd.read_csv('xxx.csv')
return df['Ticker']
class ExampleModel (BaseModel):
def __init__(self):
super.__init__()
# ...
self.equity_selection_management = ReadCSVEquitySelectionModel()
# ...
What you can do, mostly you can it in Equity Selection Model.
The software uses yfinance
in default setting. If you want it to link to other services, you have to customise it. The API store the fetched data in csv format.
# API Logic Implemenation (located in lib/APIs.py)
# Very ugly but don't judge :')
class BaseAPIModel:
def __init__(self):
self.data_columns = ['Date', 'Adj Close', 'Close', 'High', 'Low', 'Open', 'Volume']
def set_model_data_path(self, model_path):
self.model_data_path = os.path.join(model_path, "data")
def get_model_data_path(self):
return self.model_data_path
def set_data_columns(self, columns):
self.data_columns = columns
def get_data_columns(self):
return self.data_columns
def get_equities_data(self, equities, intervals, start_date, end_date):
if len(equities) == 0:
return []
failed_equities = set()
for interval in intervals:
df = yf.download(" ".join(equities), start=start_date, end=end_date, interval=interval).swaplevel(0, 1, axis=1)
for name in equities:
if name in shared._ERRORS:
failed_equities.add(name)
else:
path = os.path.join(self.model_data_path, f"{name}-{interval}.csv")
df[name].to_csv(path)
return list(failed_equities)
There are two pre-built portfolio managment in this system. The first one is a full allocation portfolio. The system would put 100% cash on the first equity allocation. The second is a percentage allocation portfolio. The system would put x% remaining cash on the next equity allocation.
# 1. ./lib/portfolioAllocations/BasePortfolioAllocation
# 2. ./prebuilt/portfolioAllocations/PercentagePortfolioAllocation
You can customise your own Portfolio Model in such a way.
class YourCustomPortfolioAllocation(BasePortfolioAllocation):
def __init__(self, capital, percent):
super().__init__(capital)
self.percent = round(float(percent) / 100.0, 4)
def allocate(self, portfolio): # Must be the same method name
allocation = round(self.free_capital * self.percent, 2)
self.free_capital = round(self.free_capital - allocation, 2)
return allocation
# On Usage for the Model
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.portfolio_management = YourCustomPortfolioAllocation(1000, 20)
# ...
The model includes several pre-built Alpha. I would implement more in the future.
# 1. BaseHold
# 2. BaseShort
# 3. EMAGoldenCrossSignal
# On Usage for the Model
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.strategies = BaseHold()
# ...
# On Usage for the Model
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.strategies = BaseShort()
# ...
Default Model does not use any risk control Mechanism. Prebuilt there is two models for usage as listed below.
# 1. ./prebuilt/riskManagement/TakeProfitStopLoss
# 2. ./prebuilt/riskManagement/TraillingStopLoss
# On Usage for the Model
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.risk_control = TakeProfitStopLoss(200, 30)
# ...
# On Usage for the Model
class ExampleModel(BaseModel):
def __init__(self):
super.__init__()
# ...
self.risk_control = TraillingStopLoss(30)
# ...
Buy when candle is 7 days low and above SMA 200. Sell when candle is at 7 days high.
class Double7SignalModel(BaseModel):
def __init__(self):
super().__init__()
self.start_date = "2024-09-21"
self.end_date = "2024-10-21"
self.universe_management.setIntervals(['5m'])
self.model_name = 'ETH-USD Double 7 Signal Model'
self.equities = ['ETH-USD']
self.alpha_model = Double7Signal()
BackTest(Double7SignalModel).run()