1.40.0 • Published 3 years ago

yieldfinity v1.40.0

Weekly downloads
-
License
ISC
Repository
github
Last release
3 years ago

Strategy backtester

A node strategy backtesting / running framework for crypto trading and more.

⚠️ This project is under active development and is not suitable for production use yet.

Summary

Getting started

Yieldfinity is a TS / node strategy backtesting framework, currently under active development. It fetches candle data from binance for any given pair, allows you to create strategies, feed them indicators and triggers, and backtest them. Strategies rely on one or many indicators and either indicator triggers or custom triggers to work. Indicators provide the computed candle data, and the indicator triggers are function checking if they should trigger an order or not.

Yieldfinity playground

Checkout the yieldfinity playground : it allows you to code and visualize your strategies directly in your web browser.

Backtester

Quickstart

Custom methods based strategies

This method uses a custom function which triggers either a buy or a sell order. Let's make a ridiculously stupid strategy : if the price is even, we buy, else we sell.

import { Strategy, ExchangePair, Binance, Candle } from "yieldfinity";
import { Indicators } from "yieldfinity/indicators";
import { Position, Order, StopLoss, TakeProfit } from "yieldfinity/orders";
import { CustomTriggerFlow } from "yieldfinity/flows";
import { CustomTrigger } from "yieldfinity/triggers";

const pair: ExchangePair = "BTCUSDT";
const sDate = new Date("2021-01-07");
const eDate = new Date("2021-03-28");
const binance = new Binance();

binance.getCandles(sDate, eDate, pair, "1m")
.then((candles: Candle[]) => {
  const indicators = new Indicators();

  // Building the indicator
  const sma = indicators.sma({ period : 12 });
  const price = indicators.price({ mode: "high" });

  // We build the trigger flow
  const customTriggerFlow = new CustomTriggerFlow({
    triggers: [
      new CustomTrigger({
        parameters: {
          indicators: [price, sma],
          pair
        },
        // If the price is odd then we place buy order
        method : ({indicators}) => {
          const [price, sma] = indicators;
          return (price.lastValue % 2) ? new Order({
            pair,
            price : "market",
            quantity : 0.01,
            side: "long",
            stopLoss : new StopLoss({ reference : "pnl",  value: -5 }),
            takeProfit : new TakeProfit({ reference : "pnl",  value: 10 })
          }) : null;
        }
      })
    ]
  })

  // Building the stategy & backtest
  const strategy = new Strategy({ indicators: [price, sma], triggerFlow: customTriggerFlow, exchanges: [binance] });

  strategy.run(candles);

  // Save your positions before being able to read them
  strategy.savePositions();

  const profit = strategy.profit;
  const pnl = strategy.pnl;
  const profitablePositions = Math.ceil(strategy.profitablePositions.length / strategy.positions.length * 100);
  console.log(`Strategy made a profit of ${profit} (${pnl}%) : ${profitablePositions}% of positions were profitable`);
});

Indicator trigger based strategies

An indicator trigger is a function that will be executed automatically at each candle. It takes standardized parameters for quick prototyping.

// Imports
import { Strategy, ExchangePair, Binance, Candle } from "yieldfinity";
import { Indicators } from "yieldfinity/indicators";
import { Position, Order, StopLoss, TakeProfit } from "yieldfinity/orders";
import { TriggerFlow } from "yieldfinity/flows";
import { PriceTrigger, SMATrigger } from "yieldfinity/triggers";

const pair: ExchangePair = "BTCUSDT";
const sDate = new Date("2021-01-01");
const eDate = new Date("2021-01-30");
const binance = new Binance();

binance.getCandles(sDate, eDate, pair, "1m")
.then((candles: Candle[]) => {
  const indicators = new Indicators();

  // Building the indicator
  const sma = indicators.sma({ period : 12 });
  const price = indicators.price({ mode: "high" });

  // Building the triggers
  const triggers = [
    new SMATrigger({ indicator: sma, field: "value", triggerValue : 2, comparer: ">=", mode: "percentage", tMinus: 60*24 }),
    new PriceTrigger({ indicator: price, field: "value", triggerValue : 10, comparer: ">=", mode: "percentage", tMinus:  60 * 12 }),
  ];

  const triggerFlow = new TriggerFlow({
    flow : [{
      triggers,
      operator : "and",
      position: new Order({
        pair,
        price : "market",
        quantity : 1,
        side: "long",
        stopLoss : new StopLoss({ reference : "pnl",  value: -5 }),
        takeProfit : new TakeProfit({ reference : "pnl",  value: 10 })
      })
    }]
  });

  // Building the stategy & backtest
  const strategy = new Strategy({ indicators: [price, sma], triggerFlow, exchanges: [binance] });
  strategy.run(candles);

  // Save your positions before being able to read them
  strategy.savePositions(candles);

  console.log(`${strategy.closedPositions.filter(pos => pos.state.profit > 0).length}/${strategy.closedPositions.length} profitable positions`)
});

