@davidavr thanks a lot, the problem wasn't so complicated afterall
@run-out thank you for the pointer, I'll consult it
@davidavr thanks a lot, the problem wasn't so complicated afterall
@run-out thank you for the pointer, I'll consult it
@run-out Thanks a lot for your code. Was great to learn that I was setting the buy price incorrectly.
However, the most confusing issue still stands: the RSI indicator, which I initialize but do NOT use (intentionally, at least), is somehow necessary for the code to run (if you comment it out on your code, the code breaks). Also, the script waits for the RSI period (14) to be over before starting to search for the buy signal. I don't understand it...
Regarding the take profit limit price of .8%... I don't get why it would be a problem. In fact it leads to a higher p&l than 5% on this very code. Also, I ran a live bot for some time with that take profit and it was profitable, and I selected it after running my own simple backtest on different options for the price. Can I not trust Backtrader backtesting results if I use .8% as the limit price?
@run-out thank you. I couldn't reproduce the problem again (that's how lost I am), but in my attempts I realized some things:
An indicator (RSI) I still had from the template code I had copy-pasted is influencing the strategy even though I don't use it anywhere after declaring it, and in fact I can't even run the code if I comment it out. I get the message "ValueError: min() arg is an empty sequence"
I'm trying to trade when the price hits a 3-hour low, placing a bracket order with that price as the buy price, and a take profit 0.8% higher and a stop loss 5% lower. That buy signal of mine apparently only starts being searched for after the period of the RSI is over... Couldn't be more lost
The order notification (and the transaction analyzer, sometimes) shows <backtrader.linebuffer.LineBuffer object at ...> instead of the price for the buy order only, showing a decimal number, as expected, for the other ones
Also, that price of the buy order, that I can't see as I explained above, is apparently set as the low price of the NEXT candle after the buy signal is found (I expected it to be set to the current candle's low and only buy if the price is met on the next candle)
I'm rather stunned at how little sense I can make of anything that is happening. Here is the full code:
import backtrader as bt
from datetime import datetime
import backtrader.analyzers as btanalyzers
import yfinance
class firstStrategy(bt.Strategy):
def __init__(self):
self.min_profit_sell = 0.008
self.stop_loss = .05
self.rsi = bt.indicators.RSI_SMA(self.data.close, period=14)
self.orefs = []
def notify_order(self, order):
print('{}: Order ref: {} / Type {} / Status {} / Size {} / Price {}'.format(
self.data.datetime.datetime(0),
order.ref, 'Buy' * order.isbuy() or 'Sell',
order.getstatusname(),
round(order.size, 2),
'NA' if not order.price else order.price))
if order.status == order.Completed:
self.holdstart = len(self)
if not order.alive() and order.ref in self.orefs:
self.orefs.remove(order.ref)
def next(self):
self.interval_low = min(self.data.low.get(size=3))
print (self.data.low.get(size=1))
if not self.position:
if self.data.low == self.interval_low:
limitprice = self.data.low * (1 + self.min_profit_sell)
print (f'Limit buy price: {self.data.low.get(size=1)[0]}')
print (f'Take profit price: {limitprice}')
stopprice = self.data.low * (1 - self.stop_loss)
print (f'Stop loss price: {stopprice}')
os = self.buy_bracket(size=100, limitprice=limitprice, price=self.data.low, stopprice=stopprice)
self.orefs = [o.ref for o in os]
#Variable for our starting cash
startcash = 10000
#Create an instance of cerebro
cerebro = bt.Cerebro()
#Add our strategy
cerebro.addstrategy(firstStrategy)
a = yfinance.download(tickers="VGAC",period="max",interval="60m", groupby='ticker', prepost=True)
data = bt.feeds.PandasData(dataname=a, name='VGAC')
#Add the data to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(startcash)
# save a CSV with results
cerebro.addwriter(bt.WriterFile, csv=True, out='backtest.csv')
cerebro.addanalyzer(btanalyzers.Transactions, _name = "trans")
cerebro.addanalyzer(btanalyzers.TradeAnalyzer, _name = "trades")
# Run over everything
back = cerebro.run()
#Get final portfolio Value
portvalue = cerebro.broker.getvalue()
pnl = portvalue - startcash
Thanks a lot to you or anyone who can point me in the right direction to not give up on this library before even really starting
I'm trying to use the Pandas Data Feed to load data and run a strategy. I load it like this:
import yfinance
a = yfinance.download(tickers="VGAC",period="max",interval="60m", groupby='ticker', prepost=True)
data = bt.feeds.PandasData(dataname=a, name='VGAC')
I add a transactions analyzer and run the strategy like this:
cerebro.addanalyzer(btanalyzers.Transactions, _name = "trans")
back = cerebro.run()
And I then look at the transactions like this:
back[0].analyzers.trans.get_analysis()
To my surprise there are many transactions happening with a datetime that isn't in the source data. For instance, a transaction at (datetime.datetime(2020, 11, 25, 19, 30), when that value isn't in the source data (the "a" dataframe) datetime column.
Does backtrader automatically fill gaps in candles? Or what is happening here? Couldn't find references to this in the documentation. After simplifying my tests by orders of magnitude in an attempt to obtain a result I actually understand and expect, I still can't. Surely it is because I lack in intellectual capacity, as apparently after several years of coding without major hassles, I discover a library that somehow is super easy to use for everyone else but makes me the most lost I've ever been when trying to tackle any problem, ever.