I just switched from Matlab to python and even newer to the backtrader library for backtestingtrading strategies. My questions might seem obvious.
My problem seems similar to this :
https://community.backtrader.com/topic/2857/wanted-exit-long-and-open-short-on-the-same-bar-and-vice-versa
and this :
https://community.backtrader.com/topic/2797/self-close-does-not-clear-position
The code below is a simple MACD strategy.
Here is the code :
# -*- coding: utf-8 -*-
"""
"""
import backtrader as bt
import argparse
import backtrader.feeds as btFeeds
import numpy as np
import yfinance as yf
import pandas as pd
import talib
class SimpleMACDStrat(bt.Strategy):
def __init__(self):
#Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.order = None
def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print(f'{dt.isoformat()} {txt}')
#Print date and close
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('LONG EXECUTED, %.2f' % order.executed.price)
elif order.issell():
self.log('SELL EXECUTED, %.2f' % order.executed.price)
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 next(self):
self.log("Close: '{0}'" .format(self.data.adj_close[0]))
print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0],
self.data.high[0],self.data.low[0],
self.data.close[0],self.data.adj_close[0],
self.data.volume[0],self.data.EMA_100[0],
self.data.RSI[0], self.data.CCI[0],
self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0]))
if self.order:
return
if self.data.MACD_hist[0]>0:
if self.position.size<0 and self.data.MACD_hist[-1]<0 :
self.close()
self.log('CLOSE SHORT POSITION, %.2f' % self.dataclose[0])
elif self.position.size==0:
self.order=self.buy()
self.log('OPEN LONG POSITION, %.2f' % self.dataclose[0])
elif self.data.MACD_hist[0]<0:
if self.position.size>0 and self.data.MACD_hist[-1]>0:
self.order=self.close()
self.log('CLOSE LONG POSITION, %.2f' % self.dataclose[0])
elif self.position.size==0:
self.order=self.sell()
self.log('OPEN SHORT POSITION, %.2f' % self.dataclose[0])
print('')
class BasicIndicatorsFeeded(btFeeds.PandasData):
lines = ('Indexx', 'adj_close', 'EMA_100', 'RSI', 'CCI', 'MACD_macd', 'MACD_sign', 'MACD_hist',)
params = ( ('Indexx', 0), ('adj_close', 5), ('volume', 6),
('EMA_100', 7), ('RSI', 8), ('CCI', 9),
('MACD_macd', 10), ('MACD_sign', 11), ('MACD_hist', 12),)
if __name__ == '__main__':
cerebro = bt.Cerebro()
#Add data feed to Cerebro
data1 = yf.download("AAPL",start="2021-08-09", end="2021-12-21",group_by="ticker")
data1.insert(0,'Indexx',' ')
data1['Indexx']=range(len(data1))
data1['EMA_100']=talib.EMA(data1['Adj Close'],100)
data1['RSI']=talib.RSI(data1['Adj Close'],14)
data1['CCI']=talib.CCI(data1['High'], data1['Low'], data1['Adj Close'], timeperiod=14)
data1['MACD_macd']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[0]
data1['MACD_sign']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[1]
data1['MACD_hist']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[2]
# data1['Long_position']
# Run Cerebro Engine
cerebro.broker.setcash(8000000000)
start_portfolio_value = cerebro.broker.getvalue()
cerebro.addstrategy(SimpleMACDStrat)
data = BasicIndicatorsFeeded(dataname=data1)
cerebro.adddata(data)
cerebro.run()
cerebro.plot()
# print(data1)
print('-------------------')
#print('%f' %data)
# print(data)
end_portfolio_value = cerebro.broker.getvalue()
pnl = end_portfolio_value - start_portfolio_value
print(f'Starting Portfolio Value: {start_portfolio_value:2f}')
print(f'Final Portfolio Value: {end_portfolio_value:2f}')
print(f'PnL: {pnl:.2f}')
Here are the results :
results
On 2021-11-10, macd_hist goes from postive to negative. We are expecting that the next day( 2021-11-11):
a)the long position is closed and right after and
b)a short position is opened
1)We see that a) is actually closed the same day. Isn't it supposed to happen the next ?
2)Also a sell is executed the day after, which is not supposed to happen.
Any suggestion for 1) and 2) would be more then welcome. Thanks.
Abbe
EDIT :
Btw, I'm aware the idea can be coded that way (only def next) :
def next(self):
#print('%f' % (self.datas[0].Indexxx[0])
self.log("Close: '{0}'" .format(self.data.adj_close[0]))
print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0],
self.data.high[0],self.data.low[0],
self.data.close[0],self.data.adj_close[0],
self.data.volume[0],self.data.EMA_100[0],
self.data.RSI[0], self.data.CCI[0],
self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0]))
if self.order:
return
print(self.position)
if self.data.MACD_hist[0]>0 and self.data.MACD_hist[-1]<0:
self.order=self.buy()
self.log('CLOSE SHORT POSITION and open long, %.2f' % self.dataclose[0])
if self.data.MACD_hist[0]<0 and self.data.MACD_hist[-1]>0:
self.order=self.sell()
self.log('CLOSE LONG POSITION and open short, %.2f' % self.dataclose[0])
print('')
But I really want to separate the
self.close()
and for instance the
self.buy()
That would allow me later to use different conditions for closing a position and opening one.
Thanks a lot for any inputs, ideas, remarks.
Abbe
In your code you are showing the following:
if self.data.MACD_hist[0]>0:
if self.position.size<0 and self.data.MACD_hist[-1]<0 :
self.close()
self.log('CLOSE SHORT POSITION, %.2f' % self.dataclose[0])
elif self.position.size==0:
self.order=self.buy()
self.log('OPEN LONG POSITION, %.2f' % self.dataclose[0])
elif self.data.MACD_hist[0]<0:
if self.position.size>0 and self.data.MACD_hist[-1]>0:
self.order=self.close()
self.log('CLOSE LONG POSITION, %.2f' % self.dataclose[0])
elif self.position.size==0:
self.order=self.sell()
self.log('OPEN SHORT POSITION, %.2f' % self.dataclose[0])
Based on the logic, it is only possible for one of these conditions to be met in next.
You are indicating that you would like to have the close and entry separate. You need to change your elif to if. Also, if you are using a criteria of self.position.size == 0, this will not happen until the close is executed, so the bar after the close, not the next one. But if you wish to have other criteria, you could enter it after another if statement.
if self.data.MACD_hist[0]>0:
if self.position.size<0 and self.data.MACD_hist[-1]<0 :
self.close()
self.log('CLOSE SHORT POSITION, %.2f' % self.dataclose[0])
#### change here ####
if SOME OTHER CONDITION:
self.order=self.buy()
self.log('OPEN LONG POSITION, %.2f' % self.dataclose[0])
...
Once you close the position, it is not really necessary to check if the position goes to 0 units. You can safely assume it will.
I am using pyjks with Python 2.7 as follows:
import jks
ks = jks.KeyStore.load('xxxx.jceks', 'xxxxx')
for alias, sk in ks.secret_keys.items():
print("Secret key: %s" % sk.alias)
print(" Algorithm: %s" % sk.algorithm)
print(" Key size: %d bits" % sk.key_size)
print(" Key: %s" % "".join("{:02x}".format(b) for b in bytearray(sk.key)))
print(sk.key)
which gives:
Secret key: xxxxxxx
Algorithm: AES
Key size: 128 bits
Key: xxxx
someweirdcharacters
Secret key: xxxxxxx
Algorithm: AES
Key size: 128 bits
Key: yyyy
someotherweirdcharacters
which is fine, but I am very rusty at Python, so was wondering, what is the best way of putting someweirdcharacters and someotherweirdcharacters into a list?
I don't know how to access sk.key from ks.secret_keys.items() except as in the loop above, but is seems there should be a simpler way.
Are you looking something like below?
import jks
arr = []
ks = jks.KeyStore.load('xxxx.jceks', 'xxxxx')
for alias, sk in ks.secret_keys.items():
print("Secret key: %s" % sk.alias)
print(" Algorithm: %s" % sk.algorithm)
print(" Key size: %d bits" % sk.key_size)
print(" Key: %s" % "".join("{:02x}".format(b) for b in bytearray(sk.key)))
arr.append(sk.key)
print(arr)
I have this code that is doing recursion but I want to be able to watch the recursions but illustrate the depth of each recursions and also when it comes back out. Just something simple as shown below. Not sure where I need to put spaces or tabs to make this happened and I'm using a string map_str and then printing it out at the end. Please see code and example of output.
def get_process_parents(batchDate, late_process):
global late_parents
global map_str
process_depends = []
#logging.info('late_process: %s ' % late_process)
process_depends = getprocessDependencies(batchDate, late_process)
late_parents[late_process.get('name')] = process_depends
map_str += (' %s ---> %s \n------>\n' % (late_process.get('name'), process_depends))
#logging.info('Late_process Name: %s ---> Depends: %s ' % (late_process.get('name'),process_depends))
if not process_depends:
#logging.info('No more depends: %s' % late_process.get('name'))
late_parents_process.add(late_process.get('name'))
map_str += (' %s ---> %s\n ---->\n' % (late_process.get('name'), process_depends))
else:
for process_depend in process_depends:
process = getprocessByName(batchDate, process_depend)
#logging.info('process_depend %s --> state: %s ' % (process_depend, process.get('state')))
if process.get('state') == 'Done':
#logging.info('process depend DONE: %s' % process_depend)
map_str += (' %s Status: %s\n ---->\n' % (process_depend, process.get('state')))
else:
late_parents_process.add(process.get('name'))
get_process_parents(batchDate, getprocessByName(batchDate,process_depend))
Output:
What I want it to look like
Process_name ----> ['process dependencies 1', 'process dependencies 2']
------>process dependencies 1 ----> [process dependencies 3, dependencies 4]
------>dependencies 3 -----> []
------>dependencies 4 -----> []
------>process dependencies 2 ----> [process dependencies 5]
------>process dependencies 5 -----> [process dependencies 6]
----->process dependencies 6 -----> []
Can you alter the function signature? If so, then the easiest way would be to define the function as:
def get_process_parents(batchDate, late_process, depth=0):
and then in your nested calls use
get_process_parents(batchDate, getprocessByName(batchDate,process_depend), depth + 1)
Then, to indent, you can use:
print '\t' * depth + ('%s---->... # etc, etc
I'd caution against using a global for map_str if you can avoid it.
I have a Pi Model A, running the latest version of Raspbian. Plugged into it is an ADC-Pi (https://www.abelectronics.co.uk/products/3/Raspberry-Pi/17/ADC-Pi-V2---Raspberry-Pi-Analogue-to-Digital-converter) with various analog sensors.
Running the demo code (which is below)
If I use './adc_demo.py' it works fine
If I use 'sudo python3 adc_demo.py' I get the error 'Import error: No module named quick2wire.i2c'.
What can I do so I can run it using the latter statement? I have another script that runs a motor through the GPIO pins on the pi, and that needs to be ran as root- and I'm trying to merge the two scripts together.
adc_demo.py
#!/usr/bin/env python3
# read abelectronics ADC Pi board inputs
# uses quick2wire from http://quick2wire.com/
# See http://elinux.org/RPi_ADC_I2C_Python for full setup instructions
import quick2wire.i2c as i2c
import re
import time
adc_address1 = 0x68
adc_address2 = 0x69
adc_channel1 = 0x98
adc_channel2 = 0xB8
adc_channel3 = 0xD8
adc_channel4 = 0xF8
for line in open('/proc/cpuinfo').readlines():
m = re.match('(.*?)\s*:\s*(.*)', line)
if m:
(name, value) = (m.group(1), m.group(2))
if name == "Revision":
if value [-4:] in ('0002', '0003'):
i2c_bus = 0
else:
i2c_bus = 1
break
with i2c.I2CMaster(i2c_bus) as bus:
def getadcreading(address, channel):
bus.transaction(i2c.writing_bytes(address, channel))
time.sleep(0.05)
h, l, r = bus.transaction(i2c.reading(address,3))[0]
time.sleep(0.05)
h, l, r = bus.transaction(i2c.reading(address,3))[0]
t = (h << 8) | l
v = t * 0.000154
if v < 5.5:
return v
else: # must be a floating input
return 0.00
while True:
print("1: %f" % getadcreading(adc_address1, adc_channel1))
print("2: %f" % getadcreading(adc_address1, adc_channel2))
print("3: %f" % getadcreading(adc_address1, adc_channel3))
print("4: %f" % getadcreading(adc_address1, adc_channel4))
print("5: %f" % getadcreading(adc_address2, adc_channel1))
print("6: %f" % getadcreading(adc_address2, adc_channel2))
print("7: %f" % getadcreading(adc_address2, adc_channel3))
print("8: %f" % getadcreading(adc_address2, adc_channel4))
time.sleep(1)
I am trying to convert my script (https://github.com/fletchermoore/n2c2) to use the default package xml.etree instead of lxml. This was an oversight on my part, but now I am realizing it would be impossible to get my target audience to set up lxml on their macs.
I think that most of the code should just work by switching out the import, but when I tried it I found out that xml.etree handles namespaces differently (which I do not understand). Specifically, what would be the easiest way to convert the setTagNames function here. xml is created from the .fromstring() method.
def setTagNames(self, xml):
officens = xml.nsmap['office']
textns = xml.nsmap['text']
drawns = xml.nsmap['draw']
xlinkns = xml.nsmap['xlink']
stylens = xml.nsmap['style']
fons = xml.nsmap['fo']
names = {}
names['body'] = '{%s}body' % officens
names['text'] = '{%s}text' % officens
names['auto-styles'] = '{%s}automatic-styles' % officens
names['list'] = '{%s}list' % textns
names['list-item'] = '{%s}list-item' % textns
names['p'] = '{%s}p' % textns
names['line-break'] = '{%s}line-break' % textns
names['tab'] = '{%s}tab' % textns
names['span'] = '{%s}span' % textns
names['frame'] = '{%s}frame' % drawns
names['image'] = '{%s}image' % drawns
names['href'] = '{%s}href' % xlinkns
names['name'] = '{%s}name' % stylens
names['style-name'] = '{%s}style-name' % textns
names['font-weight'] = '{%s}font-weight' % fons
names['text-underline-style'] = '{%s}text-underline-style' % stylens
names['font-style'] = '{%s}font-style' % fons
names['font-size'] = '{%s}font-size' % fons
names['background-color'] = '{%s}background-color' % fons
names['color'] = '{%s}color' % fons
names['style'] = '{%s}style' % stylens
names['text-properties'] = '{%s}text-properties' % stylens
self.names = names
self.builder.names = names
I found the answer in another post: Accessing XMLNS attribute with Python Elementree?
I used the function written by deancutlet to create the nsmap. Everything else in my code seems to work without modification.