Power futures settlement
-
Hi all,
I'm trying to use backtrader for backtesting trading strategies on power futures products.
Description of my case:
My products concerns weekly power delivery: each delivery week is a different product.
Date Close Week id 01/08/2016 27.09 W1 <--- all the prices with weekid W1 concern 02/08/2016 27.46 W1 ----- a delivery of the power during the next week (id W2) 03/08/2016 27.5 W1 04/08/2016 27.04 W1 05/08/2016 26.94 W1 08/08/2016 27.29 W2 09/08/2016 27.08 W2 10/08/2016 26.82 W2 11/08/2016 26.98 W2 12/08/2016 27.81 W2 15/08/2016 28.12 W3 16/08/2016 27.78 W3 17/08/2016 28.1 W3 18/08/2016 28.73 W3 19/08/2016 28.75 W3
I can buy and sell a weekly product until the trading day before the start of the delivery: in my case, the previous friday.
-
If I buy 1 unit on 02/08/2016 @ 27.46 I have until 05/08/2016 (incl.) to sell it.
-
If I don't sell it before W2, there is an automatic "settlement" at spot day ahead (prower day ahead exchange price)
In backtrader:
- I have one data stream representing future price (as in my example) (data0)
and on data stream representing my settlement price (data1)
What I try to do:
In my next method:
-
If currentline.weekid != lastline.weekid: # it means that we just entered in a new week
- If a position is already opened @ data0.close:
- I must close the position @ data1.close
- If a position is already opened @ data0.close:
-
Then, I can reopen a position on data0.
My questions:
Has anybody tried to to something like this ?
Is there a way to do this ? -
-
The lines in the objects are actually
floats
. That means that anid
likeW1
cannot be used.But the
W1
could be parsed and translated to1
and so on untilW52
which would be translated to52
.You can extend a data feed to add that
weekid
line which is parsed from your data. See these two topics:But if those weeks are standard calendar weeks, you can do the same by simply looking up the week with the
datetime
module. See:The strategy has a
datetime
line and from there you get adate
withself.lines.datetime.date()
and you can get theisocalendar
tuple.Keeping track of the second item in the tuple (the ISO Week Number) would be the same as parsing it from a data file.
-
You're right.
The table in my example is not exactely what I provide to backtrader (this was an illustration for my example). I only feed bt with prices and I use self.datawb.datetime.datetime().isocalendar()[1] to know the week id.
==> this is OKBut my problem is more on the "Buy 1 on data0 and Sell 1 on data1" problem: I can't find a way to do this and have a net position of 0 at the end of each week.
-
Something is unclear.
From the original post:
If currentline.weekid != lastline.weekid: # it means that we just entered in a new week
And from the last post:
problem: I can't find a way to do this and have a net position of 0 at the end of each week.
Because the code above kicks in when the new week has started. You will never have a net position of
0
and the end of the week. You will be bought intodata0
.To sell at the end of the week you need to know that the current trading day is the end of the week. Options:
-
Your trading asset always trades on Friday (even if the country has some bank holidays on Fridays)
You need to check that the current day is Friday and sell
data1
to be able to buydata0
-
Your trading asset has some missing Fridays because the exchange follows Bank Holidays
You need extra information to let you know that on particular calendar weeks, the last trading day is not Friday but another (it could be Wednesday for example ...)
But it also seems like if you wanted to sell with the closing price on Friday. Use
cheat-on-close
(or else intraday data andClose
orders before the market close time)- Docs - Broker Reference (Look for
set_coc
)
-
-
That's true. I need to do it on the last day of the week.
And the set_coc will help me for sure.But what about selling on another instrument (data1) while i bought on data0 ?
Date Close Week id
01/08/2016 27.09 W1 <-- buy 1 @27.09
02/08/2016 27.46 W1
03/08/2016 27.5 W1
04/08/2016 27.04 W1
05/08/2016 26.94 W1 <-- at the end of the week, no oportunity identified by my indicator => so automaticly sell 1 @ data1.close[0] (and not data0.close[0]=26.94) -
This may be a long shot, but are you using
self.sell(self.data1, ...)
... ?A code sample would be what really shows where your worries are.
-
Here is a sample where I tried to integrate all what we said before:
def islastdayofweek(self, data): return data.datetime.date(0).isocalendar()[1] != data.datetime.date(1).isocalendar()[1] def next(self): islastdayofweek = self.islastdayofweek(self.data0) p = self.getposition().size if (self.signal < 0.0): s = self.getsizing(isbuy=True) if (p + s <= self.params.limit): self.log('BUY CREATE , %.2f, %.2f MWh' % (self.data0.close[0], s)) self.buy(size=s) p = p + s else: self.log('BUY LIMIT EXCEEDED. ORDER CANCELLED.') elif (self.signal > 0.0): s = self.getsizing(isbuy=False) if (p - s >= 0): self.log('SELL CREATE , %.2f, %.2f MWh' % (self.data0.close[0], s)) self.sell(size=s) p = p - s if islastdayofweek and p > 0: self.sell(self.data1, size=p) self.log('SELL SETTLEMENT, %.2f, %.2f MWh' % (self.data1.close[0], p)) # Here, is my position equal to 0 after execution of the order? Or short on data1 and long on data0 ?
-
The default action of
buy
andsell
if nothing is specified is to apply the action todata0
.Should you wish otherwise and as stated above, you should for example:
self.sell(self.data1)
-
Ok. When I do this, here is what happens with the positions:
At the end of the 05/08/2016, the position in my example is +5 on data0 and -5 on data1.
What I want to do is net the position for the next trading day:
on 08/08/2016: Position 0 on data0 and 0 on data1Can I do something like this?
-
With that table at hand your desired use case seems clearer.
But you need to specifically address both
data0
anddata1
. There is nothing which ties both data feeds together, i.e.: an action on one of thembuy
orsell
has no actual effect on the other.The platform has no way to know that your wish is that by opening a short position on
data1
at the end of Week_N which has the implication that at the start of the Week_N+1 positions on bothdata0
anddata1
have to be closed.Let's imagine that somehow a link can be established between
data0
anddata1
. Because you are using5
and-5
it seems natural to assume that5 + (-5) = 0
. But questions on the implementation of the logic quickly arise-
Are positions on
data0
anddata1
simply added? What happens if you buy5
ondata1
, should the system then end up with10
shares ondata0
? -
You wish that on the next trading day, but ... Is it always the case? Does it have to be on week boundaries?
The strange thing about the use case is (there are for sure good reasons for it)
-
Why opening a short position on
data1
at the end of week with the expectation to have no positions open on the next trading day?It seems easier to simply close the open position on
data0
-
-
@backtrader said in Power futures settlement:
With that table at hand your desired use case seems clearer.
But you need to specifically address both
data0
anddata1
. There is nothing which ties both data feeds together, i.e.: an action on one of thembuy
orsell
has no actual effect on the other.The platform has no way to know that your wish is that by opening a short position on
data1
at the end of Week_N which has the implication that at the start of the Week_N+1 positions on bothdata0
anddata1
have to be closed.Let's imagine that somehow a link can be established between
data0
anddata1
. Because you are using5
and-5
it seems natural to assume that5 + (-5) = 0
. But questions on the implementation of the logic quickly arise-
Are positions on
data0
anddata1
simply added? What happens if you buy5
ondata1
, should the system then end up with10
shares ondata0
? -
You wish that on the next trading day, but ... Is it always the case? Does it have to be on week boundaries?
Not always week boundaries. In this case I consider weekly power futures, but I could also deal monthly of quarterly futures for example
The strange thing about the use case is (there are for sure good reasons for it)
-
Why opening a short position on
data1
at the end of week with the expectation to have no positions open on the next trading day?It seems easier to simply close the open position on
data0
data0 = weekly power delivery future. When I buy 5 on data0 in W1 it means that, If I don't sell it, I will have a physical delivery of the power on W2.
This I don't want because I'm not an energy provider. I want to avoid the physical delivery.If the 5 are not sold before the delivery week (W2), I will sell the energy on the Spot Day Ahead exchange every day.
data1 represent the average spot dahead price of the week W2.This is why, in this case, I buy 5 on data0 and sell 5 on data1.
In the end, I really have a net position of 0 because I sold the energy I bought. -
-
There is an actual link between the 2 feeds in the specific case because both relate to an asset with physical delivery and the action on one cancels the physical delivery of the other.
The problem as understood
- In the real world you have a net position of 0 because you sold the energy you bought (preventing actual physical delivery)
But
- You are initially still holding
5
units ofdata0
- You are initially still holding
-5
units ofdata1
And you:
- Expect the backtesting platform to do
5 + (-5) -> 0
on units which are actually being held because there is a physical delivery action associated to those units which are still in your hand.
It is clear that your actual real-world problem is solved (the energy is delivered, but actually to someone else) making it a zero-sum game, but the units are still there. Of course at the end of the week, the power delivery future expires and the units disappear from your account, but that's not known to the platform and you actually feed it more data for the coming weeks, so there is also no way to tell the platform that the asset no longer exists and the position could be (no such functionality is there) canceled against the last known price.
One additional question given that
data0
expires (the position disappears), butdata1
is a spot price- What happens with the
-5
trade in the real world ondata1
?
-
@backtrader said in Power futures settlement:
One additional question given that
data0
expires (the position disappears), butdata1
is a spot price- What happens with the
-5
trade in the real world ondata1
?
the -5 on data1 are sold on the da ahead spot exchange. It's sold to counterparts of the exchange who need to rebalance their short physical position.
- What happens with the
-
It seems (and this is due to not knowing how the energy exchanges actually work) that the
-5
units ofdata1
is something you sell, but doesn't stick into your account, i.e.: evolution in the prices ofdata1
after you have sold the-5
units, have no impact in the cash and/or portfolio value of your account.No previous real experience with actual physical delivery and hence the small questions to try to understand how both things are tied together.
-
A commit (which may or may not cover the actual use, given the lack of some details) is in the
development
branch with this commit: https://github.com/mementum/backtrader/commit/19a41a5921c5d9e6ed211d4659d48630c288aa72Usage pattern:
data0 = MyDataFeed(dataname=...) cerebro.adddata(data0) data1 = MyDataFeed(dataname=...) data1.compensate(data0) cerebro.adddata(data1)
Any
buy
/sell
action ondata1
will be executed againstdata0
in the broker. A long open position of+5
ondata0
will be closed by asell(data=data1, size=5)
. Commission charges in any case will be those defined fordata1
(if any) -
@backtrader Great! I'll try it
thanks -
I think that with your "compensate" method, I'm almost able to do my backtest.
One more thing:
when data0's position is 5 and I sell 5 on data1, , the position of data0 is set to 0 thanks to you're new method.
But the trade is not closed, right ?
-
Probably not if you are talking about the
Trade
instances you get in the strategy'snotify_trade
method.That part remain untouched but would also benefit from considering the compensating data feed.
-
Ok
Other question:
I use plotmaster option to plot the two streams on the same plot:
self.data1.plotinfo.plotmaster = self.data0
I expect the two lines (red and black) to share the same y axis. but I'm not sure it's the case here.
Any idea ?Does one of the y axis concern the buy and sell tickers ?
-
The 2nd data is plotted sharing the same
x-axis
but have independent `y-axes'. The rationale- The platform cannot predict if both data feeds will have compatible axes.
In your case the prices are really approximate, but if you consider plotting the
SP500
(around 2370 these days) and theDAX
(around 12000 these days) the scales are not compatible.With all that in mind it could be conceivable to let the user hint if the
y-axis
can also be shared.