For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
valid= is not working for sell orders.
-
Hello,
I'm trying to submit LIMIT sell orders and I want them to be expired at the end of the day.
I was trying to that by using this code. (I put "hours=1" to test code to see whether the order expires in one hour for now. )self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=(datetime.datetime.now()+datetime.timedelta(hours=1)),size=self.position.size)
But they are not expiring. Maybe it's related to TimeFrame. Maybe it's related to "runonce=False".
Here is the full code.
# -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. """ from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime # For datetime objects import os.path # To manage paths import sys # To find out the script name (in argv[0]) import math # Import the backtrader platform import backtrader as bt #Spyder plot problem. https://community.backtrader.com/topic/1911/cerebro-plot-does-not-work-with-anaconda-ipython/4 #import backtrader.plot #import matplotlib #matplotlib.use('QT5Agg') # code #cerebro.plot(iplot=False) import backtrader.plot import matplotlib matplotlib.use('QT5Agg') # Create a Cash Pool Indicator # Dynamic indicators # https://www.backtrader.com/blog/posts/2018-02-06-dynamic-indicator/dynamic-indicator/ class CashPool(bt.Indicator): lines = ('cash_pool',) def __init__(self): #print('CashPool Indicator: __init__') #setting to zero for now. self._cashpool = 0 self._tradeopen = False #Indicating whether there is a open position. def tradeopen(self,yesno): #print('CashBalance Indicator: tradeopen') self._tradeopen = yesno def setcashbalance(self,cashpool): #print('CashBalance Indicator: setting cash pool') self._cashpool = cashpool def next(self): #print('CashPool Indicator: next') if self._tradeopen: self.lines.cash_pool[0] = self.lines.cash_pool[-1] #print('Cash Pool (tradeopen) %.2f' % self.lines.cash_pool[0]) else : self.lines.cash_pool[0] = self._cashpool #print('Cash Pool (tradeopen=no) %.2f' % self.lines.cash_pool[0]) # Create a Stratey class IB_v1(bt.Strategy): params = ( ('division', 30.0), ('printlog', False), ) def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.datetime(0) #vol = self.datas[0].volume print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): print('strategy init') # Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close # To keep track of pending orders self.order = None # creating CashBalance indicator. # This keeps track of the balance. self.cp = CashPool() #setting cash balance #The cash is set from the main program. self.cp.setcashbalance(self.broker.getcash()) #Two different buying target #one at less than average. #one at below 1.05*average self.buytarget_avg = 0 self.buytarget_aboveavg = 0 def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do print("orders submitted/accepted") 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' % order.executed.price) self.log('BUY EXECUTED size, %.2f' % order.executed.size) self.log('Average price %.2f' % self.position.price) elif order.issell(): self.log('SELL EXECUTED, %.2f' % order.executed.price) self.log('SELL EXECUTED size, %.2f' % order.executed.size) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') # Write down: no pending order self.order = None if self.position.size == 0: #print('%.2f' % self.position) print('size %.2f' % self.position.size) print('price %.2f' % self.position.price) #self.cp.tradeopen(self.position.size, self.broker.getcash()) 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: #Check wether you can do the IB if self.cp[0]/self.params.division/2 >= self.dataclose[-1]: #print("yes! we can do this") #At 3:00(15:00) EST check the price of the target. if self.data.datetime.time() == datetime.time(15,00): print("yes! we are doing this.") self.log('Close, %.2f' % self.dataclose[0]) #print('cnt %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division) ) self.log('BUY submitting, %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division)) #print('price %.2f' % self.dataclose[0]) self.buy(size=math.ceil(self.cp[0]/self.dataclose[0]/self.params.division)) # Ok. now we are starting. else: #At 3:00(15:00) EST check the price of the target. if self.data.datetime.time() == datetime.time(15,00): #Buy some shares. if self.dataclose[0] <= self.position.price: #buy x/2 amount if the price is below average self.buy(size=math.ceil(self.cp[0]/self.dataclose[0]/self.params.division/2)) self.log('BUY submitting, %.2f' % math.ceil(self.cp[0]/self.dataclose[0]/self.params.division/2)) elif self.dataclose[0] <= self.position.price*1.05: #buy x/2 amount if the price is below 1.05*average self.buy(size=math.floor(self.cp[0]/self.dataclose[0]/self.params.division/2)) self.log('BUY submitting, %.2f' % math.floor(self.cp[0]/self.dataclose[0]/self.params.division/2)) else: print("Not buying this time.") #Now we are selling shares if the price is above 10% of average purchase price. #I'm putting order at 9:30AM #this is hourly data. 9:30 does not work. #It seems bt.Oder.DAY does not work. Use "0" instead. if self.data.datetime.time() == datetime.time(9,00): #print("putting a sell order") #print("current position %d" % self.getposition().size) if self.position.size > 0: self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=(datetime.datetime.now()+datetime.timedelta(hours=1)),size=self.position.size) #self.log('SELL submitting, %.2f' % self.position.price*1.1) self.log('SELL submitting, %.2f' % self.position.size) #if self.dataclose[0] < #self.buy(size=math.ceil(self.cp[0]/self.dataclose[-1]/self.params.division/2),price=self.dataclose[0]) #self.buy(size=math.floor(self.cp[0]/self.dataclose[-1]/self.params.division/2),price=self.dataclose[-1]*1.05) """ # Not yet ... we MIGHT BUY if ... if self.dataclose[0] < self.dataclose[-1]: # current close less than previous close if self.dataclose[-1] < self.dataclose[-2]: # previous close less than the previous close # BUY, BUY, BUY!!! (with default parameters) self.log('BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() """ # else: """ # Already in the market ... we might sell if len(self) >= (self.bar_executed + 5): # SELL, SELL, SELL!!! (with all possible default parameters) self.log('SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.sell() """ if __name__ == '__main__': # Create a cerebro entity # To create a dynamic indicator "runonce=Fale" is required. cerebro = bt.Cerebro(runonce=False) # Add a strategy cerebro.addstrategy(IB_v1) # Datas are in a subfolder of the samples. Need to find where the script is # because it could have been called from anywhere modpath = os.getcwd() datapath = os.path.join(modpath, 'data/TQQQ/TQQQ_1min.txt') #datapath = 'C:/Users/jinwo/OneDrive/BackTrader/data/TQQQ/TQQQ_1hour_sample.txt' # Create a Data Feed data = bt.feeds.GenericCSVData( dataname=datapath, # Do not pass values before this date fromdate=datetime.datetime(2021, 8, 2), # Do not pass values before this date todate=datetime.datetime(2021, 9, 1), # Do not pass values after this date openinterest = -1, #This is required to have non-daily data. #There is no hourly time frame #https://community.backtrader.com/topic/309/hourly-bars/4?_=1639874160786 timeframe=bt.TimeFrame.Minutes, #compression = 60, reverse=False) # Add the Data Feed to Cerebro cerebro.adddata(data) # Set our desired cash start cerebro.broker.setcash(10000.0) # Add a FixedSize sizer according to the stake cerebro.addsizer(bt.sizers.FixedSize, stake=10) # Set the commission cerebro.broker.setcommission(commission=0.0) # Print out the starting conditions print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Run over everything cerebro.run() # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Plot the result cerebro.plot(iplot=False,style='candlestick')
-
@jinwook-jang I think there is a bug. I copied the one of the example from the Quickstart. (It's plotting example.) I only changed a few lines to test Limit order with "valid=". It's clear the sell orders does not expire.
from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime # For datetime objects import os.path # To manage paths import sys # To find out the script name (in argv[0]) # Import the backtrader platform import backtrader as bt import backtrader.plot import matplotlib matplotlib.use('QT5Agg') # Create a Stratey class TestStrategy(bt.Strategy): params = ( ('maperiod', 15), ) def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.datetime(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close # To keep track of pending orders and buy price/commission self.order = None self.buyprice = None self.buycomm = None # Add a MovingAverageSimple indicator self.sma = bt.indicators.SimpleMovingAverage( self.datas[0], period=self.params.maperiod) # Indicators for the plotting show bt.indicators.ExponentialMovingAverage(self.datas[0], period=25) bt.indicators.WeightedMovingAverage(self.datas[0], period=25, subplot=True) bt.indicators.StochasticSlow(self.datas[0]) bt.indicators.MACDHisto(self.datas[0]) rsi = bt.indicators.RSI(self.datas[0]) bt.indicators.SmoothedMovingAverage(rsi, period=10) bt.indicators.ATR(self.datas[0], plot=False) 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') # Write down: no pending order self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('OPERATION PROFIT, 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: # Not yet ... we MIGHT BUY if ... if self.dataclose[0] > self.sma[0]: # BUY, BUY, BUY!!! (with all possible default parameters) self.log('BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() else: if self.dataclose[0] < self.sma[0]: # SELL, SELL, SELL!!! (with all possible default parameters) self.log('SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order #self.order = self.sell() self.order = self.sell(price=self.position.price*1.1,exectype=bt.Order.Limit,valid=datetime.datetime.now() + datetime.timedelta(minutes=30),size=self.position.size) if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(TestStrategy) # Datas are in a subfolder of the samples. Need to find where the script is # because it could have been called from anywhere modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, 'data/yfinance/TQQQ/TQQQ.csv') # Create a Data Feed data = bt.feeds.YahooFinanceCSVData( dataname=datapath, # Do not pass values before this date fromdate=datetime.datetime(2021, 7, 2), # Do not pass values before this date todate=datetime.datetime(2021, 12, 10), # Do not pass values after this date sessionend=datetime.time(17, 30), reverse=False) # Add the Data Feed to Cerebro cerebro.adddata(data) # Set our desired cash start cerebro.broker.setcash(100000.0) # Add a FixedSize sizer according to the stake cerebro.addsizer(bt.sizers.FixedSize, stake=10) # Set the commission cerebro.broker.setcommission(commission=0.0) # Print out the starting conditions print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Run over everything cerebro.run() # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Plot the result cerebro.plot(iplot=False,style='candlestick')
-
@jinwook-jang I think I found the problem. I think it's my code. datetime.datetime.now() is giving wrong expiration date.