CoinHarbour.xyz Dev Documentation

Welcome to the CoinHarbour.xyz developer documentation. Here you can find information on the callback functions, helper methods, and dataset fields for the CoinHarbour.xyz algorithmic trading platform. Want a feature added? Email us here and we'll add it ASAP.

Assets Supported

Name Symbol TradingView Link Years of Data Available Max Leverage
AAVE AAVE View 3 50
APE APE View 1 50
ARB ARB View 1 75
ATOM ATOM View 5 60
AVAX AVAX View 3 60
BNB BNB View 6 100
BTC BTC View 7 100
DOGE DOGE View 5 100
ETH ETH View 7 100
LINK LINK View 5 100
LTC LTC View 6 100
NEAR NEAR View 3 50
OP OP View 1 50
PEPE PEPE View < 1 Year 50
POL POL View < 1 Year 50
Ripple XRP View 5 100
SATS SATS View < 1 Year 50
SHIB SHIB View 3 50
Solana SOL View 3 100
UNI UNI View 3 60
WIF WIF View < 1 Year 50

Callback Functions

__init__(self, trading_helper: TradingHelper)

Initializes the algorithm with the trading helper.

on_ticker_recd(self, ticker_data: dict)

Called when a new ticker data is received. The ticker_data object contains fields as described in the Dataset Fields section.

on_algo_start(self)

Called when the algorithm starts.

on_algo_stop(self)

Called when the algorithm stops.

on_order_success(self)

Called when an order is successfully executed.

on_order_error(self, error)

Called when an error occurs during order execution.

Dataset Fields

The ticker_data object contains the following fields for each asset, accessed via ticker_data[asset_symbol] with the below fields:

Field Type Description
timestamp int Unix timestamp of the data point in seconds
open float Opening price of the asset for this time period
high float Highest price reached during this time period
low float Lowest price reached during this time period
close float Closing price of the asset for this time period
volume float Total trading volume in base asset units (e.g., BTC for BTC/USD)
quote_volume float Total trading volume in quote currency (USD)
trades int Number of trades executed during this time period
taker_buy_base_volume float Volume of base asset bought by takers (market buyers)
taker_buy_quote_volume float Volume in quote currency (USD) bought by takers

Helper Methods

open_position(asset: str, long: bool, collateral_usd: float, leverage: float, slippage_percent: float = 0.03)

Opens a new position or adds to an existing position.

Input Parameters:

Output:

Position object or None if the operation fails.

Example:

position = self.trading_helper.open_position("BTC", True, 1000, 2)
print(position)
# Output:
{
    "chain": "arbitrum",
    "index_token_symbol": "BTC",
    "collateral_token_symbol": "USDC",
    "start_token_symbol": "USDC",
    "is_long": true,
    "size_delta_usd": 2000.0,  # position_size_usd = collateral_usd * leverage
    "leverage": 2.0,
    "slippage_percent": 0.03,
    "timestamp": 1679529600,
    "asset_price_at_open": 45000.0,
    "fee": 0.25  # tx_fee (0.15) + network_fee (0.1)
}

close_position(asset: str, long: bool, position_percent_to_close: float = 100, slippage_percent: float = 0.03)

Closes an existing position partially or fully.

Input Parameters:

Output:

Position object or None if the operation fails.

Example:

position = self.trading_helper.close_position("BTC", True, 50)
print(position)
# Output:
{
    "chain": "arbitrum",
    "index_token_symbol": "BTC",
    "collateral_token_symbol": "USDC",
    "start_token_symbol": "USDC",
    "is_long": true,
    "size_delta_usd": 1000.0,  # Amount being closed (50% of 2000)
    "leverage": 2.0,
    "slippage_percent": 0.03,
    "timestamp": 1679529600,
    "asset_price_at_close": 46000.0,
    "fee": 0.25,  # tx_fee (0.15) + network_fee (0.1)
}

get_usd_balance()

Retrieves the current USD balance.

Output:

float

Example:

balance = self.trading_helper.get_usd_balance()
print(balance)
# Output: 10500.75  # Current USD balance

get_open_positions()

Retrieves all open positions.

Output:

Dictionary of open positions

Example:

