panifie / PingPong.jl

Cryptocurrency trading bot, and backtesting framework in julia
https://panifie.github.io/PingPong.jl/
GNU General Public License v3.0
36 stars 9 forks source link

add support for python strategies #6

Open untoreh opened 1 year ago

untoreh commented 1 year ago

Although not really recommended, embedding python strategies is a very easy task.

c0indev3l commented 1 year ago

What are you suggesting by "embedding" Python strategies. Trality was great for writing Python strategies unfortunately they are closing https://www.trality.com/blog/trality-winding-down-product at end of month. Maybe you could find some ideas in the way Python strategies are written. See https://docs.trality.com/

Here is an example EMA CROSS AND RSI (Exponential moving average in combination with RSI.)

'''
In this bot an example of using the exponential moving average in combination with RSI is presented.
If you are new and trying to get started we recommend checking our https://app.trality.com/masterclass
For more information on each part of a trading bot, please visit our documentation pages: https://docs.trality.com
'''

'''
Initializing state object and setting number_offset_trades to 0 before the first bot execution.
'''
def initialize(state):
    state.number_offset_trades = 0;

@schedule(interval="1h", symbol="BTCUSDT")
def handler(state, data):
    '''
    1) Compute indicators from data
    '''

    ema_long_ind = data.ema(40)
    ema_short_ind = data.ema(20)
    rsi_ind = data.rsi(14)

    # on erroneous data return early (indicators are of NoneType)
    if rsi_ind is None or ema_long_ind is None or ema_short_ind is None:
        return

    ema_long = ema_long_ind.last # Getting latest value for ema_long_ind from data object
    ema_short = ema_short_ind.last
    rsi = rsi_ind.last

    current_price = data.close_last

    '''
    2) Fetch portfolio
        > check liquidity (in quoted currency)
        > resolve buy value
    '''

    portfolio = query_portfolio()
    balance_quoted = portfolio.excess_liquidity_quoted
    # we invest only 80% of available liquidity
    buy_value = float(balance_quoted) * 0.80

    '''
    3) Fetch position for symbol
        > has open position
        > check exposure (in base currency)
    '''

    position = query_open_position_by_symbol(data.symbol,include_dust=False)
    has_position = position is not None

    '''
    4) Resolve buy or sell signals
        > create orders using the order api
        > print position information

    '''
    if ema_short > ema_long and rsi < 30 and not has_position:
        print("-------")
        print("Buy Signal: creating market order for {}".format(data.symbol))
        print("Buy value: ", buy_value, " at current market price: ", data.close_last)

        order_market_value(symbol=data.symbol, value=buy_value)  # creating market order

    elif ema_short < ema_long and rsi > 70 and has_position:
        print("-------")
        logmsg = "Sell Signal: closing {} position with exposure {} at current market price {}"
        print(logmsg.format(data.symbol,float(position.exposure),data.close_last))

        close_position(data.symbol)  # closing position

    '''
    5) Check strategy profitability
        > print information profitability on every offsetting trade
    '''

    if state.number_offset_trades < portfolio.number_of_offsetting_trades:

        pnl = query_portfolio_pnl()
        print("-------")
        print("Accumulated Pnl of Strategy: {}".format(pnl))

        offset_trades = portfolio.number_of_offsetting_trades
        number_winners = portfolio.number_of_winning_trades
        print("Number of winning trades {}/{}.".format(number_winners,offset_trades))
        print("Best trade Return : {:.2%}".format(portfolio.best_trade_return))
        print("Worst trade Return : {:.2%}".format(portfolio.worst_trade_return))
        print("Average Profit per Winning Trade : {:.2f}".format(portfolio.average_profit_per_winning_trade))
        print("Average Loss per Losing Trade : {:.2f}".format(portfolio.average_loss_per_losing_trade))
        # reset number offset trades
        state.number_offset_trades = portfolio.number_of_offsetting_trades
panbonker commented 1 year ago

In our case it is more about testing support for using python within a strategy (julia) module. It's not about offering a "simplified" interface for building strategies.

In the simplest case one can do

using Python
ping!(s, ts, args...) =  pyimport(...)

There are untested scenarios (e.g. when backtesting or optimizing a strategy that call python code)