overfitting


Nameoverfitting JSON
Version 0.1.8 PyPI version JSON
download
home_pageNone
SummaryA Robust Futures CryptoCurrency Backtesting Library.
upload_time2025-09-08 07:52:29
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords algo bitcoin ethereum crypto cryptocurrency crypto derivatives futures finance quantitative liquidation solana systematic quant trading
VCS
bugtrack_url
requirements numpy pandas seaborn matplotlib scipy
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Overfitting

A robust and modular backtesting engine designed for crypto futures trading strategies.  
Built for speed, simplicity, and accuracy. Overfitting simulates a realistic crypto trading environment — including **liquidation**, **margin**, and **leverage** — for stress-testing your strategies.

## Prerequisites

Before using **Overfitting**, you’ll need to provide your own historical data.  
The engine is designed to work with **crypto futures price data**, with **OHLCV format**.

### Required Columns

Your dataset must be a CSV or DataFrame that includes at least the following columns:
- timestamp, open, high, low, close
  - `timestamp` should be a **UNIX timestamp in seconds or milliseconds**

## Installation
    $ pip install overfitting


## Usage
```python
import pandas as pd
from overfitting import Strategy

def load_data():
    df = pd.read_csv('./data/BTCUSDT.csv')
    benchamrk_df = pd.read_csv('./data/BTCUSDT.csv') # BTC buy and Hold
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    df.set_index('timestamp', inplace=True)

    start_time = pd.to_datetime('2023-01-01 00:00:00')
    df = df.loc[start_time:]
    # Compute short and long SMAs
    df['sma_short'] = df['close'].rolling(window=20).mean().shift(1)
    df['sma_long'] = df['close'].rolling(window=50).mean().shift(1)

    return df, benchamrk_df

class MyStrategy(Strategy):
    def init(self):
        self.asset = 'BTC'
        self.set_leverage(self.asset, 1)

    def next(self, i):
        if i == 0:
            return

        sma_short = self.val(self.asset, i, "sma_short")
        sma_long = self.val(self.asset, i, "sma_long")
        previous_sma_short = self.val(self.asset, i - 1, "sma_short") 
        previous_sma_long = self.val(self.asset, i - 1, "sma_long")

        # Also skip if values are not available
        if (pd.isna(sma_short) or pd.isna(sma_long) or 
            pd.isna(previous_sma_short) or pd.isna(previous_sma_long)):
            return

        # Fetch the current position
        position = self.get_position(self.asset)

        # Golden cross (entry)
        if previous_sma_short <= previous_sma_long and sma_short > sma_long and position.qty == 0:
            # First fetch current open price which is the target Price
            open_price = self.open(self.asset, i)
            # Determine Lot Size
            lot_size = self.get_balance() // open_price
            # Create LIMIT ORDER
            self.limit_order(self.asset, lot_size, open_price)

        # Death cross (exit)
        if previous_sma_short >= previous_sma_long and sma_short < sma_long and position.qty > 0:
            self.market_order(self.asset, -position.qty)

backtest_data, benchmark_data = load_data()
strategy = MyStrategy(
    data=backtest_data,
    benchmark=benchmark_data, # Default = None Optional
    initial_capital=100_000, # Default Optional
    commission_rate=0.0002, # Default Optional
    maint_maring_rate=0.005, # Default Optional
    maint_amount=50  # Default Optional
)
returns = strategy.run()
strategy.plot(returns)
```

