Skip to content
This repository has been archived by the owner on Feb 20, 2019. It is now read-only.

Commit

Permalink
Breakout strategy succesfully extracted
Browse files Browse the repository at this point in the history
  • Loading branch information
shads2 committed Dec 28, 2017
1 parent c9241c6 commit eab9214
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 153 deletions.
18 changes: 18 additions & 0 deletions app/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Executes the trading strategies and analyzes the results.
"""

from strategies.breakout import Breakout
#from strategies.ichimoku_cloud import IchimokuCloud
#from strategies.relative_string_index import RelativeStrengthIndex
#from strategies.moving_averages import MovingAverages


class StrategyAnalyzer():
"""
Executes the trading strategies and analyzes the results.
"""
def analyze_breakout(self, historical_data):
breakout_analyzer = Breakout()
breakout_value, is_breaking_out = breakout_analyzer.find_breakout(historical_data)
return breakout_value, is_breaking_out
169 changes: 16 additions & 153 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,167 +9,31 @@
import logging
from string import whitespace

from exchange import ExchangeAggregator
from notification import Notifier
from exchanges.bittrex import Bittrex
from analysis import StrategyAnalyzer


# Let's test an API call to get our BTC balance as a test
# print(BITTREX_CLIENT.get_balance('BTC')['result']['Balance'])

#print(historical_data = BITTREX_CLIENT.get_historical_data('BTC-ETH', 30, "thirtyMin"))
def get_closing_prices(coin_pair, period, unit):
"""
Returns closing prices within a specified time frame for a coin pair
:type coin_pair: str
:type period: str
:type unit: int
:return: Array of closing prices
"""

historical_data = BITTREX_CLIENT.get_historical_data(coin_pair, period, unit)
closing_prices = []
for data_point in historical_data:
closing_prices.append(data_point['C'])
return closing_prices

def calculate_sma(coin_pair, period, unit):
"""
Returns the Simple Moving Average for a coin pair
"""

total_closing = sum(get_closing_prices(coin_pair, period, unit))
return total_closing / period

def calculate_ema(coin_pair, period, unit):
"""
Returns the Exponential Moving Average for a coin pair
"""

closing_prices = get_closing_prices(coin_pair, period, unit)
previous_ema = calculate_sma(coin_pair, period, unit)
period_constant = 2 / (1 + period)
current_ema = (closing_prices[-1] * period_constant) \
+ (previous_ema * (1 - period_constant))
return current_ema

# Improvemnts to calculate_rsi are courtesy of community contributor "pcartwright81"
def calculate_rsi(coin_pair, period, unit):
"""
Calculates the Relative Strength Index for a coin_pair
If the returned value is above 70, it's overbought (SELL IT!)
If the returned value is below 30, it's oversold (BUY IT!)
"""
closing_prices = get_closing_prices(coin_pair, period * 3, unit)
count = 0
changes = []
# Calculating price changes
for closing_price in closing_prices:
if count != 0:
changes.append(closing_price - closing_prices[count - 1])
count += 1
if count == 15:
break

# Calculating gains and losses
advances = []
declines = []
for change in changes:
if change > 0:
advances.append(change)
if change < 0:
declines.append(abs(change))

average_gain = (sum(advances) / 14)
average_loss = (sum(declines) / 14)
new_average_gain = average_gain
new_average_loss = average_loss
for closing_price in closing_prices:
if count > 14 and count < len(closing_prices):
close = closing_prices[count]
new_change = close - closing_prices[count - 1]
add_loss = 0
add_gain = 0
if new_change > 0:
add_gain = new_change
if new_change < 0:
add_loss = abs(new_change)
new_average_gain = (new_average_gain * 13 + add_gain) / 14
new_average_loss = (new_average_loss * 13 + add_loss) / 14
count += 1

rs = new_average_gain / new_average_loss
new_rs = 100 - 100 / (1 + rs)
return new_rs


def calculate_base_line(coin_pair, unit):
"""
Calculates (26 period high + 26 period low) / 2
Also known as the "Kijun-sen" line
"""

closing_prices = get_closing_prices(coin_pair, 26, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2

def calculate_conversion_line(coin_pair, unit):
"""
Calculates (9 period high + 9 period low) / 2
Also known as the "Tenkan-sen" line
"""
closing_prices = get_closing_prices(coin_pair, 9, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2

def calculate_leading_span_a(coin_pair, unit):
"""
Calculates (Conversion Line + Base Line) / 2
Also known as the "Senkou Span A" line
"""

base_line = calculate_base_line(coin_pair, unit)
conversion_line = calculate_conversion_line(coin_pair, unit)
return (base_line + conversion_line) / 2

def calculate_leading_span_b(coin_pair, unit):
"""
Calculates (52 period high + 52 period low) / 2
Also known as the "Senkou Span B" line
"""
closing_prices = get_closing_prices(coin_pair, 52, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2

def find_breakout(coin_pair, period, unit):
"""
Finds breakout based on how close the High was to Closing and Low to Opening
"""
hit = 0
historical_data = BITTREX_CLIENT.get_historical_data(coin_pair, period, unit)
for data_point in historical_data:
if (data_point['C'] == data_point['H']) and (data_point['O'] == data_point['L']):
hit += 1

if (hit / period) >= .75:
notifier = Notifier(CONFIG)
notifier.notify_all(message="{} is breaking out!".format(coin_pair))
return "Breaking out!"
else:
return "#Bagholding"
exit()

def get_signal():

for coin_pair in COIN_PAIRS:
breakout = find_breakout(coin_pair=coin_pair, period=5, unit="fiveMin")
rsi = calculate_rsi(coin_pair=coin_pair, period=14, unit="thirtyMin")
print("{}: \tBreakout: {} \tRSI: {}".format(coin_pair, breakout, rsi))
breakout_value, is_breaking_out = STRATEGY_ANALYZER.analyze_breakout(
EXCHANGE_AGGREGATOR.get_historical_data(coin_pair))
if is_breaking_out:
NOTIFIER.notify_all(message="{} is breaking out!".format(coin_pair))
print("{}: \tBreakout: {}".format(coin_pair, breakout_value))
time.sleep(300)

if __name__ == "__main__":
# Load settings and create the CONFIG object
SECRETS = json.load(open('secrets.json'))
SECRETS = {}
if os.path.isfile('secrets.json'):
SECRETS = json.load(open('secrets.json'))
CONFIG = json.load(open('default-config.json'))

CONFIG.update(SECRETS)
Expand All @@ -193,17 +57,16 @@ def get_signal():
LOG_HANDLE.setFormatter(LOG_FORMAT)
LOGGER.addHandler(LOG_HANDLE)

# Configure clients for bittrex and twilio
BITTREX_CLIENT = Bittrex(
CONFIG['exchanges']['bittrex']['required']['key'],
CONFIG['exchanges']['bittrex']['required']['secret'])
EXCHANGE_AGGREGATOR = ExchangeAggregator(CONFIG)
STRATEGY_ANALYZER = StrategyAnalyzer()
NOTIFIER = Notifier(CONFIG)

# The coin pairs
COIN_PAIRS = []
if CONFIG['settings']['market_pairs']:
COIN_PAIRS = CONFIG['settings']['market_pairs'].translate(str.maketrans('', '', whitespace)).split(",")
else:
user_markets = BITTREX_CLIENT.get_balances()
user_markets = EXCHANGE_AGGREGATOR.get_user_markets()
for user_market in user_markets['result']:
if 'BTC' in user_market['Currency']:
continue
Expand Down
23 changes: 23 additions & 0 deletions app/exchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Collect required information from exchanges
"""

