=====================
positions_backtester
=====================

.. image:: https://img.shields.io/github/last-commit/stas-prokopiev/positions_backtester
   :target: https://img.shields.io/github/last-commit/stas-prokopiev/positions_backtester
   :alt: GitHub last commit

.. image:: https://img.shields.io/github/license/stas-prokopiev/positions_backtester
    :target: https://github.com/stas-prokopiev/positions_backtester/blob/master/LICENSE.txt
    :alt: GitHub license<space><space>

.. image:: https://img.shields.io/pypi/v/positions_backtester
   :target: https://img.shields.io/pypi/v/positions_backtester
   :alt: PyPI

.. image:: https://img.shields.io/pypi/pyversions/positions_backtester
   :target: https://img.shields.io/pypi/pyversions/positions_backtester
   :alt: PyPI - Python Version


.. contents:: **Table of Contents**

Short Overview.
=========================
positions_backtester is a python package (**py>=3.7**) to backtest trading strategies (dataframe with positions) with execution costs modeling

| This package is trying to solve a problem of slow trading with fast data.
| Let's say that you want to update your trading position once in a hour, day, week, ...
| But you have data with much higher time resolution - minutes, seconds, miliseconds
| Then you can give the wanted positions dataframe with the tick with which you want to trade
| And higher resolution will be used to calculate approximate execution prices
| (Which are just the mean price over the choosen execution time period)

Installation via pip:
======================

.. code-block:: bash

    pip install positions_backtester


How to backtest your dataframe with positions
================================================

.. code-block:: python

    from positions_backtester import run_backtest

    df_backtest_res = run_backtest(
        df_positions,
        df_real_exec_prices,
        is_to_neutralize=True,
        const_trading_fees_percent=0.01,
    )

!!! **df_real_exec_prices should have the same indices as df_positions**

Arguments:

#. **df_positions**:
    | pd.DataFrame
    | Positions we want to take with the frequency with which we want to change our positions
#. **df_real_exec_prices**:
    | pd.DataFrame
    | Prices by which we can buy assets at the given moment
#. **is_to_neutralize=True,**:
    | bool
    | Flag if to have long-short equal positions
#. **td_trading_delay=None**:
    | datetime.timedelta
    | Delay needed to calculate the wanted positions
#. **td_execution_duration**:
    | datetime.timedelta
    | How long should the execution take
    | Execution price will be the mean price over execution time period
#. **const_trading_fees_percent**:
    | float
    | Broker trading fees


Input example: **df_positions_short**
-----------------------------------------------

..
    This is a comment

    .. raw:: html

    <embed>
        <table border="1" class="dataframe">
        <thead>
            <tr style="text-align: right;">
            <th></th>
            <th>asset_1</th>
            <th>asset_2</th>
            <th>asset_3</th>
            </tr>
            <tr>
            <th>Close datetime</th>
            <th></th>
            <th></th>
            <th></th>
            </tr>
        </thead>
        <tbody>
            <tr>
            <th>2021-07-06 22:00:00+00:00</th>
            <td>0.285602</td>
            <td>NaN</td>
            <td>NaN</td>
            </tr>
            <tr>
            <th>2021-07-06 23:00:00+00:00</th>
            <td>0.296204</td>
            <td>NaN</td>
            <td>NaN</td>
            </tr>
            <tr>
            <th>2021-07-07 00:00:00+00:00</th>
            <td>0.294426</td>
            <td>NaN</td>
            <td>NaN</td>
            </tr>
        </tbody>
        </table>
    </embed>



Output: df_backtest_res
-----------------------------------------------

