Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion letsberich/ig/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django import forms

from letsberich.ig.models import Position
from letsberich.ig.models import Position, Autotrade


class OpenPositionForm(forms.ModelForm):
Expand All @@ -11,3 +11,13 @@ def __init__(self, *args, **kwargs):
class Meta:
model = Position
exclude = ("created_by",)


class AutoTradeForm(forms.ModelForm):

def __init__(self, *args, **kwargs):
super(AutoTradeForm, self).__init__(*args, **kwargs)

class Meta:
model = Autotrade
fields = ['status']
37 changes: 31 additions & 6 deletions letsberich/ig/ig_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,17 @@ def get_node_list(self, node_id) -> dict:
else:
raise IGServiceError

def get_prices(self):
# TODO

def get_instrument_characteristics(self, epic: str):
self.get_token()

url = self._get_endpoint('PRICES').format('KA.D.VOD.CASH.IP')

url = self._get_endpoint('GET_INSTRUMENT_CHARACTERISTICS').format(epic)
response = self._make_request(url, version='3')

if response.status_code < 300:
response_dict = json.loads(response.content.decode('utf-8'))
return response_dict
else:
raise IGServiceError

def get_account_useful_data(self):
self.get_token()
url = self._get_endpoint('ACCOUNT_USEFUL_DATA')
Expand Down Expand Up @@ -222,6 +224,29 @@ def open_position_wrapper(self, payload: dict):
open_position_data = self.open_position(deal_id)
return open_position_data

def get_instrument_list(self) -> dict:
self.get_token()
url = self._get_endpoint('SPECIFIC_WATCHLIST').format('12047692') #weekend_watch is 12047692; pm_watchlist id is 11899563. ftse_watchlist is 11899563
response = self._make_request(url, version='1')

if response.status_code < 300:
response_dict = json.loads(response.content.decode('utf-8'))
return response_dict['markets']
raise IGServiceError("Error getting watchlist: {}".format(response.content))

def get_past_price(self, instrument: dict, data_points: int):
self.get_token()
url = self._get_endpoint('GET_HISTORICAL_PRICES').format(instrument['epic'], 'MINUTE', data_points)
response = self._make_request(url, version='2')

if response.status_code < 300:
response_dict = json.loads(response.content.decode('utf-8'))
return response_dict['prices']

raise IGServiceError("Error getting past price: {}".format(response.content))

def close_position_wrapper(self): #TODO
return 1

def get_ig_api() -> IGService:
return IGService()
6 changes: 6 additions & 0 deletions letsberich/ig/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ class Position(models.Model):
)


class Autotrade(models.Model):
status = models.CharField(
max_length=7,
help_text="This activates the Auto Trade tool",
choices=(("ON", "TURN ON"), ("OFF", "TURN OFF"), ("STATUS", "STATUS")),
)
126 changes: 126 additions & 0 deletions letsberich/ig/strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from letsberich.ig.ig_services import get_ig_api
from time import time, sleep
import pandas as pd
import numpy as np
import random


# This method manages strategy and returns strategy status
class StrategyOne(object):

def __init__(self):
self.status = 'OFF'
self.account_statistics ={}
self.previous_position_meets_criteria = [{'epic': 'CS.D.BITCOIN.TODAY.IP', 'status': 'false'},
{'epic': 'CS.D.LTCUSD.TODAY.IP', 'status': 'false'},
{'epic': 'CS.D.BCHXBT.TODAY.IP', 'status': 'true'}]
self.position_meets_criteria = []
self.transaction_history = []

def get_status(self, status_request='CHECK'):
ig_api = get_ig_api()
# This starts monitoring and buys when signals are right

if status_request == 'ON':
# Firstly, select instrument of complete FTSE, DOW30 and S&P to trade automatically depending on parameters
# Need to find a way to keep the while loop running in the background so that we can stop it whenever we want
self.status = status_request
end_time = time() + 30
price_feed = []

while time() < end_time:
instrument_list = ig_api.get_instrument_list()

for instrument in instrument_list:
price_feed_dict = {}
price_feed_dict.update({'epic': instrument['epic']})
price_feed_dict.update({'currentPrice': instrument['offer']}) # offer is buy price ['EPIC1': ] data_feed is a time series that contains all relevant data for the instrument_list
parse_data = ig_api.get_past_price(instrument, data_points=200) # time series containing past price data. For now we will get this from the API but it could be more efficient to have this info in our DB?
parse_data_closePrice = [parse_data_element.get('closePrice').get('ask') for parse_data_element in parse_data]
# import ipdb; ipdb.set_trace()
# parse_data is a list and needs to be accessed element by element.
price_feed_dict.update({'pastClosePrice': parse_data_closePrice})
price_feed.append(price_feed_dict)

