For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

# order_target_percent is not working as expected. Please read further; complete code, inputs and outputs are provided

• Hi,

I am running the Longshort strategy (provided as an example here: https://www.backtrader.com/blog/posts/2015-09-14-write-it-down/write-it-down/) on BTC USD data (using fractional sizing as explained here: https://www.backtrader.com/blog/posts/2019-08-29-fractional-sizes/fractional-sizes/).

I'm using order_target_percent(0.5) in my code. I expect that for each buy operation, 50.0% of my cash should get used up. This does happen most of the time. Except when there is a buy immediately after a sell. The amount of cash that is used to make such buy operations is around 50%, but not exactly 50%.

Check the buy operations on Row Id = 20 and Row Id = 46 in the linked output to see what I'm talking about. For ex, in row 20: I expect my cash value to go from 9908.73 to 4954.36 (which is 50%). Instead it goes from 9908.73 to 4947.38 (which is 49.92%). Buy operations on row 72 and 91 don't have this discrepancy.

I suspect this could be due to rounding, but not sure. Let me know if any more info is required.

Code:

``````from __future__ import (absolute_import, division, print_function,
unicode_literals)

import pandas as pd
import numpy as np
import datetime
import os
import seaborn as sns
import sys
import copy
import argparse
import logging

class CommInfoFractional(bt.CommissionInfo):
def getsize(self, price, cash):
'''Returns fractional size for cash operation @price'''
return self.p.leverage * (cash / price)

' For actual buy and sell prices'

plotinfo = dict(plot=False, subplot=False)

def next(self):
for order in self._owner._orderspending:

if order.data is not self.data:
continue

self.lines.order_size = order.executed.size

elif order.issell():
self.lines.act_sell = order.executed.price
self.lines.order_size = order.executed.size

class LongShortStrategy(bt.Strategy):
'''This strategy buys/sells upong the close price crossing
upwards/downwards a Simple Moving Average.

It can be a long-only strategy by setting the param "onlylong" to True
'''
params = dict(
period=15,
stake=1,
printout=False,
onlylong=False,
csvcross=False,
)

def start(self):
pass

def stop(self):
pass

def log(self, txt, dt=None):
if self.p.printout:
dt = dt or self.data.datetime
dt = bt.num2date(dt)
print('%s, %s' % (dt.isoformat(), txt))

def __init__(self):
# To control operation entries
self.orderid = None

# Create SMA on 2nd data
sma = btind.MovAv.SMA(self.data, period=self.p.period)
# Create a CrossOver Signal from close an moving average
self.signal = btind.CrossOver(self.data.close, sma)
self.signal.csv = self.p.csvcross

def next(self):
if self.orderid:
return  # if an order is active, no new orders are allowed

if self.signal > 0.0:  # cross upwards
if self.position:
self.log('CLOSE SHORT , %.2f' % self.data.close)
self.close()

self.log('BUY CREATE , %.2f' % self.data.close)
self.order_target_percent(target=self.p.stake)

elif self.signal < 0.0:
if self.position:
self.log('CLOSE LONG , %.2f' % self.data.close)
self.close()

if not self.p.onlylong:
self.log('SELL CREATE , %.2f' % self.data.close)
self.order_target_percent(target=-self.p.stake)

def notify_order(self, order):
if order.status in [bt.Order.Submitted, bt.Order.Accepted]:

if order.status == order.Completed:
else:
selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
self.log(selltxt, order.executed.dt)

elif order.status in [order.Expired, order.Canceled, order.Margin]:
self.log('%s ,' % order.Status[order.status])
pass  # Simply log

# Allow new orders
self.orderid = None

self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' %

def runstrategy(period = 15
,onlylong = True
,csvcross = True
,stake = 1
,comm = 0
,plot = True
,fractional = True):

# Create a cerebro
cerebro = bt.Cerebro()

# Create the 1st data
data = btfeeds.GenericCSVData(
dataname=data #refer to attached data
dtformat=('%Y-%m-%d'),
fromdate=datetime.datetime(2017, 1, 1),
todate=datetime.datetime(2017, 4, 9),
openinterest=-1
)

# Add the 1st data to cerebro

if fractional:

else:
# Add the commission - only stocks like a for each operation
cerebro.broker.setcommission(commission=comm)

period=period,
onlylong=onlylong,
csvcross=csvcross,
stake=stake)

# Add the commission - only stocks like a for each operation
cerebro.broker.setcash(10000)

# And run it
cerebro.run()

# Plot if requested
if plot:
cerebro.plot(volume=True, zdown=False)

runstrategy(plot = False, stake = 0.5)``````

• @backtrader @ab_trader @run-out please have a look when you get the chance as this could potentially be a bug. Thanks!

});