Topic de GhostPriders2 :

Problème avec la librairie backtrader de python

  • 1

Bonsoir, je suis encore en train d'apprendre à utiliser la libraire Backtrader et mon code ne possède pas de réelle problème en soit mais ne m'affiche pas les résultats espérés.
J'ai mis une stratégie banale qui se base sur le croisement d'un SMA (période = 20) avec un autre SMA (période = 200).
Je vous explique le but: lorsque le SMA de 20 croise le SMA de 200 en venant du haut (crossdown), un ordre de vente doit être exécuté, et inversement, lorsque le SMA de 20 croise le SMA de 200 en venant du bas (crossup), un ordre d'achat doit être exécuté.
Et mon problème et que les achats et ventes ne sont pas exécutés selon cette stratégie, car en regardant le moment du crossup ou du crossdown quasi aucun ordre n'est exécuté au bon moment.
Voilà mon code:
<code>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])

  1. Import the backtrader platform
    import backtrader as bt
  1. Create a Stratey
    class TestStrategy(bt.Strategy):
    params = (
    ('maperiod', 20),
    ('printlog', True),
    )

def log(self, txt, dt=None, doprint=False):
Logging function fot this strategy
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(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)
self.sma200 = bt.indicators.SimpleMovingAverage(self.datas[0], period=200)
self.price = self.datas[0]
self.crossup = bt.indicators.CrossUp(self.sma, self.sma200)
self.crossdown = bt.indicators.CrossDown(self.sma, self.sma200)

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.crossup:

balance = float(self.broker.getcash())
tp = 0.02 * balance / (0.002 * self.price)
sl = 0.01 * balance / (0.002 * self.price)
long_sl_price = self.price * (1 - sl)
long_tp_price = self.price * (1 + tp)
# 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_bracket(limitprice=long_tp_price, stopprice=long_sl_price, exectype=bt.Order.Market, price=self.price)

elif self.crossdown:

balance = float(self.broker.getcash())
tp = 0.02 * balance / (0.002 * self.price)
sl = 0.01 * balance / (0.002 * self.price)
short_sl_price = self.price * (1 + sl)
short_tp_price = self.price * (1 - tp)
self.order = self.sell_bracket(limitprice=short_tp_price, stopprice=short_sl_price, exectype=bt.Order.Market, price=self.price)

else:
self.order = None

else:
if self.order:
return

def stop(self):
self.log('(MA Period %2d) Ending Value %.2f' %
(self.params.maperiod, self.broker.getvalue()), doprint=True)

if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()

# Add a strategy
strats = cerebro.optstrategy(
TestStrategy,
maperiod=range(10, 31))

# 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, '../../datas/orcl-1995-2014.txt')

# Create a Data Feed

cerebro = bt.Cerebro()

cerebro.addstrategy(TestStrategy)

data = bt.feeds.GenericCSVData(
dataname='BTCUSDT_15m.csv',
fromdate=datetime.datetime(2021, 7, 1),
todate=datetime.datetime(2021, 8, 1),
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1
)

# Add the Data Feed to Cerebro
cerebro.adddata(data)

# Set our desired cash start
cerebro.broker.setcash(1000.0)

# Add a FixedSize sizer according to the stake
cerebro.addsizer(bt.sizers.FixedSize, stake=0.002)

# Set the commission
cerebro.broker.setcommission(commission=0.0)

# Run over everything
cerebro.run(maxcpus=1)

cerebro.plot(style='candlestick')</code>

Et voici aussi une image du graph avec entouré en vert là où un achat aurait dû être créé et en rouge inversement : https://image.noelshack.com/fichiers/2021/36/3/1631125785-backtrader.png

Pour ceux qui sont familiers avec cette librairie, merci d'avance pour vos réponses. :hap:

  • 1

Données du topic

Auteur
GhostPriders2
Date de création
8 septembre 2021 à 20:31:32
Nb. messages archivés
3
Nb. messages JVC
3
En ligne sur JvArchive 380