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 — kapcoin-usd_train, metucoin-usd_train, tamcoin-usd_train — 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

pip install cnlib

Or from source:

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["kapcoin-usd_train"]["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": "kapcoin-usd_train",  "signal": signal, "allocation": 0.5, "leverage": 2},
            {"coin": "metucoin-usd_train", "signal": 0,      "allocation": 0.0, "leverage": 1},
            {"coin": "tamcoin-usd_train",  "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 = {
    "kapcoin-usd_train":  pd.DataFrame,  # columns: Date, Open, High, Low, Close, Volume
    "metucoin-usd_train": pd.DataFrame,
    "tamcoin-usd_train":  pd.DataFrame,
}

Each DataFrame only contains candles up to now — no future data leakage.

Output

A list with exactly one entry per coin, every candle:

return [
    {"coin": "kapcoin-usd_train",  "signal": 1,  "allocation": 0.5, "leverage": 10},
    {"coin": "metucoin-usd_train", "signal": -1, "allocation": 0.3, "leverage": 2},
    {"coin": "tamcoin-usd_train",  "signal": 0,  "allocation": 0.0, "leverage": 1},
]
Field Type Description
coin str One of kapcoin-usd_train, metucoin-usd_train, tamcoin-usd_train
signal int 1 = long, -1 = short, 0 = close any open position
allocation float Fraction of portfolio to allocate [0.0 – 1.0]
leverage int 1, 2, 3, 5, or 10

Rules

  • All three coins must appear in every list — no omissions
  • To hold an open position, re-state the same signal
  • To stay flat, use signal=0, allocation=0.0
  • signal=0allocation must be 0.0
  • Sum of active allocations cannot exceed 1.0
  • Leverage must be one of {1, 2, 3, 5, 10} (ignored when signal=0)
  • Violations raise ValidationError — that candle is skipped, positions are held

Liquidation

Positions are force-closed when the candle's intrabar extreme hits the liquidation threshold — longs on the candle Low, shorts on the candle High. Capital is fully lost on liquidation — no cash is returned.

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

Example: long at $100 with 10x leverage
  Liquidation price = 100 × (1 - 1/10) = $90
  If the candle Low touches $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 = {
            "kapcoin-usd_train":  0,
            "metucoin-usd_train": 0,
            "tamcoin-usd_train":  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/
├── pyproject.toml
├── cnlib/
│   ├── base_strategy.py      # BaseStrategy — inherit this
│   ├── backtest.py           # backtest.run()
│   ├── portfolio.py          # position & liquidation logic
│   ├── validator.py          # predict() output validation
│   └── data/
│       ├── kapcoin-usd_train.parquet
│       ├── metucoin-usd_train.parquet
│       └── tamcoin-usd_train.parquet
└── docs/
    └── README.md             # participant guide (Turkish)

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.4.tar.gz (206.4 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.4-py3-none-any.whl (207.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cnlib-0.1.4.tar.gz
  • Upload date:
  • Size: 206.4 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.4.tar.gz
Algorithm Hash digest
SHA256 424a91f0b2150d822fe4271eee22dede1e4f28e4208a6d56363ab4f30af70cd4
MD5 75bb94a8984dd3e1c274ca320a10f5b9
BLAKE2b-256 f8a21d89d17a7faa709582da54254da7220e9d4ee570a922cf48c89de6f26f54

See more details on using hashes here.

File details

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

File metadata

  • Download URL: cnlib-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 207.4 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1ebb6c167b9036d662d0a27af77ee1099769e30cd8b6cd73ed90d9eec87f946f
MD5 8d5e2bef44e27e7f78e43646ef65e1f3
BLAKE2b-256 99425989dfe37384f7ab6e593c745d101453d86a449ca89f02a60fea29b4baef

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