positions = self.trading_helper.get_open_positions()
print(positions)
# Output:
{
    "BTC_long": {
        "chain": "arbitrum",
        "index_token_symbol": "BTC",
        "collateral_token_symbol": "USDC",
        "start_token_symbol": "USDC",
        "is_long": true,
        "size_delta_usd": 2000.0,
        "leverage": 2.0,
        "slippage_percent": 0.03,
        "timestamp": 1679529600,
        "asset_price_at_open": 45000.0,
        "fee": 0.25  # tx_fee (0.15) + network_fee (0.1)
    },
    "ETH_short": {
        "chain": "arbitrum",
        "index_token_symbol": "ETH",
        "collateral_token_symbol": "USDC",
        "start_token_symbol": "USDC",
        "is_long": false,
        "size_delta_usd": 1500.0,
        "leverage": 3.0,
        "slippage_percent": 0.03,
        "timestamp": 1679529600,
        "asset_price_at_open": 2800.0,
        "fee": 0.25  # tx_fee (0.15) + network_fee (0.1)
    }
}

get_previous_tickers(asset: str, previous_tickers: int)

Retrieves previous ticker data for a specific asset.

Input Parameters:

Output:

List of dictionaries containing previous ticker data

Example:

tickers = self.trading_helper.get_previous_tickers("BTC", 2)
print(tickers)
# Output:
[
    {
        "timestamp": 1679529600,
        "open": 44950.0,
        "high": 45100.0,
        "low": 44900.0,
        "close": 45000.0,
        "volume": 125.5,
        "quote_volume": 5647500.0,
        "trades": 2450,
        "taker_buy_base_volume": 75.3,
        "taker_buy_quote_volume": 3388500.0
    },
    {
        "timestamp": 1679529660,
        "open": 45000.0,
        "high": 45200.0,
        "low": 44980.0,
        "close": 45150.0,
        "volume": 98.2,
        "quote_volume": 4428090.0,
        "trades": 1850,
        "taker_buy_base_volume": 58.9,
        "taker_buy_quote_volume": 2657385.0
    }
]

Pricing

Transaction fees are structured as follows:

Simple Moving Average

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.prices = []
        self.trading_helper = trading_helper

    def on_ticker_recd(self, ticker_data):
        current_price = ticker_data["BTC"]["close"]
        self.prices.append(current_price)

        if len(self.prices) > 5:
            self.prices.pop(0)

        if len(self.prices) == 5:
            sma_5 = sum(self.prices) / 5
            balance = self.trading_helper.get_usd_balance()

            if balance > 0 and current_price > sma_5:
                self.trading_helper.open_position(
                    asset="BTC",
                    long=True,
                    collateral_usd=balance,
                    leverage=1,
                    slippage_percent=0.03,
                )
            else:
                if len(self.trading_helper.get_open_positions()) > 0:
                    self.trading_helper.close_position(
                        asset="BTC",
                        long=True,
                        position_percent_to_close=100,
                        slippage_percent=0.01,
                    )

    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass  
        

Relative Strength Index

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.prices = []
        self.trading_helper = trading_helper

    def on_ticker_recd(self, ticker_data):
        current_price = ticker_data["BTC"]["close"]
        self.prices.append(current_price)

        if len(self.prices) > 14:
            self.prices.pop(0)

        if len(self.prices) == 14:
            gains = 0
            losses = 0
            for i in range(1, 14):
                gains = gains +(max(0, self.prices[i] - self.prices[i - 1]))
                losses = losses + (max(0, self.prices[i - 1] - self.prices[i]))
            rsi = 100 - (100 / (1 + gains / losses))

            # Open a long position if RSI < 30 (oversold)
            if rsi < 30 :
                self.trading_helper.close_position( 
                    asset="ETH", 
                    long=False, 
                    position_percent_to_close=100, 
                    slippage_percent=0.01 
                )
                balance = self.trading_helper.get_usd_balance()
                self.trading_helper.open_position( 
                    asset="ETH", 
                    long=True, 
                    collateral_usd=balance, 
                    leverage=2, 
                    slippage_percent=0.02 
                )

            # Open a short position if RSI > 70 (overbought)
            elif rsi > 70:
                self.trading_helper.close_position( 
                    asset="ETH", 
                    long=True, 
                    position_percent_to_close=100, 
                    slippage_percent=0.01 
                )
                balance = self.trading_helper.get_usd_balance()
                self.trading_helper.open_position( 
                    asset="ETH", 
                    long=False, 
                    collateral_usd=balance, 
                    leverage=2, 
                    slippage_percent=0.02
                )

    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass
        