self.position_meets_criteria = self._strategy_200movingaverage(price_feed,
self.previous_position_meets_criteria) # position_meets_criteria will be a dict of boolean values with True=open trade or False=Close trade and EPIC

if self.previous_position_meets_criteria[0]['epic'] != "": #skip if it iś initial iteration#

for position in self.position_meets_criteria:
#this for loop execute buy or sell depending on what strat is telling
pos_st = position['status']
prev_pos_st = next((element['status'] for element in self.previous_position_meets_criteria
if element['epic'] == position['epic']), None)
instrument = ig_api.get_instrument_characteristics(position['epic'])

if pos_st == 'true' and pos_st != prev_pos_st:
data_for_position = {
'currency_code': 'GBP',
'deal_reference': 'TESTPos' + str(random.randint(1,100)),
'direction': 'BUY',
'epic': position['epic'],
'expiry': 'DFB',
'force_open': 'True',
'guaranteed_stop': 'False',
'order_type': 'MARKET',
'size': '0.5',
'stop_level': str(next((element['currentPrice'] for element in price_feed if element['epic']
== position['epic'])) * 0.97)
}
open_position_details = ig_api.open_position_wrapper(data_for_position)
self.transaction_history.append(open_position_details)

if pos_st == 'false' and pos_st != prev_pos_st:
closed_position_details = ig_api.close_position_wrapper(position['epic'])
self.transaction_history.append(closed_position_details)

self.previous_position_meets_criteria = self.position_meets_criteria
sleep(10)
return {'transactions': self.transaction_history, 'status': self.status}

if status_request == 'OFF':
# This closes all positions and stops monitoring.
response_dict = ig_api.close_position_wrapper()
self.status = response_dict['status']
return {'transactions': self.transaction_history, 'status': self.status}

if status_request == 'CHECK':
response_dict = ig_api.get_account_useful_data()
return {'transactions': response_dict, 'status': self.status}

def _strategy_200movingaverage(self, price_feed: list, previous_position_meets_criteria: list):
# this method contains the strategy. I have split this from main method to facilitate changing or plugging in a different strategy
#
# /// HERE: code strategy that will come up with update list of boolean to decide whether to buy or sell.
# strategy example: Calculate average over past 100 prices and if current price in data_feed goes through
# downwards, then sell (boolean set to false). If goes through upwards, then buy (blooean set to true).
# Finally, return dict with boole to buy or sell/
position_meets_criteria = []

for instrument in price_feed:
pos_meet_cri_dict = {}
pos_meet_cri_dict.update({'epic': instrument['epic']})

past_data_feed_numpy = np.array(instrument['pastClosePrice'])
moving_avg_200 = np.sum(past_data_feed_numpy[1:200])/200 #pandas rolling method could do this
if previous_position_meets_criteria[0]['epic'] != '':
prev_pos_elem_status = next((element['status'] for element in previous_position_meets_criteria
if element['epic'] == instrument['epic']), None)
#prev_pos_elem_status = [element.get('epic', instrument['epic']).get('status') for element in previous_position_meets_criteria]
else:
prev_pos_elem_status = ''

if instrument['currentPrice'] >= moving_avg_200 and (prev_pos_elem_status != 'true' or prev_pos_elem_status != '' ):
pos_meet_cri_dict.update({'status': 'true'})
position_meets_criteria.append(pos_meet_cri_dict)

if instrument['currentPrice'] < moving_avg_200 and ( prev_pos_elem_status != 'false' or prev_pos_elem_status != '' ):
pos_meet_cri_dict.update({'status': 'false'})
position_meets_criteria.append(pos_meet_cri_dict)

return position_meets_criteria


def get_strategy() -> StrategyOne:
return StrategyOne()
73 changes: 73 additions & 0 deletions letsberich/ig/templates/ig/auto_trade_launch_interface.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{% extends 'ig/base.html' %}
{% load crispy_forms_tags %}

{% block title %}Operate Auto Trade Tool{% endblock %}

