加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
comminfo.py 11.42 KB
一键复制 编辑 原始数据 按行查看 历史
云金杞 提交于 2021-12-18 20:38 . update backtrader files
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2020 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime
from .utils.py3 import with_metaclass
from .metabase import MetaParams
class CommInfoBase(with_metaclass(MetaParams)):
'''Base Class for the Commission Schemes.
Params:
- ``commission`` (def: ``0.0``): base commission value in percentage or
monetary units
- ``mult`` (def ``1.0``): multiplier applied to the asset for
value/profit
- ``margin`` (def: ``None``): amount of monetary units needed to
open/hold an operation. It only applies if the final ``_stocklike``
attribute in the class is set to ``False``
- ``automargin`` (def: ``False``): Used by the method ``get_margin``
to automatically calculate the margin/guarantees needed with the
following policy
- Use param ``margin`` if param ``automargin`` evaluates to ``False``
- Use param ``mult`` * ``price`` if ``automargin < 0``
- Use param ``automargin`` * ``price`` if ``automargin > 0``
- ``commtype`` (def: ``None``): Supported values are
``CommInfoBase.COMM_PERC`` (commission to be understood as %) and
``CommInfoBase.COMM_FIXED`` (commission to be understood as monetary
units)
The default value of ``None`` is a supported value to retain
compatibility with the legacy ``CommissionInfo`` object. If
``commtype`` is set to None, then the following applies:
- ``margin`` is ``None``: Internal ``_commtype`` is set to
``COMM_PERC`` and ``_stocklike`` is set to ``True`` (Operating
%-wise with Stocks)
- ``margin`` is not ``None``: ``_commtype`` set to ``COMM_FIXED`` and
``_stocklike`` set to ``False`` (Operating with fixed rount-trip
commission with Futures)
If this param is set to something else than ``None``, then it will be
passed to the internal ``_commtype`` attribute and the same will be
done with the param ``stocklike`` and the internal attribute
``_stocklike``
- ``stocklike`` (def: ``False``): Indicates if the instrument is
Stock-like or Futures-like (see the ``commtype`` discussion above)
- ``percabs`` (def: ``False``): when ``commtype`` is set to COMM_PERC,
whether the parameter ``commission`` has to be understood as XX% or
0.XX
If this param is ``True``: 0.XX
If this param is ``False``: XX%
- ``interest`` (def: ``0.0``)
If this is non-zero, this is the yearly interest charged for holding a
short selling position. This is mostly meant for stock short-selling
The formula: ``days * price * abs(size) * (interest / 365)``
It must be specified in absolute terms: 0.05 -> 5%
.. note:: the behavior can be changed by overriding the method:
``_get_credit_interest``
- ``interest_long`` (def: ``False``)
Some products like ETFs get charged on interest for short and long
positions. If ths is ``True`` and ``interest`` is non-zero the interest
will be charged on both directions
- ``leverage`` (def: ``1.0``)
Amount of leverage for the asset with regards to the needed cash
Attributes:
- ``_stocklike``: Final value to use for Stock-like/Futures-like behavior
- ``_commtype``: Final value to use for PERC vs FIXED commissions
This two are used internally instead of the declared params to enable the
compatibility check described above for the legacy ``CommissionInfo``
object
'''
COMM_PERC, COMM_FIXED = range(2)
params = (
('commission', 0.0), ('mult', 1.0), ('margin', None),
('commtype', None),
('stocklike', False),
('percabs', False),
('interest', 0.0),
('interest_long', False),
('leverage', 1.0),
('automargin', False),
)
def __init__(self):
super(CommInfoBase, self).__init__()
self._stocklike = self.p.stocklike
self._commtype = self.p.commtype
# The intial block checks for the behavior of the original
# CommissionInfo in which the commission scheme (perc/fixed) was
# determined by parameter "margin" evaluating to False/True
# If the parameter "commtype" is None, this behavior is emulated
# else, the parameter values are used
if self._commtype is None: # original CommissionInfo behavior applies
if self.p.margin:
self._stocklike = False
self._commtype = self.COMM_FIXED
else:
self._stocklike = True
self._commtype = self.COMM_PERC
if not self._stocklike and not self.p.margin:
self.p.margin = 1.0 # avoid having None/0
if self._commtype == self.COMM_PERC and not self.p.percabs:
self.p.commission /= 100.0
self._creditrate = self.p.interest / 365.0
@property
def margin(self):
return self.p.margin
@property
def stocklike(self):
return self._stocklike
def get_margin(self, price):
'''Returns the actual margin/guarantees needed for a single item of the
asset at the given price. The default implementation has this policy:
- Use param ``margin`` if param ``automargin`` evaluates to ``False``
- Use param ``mult`` * ``price`` if ``automargin < 0``
- Use param ``automargin`` * ``price`` if ``automargin > 0``
'''
if not self.p.automargin:
return self.p.margin
elif self.p.automargin < 0:
return price * self.p.mult
return price * self.p.automargin # int/float expected
def get_leverage(self):
'''Returns the level of leverage allowed for this comission scheme'''
return self.p.leverage
def getsize(self, price, cash):
'''Returns the needed size to meet a cash operation at a given price'''
if not self._stocklike:
return int(self.p.leverage * (cash // self.get_margin(price)))
return int(self.p.leverage * (cash // price))
def getoperationcost(self, size, price):
'''Returns the needed amount of cash an operation would cost'''
if not self._stocklike:
return abs(size) * self.get_margin(price)
return abs(size) * price
def getvaluesize(self, size, price):
'''Returns the value of size for given a price. For future-like
objects it is fixed at size * margin'''
if not self._stocklike:
return abs(size) * self.get_margin(price)
return size * price
def getvalue(self, position, price):
'''Returns the value of a position given a price. For future-like
objects it is fixed at size * margin'''
if not self._stocklike:
return abs(position.size) * self.get_margin(price)
size = position.size
if size >= 0:
return size * price
# With stocks, a short position is worth more as the price goes down
value = position.price * size # original value
value += (position.price - price) * size # increased value
return value
def _getcommission(self, size, price, pseudoexec):
'''Calculates the commission of an operation at a given price
pseudoexec: if True the operation has not yet been executed
'''
if self._commtype == self.COMM_PERC:
return abs(size) * self.p.commission * price
return abs(size) * self.p.commission
def getcommission(self, size, price):
'''Calculates the commission of an operation at a given price
'''
return self._getcommission(size, price, pseudoexec=True)
def confirmexec(self, size, price):
return self._getcommission(size, price, pseudoexec=False)
def profitandloss(self, size, price, newprice):
'''Return actual profit and loss a position has'''
return size * (newprice - price) * self.p.mult
def cashadjust(self, size, price, newprice):
'''Calculates cash adjustment for a given price difference'''
if not self._stocklike:
return size * (newprice - price) * self.p.mult
return 0.0
def get_credit_interest(self, data, pos, dt):
'''Calculates the credit due for short selling or product specific'''
size, price = pos.size, pos.price
if size > 0 and not self.p.interest_long:
return 0.0 # long positions not charged
dt0 = dt.date()
dt1 = pos.datetime.date()
if dt0 <= dt1:
return 0.0
return self._get_credit_interest(data, size, price,
(dt0 - dt1).days, dt0, dt1)
def _get_credit_interest(self, data, size, price, days, dt0, dt1):
'''
This method returns the cost in terms of credit interest charged by
the broker.
In the case of ``size > 0`` this method will only be called if the
parameter to the class ``interest_long`` is ``True``
The formulat for the calculation of the credit interest rate is:
The formula: ``days * price * abs(size) * (interest / 365)``
Params:
- ``data``: data feed for which interest is charged
- ``size``: current position size. > 0 for long positions and < 0 for
short positions (this parameter will not be ``0``)
- ``price``: current position price
- ``days``: number of days elapsed since last credit calculation
(this is (dt0 - dt1).days)
- ``dt0``: (datetime.datetime) current datetime
- ``dt1``: (datetime.datetime) datetime of previous calculation
``dt0`` and ``dt1`` are not used in the default implementation and are
provided as extra input for overridden methods
'''
return days * self._creditrate * abs(size) * price
class CommissionInfo(CommInfoBase):
'''Base Class for the actual Commission Schemes.
CommInfoBase was created to keep suppor for the original, incomplete,
support provided by *backtrader*. New commission schemes derive from this
class which subclasses ``CommInfoBase``.
The default value of ``percabs`` is also changed to ``True``
Params:
- ``percabs`` (def: True): when ``commtype`` is set to COMM_PERC, whether
the parameter ``commission`` has to be understood as XX% or 0.XX
If this param is True: 0.XX
If this param is False: XX%
'''
params = (
('percabs', True), # Original CommissionInfo took 0.xx for percentages
)
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化