Results
-------
```text
Performance Summary
Number of Years               1.66000000
Start Date           2023-01-01 00:00:00
End Date             2024-08-29 00:00:00
Initial Balance         100,000.00000000
Final Balance           205,328.91120000
CAGR                          0.52684228
Cumulative Return             2.05328911
Sharpe Ratio                  1.24678659
Sortino Ratio                 3.54979579
Max Drawdown                 -0.26332695
Daily Value At Risk          -0.04147282
Skew                          0.44515551
Kurtosis                      2.66444346
Total Trades                182.00000000
Winning Trades               69.00000000
Losing Trades               113.00000000
Win Rate (%)                 37.91208791
Gross Profit            399,044.19246000
Gross Loss             -293,715.28126000
Net Profit              105,328.91120000
Avg Return (%)                0.38834383
Avg Profit (%)                3.54140613
Avg Loss (%)                 -1.53697740
  Net drawdown in %  Peak date Valley date Recovery date Duration
0         26.332695 2024-03-13  2024-06-30           NaT      NaN
1         19.678014 2023-03-20  2023-09-07    2023-10-26      159
2          6.297244 2023-12-07  2024-01-24    2024-02-14       50
3          5.585429 2023-01-22  2023-02-14    2023-02-17       20
4          3.898568 2023-02-17  2023-03-11    2023-03-15       19
5          3.336877 2023-11-12  2023-11-18    2023-12-07       19
6          2.699556 2024-02-20  2024-02-26    2024-03-01        9
7          0.767196 2024-03-01  2024-03-03    2024-03-06        4
8          0.324161 2023-01-03  2023-01-07    2023-01-18       12
9          0.019817 2023-11-03  2023-11-04    2023-11-07        3
```

## Performance Visualizations Examples

![Cumulative Returns](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/culmulative_returns.png)
![Daily Drawdowns](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/daily_drawdowns.png)
![Monthly Heat Maps](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/monthly_heat_maps.png)
![Rolling Sharpe Ratio](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/rolling_sharpe_ratio.png)

## Liquidation Handling

Unlike many basic backtesting engines, **overfitting** simulates realistic crypto futures trading, including **forced liquidation** based on margin conditions.

The liquidation logic is based on **isolated margin mode** (similar to Binance Futures):

- **Initial Margin** = Entry Price × Quantity / Leverage  
- **Maintenance Margin** = Entry Price × Quantity × Maintenance Margin Rate − Maintenance Amount  
- **Liquidation Price** is then calculated based on whether the position is long or short.

When the price crosses the calculated liquidation level, the position is force-closed and the **entire margin is lost**, just like in real crypto markets.

### Liuqidation Calculation

```python
# For long positions
liquid_price = entry_price - (initial_margin - maintenance_margin)

# For short positions
liquid_price = entry_price + (initial_margin - maintenance_margin)
```

## Supported Order Types
Supports four order types: LIMIT, MARKET, STOP LIMIT, and STOP MARKET. Each behaves according to standard trading conventions.

[NOTE] For MAKRET Orders, the system will automatically execute the trade with "open" price.

**The Rules for Stop Order to Trigger is:** <br>
LONG: Price (High) >= Stop Price <br>
SHORT: Price (low) <= Stop Price
```python
# For Long qty > 0 for short qty < 0
# Example 1. if qty == -1. This means Position is Short
# Example 2. if qty == 1. This means Position is Long
limit_order(symbol: str, qty: float, price: float)
market_order(symbol: str, qty: float)
stop_limit_order(symbol: str, qty: float, price: float, stop_price: float)
stop_market_order(symbol: str, qty: float, stop_price: float)
```

### Stop Order Immediate Rejection Rule
If a STOP LIMIT or STOP MARKET order would trigger immediately upon creation (because the current price already breaches the stop price), the system rejects the order with "STOP order would Immediately Trigger" message.

## Multiple Currency Backtesting
You can simply test multiple currencies by passing data as dict[str, pd.DataFrame]. Here key value should be the name of the currency.

### **[IMPORTANT]** When you are running simulations in multi currency mode please make sure that "timestamp" are identical for every symbols

## Helper Functions for Strategy Definitions
```python
class MyStrategy(Strategy):
    def init(self):
        self.asset = 'BTC'
        
    def next(self, i):
        # Fetch the indicator or other custom column from data
        val = self.val(self.asset, i, "the indicator value") 
        # OHLV data
        open  = self.open(self.asset, i)
        high  = self.high(self.asset, i)
        low   = self.low(self.asset, i)
        close = self.close(self.asset, i)
        o, h, l, c = self.bars(self.asset, i)
        # ACCOUNT data
        position = self.get_position(self.asset)
        balance = self.get_balance()
        open_orders = self.get_open_orders() # returns all open orders regardless of symbols
```

## Upcoming Features

- **Parameter Optimizer**  
  A simple optimizer to help find the best-performing strategy parameters (like SMA windows, thresholds, etc.) based on backtest results.

