Skip to main content

Algorithmic trading competition library — leveraged long/short backtest engine

Project description

cnlib — Code Night Algorithmic Trading Library

Leveraged long/short backtest engine for the Code Night trading competition.
Participants write a strategy, the platform runs it, results are ranked.


How It Works

Three synthetic crypto assets — Varlik_A, Varlik_B, Varlik_C — modeled after BTC, SOL, and XRP volatility profiles.

  • Starting capital: $3,000
  • Available leverage: 1x, 2x, 3x, 5x, 10x
  • Positions: Long (profit when price rises) or Short (profit when price falls)
  • predict() is called on every candle close — you decide what to do next

Installation

git clone https://github.com/IYTE-Yazilim-Toplulugu/code-night-lib.git
cd code-night-lib
pip install -e .

Quickstart

Create a strategy.py file anywhere:

from cnlib.base_strategy import BaseStrategy

class MyStrategy(BaseStrategy):
    def predict(self, data):
        closes = data["Varlik_A"]["close"]

        if closes.iloc[-1] > closes.iloc[-2]:
            signal = 1   # price went up → go long
        else:
            signal = -1  # price went down → go short

        return [
            {"coin": "Varlik_A", "signal": signal, "allocation": 0.5, "leverage": 2},
            {"coin": "Varlik_B", "signal": 0,      "allocation": 0.0, "leverage": 1},
            {"coin": "Varlik_C", "signal": 0,      "allocation": 0.0, "leverage": 1},
        ]

Run the backtest:

from cnlib import backtest
from strategy import MyStrategy

result = backtest.run(MyStrategy(), initial_capital=3000.0)
result.print_summary()

Output:

=======================================================
  BACKTEST RESULTS
=======================================================
  Initial Capital     : $    3,000.00
  Final Portfolio     : $    3,220.35
  Net P&L             : $     +220.35
  Return              :      +7.3450%
-------------------------------------------------------
  Total Candles       :         1,000
  Total Trades        :            54
  Liquidations        :             0
  Liquidation Loss    : $        0.00
  Validation Errors   :             0
=======================================================

The predict() Contract

Called on every candle close. Receives all historical OHLCV data up to the current candle.

Input

data = {
    "Varlik_A": pd.DataFrame,  # columns: timestamp, open, high, low, close, volume
    "Varlik_B": pd.DataFrame,
    "Varlik_C": pd.DataFrame,
}

data["Varlik_A"] only contains candles up to now — no future data leakage.

Output

A list with exactly one entry per coin:

return [
    {"coin": "Varlik_A", "signal": 1,  "allocation": 0.5, "leverage": 10},
    {"coin": "Varlik_B", "signal": -1, "allocation": 0.3, "leverage": 2},
    {"coin": "Varlik_C", "signal": 0,  "allocation": 0.0, "leverage": 1},
]
Field Type Description
coin str One of Varlik_A, Varlik_B, Varlik_C
signal int 1 = long, -1 = short, 0 = hold (close if open)
allocation float Fraction of portfolio to allocate [0.0 – 1.0]
leverage int 1, 2, 3, 5, or 10

Rules

  • signal=0allocation must be 0.0
  • Sum of all active allocations cannot exceed 1.0
  • Leverage must be one of {1, 2, 3, 5, 10}
  • Each coin can have at most one open position at a time
  • Violations raise ValidationError — that candle is skipped, positions are held

Liquidation

Positions are force-closed when price hits the liquidation threshold. Capital is fully lost on liquidation — no cash is returned.

Long  → liquidated when price ≤ entry × (1 - 1/leverage)
Short → liquidated when price ≥ entry × (1 + 1/leverage)

Example: long at $100 with 10x leverage
  Liquidation price = 100 × (1 - 1/10) = $90
  If price drops to $90 or below → position wiped out

Position Sizing

allocation is a fraction of your current total portfolio value, not just cash:

# Portfolio value: $3,000, allocation=0.4, leverage=5
allocated_capital = 3000 × 0.4 = $1,200
effective_exposure = $1,200 × 5 = $6,000

# If price rises 2%:
pnl = $1,200 × 5 × 0.02 = +$120  (+10% on allocated capital)

Strategy State

self persists between candles — use it to store state:

class MyStrategy(BaseStrategy):
    def __init__(self):
        super().__init__()
        self.prev_signal = {"Varlik_A": 0, "Varlik_B": 0, "Varlik_C": 0}
        self.entry_prices = {}

    def predict(self, data):
        # self.candle_index → current candle number (0-based)
        # self.coin_data    → full OHLCV history dict
        ...

Result Object

result = backtest.run(strategy)

result.print_summary()              # formatted console output
result.portfolio_dataframe()        # time-series DataFrame: candle_index, portfolio_value, cash, *_price
result.trade_history                # list of dicts for candles where trades occurred
result.final_portfolio_value        # float
result.net_pnl                      # float
result.return_pct                   # float
result.total_liquidations           # int
result.validation_errors            # int

Project Structure

code-night-lib/
├── setup.py
├── data/
│   ├── generate_dummy.py     # regenerate synthetic data
│   ├── Varlik_A.parquet
│   ├── Varlik_B.parquet
│   └── Varlik_C.parquet
├── cnlib/
│   ├── base_strategy.py      # BaseStrategy — inherit this
│   ├── backtest.py           # backtest.run()
│   ├── portfolio.py          # position & liquidation logic
│   └── validator.py          # predict() output validation
└── example/
    ├── strateji_example.py   # SMA crossover example
    ├── advanced_strategy.py  # RSI + volume filter example
    └── run_example.py        # end-to-end runner

Regenerating Data

cd data
python generate_dummy.py

Edit generate_dummy.py to change candle count, seed, or volatility profiles.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cnlib-0.1.0.tar.gz (204.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cnlib-0.1.0-py3-none-any.whl (205.7 kB view details)

Uploaded Python 3

File details

Details for the file cnlib-0.1.0.tar.gz.

File metadata

  • Download URL: cnlib-0.1.0.tar.gz
  • Upload date:
  • Size: 204.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for cnlib-0.1.0.tar.gz
Algorithm Hash digest
SHA256 55dde56f9fba338d4540235e53074c0c5eaf5b5bb454a3309f58228630013670
MD5 9ebeaedf36cd3e7146329c9d2b28ace4
BLAKE2b-256 9342e80af60e2aac5f20272ce7ecfa94faf6b6136d44a567e33fc951260bc6e6

See more details on using hashes here.

File details

Details for the file cnlib-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cnlib-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 205.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for cnlib-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 49555a4516937809e8b63649123285f678d331974156a002b4524836aac13b6e
MD5 8f51daa0f26aaad47c6d9b72f390e431
BLAKE2b-256 df6222e3eec2d4e037c861668db1ec73976bcb21ccc8755a47f7c80bf7741f7e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page