from exchanges.bittrex import Bittrex

class ExchangeAggregator():
"""
Collect required information from exchanges
"""
def __init__(self, config):
self.bittrex_client = Bittrex(
config['exchanges']['bittrex']['required']['key'],
config['exchanges']['bittrex']['required']['secret'])

def get_historical_data(self, coin_pair, period_count=5, time_unit='fiveMin'):
"""
Get history data
"""
return self.bittrex_client.get_historical_data(coin_pair, period_count, time_unit)

def get_user_markets(self):
return self.bittrex_client.get_balances()
Empty file added app/strategies/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions app/strategies/breakout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Runs the breakout strategy over the market data
"""

class Breakout():
"""
Runs the breakout strategy over the market data
"""
def find_breakout(self, historical_data, breakout_threshold=.75):
"""
Finds breakout based on how close the High was to Closing and Low to Opening
"""
hit = 0
for data_period in historical_data:
if (data_period['C'] == data_period['H']) and (data_period['O'] == data_period['L']):
hit += 1

percent_positive_trend = hit / len(historical_data)
if percent_positive_trend >= breakout_threshold:
is_breaking_out = True
else:
is_breaking_out = False
return percent_positive_trend, is_breaking_out
40 changes: 40 additions & 0 deletions app/strategies/ichimoku_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
def calculate_base_line(coin_pair, unit):
"""
Calculates (26 period high + 26 period low) / 2
Also known as the "Kijun-sen" line
"""

closing_prices = get_closing_prices(coin_pair, 26, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2

def calculate_conversion_line(coin_pair, unit):
"""
Calculates (9 period high + 9 period low) / 2
Also known as the "Tenkan-sen" line
"""
closing_prices = get_closing_prices(coin_pair, 9, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2

def calculate_leading_span_a(coin_pair, unit):
"""
Calculates (Conversion Line + Base Line) / 2
Also known as the "Senkou Span A" line
"""

base_line = calculate_base_line(coin_pair, unit)
conversion_line = calculate_conversion_line(coin_pair, unit)
return (base_line + conversion_line) / 2

def calculate_leading_span_b(coin_pair, unit):
"""
Calculates (52 period high + 52 period low) / 2
Also known as the "Senkou Span B" line
"""
closing_prices = get_closing_prices(coin_pair, 52, unit)
period_high = max(closing_prices)
period_low = min(closing_prices)
return (period_high + period_low) / 2
19 changes: 19 additions & 0 deletions app/strategies/moving_averages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def calculate_sma(coin_pair, period, unit):
"""
Returns the Simple Moving Average for a coin pair
"""

total_closing = sum(get_closing_prices(coin_pair, period, unit))
return total_closing / period

def calculate_ema(coin_pair, period, unit):
"""
Returns the Exponential Moving Average for a coin pair
"""

closing_prices = get_closing_prices(coin_pair, period, unit)
previous_ema = calculate_sma(coin_pair, period, unit)
period_constant = 2 / (1 + period)
current_ema = (closing_prices[-1] * period_constant) \
+ (previous_ema * (1 - period_constant))
return current_ema
48 changes: 48 additions & 0 deletions app/strategies/relative_strength_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Improvemnts to calculate_rsi are courtesy of community contributor "pcartwright81"
def calculate_rsi(coin_pair, period, unit):
"""
Calculates the Relative Strength Index for a coin_pair
If the returned value is above 70, it's overbought (SELL IT!)
If the returned value is below 30, it's oversold (BUY IT!)
"""
closing_prices = get_closing_prices(coin_pair, period * 3, unit)
count = 0
changes = []
# Calculating price changes
for closing_price in closing_prices:
if count != 0:
changes.append(closing_price - closing_prices[count - 1])
count += 1
if count == 15:
break

# Calculating gains and losses
advances = []
declines = []
for change in changes:
if change > 0:
advances.append(change)
if change < 0:
declines.append(abs(change))

average_gain = (sum(advances) / 14)
average_loss = (sum(declines) / 14)
new_average_gain = average_gain
new_average_loss = average_loss
for closing_price in closing_prices:
if count > 14 and count < len(closing_prices):
close = closing_prices[count]
new_change = close - closing_prices[count - 1]
add_loss = 0
add_gain = 0
if new_change > 0:
add_gain = new_change
if new_change < 0:
add_loss = abs(new_change)
new_average_gain = (new_average_gain * 13 + add_gain) / 14
new_average_loss = (new_average_loss * 13 + add_loss) / 14
count += 1

rs = new_average_gain / new_average_loss
new_rs = 100 - 100 / (1 + rs)
return new_rs
14 changes: 14 additions & 0 deletions app/strategies/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
def get_closing_prices(coin_pair, period, unit):
"""
Returns closing prices within a specified time frame for a coin pair
:type coin_pair: str
:type period: str
:type unit: int
:return: Array of closing prices
"""

historical_data = BITTREX_CLIENT.get_historical_data(coin_pair, period, unit)
closing_prices = []
for data_point in historical_data:
closing_prices.append(data_point['C'])
return closing_prices

0 comments on commit eab9214

Please sign in to comment.