Moving Average Convergence Divergence

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.prices = []
        self.trading_helper = trading_helper
        self.position_open = False

    def on_ticker_recd(self, ticker_data):
        current_price = ticker_data["BTC"]["close"]
        self.prices.append(current_price)

        if len(self.prices) >= 26:
            # Calculate MACD and signal line
            ema12 = sum(self.prices[-12:]) / 12
            ema26 = sum(self.prices[-26:]) / 26
            macd = ema12 - ema26
            signal_line = sum(self.prices[-9:]) / 9
            balance = self.trading_helper.get_usd_balance()
            
            # Determine leverage and collateral dynamically
            macd_diff = abs(macd - signal_line)
            leverage = 1 + min(4, macd_diff / 0.01)  # Max 5x
            collateral_usd = balance * min(1, macd_diff / 0.05)  

            if macd < signal_line and collateral_usd > 0:
                # Open a short position if MACD goes below signal line
                self.trading_helper.open_position(
                    asset="BTC", 
                    long=False, 
                    collateral_usd=collateral_usd, 
                    leverage=leverage, 
                    slippage_percent=0.02
                )
                self.position_open = True

            elif macd > signal_line and self.position_open:
                self.trading_helper.close_position(
                    asset="BTC", 
                    long=False, 
                    position_percent_to_close=100, 
                    slippage_percent=0.01
                )
                self.position_open = False

    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass
        

Moving Average Crossover

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.short_prices = []
        self.long_prices = []
        self.trading_helper = trading_helper

    def on_ticker_recd(self, ticker_data):
        current_price = ticker_data["ETH"]["close"]
        self.short_prices.append(current_price)
        self.long_prices.append(current_price)

        # Maintain short and long windows (5 and 20 periods)
        if len(self.short_prices) > 5:
            self.short_prices.pop(0)
        if len(self.long_prices) > 20:
            self.long_prices.pop(0)

        # Calculate moving averages
        if len(self.short_prices) == 5 and len(self.long_prices) == 20:
            sma_short = sum(self.short_prices) / 5
            sma_long = sum(self.long_prices) / 20
            balance = self.trading_helper.get_usd_balance()

            # Buy if short-term MA crosses above long-term MA;
            if sma_short > sma_long:
                self.trading_helper.open_position(
                    asset="ETH",
                    long=True,
                    collateral_usd=balance,
                    leverage=1,
                    slippage_percent=0.03,
                )
            elif sma_short < sma_long:
                self.trading_helper.close_position(
                    asset="ETH",
                    long=True,
                    position_percent_to_close=100,
                    slippage_percent=0.01,
                )
    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass
        

Pairs Trading

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.spread_history = []
        self.trading_helper = trading_helper
        self.position_open = False
        self.pair = ("BTC", "ETH")  # Example pair

    def on_ticker_recd(self, ticker_data):
        btc_price = ticker_data[self.pair[0]]["close"]
        eth_price = ticker_data[self.pair[1]]["close"]

        spread = btc_price / eth_price
        self.spread_history.append(spread)

        if len(self.spread_history) > 30:
            self.spread_history.pop(0)

        if len(self.spread_history) == 30:
            avg_spread = sum(self.spread_history) / len(self.spread_history)
            current_spread = self.spread_history[-1]
            balance = self.trading_helper.get_usd_balance()
            collateral_usd = balance * 0.25
            leverage = 2

            if current_spread > avg_spread * 1.05 and not self.position_open:
                self.trading_helper.open_position(
                    asset=self.pair[0],
                    long=False,
                    collateral_usd=collateral_usd,
                    leverage=leverage,
                    slippage_percent=0.02,
                )
                self.trading_helper.open_position(
                    asset=self.pair[1],
                    long=True,
                    collateral_usd=collateral_usd,
                    leverage=leverage,
                    slippage_percent=0.02,
                )
                self.position_open = True

            elif current_spread < avg_spread * 0.95 and not self.position_open:
                self.trading_helper.open_position(
                    asset=self.pair[0],
                    long=True,
                    collateral_usd=collateral_usd,
                    leverage=leverage,
                    slippage_percent=0.02,
                )
                self.trading_helper.open_position(
                    asset=self.pair[1],
                    long=False,
                    collateral_usd=collateral_usd,
                    leverage=leverage,
                    slippage_percent=0.02,
                )
                self.position_open = True

            elif avg_spread * 0.98 < current_spread < avg_spread * 1.02:
                if self.position_open: 
                    self.trading_helper.close_position(
                        asset=self.pair[0],
                        long=(current_spread < avg_spread),
                        position_percent_to_close=100,
                        slippage_percent=0.01,
                    )
                    self.trading_helper.close_position(
                        asset=self.pair[1],
                        long=(current_spread > avg_spread),
                        position_percent_to_close=100,
                        slippage_percent=0.01,
                    )
                    self.position_open = False
    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass
        