..
    This is a comment
    .. raw:: html

    <embed>
        <table border="1" class="dataframe">
        <thead>
            <tr style="text-align: right;">
            <th></th>
            <th>PNL before_costs</th>
            <th>PNL execution_fee</th>
            <th>PNL const_trading_fee</th>
            <th>PNL after_costs</th>
            <th>PNL half_costs</th>
            <th>trading_volume</th>
            <th>booksize</th>
            <th>max weight</th>
            <th>long count</th>
            <th>short count</th>
            <th>long value</th>
            <th>short value</th>
            </tr>
            <tr>
            <th>Close datetime</th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            </tr>
        </thead>
        <tbody>
            <tr>
            <th>2021-07-06 20:00:00+00:00</th>
            <td>-0.002108</td>
            <td>-0.004361</td>
            <td>0.000003</td>
            <td>0.002250</td>
            <td>0.000071</td>
            <td>0.034720</td>
            <td>1.0</td>
            <td>0.14</td>
            <td>33</td>
            <td>200</td>
            <td>0.5</td>
            <td>0.5</td>
            </tr>
            <tr>
            <th>2021-07-06 21:00:00+00:00</th>
            <td>-0.005282</td>
            <td>-0.000222</td>
            <td>0.000005</td>
            <td>-0.005065</td>
            <td>-0.005174</td>
            <td>0.053568</td>
            <td>1.0</td>
            <td>0.11</td>
            <td>32</td>
            <td>198</td>
            <td>0.5</td>
            <td>0.5</td>
            </tr>
        </tbody>
        </table>
    </embed>




How to create execution prices
===================================

.. code-block:: python

    from positions_backtester import create_df_simple_execution_prices

    df_real_exec_prices = create_df_simple_execution_prices(
        df_prices_full,
        data_frequency="30min",  # 30min, 1h, 3h, 6h, 1d
        offset="-3min",  # "-3min"
        td_trading_delay=datetime.timedelta(minutes=3),
        td_execution_duration=datetime.timedelta(minutes=2),
    )


Arguments:

#. **df_prices_full**:
    | pd.DataFrame
    | Prices of assets in higher resolution
    | Higher resolution needed for more pricise execution evaluation
#. **td_trading_delay=None**:
    | datetime.timedelta
    | Delay (Time) needed to calculate the wanted positions
#. **td_execution_duration**:
    | datetime.timedelta
    | How long should the execution take
    | Execution price will be the mean price over execution time period


Inputs:
-----------------------------------------------


df_prices_full
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

..
    This is a comment
    .. raw:: html

    <embed>
        <table border="1" class="dataframe">
        <thead>
            <tr style="text-align: right;">
            <th></th>
            <th>asset_1</th>
            <th>asset_2</th>
            <th>asset_3</th>
            </tr>
            <tr>
            <th>Close datetime</th>
            <th></th>
            <th></th>
            <th></th>
            </tr>
        </thead>
        <tbody>
            <tr>
            <th>2021-07-06 23:57:00+00:00</th>
            <td>317.86</td>
            <td>57.00</td>
            <td>15.488</td>
            </tr>
            <tr>
            <th>2021-07-06 23:58:00+00:00</th>
            <td>317.11</td>
            <td>57.04</td>
            <td>15.480</td>
            </tr>
            <tr>
            <th>2021-07-06 23:59:00+00:00</th>
            <td>316.49</td>
            <td>57.01</td>
            <td>15.459</td>
            </tr>
        </tbody>
        </table>
    </embed>



Formulas
===========================

| PNL before_costs = (previous_position) * (price_change_%)
| trading_volume = abs(new_wanted_position - previous_position)
| PNL const_trading_fee = trading_volume * broker_commision
| PNL after_costs = PNL before_costs - PNL const_trading_fee
| PNL half_costs = PNL before_costs - PNL const_trading_fee/2.0

Links
=====

    * `PYPI <https://pypi.org/project/positions_backtester/>`_
    * `GitHub <https://github.com/stas-prokopiev/positions_backtester>`_

Project local Links
===================

    * `CHANGELOG <https://github.com/stas-prokopiev/positions_backtester/blob/master/CHANGELOG.rst>`_.
    * `CONTRIBUTING <https://github.com/stas-prokopiev/positions_backtester/blob/master/CONTRIBUTING.rst>`_.

Contacts
========

    * Email: stas.prokopiev@gmail.com
    * `vk.com <https://vk.com/stas.prokopyev>`_
    * `Facebook <https://www.facebook.com/profile.php?id=100009380530321>`_

License
=======

This project is licensed under the MIT License.