For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
Adding multiple data feeds in a for loop gives error
-
When trying to add multiple data feeds I'm getting the following error:
❯ python3 optimize_all_indexes.py Traceback (most recent call last): File "optimize_all_indexes.py", line 43, in <module> optimized_runs = cerebro.run() File "/home/user/.local/lib/python3.8/site-packages/backtrader/cerebro.py", line 1143, in run for r in pool.imap(self, iterstrats): File "/usr/lib/python3.8/multiprocessing/pool.py", line 868, in next raise value multiprocessing.pool.MaybeEncodingError: Error sending result: '[<strategies.strategies.EMAcrossover object at 0x7f9dda1051f0>]'. Reason: 'PicklingError("Can't pickle <class 'backtrader.metabase.AutoInfoClass_95a5b7035dfc45a8b0580d4ca9a9ac62_AXJO.csv'>: attribute lookup AutoInfoClass_95a5b7035dfc45a8b0580d4ca9a9ac62_AXJO.csv on backtrader.metabase failed")'
The code works for a single data feed. This is what I've changed from the code:
modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, 'data/indexes/') for file in os.listdir(datapath): path = os.path.join(modpath, f'data/indexes/{file}') data = bt.feeds.YahooFinanceCSVData( dataname=path, name=file, reverse=False ) cerebro.adddata(data)
The code for a single data feed:
import backtrader as bt class MaxRiskSizer(bt.Sizer): params = (('risk', 0.98),) 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): position = self.broker.getposition(data) if not position: size = comminfo.getsize(data.close[0], cash * self.p.risk) else: size = position.size return size
import backtrader as bt class DegiroCommission(bt.CommInfoBase): params = (('per_share', 0.004), ('flat', 0.5),) def _getcommission(self, size, price, pseudoexec): return self.p.flat + size * self.p.per_share
from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime import os.path import sys import math import backtrader as bt from strategies.strategies import * from sizers import MaxRiskSizer from commissions import DegiroCommission STARTING_CASH = 1700 if __name__ == '__main__': cerebro = bt.Cerebro(optreturn=False) cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annual_return') cerebro.optstrategy(EMAcrossover, fast=range(5, 7), slow=range(10,12)) #cerebro.optstrategy(EMAcrossover, fast=range(5, 55, 5), slow=range(60, 300, 10)) #cerebro.optstrategy(EMAcrossover, fast=range(15, 55), slow=range(40, 60)) cerebro.broker.set_cash(STARTING_CASH) cerebro.broker.set_coc(True) modpath = os.path.dirname(os.path.abspath(sys.argv[0])) datapath = os.path.join(modpath, 'data/SPY.csv') data = bt.feeds.YahooFinanceCSVData( dataname=datapath, reverse=False ) cerebro.adddata(data) cerebro.addsizer(MaxRiskSizer) comminfo = DegiroCommission() cerebro.broker.addcommissioninfo(comminfo) optimized_runs = cerebro.run() final_results_list = [] for run in optimized_runs: for strategy in run: PnL = round(strategy.broker.get_value() - STARTING_CASH, 2) my_dict = strategy.analyzers.annual_return.get_analysis() annual_returns = [v for _, v in my_dict.items() if v != 0] average_annual_return = sum(annual_returns) / len(annual_returns) final_results_list.append([strategy.params.fast, strategy.params.slow, PnL, round(average_annual_return*100, 2)]) sort_by_return = sorted(final_results_list, key=lambda x: x[3], reverse=True) print('\nSorted by Annualized Return:') for line in sort_by_return[:10]: print(line) sort_by_pnl = sorted(final_results_list, key=lambda x: x[2], reverse=True) print('\nSorted by PnL:') for line in sort_by_pnl[:10]: print(line)
The indexes have different starting day for the datas. Maybe that affects the optimization somehow.
-
The strategy was missing.
class EMAcrossover(bt.Strategy): params = (('fast', 20), ('slow', 50),) def log(self, txt, dt=None): dt = dt or self.datas[0].datetime.date(0) #print(f'{dt.isoformat()} {txt}') # Comment this line when running optimization def __init__(self): self.dataclose = self.datas[0].close self.order = None fast_ema, slow_ema = bt.ind.EMA(period=self.p.fast), bt.ind.EMA(period=self.p.slow) self.crossover = bt.indicators.CrossOver(fast_ema, slow_ema) #self.signal_add(bt.SIGNAL_LONGSHORT, bt.ind.CrossOver(ema1, ema2)) def notify_trade(self, trade): if not trade.isclosed: return self.log(f'GROSS {trade.pnl:.2f}, NET {trade.pnlcomm:.2f}') def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # An active Buy/Sell order has been submitted/accepted - 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(f'BUY EXECUTED, {order.executed.price:.2f}') elif order.issell(): self.log(f'SELL EXECUTED, {order.executed.price:.2f}') 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 next(self): if self.order: return if self.crossover > 0: self.log(f'BUY CREATE {self.dataclose[0]:.2f}') self.order = self.buy() elif self.crossover < 0: self.log(f'SELL CREATE {self.dataclose[0]:.2f}') self.order = self.sell()
-
Same error, any help?
-
@willt Can you please elaborate on your error and start a new thread? Thanks.