Skip to content

Commit c9bf423

Browse files
committed
Stop loss distance is calculated based on market info from IG API and configurable for controlled risk accounts
1 parent f458042 commit c9bf423

3 files changed

Lines changed: 27 additions & 17 deletions

File tree

config/config.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"general" : {
33
"year": 2018,
4-
"max_account_usable": 70,
4+
"max_account_usable": 50,
55
"esma_stocks_margin_perc": 20,
66
"debug_log": false,
77
"time_zone": "Europe/London",
@@ -10,14 +10,15 @@
1010
},
1111
"ig_interface" : {
1212
"order_type": "MARKET",
13-
"order_size": 2,
13+
"order_size": 1,
1414
"order_expiry": "DFB",
1515
"order_currency": "GBP",
1616
"order_force_open": true,
1717
"use_g_stop": true,
18-
"api_key": "YOUR API KEY",
19-
"account_id": "YOUR SPREAD BETTING ACCOUNT ID",
20-
"use_demo_account": true
18+
"api_key": "",
19+
"account_id": "",
20+
"use_demo_account": true,
21+
"controlled_risk": true
2122
},
2223
"strategies": {
2324
"faig": {

scripts/IGInterface.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,12 @@ def get_open_positions(self):
100100
logging.debug("Current position summary: {}".format(positionMap))
101101
return positionMap
102102

103-
def get_market_price(self, epic_id):
103+
104+
def get_market_info(self, epic_id):
104105
base_url = self.apiBaseURL + '/markets/' + str(epic_id)
105106
auth_r = requests.get(base_url, headers=self.authenticated_headers)
106-
d = json.loads(auth_r.text)
107-
bid = None
108-
offer = None
109-
if 'snapshot' in d:
110-
bid = d['snapshot']['bid']
111-
offer = d['snapshot']['offer']
112-
logging.debug("Market prices of {} are: bid={}, offer={}".format(epic_id,bid,offer))
113-
return bid, offer
107+
return json.loads(auth_r.text)
108+
114109

115110
def get_prices(self, epic_id, resolution, range):
116111
# Price resolution (MINUTE, MINUTE_2, MINUTE_3, MINUTE_5,

scripts/Strategies/SimpleMACD.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def __init__(self, config):
1818

1919
def read_configuration(self, config):
2020
self.interval = config['strategies']['simple_macd']['interval']
21+
self.controlledRisk = config['ig_interface']['controlled_risk']
2122
self.timeout = 1
2223

2324
# TODO make as generic as possible and move in Strategy class
@@ -81,13 +82,17 @@ def find_trade_signal(self, broker, epic_id):
8182
limit = None
8283
stop = None
8384

85+
# Collect data from the broker interface
8486
prices = broker.get_prices(epic_id, self.interval, 30)
85-
current_bid, current_offer = broker.get_market_price(epic_id)
87+
market = broker.get_market_info(epic_id)
8688

87-
if prices is None or current_bid is None or current_offer is None or 'prices' not in prices:
89+
# Safety checks before processing the epic
90+
if (prices is None or 'prices' not in prices
91+
or market is None or 'markets' in market):
8892
logging.warn('Strategy can`t process {}'.format(epic_id))
8993
return TradeDirection.NONE, None, None
9094

95+
# Create a list of close prices
9196
data = []
9297
prevBid = 0
9398
for p in prices['prices']:
@@ -97,6 +102,7 @@ def find_trade_signal(self, broker, epic_id):
97102
data.append(p['closePrice']['bid'])
98103
prevBid = p['closePrice']['bid']
99104

105+
# Calculate the MACD indicator and find signals where macd cross its sma(9) average
100106
px = pd.DataFrame({'close': data})
101107
px['26_ema'] = pd.DataFrame.ewm(px['close'], span=26).mean()
102108
px['12_ema'] = pd.DataFrame.ewm(px['close'], span=12).mean()
@@ -106,16 +112,24 @@ def find_trade_signal(self, broker, epic_id):
106112
px['positions'][9:]=np.where(px['macd'][9:]>=px['macd_signal'][9:],1,0)
107113
px['signals']=px['positions'].diff()
108114

115+
# Identify the trade direction looking at the last signal
109116
if len(px['signals']) > 0 and px['signals'].iloc[-1] > 0:
110117
tradeDirection = TradeDirection.BUY
111118
elif len(px['signals']) > 0 and px['signals'].iloc[-1] < 0:
112119
tradeDirection = TradeDirection.SELL
113120
else:
114121
tradeDirection = TradeDirection.NONE
115122

123+
# Extract market data to calculate stop and limit values
124+
key = 'minNormalStopOrLimitDistance'
125+
if self.controlledRisk:
126+
key = 'minControlledRiskStopDistance'
127+
stop_perc = market['dealingRules'][key]['value'] + 1 # +1 to avoid rejection
128+
current_bid = market['snapshot']['bid']
116129
limit = current_bid + percentage_of(10, current_bid)
117-
stop = current_bid - percentage_of(10, current_bid)
130+
stop = current_bid - percentage_of(stop_perc, current_bid)
118131

132+
# Log only tradable epics
119133
if tradeDirection is not TradeDirection.NONE:
120134
logging.info("SimpleMACD says: {} {}".format(tradeDirection, epic_id))
121135

0 commit comments

Comments
 (0)