Problems with buy/sell signals
-
I have been messing with this for quite a while, and can't seem to get it to buy/sell when I want it to. Most of the time the problem I end up with is it just sells really fast for every buy. I got some of this code from a forum and it looks very complete so I thought it would be good to build on top of it, and it appears you set self.buysig to 1 to buy and -1 to sell, but it doesn't seem to be working this way. Please let me know if you need anymore info. If you want to run it, I think the KST indicator is in backtrader, but if it isn't I can send it. I just put it in an indicators folder so I can modify it easily later on. Thanks in advance for any help, it seems like some sort of syntax error with the selling but not really sure.
import alpaca_backtrader_api import backtrader as bt from datetime import datetime import pandas as pd import os import math from indicators.indicators import * # Your credentials here ALPACA_API_KEY = 'PK2BD1DG3DLG9L73XACC' ALPACA_SECRET_KEY = 'IjxUeI0nIDxiS8zcOR/JCDVtQbnGrEU3Q0yvUrFR' # change to True if you want to do live paper trading with Alpaca Broker. # False will do a back test TRADE_LIVE = False TICKER_SYMBOL = 'TEVA' CHART_RESOLUTION = 60 # minutes per candle BACKTEST_START_MONTH = 1 BACKTEST_START_DAY = 1 BACKTEST_START_YEAR = 2019 # Create a Strategy class The_Machine(bt.Strategy): def log(self, txt, dt=None): dt = dt or self.data.datetime[0] dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # Easy access for candle data self.dataopen = self.datas[0].open self.datahigh = self.datas[0].high self.datalow = self.datas[0].low self.dataclose = self.datas[0].close self.datavolume = self.datas[0].volume # To keep track of pending orders and buy price/commission self.order = None self.buyprice = None self.buycomm = None self.knowSureThing = KnowSureThing(self.data) self.kst = self.knowSureThing.l.kst self.buysig = 0 self.sellsig = 0 self.buysig = bt.ind.CrossOver(self.kst, 0.00) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enough cash if order.status in [order.Completed]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) def next(self): # Simply log the closing price of the series from the reference self.log('Close, %.2f' % self.dataclose[0]) # Check if an order is pending ... if yes, we cannot send a 2nd one if self.order: return # Check if we are in the market if not self.position: # buy signal if self.buysig > 0: # BUY self.log('BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() elif self.sellsig > 0: # if len(self) >= (self.bar_executed + self.params.exitbars): # Sell self.log('SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.sell(size=1000) # else: # return else: return else: # in a position if self.buysig < 0: # Buy Closed self.log('BUY CLOSE, %.2f' % self.dataclose[0]) self.order = self.close() self.order = self.sell(size=1000) # elif ignore zero case elif self.sellsig < 0: # Sell Closed self.log('SELL CLOSE, %.2f' % self.dataclose[0]) self.order = self.close() self.order = self.buy() else: return class maxRiskSizer(bt.Sizer): ''' Returns the number of shares rounded down that can be purchased for the max risk tolerance ''' params = (('risk', 0.98),) # this will be something like 0.05 when run on multiple stocks def __init__(self): if self.p.risk > 1 or self.p.risk < 0: raise ValueError('The risk parameter is a percentage which must be' 'entered as a float. e.g. 0.5') def _getsizing(self, comminfo, cash, data, isbuy): if isbuy == True: size = math.floor((cash * self.p.risk) / data[0]) return size if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(The_Machine) # Create a Data Feed store = alpaca_backtrader_api.AlpacaStore( key_id=ALPACA_API_KEY, secret_key=ALPACA_SECRET_KEY, paper=True, usePolygon=True ) DataFactory = store.getdata # or use alpaca_backtrader_api.AlpacaData if TRADE_LIVE: data = DataFactory(dataname=TICKER_SYMBOL, timeframe=bt.TimeFrame.Minutes, compression = CHART_RESOLUTION, historical=False ) broker = store.getbroker() cerebro.setbroker(broker) # or just alpaca_backtrader_api.AlpacaBroker() else: data = DataFactory(dataname=TICKER_SYMBOL, historical=True, fromdate=datetime( BACKTEST_START_YEAR, BACKTEST_START_MONTH, BACKTEST_START_DAY), timeframe=bt.TimeFrame.Minutes, compression=CHART_RESOLUTION) # set backtest cash cerebro.broker.setcash(20000.0) # Add the Data Feed to Cerebro cerebro.adddata(data) # Resampler for chart resolution cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=CHART_RESOLUTION) # Add sizer cerebro.addsizer(maxRiskSizer) print('Starting Portfolio Value: {}'.format(cerebro.broker.getvalue())) cerebro.run() print('Final Portfolio Value: {}'.format(cerebro.broker.getvalue())) cerebro.plot()
-
@Wayne-Filkins-0 Wow I really wish I could edit my post to take the key codes out....they should really add editing to this, or deleting because now I can't remove them.
-
This is what it does on the backtest, looks like it just buys then sells instantly or something [link text](
link url)
-
@Wayne-Filkins-0 said in Problems with buy/sell signals:
not really sure
If you develop something, you need to understand what is going on in your script. This script will be applied to your money, do you really want to lose your money?
Evidently the only picture shown doesn't say what is going on. It shows just two almost coincident triangles appeared several times.
What prevents you from printing of prices, indicator values etc, plotting of necessary indicators, then going from trade to trade along the backtest history and figure out what is going on?
-
I did all that. What I'm saying is that there seems to be something wrong with the buy/sell syntax. I have tried several different methods of selling and it does this every time. I tried even doing a counter and just selling 50 candles later....even that doesn't work. So the selling syntax seems to be wrong. The last project did the target buy/sell method and I had problems with that too. I can't seem to get backtrader to buy/sell properly in any of my projects using any of the various methods. So I thought that maybe people who have gotten it working could maybe tell me what I'm doing wrong.
-
I know that the problem isn't with the indicators or any of that because I printed those out, and based off those values, self.buysig is being set to the correct variables on every candle. That's not the problem. So it's something in the buying/selling. I can't pull the methods from the other project because it used target buy/sell and had this random thing where it would just short...for no reason....in a strategy with only going long. It would short and stay in short trade the entire time...it made zero sense. I had no code telling it to ever go short.
I don't know why you said "This script will be applied to your money, do you really want to lose your money?". I'm just backtesting. I'm working on it...I'm trying to FIX it lol and better understand why it's not working and how to get it working, but I'm stuck I just can't figure this out.
-
@Wayne-Filkins-0 said in Problems with buy/sell signals:
self.buysig is being set to the correct variables on every candle.
Did you compare what your script is doing for different
self.buysig
values with your expectations of what should be done?Is it your expectation that you open long position based on sizer if
self.buysig > 0
, and close this long position and short 1000 stocks whenself.buysig < 0
? And then you just close short position and open it again on eachself.buysig <0
? -
I was assuming it opens position when buysig = 1 and closes position when buysig = -1, but I tried a bunch of other variations and just can't get it to do what I want . I don't want to short at all. I just want to buy when crossover, and sell when crossunder. That's not the extent of my strategy, I'm just trying to get it working properly before I move on because I noticed that it wasn't doing what it should be doing
-
@Wayne-Filkins-0 said in Problems with buy/sell signals:
I don't want to short at all.
than why do you sell 1000 shares in your code?
-
Because I can't sell any other way. If I don't put size I get:
UnboundLocalError: local variable 'size' referenced before assignment
I don't now how else to sell the shares. I don't want to just buy shares and then hold them until I die.
-
Everything else I try leads to syntax errors. How do you sell after you buy something? I have it so that it's buying on crossover, and so I buy shares. I now have shares. I want to sell shares on crossunder. I have it all set up but how do I sell them? self.sell() gives error.
-
@ab_trader Wait I think I see what you mean
-
@ab_trader YES I think I got it working. I didn't realize there was self.close AND self.sell. I thought one of them would close an open order, not position but like if you put in an order and it doesn't go through or something...I thought it was just a backup thing.
-
if you would truly do what i've recommend above (print and plot all data and look thru it bar by bar) you would see two notifications of sell orders much earlier. and (probably) only one notification of the buy order.
anyway, since you have very light understanding of the basic things (buy nd sell), i would recommend you to read docs and maybe do quickstart one more time (or even first time). will save you time later.