Candles

Model

interface Candle {
  openAt: Date;
  closeAt: Date;
  open: number;
  close: number;
  high: number;
  low: number;
  volume: number;
  exchange: string;
  pair: ExchangePair;
  interval: ExchangeInterval;
}

You can fetch the candles from Binance as such :

import { ExchangePair, Binance } from "yieldfinity";

const pair: ExchangePair  =  "BTCUSDT";
const sDate = new Date("2021-01-01");
const eDate = new Date("2021-04-30");
const candles = await new Binance().getCandles(sDate, eDate, pair, "1m"); // Must be 1m for now

Indicators

Model

interface Indicator {
  method(): Function;
  name(): string;
  values():IndicatorOutput[];
  parameters(): IndicatorParameters;
  lastValue() : IndicatorOutput;
  lastIndex() : number;
}

Your indicators can be anything, from a regular technical indicator to an external datasource. They simply are generator functions returning a value for each candle being fed to it.

Import the indicators as such :

import { Indicators } from "yieldfinity";
// or
import { Indicators } from "yieldfinity/indicators";

const indicators = new Indicators();

Existing indicators

Price

const price = indicators.price({
  mode: "high" // "high" | "low" | "close" | "open";
});

price.generate(candle);
console.log(price.lastValue) // 152400.2

SMA

const sma = indicators.sma({
  period : 12 // number;
});

sma.generate(candle);
console.log(sma.lastValue) // 254.2

EMA

const ema = indicators.ema({
  period : 12 // number;
});

ema.generate(candle);
console.log(ema.lastValue) // 124.5

RSI

const rsi = indicators.rsi({
  period : 12 // number;
});

rsi.generate(candle);
console.log(rsi.lastValue) // 748.5

ATR - ⚠ not available yet

const atr = indicators.atr({
  period : 12 // number;
});

atr.generate(candle);
console.log(atr.lastValue) // 748.5

MACD

const macd = indicators.macd({
  fastPeriod: 12, // number
  slowPeriod: 24, // number
  signalPeriod: 12 // number
});

macd.generate(candle);
console.log(macd.lastValue)
/*
{
  MACD: 20, // number
  histogram: 50, // number
  signal: 10, // number
}
*/

Triggers

Custom triggers

You can create your own triggering method (useful if you want to create signals, code and debug your own strategy, or if you wish to have more control over what your backtesting process) .

A custom trigger method must have the following prototype :

type CustomTriggerMethod = (parameters : any) => Position | null;

Custom Trigger flow

After creating your custom trigger, you must then insert it into a CustomTriggerFlow, and pass it to a strategy as such :

import { CustomTriggerFlow } from "yieldfinity";
// or
import { CustomTriggerFlow } from "yieldfinity/flows";

// Build a custom strategy flow
const customTriggerFlow = new CustomTriggerFlow({
  triggers: [
    new CustomTrigger({
      parameters: [price], // Indicator[]
      method : ([price]) => {
        // Define your own method here, return a position or null;
        return null;
      }
    })
  ]
})

// Building the stategy & backtest
const strategy = new Strategy({ indicators: [price, sma], triggerFlow: customTriggerFlow });
strategy.run(candles);

Indicator triggers

Basic usage

You can create strategies using triggers. Once your indicator has been generated, the trigger will compare the value of the indicator with the parameters you fed to the trigger. For each trigger, the field corresponds to either of the indicator output. For instance, values available for the MACDTrigger are MACD or histogram or signal.

Model

/**
 * Indicator trigger
 
 * @param field: the name of the output of an indicator you can set a trigger on
 
 * @param triggerField: ❌ Not available yet
 Optionnal, only for multiple output indicators. For instance, allows you to compare the MACD
 signal against the histogram to check if the lines are crossing. If specified, tMinus will
 be ignored and only market values will be compared.
 
 * @param triggerValue :
 If tMinus = 0, the market value of the indicator and the
 triggerValue are compared, else it will take the value of the indicator at t - tMinus
 and compare it using the triggerValue
 
 * @param tMinus : in minutes

 * @param comparer : basic TS comparison operators ("<" or ">" or "<=" or "=>" or "=" )

 * @param mode :
 If "percentage", will check the evolution of the indicator in % (tMinus must be > 0).
 If "relative", the triggerValue will be added to the indicator's value.
 If "absolute", the triggerValue will directly be compared to the indicator's value.

**/
interface IndicatorTrigger {
  indicator: Indicator;
  field: string; // See each available trigger example below for usage
  triggerField ?: string;
  triggerValue : number;
  tMinus ?: number;
  comparer : Comparer;
  mode : ComparerMode;
}