Bollinger Bands

class MyAlgorithm:
    def __init__(self, trading_helper):
        self.prices = []
        self.trading_helper = trading_helper
        self.open = False
        self.long = None

    def on_ticker_recd(self, ticker_data):
        current_price = ticker_data["BTC"]["close"]
        self.prices.append(current_price)

        if len(self.prices) > 20:
            self.prices.pop(0)

        if len(self.prices) == 20:
            # Calculate Bollinger Bands
            avg_price = sum(self.prices) / 20
            std_dev = (sum((p - avg_price)**2 for p in self.prices)/20)**0.5
            upper_band = avg_price + (2 * std_dev)
            lower_band = avg_price - (2 * std_dev)
            balance = self.trading_helper.get_usd_balance()

            if current_price > upper_band:
                # Open a short position if price breaks above upper band
                if not self.open or self.long:
                    if self.long:
                        # Close any existing long position
                        self.trading_helper.close_position(
                            asset="BTC", 
                            long=True, 
                            position_percent_to_close=100, 
                            slippage_percent=0.01
                        )
                    # Open a short position
                    self.trading_helper.open_position(
                        asset="BTC", 
                        long=False, 
                        collateral_usd=balance / 3, 
                        leverage=2, 
                        slippage_percent=0.02
                    )
                    self.open = True
                    self.long = False

            elif current_price < lower_band:
                # Open a long position if price drops below lower band
                if not self.open or not self.long:
                    if self.open and not self.long:
                        # Close any existing short position
                        self.trading_helper.close_position(
                            asset="BTC", 
                            long=False, 
                            position_percent_to_close=100, 
                            slippage_percent=0.01
                        )
                    # Open a long position
                    self.trading_helper.open_position(
                        asset="BTC", 
                        long=True, 
                        collateral_usd=balance / 3, 
                        leverage=2, 
                        slippage_percent=0.02
                    )
                    self.open = True
                    self.long = True

            elif current_price > avg_price and self.open and not self.long:
                # Close short if price reverts to the mean after upper breakout
                self.trading_helper.close_position(
                    asset="BTC", 
                    long=False, 
                    position_percent_to_close=100, 
                    slippage_percent=0.01
                )
                self.open = False

            elif current_price < avg_price and self.open and self.long:
                # Close long if price reverts to the mean after lower breakout
                self.trading_helper.close_position(
                    asset="BTC", 
                    long=True, 
                    position_percent_to_close=100, 
                    slippage_percent=0.01
                )
                self.open = False
    def on_algo_start(self):
        pass

    def on_algo_stop(self):
        pass

    def on_order_success(self):
        pass

    def on_order_error(self, error):
        pass
        

Deployment Returns

Assets 2017 2018 2019 2020 2021 2022 2023 2024
BTC 205.61 -72.12 97.82 270.27 72.69 -62.02 146.79 59.18
ETH 130.99 -80.7 -1.97 465.62 400.93 -67.72 91.4 5.56

Available Libraries

Library Version Docs
pandas 2.2.2 Link
numpy 1.26.3 Link
ta 0.11.0 Link
matplotlib 3.9.1.post1 Link
scipy 1.14.0 Link
quantstats 0.0.62 Link
cvxopt 1.3.0 Link
cvxpy 1.3.1 Link
hmmlearn 0.3.2 Link
hurst 0.0.5 Link
arch 7.1.0 Link
keras 2.14.0 Link
pytz 2024.1 Link
scikit-learn 1.3.1 Link
statsmodels 0.14.1 Link
xgboost 1.7.6 Link