stock-trek 0.7.1

Stock Trek time-series analysis
Documentation

stock-trek

A lightweight, composable time series and statistical toolkit designed for running crypto bots on stock-trek.com. Rust-native core with optional Python bindings

Overview

stock-trek provides core abstractions and utilities for working with market data, including:

  • Order books
  • Aligned/Rolling windows
  • Ticks
  • Statistical and analytical functions

Installation

Add to your Cargo.toml:

[dependencies]
stock-trek = "0.7.1"

Python Bindings (coming soon)

stock-trek will also provide Python bindings in the future, available for installation via

pip install stock-trek

Usage

Implement the Strategy and Default traits for your algorithm and register it with the annotation #[register_strategy(default)]. An example implementing a cost averaging strategy follows:

use stock_trek::prelude::*;
use std::cmp::Ordering;

pub struct CostAveraging {
    key_exchange: ScratchKey<ExchangeId>,
    key_market_exists: ScratchKey<bool>,
    key_satoshi_price: ScratchKey<f64>,
    key_satoshi_quantity: ScratchKey<f64>,
}

impl Default for CostAveraging {
    fn default() -> Self {
        Self {
            key_exchange: ScratchKey::new_required("EXCHANGE"),
            key_market_exists: ScratchKey::new_optional("MARKET_EXISTS", false),
            key_satoshi_price: ScratchKey::new_required("SATOSHI_PRICE"),
            key_satoshi_quantity: ScratchKey::new_required("SATOSHI_QUANTITY"),
        }
    }
}

#[register_strategy(default)]
impl Strategy for CostAveraging {
    fn preferences(&self) -> Preferences {
        Preferences {
            rounding: Rounding {
                price: RoundingStrategy::ToZero,
                quantity: RoundingStrategy::ToZero,
                callback_rate: RoundingStrategy::ToZero,
            },
            multi_leg: MultiLeg {
                if_different_price_unsupported: OnDifferent::UseDataFromPrimary,
                if_different_symbol_unsupported: OnDifferent::UseDataFromPrimary,
            },
        }
    }
    fn calculate(&self, c: &StrategyContext) -> ScratchPad {
        let mut scratch_pad = ScratchPad::new();
        let one_millionth = 1.0 / 1_000_000.0;
        scratch_pad.write(&self.key_satoshi_quantity, one_millionth);
        let iter = c.exchange_markets_for(AssetId::bitcoin_native(), AssetId::ethereum_usdt());
        let min_by_last_ask = iter.min_by(|(_a_exch, a_market), (_b_exch, b_market)| {
            let a_last_ask = a_market.ticks.ticks[0].ask.price;
            let b_last_ask = b_market.ticks.ticks[0].ask.price;
            a_last_ask.partial_cmp(&b_last_ask).unwrap()
        });
        if let Some((cheapest_exchange_name, market)) = min_by_last_ask {
            scratch_pad.write(&self.key_exchange, cheapest_exchange_name);
            scratch_pad.write(&self.key_market_exists, true);
            let satoshi_price = market.ticks.ticks[0].ask.price / 1_000_000.0;
            scratch_pad.write(&self.key_satoshi_price, satoshi_price);
        }
        scratch_pad
    }
    fn resolver(&self, c: &ResolverContext) -> Resolver {
        let exchange = c.scratch_pad.exchange_id(&self.key_exchange);
        let btc = c.literals.asset_id(AssetId::bitcoin_native());
        let usdt = c.literals.asset_id(AssetId::ethereum_usdt());
        let satoshi_price = c.scratch_pad.number(&self.key_satoshi_price);
        let quantity = c.scratch_pad.number(&self.key_satoshi_quantity);
        c.resolvers.if_else(
            c.predicates.scratch_pad(&self.key_market_exists),
            c.resolvers.if_else(
                c.predicates.compare(
                    c.portfolio
                        .asset_in_exchange(exchange.clone(), usdt.clone()),
                    Ordering::Greater,
                    satoshi_price,
                ),
                c.resolvers.enqueue_order(
                    exchange.clone(),
                    c.orders.single(
                        btc,
                        usdt.clone(),
                        OrderIntent::Open,
                        OrderSide::Buy,
                        OrderActivation::Immediate,
                        OrderPricing::Market,
                        OrderQuantity::OfQuote(quantity),
                        vec![OrderConstraint::FillPolicy {
                            allow_partial: true,
                        }],
                    ),
                ),
                c.resolvers.no_op(),
            ),
            c.resolvers.no_op(),
        )
    }
}

Stock-Trek verifies code before running it and disallows certain syntax elements. To verify code locally, install it with

cargo install stock-trek

then run the verify command with

stock-trek verify --file ./path/strategy.rs

Roadmap

Planned features include:

  • Technical indicators (EMA, RSI, MACD, etc.)
  • Backtesting and simulation utilities

Status

This project is in early development (0.x). APIs may change.

License

MIT