Trigger flow

After creating your trigger, you must then insert it into a TriggerFlow, and pass it to a strategy as such :

import { SMATrigger, PriceTrigger } from "yieldfinity/triggers";
import { TriggerFlow } from "yieldfinity/flows";
// or
import { SMATrigger, PriceTrigger, TriggerFlow } from "yieldfinity/flows";

// Building the triggers
const triggers = [
  new SMATrigger({ indicator: sma, field: "value", triggerValue : 2, comparer: ">=", mode: "percentage", tMinus: 60*24 }),
  new PriceTrigger({ indicator: price, field: "value", triggerValue : 10, comparer: ">=", mode: "percentage", tMinus:  60 * 12 }),
];

const triggerFlow = new TriggerFlow({
  flow : [{ triggers : triggers, operator : "and", position: askOrder }]
});

// Building the stategy & backtest
const strategy = new Strategy({ indicators: [price, sma], triggerFlow: customTriggerFlow });
strategy.run(candles);

Available triggers

You can set a trigger for each indicator already available :

Price

const price = indicators.price({ mode: "high" });
const priceTrigger = new  PriceTrigger({ indicator: price, field: "value", triggerValue : 10, comparer: ">=", mode: "percentage", tMinus: 60  *  12 })
// Check if price raised by 10% over the last 12 hours

*

SMA

const sma = indicators.sma({ period: 12 });
const emaTrigger = new  SMATrigger({ indicator: price, field: "value", triggerValue : 100, comparer: "<", mode: "absolute", tMinus: 0 })
// Check if SMA is marketly < 100

EMA

const ema = indicators.ema({ period: 12 });
const emaTrigger = new  EMATrigger({ indicator: price, field: "value", triggerValue : 200, comparer: ">=", mode: "relative", tMinus: 60 * 24 })
// Check if EMA as gained 200 points over the last 24 hours

RSI

const rsi = indicators.rsi({ period: 12 });
const rsiTrigger = new  RSITrigger({ indicator: price, field: "value", triggerValue : 200, comparer: ">=", mode: "relative", tMinus: 60 * 24 })
// Check if RSI as gained 200 points over the last 24 hours

ATR - ⚠ not available yet

const ATR = indicators.ATR({ period: 12 });
const ATRTrigger = new  ATRTrigger({ indicator: price, field: "value", triggerValue : 200, comparer: ">=", mode: "relative", tMinus: 60 * 24 })
// Check if ATR as gained 200 points over the last 24 hours

MACD

const macd = indicators.macd({
  fastPeriod: 12, // number
  slowPeriod: 24, // number
  signalPeriod: 12 // number
});
const macdTrigger = new  MACDTrigger({
  indicator: macd,
  field: "histogram", // can be either "MACD" | "histogram" | "signal"
  triggerValue : 2,
  comparer: ">=", mode:
  "percentage",
  tMinus: 5
})
// Check if MACD histogram as gained 2% over the last 5 minutes

Strategies

Strategies handle the indicators and the triggers for you, and are defined as such :

Model

interface Strategy {
  indicators: Indicator[];
  triggerFlow : TriggerFlow | CustomTriggerFlow;
}

Backtest

You can feed your indicators, trigger flow or custom trigger flow and new candles to your strategy and start backtesting like this :

import { Strategy } from "yieldfinity";

// Building the stategy & backtest
const strategy = new Strategy({ indicators: [price, sma], triggerFlow: customTriggerFlow });

strategy.run(candles);
1.38.0

3 years ago

1.39.0

3 years ago

1.40.0

3 years ago

1.37.0

3 years ago

1.36.0

3 years ago

1.32.0

3 years ago

1.30.0

3 years ago

1.31.0

3 years ago

1.29.0

3 years ago

1.27.0

3 years ago

1.28.0

3 years ago

1.19.0

3 years ago

1.18.0

3 years ago

1.21.0

3 years ago

1.20.0

3 years ago

1.25.0

3 years ago

1.26.0

3 years ago

1.23.0

3 years ago

1.24.0

3 years ago

1.17.0

3 years ago

1.16.0

3 years ago

1.15.0

3 years ago

1.14.0

3 years ago

1.13.0

3 years ago

1.12.0

3 years ago

1.11.0

3 years ago

1.10.0

3 years ago

1.9.0

3 years ago

1.8.0

3 years ago

1.7.0

3 years ago

1.6.0

3 years ago

1.5.0

3 years ago

1.4.0

3 years ago

1.3.0

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.9

3 years ago

1.0.8

3 years ago

1.0.7

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago