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.3.tar.gz (205.9 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.3-py3-none-any.whl (207.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cnlib-0.1.3.tar.gz
  • Upload date:
  • Size: 205.9 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.3.tar.gz
Algorithm Hash digest
SHA256 c9f97edc23af5108be5c2c73831da2fb7bbbbad029f4b3b24bcf14afe47eceee
MD5 e6785ff181164feb97b111b018330204
BLAKE2b-256 ee7bf54a2c8d4ac3f1321a47936aeba9f99cffdc2784862b9d71c291bede6324

See more details on using hashes here.

File details

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

File metadata

  • Download URL: cnlib-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 207.0 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ac22d948e681e9da1a1e10b9a1563cce2a9af700e78482beae07940c2b61ba92
MD5 ca06cdf2d7a0d2ff62a87650f9305f17
BLAKE2b-256 1170fadfc6137755489c31bce46254d45b3f4a01dfba870837a4dfdf4aae5ec1

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