{% block content %}
<div class="col-12 p-5">
<div>
{% if api_error %}
<div class="alert alert-danger" role="alert">
{{ api_error }}
</div>
{% endif %}
</div>
<div class="row">
<a href="{% url 'start-auto-trade' %}">
<button type="submit" class="btn btn-success">Start Auto Trade</button>
</a>
</div>
<div class="row">
<a href="{% url 'pause-auto-trade' %}">
<button type="submit" class="btn btn-success">Pause Auto Trade</button>
</a>
</div>
<div class="row">
<a href="{% url 'status-auto-trade' %}">
<button type="submit" class="btn btn-success">Strategy Status</button>
</a>
</div>
</div>
<table style="border: 1px solid black">
<thead>
<tr>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ status }}</td>
</tr>
</tbody>
</table>

<table style="border: 3px solid blue">
<thead>
<tr>
<th>Instrument</th>
<th>Epic</th>
<th>Size</th>
<th>Buy Price</th>
<th>Limit Level</th>
<th>Date</th>
<th>Deal ID (IG)</th>
<th>Deal Ref (ours)</th>

</tr>
</thead>
<tbody>
{% for transaction in transactions %}
<tr>
<td>{{ transaction.market.instrumentName }}</td>
<td>{{ transaction.market.epic }}</td>
<td>{{ transaction.position.size }}</td>
<td>{{ transaction.position.level }}</td>
<td>{{ transaction.position.limitLevel }}</td>
<td>{{ transaction.position.createdDate }}</td>
<td>{{ transaction.position.dealId }}</td>
<td>{{ transaction.position.dealReference }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
2 changes: 1 addition & 1 deletion letsberich/ig/templates/ig/ig_popular_markets.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{% if api_error %}
<div>{{ api_error }}</div>
{% endif %}
<form action="{% url 'ig-popular_markets' %}" method="post">
<form action="{% url 'ig-popular-markets' %}" method="post">
{% csrf_token %}
<button type="submit">Show Popular Markets</button>
</form>
Expand Down
5 changes: 4 additions & 1 deletion letsberich/ig/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@
name='node-navigation'
),
path('account_summary/', views.IGAccountSummary.as_view(), name='account-summary'),
path('open_position/', views.IGOpenPosition.as_view(), name='open-position')
path('open_position/', views.IGOpenPosition.as_view(), name='open-position'),
path('start_auto_trade/', views.IGAutoTradeStart.as_view(), name='start-auto-trade'),
path('pause_auto_trade/', views.IGAutoTradePause.as_view(), name='pause-auto-trade'),
path('status_auto_trade/', views.IGAutoTradeStatus.as_view(), name='status-auto-trade')
]
31 changes: 31 additions & 0 deletions letsberich/ig/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from letsberich.ig.exceptions import IGServiceError
from letsberich.ig.ig_services import get_ig_api
from letsberich.ig.strategy import get_strategy

from letsberich.ig.forms import OpenPositionForm

Expand Down Expand Up @@ -121,3 +122,33 @@ def post(self, request):
context['created_position_data'] = created_position_data

return render(request, 'ig/open_position.html', context)


class IGAutoTradeStart(generic.View):

def get(self, request):
day_strat = get_strategy()
context = {}
context['data'] = day_strat.get_status('ON')
return render(request, 'ig/auto_trade_launch_interface.html', {'transactions': context['data']['transactions'],
'status': context['data']['status']})

class IGAutoTradePause(generic.View):

def get(self, request):
context = {}
day_strat = get_strategy()
context['data'] = day_strat.get_status('OFF')
return render(request, 'ig/auto_trade_launch_interface.html', {'transactions': context['data']['transactions'],
'status': context['data']['status']})


class IGAutoTradeStatus(generic.View):
ig_api = get_ig_api()

def get(self, request):
context = {}
day_strat = get_strategy()
context['data'] = day_strat.get_status()
return render(request, 'ig/auto_trade_launch_interface.html', {'transactions': context['data']['transactions'],
'status': context['data']['status']})
4 changes: 3 additions & 1 deletion letsberich/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@
'ACCOUNT_USEFUL_DATA': '/positions',
'CREATE_POSITION': '/positions/otc',
'CONFIRM_POSITION': '/confirms/{}',
'OPEN_POSITION': '/positions/{}'
'OPEN_POSITION': '/positions/{}',
'GET_HISTORICAL_PRICES': '/prices/{}/{}/{}',
'GET_INSTRUMENT_CHARACTERISTICS': '/markets/{}'
}
}

Expand Down
Loading