- **Improved Slippage Modeling**  
  Dynamic slippage models based on volume, volatility, or order size.

> 💡 Got feedback or suggestions? Feel free to open an issue or contribute via pull request.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "overfitting",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "algo, bitcoin, ethereum, crypto, cryptocurrency, crypto derivatives, futures, finance, quantitative, liquidation, solana, systematic, quant, trading",
    "author": null,
    "author_email": "Dohyun Kim <dohyun.k.july@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/ce/ea/5c6f070aef191a23ea144cf23972872dc3f230f88916a957bc57872020f7/overfitting-0.1.8.tar.gz",
    "platform": null,
    "description": "# Overfitting\n\nA robust and modular backtesting engine designed for crypto futures trading strategies.  \nBuilt for speed, simplicity, and accuracy. Overfitting simulates a realistic crypto trading environment \u2014 including **liquidation**, **margin**, and **leverage** \u2014 for stress-testing your strategies.\n\n## Prerequisites\n\nBefore using **Overfitting**, you\u2019ll need to provide your own historical data.  \nThe engine is designed to work with **crypto futures price data**, with **OHLCV format**.\n\n### Required Columns\n\nYour dataset must be a CSV or DataFrame that includes at least the following columns:\n- timestamp, open, high, low, close\n  - `timestamp` should be a **UNIX timestamp in seconds or milliseconds**\n\n## Installation\n    $ pip install overfitting\n\n\n## Usage\n```python\nimport pandas as pd\nfrom overfitting import Strategy\n\ndef load_data():\n    df = pd.read_csv('./data/BTCUSDT.csv')\n    benchamrk_df = pd.read_csv('./data/BTCUSDT.csv') # BTC buy and Hold\n    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')\n    df.set_index('timestamp', inplace=True)\n\n    start_time = pd.to_datetime('2023-01-01 00:00:00')\n    df = df.loc[start_time:]\n    # Compute short and long SMAs\n    df['sma_short'] = df['close'].rolling(window=20).mean().shift(1)\n    df['sma_long'] = df['close'].rolling(window=50).mean().shift(1)\n\n    return df, benchamrk_df\n\nclass MyStrategy(Strategy):\n    def init(self):\n        self.asset = 'BTC'\n        self.set_leverage(self.asset, 1)\n\n    def next(self, i):\n        if i == 0:\n            return\n\n        sma_short = self.val(self.asset, i, \"sma_short\")\n        sma_long = self.val(self.asset, i, \"sma_long\")\n        previous_sma_short = self.val(self.asset, i - 1, \"sma_short\") \n        previous_sma_long = self.val(self.asset, i - 1, \"sma_long\")\n\n        # Also skip if values are not available\n        if (pd.isna(sma_short) or pd.isna(sma_long) or \n            pd.isna(previous_sma_short) or pd.isna(previous_sma_long)):\n            return\n\n        # Fetch the current position\n        position = self.get_position(self.asset)\n\n        # Golden cross (entry)\n        if previous_sma_short <= previous_sma_long and sma_short > sma_long and position.qty == 0:\n            # First fetch current open price which is the target Price\n            open_price = self.open(self.asset, i)\n            # Determine Lot Size\n            lot_size = self.get_balance() // open_price\n            # Create LIMIT ORDER\n            self.limit_order(self.asset, lot_size, open_price)\n\n        # Death cross (exit)\n        if previous_sma_short >= previous_sma_long and sma_short < sma_long and position.qty > 0:\n            self.market_order(self.asset, -position.qty)\n\nbacktest_data, benchmark_data = load_data()\nstrategy = MyStrategy(\n    data=backtest_data,\n    benchmark=benchmark_data, # Default = None Optional\n    initial_capital=100_000, # Default Optional\n    commission_rate=0.0002, # Default Optional\n    maint_maring_rate=0.005, # Default Optional\n    maint_amount=50  # Default Optional\n)\nreturns = strategy.run()\nstrategy.plot(returns)\n```\n\nResults\n-------\n```text\nPerformance Summary\nNumber of Years               1.66000000\nStart Date           2023-01-01 00:00:00\nEnd Date             2024-08-29 00:00:00\nInitial Balance         100,000.00000000\nFinal Balance           205,328.91120000\nCAGR                          0.52684228\nCumulative Return             2.05328911\nSharpe Ratio                  1.24678659\nSortino Ratio                 3.54979579\nMax Drawdown                 -0.26332695\nDaily Value At Risk          -0.04147282\nSkew                          0.44515551\nKurtosis                      2.66444346\nTotal Trades                182.00000000\nWinning Trades               69.00000000\nLosing Trades               113.00000000\nWin Rate (%)                 37.91208791\nGross Profit            399,044.19246000\nGross Loss             -293,715.28126000\nNet Profit              105,328.91120000\nAvg Return (%)                0.38834383\nAvg Profit (%)                3.54140613\nAvg Loss (%)                 -1.53697740\n  Net drawdown in %  Peak date Valley date Recovery date Duration\n0         26.332695 2024-03-13  2024-06-30           NaT      NaN\n1         19.678014 2023-03-20  2023-09-07    2023-10-26      159\n2          6.297244 2023-12-07  2024-01-24    2024-02-14       50\n3          5.585429 2023-01-22  2023-02-14    2023-02-17       20\n4          3.898568 2023-02-17  2023-03-11    2023-03-15       19\n5          3.336877 2023-11-12  2023-11-18    2023-12-07       19\n6          2.699556 2024-02-20  2024-02-26    2024-03-01        9\n7          0.767196 2024-03-01  2024-03-03    2024-03-06        4\n8          0.324161 2023-01-03  2023-01-07    2023-01-18       12\n9          0.019817 2023-11-03  2023-11-04    2023-11-07        3\n```\n\n## Performance Visualizations Examples\n\n![Cumulative Returns](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/culmulative_returns.png)\n![Daily Drawdowns](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/daily_drawdowns.png)\n![Monthly Heat Maps](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/monthly_heat_maps.png)\n![Rolling Sharpe Ratio](https://raw.githubusercontent.com/dohyunkjuly/overfitting/main/documents/rolling_sharpe_ratio.png)\n\n## Liquidation Handling\n\nUnlike many basic backtesting engines, **overfitting** simulates realistic crypto futures trading, including **forced liquidation** based on margin conditions.\n\nThe liquidation logic is based on **isolated margin mode** (similar to Binance Futures):\n\n- **Initial Margin** = Entry Price \u00d7 Quantity / Leverage  \n- **Maintenance Margin** = Entry Price \u00d7 Quantity \u00d7 Maintenance Margin Rate \u2212 Maintenance Amount  \n- **Liquidation Price** is then calculated based on whether the position is long or short.\n\nWhen the price crosses the calculated liquidation level, the position is force-closed and the **entire margin is lost**, just like in real crypto markets.\n\n### Liuqidation Calculation\n\n```python\n# For long positions\nliquid_price = entry_price - (initial_margin - maintenance_margin)\n\n# For short positions\nliquid_price = entry_price + (initial_margin - maintenance_margin)\n```\n\n## Supported Order Types\nSupports four order types: LIMIT, MARKET, STOP LIMIT, and STOP MARKET. Each behaves according to standard trading conventions.\n\n[NOTE] For MAKRET Orders, the system will automatically execute the trade with \"open\" price.\n\n**The Rules for Stop Order to Trigger is:** <br>\nLONG: Price (High) >= Stop Price <br>\nSHORT: Price (low) <= Stop Price\n```python\n# For Long qty > 0 for short qty < 0\n# Example 1. if qty == -1. This means Position is Short\n# Example 2. if qty == 1. This means Position is Long\nlimit_order(symbol: str, qty: float, price: float)\nmarket_order(symbol: str, qty: float)\nstop_limit_order(symbol: str, qty: float, price: float, stop_price: float)\nstop_market_order(symbol: str, qty: float, stop_price: float)\n```\n\n### Stop Order Immediate Rejection Rule\nIf a STOP LIMIT or STOP MARKET order would trigger immediately upon creation (because the current price already breaches the stop price), the system rejects the order with \"STOP order would Immediately Trigger\" message.\n\n## Multiple Currency Backtesting\nYou can simply test multiple currencies by passing data as dict[str, pd.DataFrame]. Here key value should be the name of the currency.\n\n### **[IMPORTANT]** When you are running simulations in multi currency mode please make sure that \"timestamp\" are identical for every symbols\n\n## Helper Functions for Strategy Definitions\n```python\nclass MyStrategy(Strategy):\n    def init(self):\n        self.asset = 'BTC'\n        \n    def next(self, i):\n        # Fetch the indicator or other custom column from data\n        val = self.val(self.asset, i, \"the indicator value\") \n        # OHLV data\n        open  = self.open(self.asset, i)\n        high  = self.high(self.asset, i)\n        low   = self.low(self.asset, i)\n        close = self.close(self.asset, i)\n        o, h, l, c = self.bars(self.asset, i)\n        # ACCOUNT data\n        position = self.get_position(self.asset)\n        balance = self.get_balance()\n        open_orders = self.get_open_orders() # returns all open orders regardless of symbols\n```\n\n## Upcoming Features\n\n- **Parameter Optimizer**  \n  A simple optimizer to help find the best-performing strategy parameters (like SMA windows, thresholds, etc.) based on backtest results.\n\n- **Improved Slippage Modeling**  \n  Dynamic slippage models based on volume, volatility, or order size.\n\n> \ud83d\udca1 Got feedback or suggestions? Feel free to open an issue or contribute via pull request.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Robust Futures CryptoCurrency Backtesting Library.",
    "version": "0.1.8",
    "project_urls": {
        "Author": "https://github.com/dohyunkjuly",
        "Homepage": "https://github.com/dohyunkjuly/overfitting",
        "Source": "https://github.com/dohyunkjuly/overfitting"
    },
    "split_keywords": [
        "algo",
        " bitcoin",
        " ethereum",
        " crypto",
        " cryptocurrency",
        " crypto derivatives",
        " futures",
        " finance",
        " quantitative",
        " liquidation",
        " solana",
        " systematic",
        " quant",
        " trading"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "46058c17ea4c5cdc41b2905b2ac567e44d6baf58ebb29812a5da7e95fd7dac1e",
                "md5": "5091007a1a90b1b4d0974e4e0ea2633f",
                "sha256": "5d68c8f050d55325618efe32767f052f3044c39d4b3873e4a3fa39a904319563"
            },
            "downloads": -1,
            "filename": "overfitting-0.1.8-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5091007a1a90b1b4d0974e4e0ea2633f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 26339,
            "upload_time": "2025-09-08T07:52:28",
            "upload_time_iso_8601": "2025-09-08T07:52:28.215125Z",
            "url": "https://files.pythonhosted.org/packages/46/05/8c17ea4c5cdc41b2905b2ac567e44d6baf58ebb29812a5da7e95fd7dac1e/overfitting-0.1.8-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ceea5c6f070aef191a23ea144cf23972872dc3f230f88916a957bc57872020f7",
                "md5": "086b1f2433be87d1c30dc69572de38c7",
                "sha256": "08456dfc41ae2cd4f43579daf346ec308ce3d4f503dac27720e4f4acc825b203"
            },
            "downloads": -1,
            "filename": "overfitting-0.1.8.tar.gz",
            "has_sig": false,
            "md5_digest": "086b1f2433be87d1c30dc69572de38c7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 27274,
            "upload_time": "2025-09-08T07:52:29",
            "upload_time_iso_8601": "2025-09-08T07:52:29.381200Z",
            "url": "https://files.pythonhosted.org/packages/ce/ea/5c6f070aef191a23ea144cf23972872dc3f230f88916a957bc57872020f7/overfitting-0.1.8.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-08 07:52:29",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dohyunkjuly",
    "github_project": "overfitting",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "numpy",
            "specs": [
                [
                    ">=",
                    "1.17.0"
                ]
            ]
        },
        {
            "name": "pandas",
            "specs": [
                [
                    ">=",
                    "0.25.0"
                ]
            ]
        },
        {
            "name": "seaborn",
            "specs": []
        },
        {
            "name": "matplotlib",
            "specs": []
        },
        {
            "name": "scipy",
            "specs": []
        }
    ],
    "lcname": "overfitting"
}
        
Elapsed time: 1.64757s